Skip to content

Commit 0165187

Browse files
committed
feat: osmosis swap messages
1 parent dedbae7 commit 0165187

File tree

13 files changed

+535
-1
lines changed

13 files changed

+535
-1
lines changed

apps/namadillo/src/App/AppRoutes.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { ProposalAndVote } from "./Governance/ProposalAndVote";
2020
import { SubmitVote } from "./Governance/SubmitVote";
2121
import { ViewJson } from "./Governance/ViewJson";
2222
import { IbcShieldAll } from "./Ibc/IbcShieldAll";
23+
import { OsmosisSwap } from "./Ibc/OsmosisSwap";
2324
import { MoveAssets } from "./Layout/MoveAssets";
2425
import { routes } from "./routes";
2526
import { Advanced } from "./Settings/Advanced";
@@ -110,6 +111,13 @@ export const MainRoutes = (): JSX.Element => {
110111
</>
111112
)}
112113

114+
{/* Swapping */}
115+
{true && (
116+
<>
117+
<Route path={routes.swap} element={<OsmosisSwap />} />
118+
</>
119+
)}
120+
113121
{/* Transaction History */}
114122
{(features.namTransfersEnabled || features.ibcTransfersEnabled) && (
115123
<Route>
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { defaultAccountAtom } from "atoms/accounts";
2+
import { createOsmosisSwapTxAtom } from "atoms/transfer/atoms";
3+
import BigNumber from "bignumber.js";
4+
import invariant from "invariant";
5+
import { useAtomValue } from "jotai";
6+
import { useEffect } from "react";
7+
8+
export const OsmosisSwap: React.FC = () => {
9+
const { mutateAsync: performOsmosisSwap } = useAtomValue(
10+
createOsmosisSwapTxAtom
11+
);
12+
13+
const account = useAtomValue(defaultAccountAtom);
14+
15+
useEffect(() => {
16+
const handleOsmosisSwap = async (): Promise<void> => {
17+
invariant(account.data, "No transparent account is found");
18+
19+
const transfer = {
20+
amountInBaseDenom: BigNumber(10),
21+
// osmosis channel
22+
channelId: "channel-7",
23+
portId: "transfer",
24+
token: "tnam1p4zuqqd94csj6zv8n0jxylz9kex4vdsgvg3uglw9",
25+
source:
26+
"03d5935721000000803fe3d7a9c42c483e5c3840c13eb7dadec2e420f850a769342a2786b58c86215d1fd1ca29f99d94bf033533a30b3461a2dacc4d0f968a080a2a335a085525d18b29f5e59e281a297c35d62299ff82a1525ae327862aca92d01faceebe375af12530bf9eff49e6f90c2eb554db591b1fc30694c716635f0bd2050682d6eeb6a2c5438dd7725495fb866d76db12de4e44ad9be424af57d12c8c19a6dc8664825d8701000000000000000000000000000000000000000000000000000000000000000001f3d7b291d734e35aefd38601bc947778d3adefc9ee2defd8d745fe124e850d0b",
27+
receiver:
28+
"osmo1ewll8h7up3g0ca2z9ur9e6dv6an64snxg5k8tmzylg6uprkyhgzszjgdzr",
29+
gasSpendingKey:
30+
"03d5935721000000803fe3d7a9c42c483e5c3840c13eb7dadec2e420f850a769342a2786b58c86215d1fd1ca29f99d94bf033533a30b3461a2dacc4d0f968a080a2a335a085525d18b29f5e59e281a297c35d62299ff82a1525ae327862aca92d01faceebe375af12530bf9eff49e6f90c2eb554db591b1fc30694c716635f0bd2050682d6eeb6a2c5438dd7725495fb866d76db12de4e44ad9be424af57d12c8c19a6dc8664825d8701000000000000000000000000000000000000000000000000000000000000000001f3d7b291d734e35aefd38601bc947778d3adefc9ee2defd8d745fe124e850d0b",
31+
refundTarget: "tnam1qz24lx0mz3y9uahfkxvnurnynqe898actvzyy22y",
32+
};
33+
const params = {
34+
transfer,
35+
// We want to receive TIA
36+
outputDenom: "transfer/channel-10/utia",
37+
recipient: "tnam1qqshvryx9pngpk7mmzpzkjkm6klelgusuvmkc0uz",
38+
overflow: "tnam1qqshvryx9pngpk7mmzpzkjkm6klelgusuvmkc0uz",
39+
slippage: { 0: "1" },
40+
localRecoveryAddr: "osmo1sy99khct7t7wth2wus5xpwkcf6n8p0lvrnwelh",
41+
route: [
42+
{
43+
poolId: "1464",
44+
tokenOutDenom:
45+
"ibc/498A0751C798A0D9A389AA3691123DADA57DAA4FE165D5C75894505B876BA6E4",
46+
},
47+
{
48+
poolId: "1247",
49+
tokenOutDenom:
50+
"ibc/D79E7D83AB399BFFF93433E54FAA480C191248FC556924A2A8351AE2638B3877",
51+
},
52+
],
53+
osmosisRestRpc: "https://osmosis-rest.publicnode.com",
54+
};
55+
56+
try {
57+
await performOsmosisSwap({
58+
signer: {
59+
publicKey: account.data.publicKey!,
60+
address: account.data.address!,
61+
},
62+
account: account.data,
63+
params: [params],
64+
gasConfig: {
65+
gasLimit: BigNumber(75000),
66+
gasPriceInMinDenom: BigNumber(0.000001),
67+
gasToken: "tnam1p4zuqqd94csj6zv8n0jxylz9kex4vdsgvg3uglw9",
68+
},
69+
});
70+
} catch (error) {
71+
console.error("Error performing Osmosis swap:", error);
72+
}
73+
};
74+
75+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
76+
(window as any).osmosisSwap = handleOsmosisSwap;
77+
}, [account.data]);
78+
79+
return <></>;
80+
};

apps/namadillo/src/App/routes.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ export const routes = {
3232
transaction: "/transaction/:hash",
3333
receive: "/receive",
3434

35+
// Swap
36+
swap: "/swap",
37+
3538
// Settings
3639
settings: "/settings",
3740
settingsAdvanced: "/settings/advanced",

apps/namadillo/src/atoms/transfer/atoms.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {
22
DatedViewingKey,
33
IbcTransferMsgValue,
4+
OsmosisSwapMsgValue,
45
ShieldedTransferMsgValue,
56
ShieldingTransferMsgValue,
67
TransparentTransferMsgValue,
@@ -21,6 +22,7 @@ import { atomWithMutation } from "jotai-tanstack-query";
2122
import { BuildTxAtomParams } from "types";
2223
import {
2324
createIbcTx,
25+
createOsmosisSwapTx,
2426
createShieldedTransferTx,
2527
createShieldingTransferTx,
2628
createTransparentTransferTx,
@@ -236,3 +238,38 @@ export const createIbcTxAtom = atomWithMutation((get) => {
236238
},
237239
};
238240
});
241+
242+
export const createOsmosisSwapTxAtom = atomWithMutation((get) => {
243+
const account = get(defaultAccountAtom);
244+
const chain = get(chainAtom);
245+
const rpcUrl = get(rpcUrlAtom);
246+
247+
return {
248+
enabled: account.isSuccess && chain.isSuccess,
249+
mutationKey: ["create-ibc-tx"],
250+
mutationFn: async ({
251+
params,
252+
gasConfig,
253+
account,
254+
signer,
255+
memo,
256+
}: BuildTxAtomParams<OsmosisSwapMsgValue>) => {
257+
invariant(
258+
signer,
259+
"We always expect signer to be passed explicitly, because we might also need to unshield"
260+
);
261+
invariant(account, "No account");
262+
invariant(params.length !== 0, "No params");
263+
264+
return await createOsmosisSwapTx(
265+
chain.data!,
266+
account,
267+
params,
268+
gasConfig,
269+
rpcUrl,
270+
signer?.publicKey,
271+
memo
272+
);
273+
},
274+
};
275+
});

apps/namadillo/src/atoms/transfer/services.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import {
55
GenDisposableSignerResponse,
66
IbcTransferMsgValue,
77
IbcTransferProps,
8+
OsmosisSwapMsgValue,
9+
OsmosisSwapProps,
810
ShieldedTransferMsgValue,
911
ShieldedTransferProps,
1012
ShieldingTransferMsgValue,
@@ -22,6 +24,7 @@ import { Address, ChainSettings, GasConfig } from "types";
2224
import { getSdkInstance } from "utils/sdk";
2325
import {
2426
IbcTransfer,
27+
OsmosisSwap,
2528
Shield,
2629
ShieldedTransfer,
2730
Unshield,
@@ -309,3 +312,52 @@ export const createIbcTx = async (
309312
},
310313
});
311314
};
315+
316+
export const createOsmosisSwapTx = async (
317+
chain: ChainSettings,
318+
account: Account,
319+
props: OsmosisSwapProps[],
320+
gasConfig: GasConfig,
321+
rpcUrl: string,
322+
signerPublicKey: string,
323+
memo?: string
324+
): Promise<EncodedTxData<OsmosisSwapProps>> => {
325+
let bparams: BparamsMsgValue[] | undefined;
326+
if (account.type === AccountType.Ledger) {
327+
const sdk = await getSdkInstance();
328+
const ledger = await sdk.initLedger();
329+
bparams = await ledger.getBparams();
330+
ledger.closeTransport();
331+
}
332+
const { transfer } = props[0];
333+
334+
return await workerBuildTxPair({
335+
rpcUrl,
336+
token: transfer.token,
337+
buildTxFn: async (workerLink) => {
338+
const msgValue = new OsmosisSwapMsgValue({
339+
...props[0],
340+
transfer: {
341+
...transfer,
342+
gasSpendingKey: transfer.gasSpendingKey,
343+
bparams,
344+
},
345+
});
346+
const msg: OsmosisSwap = {
347+
type: "osmosis-swap",
348+
payload: {
349+
account: {
350+
...account,
351+
publicKey: signerPublicKey,
352+
},
353+
gasConfig,
354+
props: [msgValue],
355+
chain,
356+
memo,
357+
},
358+
};
359+
360+
return (await workerLink.osmosisSwap(msg)).payload;
361+
},
362+
});
363+
};

apps/namadillo/src/workers/MaspTxMessages.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {
22
Account,
33
IbcTransferMsgValue,
4+
OsmosisSwapMsgValue,
45
ShieldedTransferMsgValue,
56
ShieldingTransferMsgValue,
67
TxResponseMsgValue,
@@ -76,6 +77,19 @@ export type IbcTransferDone = WebWorkerMessage<
7677
EncodedTxData<IbcTransferMsgValue>
7778
>;
7879

80+
type OsmosisSwapPayload = {
81+
account: Account;
82+
gasConfig: GasConfig;
83+
props: OsmosisSwapMsgValue[];
84+
chain: ChainSettings;
85+
memo?: string;
86+
};
87+
export type OsmosisSwap = WebWorkerMessage<"osmosis-swap", OsmosisSwapPayload>;
88+
export type OsmosisSwapDone = WebWorkerMessage<
89+
"osmosis-swap-done",
90+
EncodedTxData<OsmosisSwapMsgValue>
91+
>;
92+
7993
type GenerateIbcShieldingMemoPayload = {
8094
target: string;
8195
token: string;

apps/namadillo/src/workers/MaspTxWorker.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { initMulticore } from "@namada/sdk/inline-init";
22
import { getSdk, Sdk } from "@namada/sdk/web";
33
import {
44
IbcTransferMsgValue,
5+
OsmosisSwapMsgValue,
56
ShieldedTransferMsgValue,
67
ShieldingTransferMsgValue,
78
TxResponseMsgValue,
@@ -18,6 +19,8 @@ import {
1819
IbcTransferDone,
1920
Init,
2021
InitDone,
22+
OsmosisSwap,
23+
OsmosisSwapDone,
2124
Shield,
2225
ShieldDone,
2326
ShieldedTransfer,
@@ -89,6 +92,17 @@ export class Worker {
8992
};
9093
}
9194

95+
async osmosisSwap(m: OsmosisSwap): Promise<OsmosisSwapDone> {
96+
if (!this.sdk) {
97+
throw new Error("SDK is not initialized");
98+
}
99+
100+
return {
101+
type: "osmosis-swap-done",
102+
payload: await osmosisSwap(this.sdk, m.payload),
103+
};
104+
}
105+
92106
async broadcast(m: Broadcast): Promise<BroadcastDone> {
93107
if (!this.sdk) {
94108
throw new Error("SDK is not initialized");
@@ -189,6 +203,27 @@ async function ibcTransfer(
189203
return encodedTxData;
190204
}
191205

206+
async function osmosisSwap(
207+
sdk: Sdk,
208+
payload: OsmosisSwap["payload"]
209+
): Promise<EncodedTxData<OsmosisSwapMsgValue>> {
210+
const { account, gasConfig, chain, props } = payload;
211+
212+
await sdk.masp.loadMaspParams("", chain.chainId);
213+
const encodedTxData = await buildTx<OsmosisSwapMsgValue>(
214+
sdk,
215+
account,
216+
gasConfig,
217+
chain,
218+
props,
219+
sdk.tx.buildOsmosisSwap,
220+
undefined,
221+
false
222+
);
223+
224+
return encodedTxData;
225+
}
226+
192227
async function generateIbcShieldingMemo(
193228
sdk: Sdk,
194229
payload: GenerateIbcShieldingMemo["payload"]
@@ -240,6 +275,8 @@ export const registerTransferHandlers = (): void => {
240275
registerBNTransferHandler<UnshieldDone>("unshield-done");
241276
registerBNTransferHandler<IbcTransfer>("ibc-transfer");
242277
registerBNTransferHandler<IbcTransferDone>("ibc-transfer-done");
278+
registerBNTransferHandler<OsmosisSwap>("osmosis-swap");
279+
registerBNTransferHandler<OsmosisSwapDone>("osmosis-swap-done");
243280
registerBNTransferHandler<Unshield>("unshield");
244281
registerBNTransferHandler<GenerateIbcShieldingMemo>(
245282
"generate-ibc-shielding-memo"

packages/sdk/src/tx/tx.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import {
1515
IbcTransferMsgValue,
1616
IbcTransferProps,
1717
Message,
18+
OsmosisSwapMsgValue,
19+
OsmosisSwapProps,
1820
RedelegateMsgValue,
1921
RedelegateProps,
2022
RevealPkMsgValue,
@@ -280,6 +282,31 @@ export class Tx {
280282
return deserialize(Buffer.from(serializedTx), TxMsgValue);
281283
}
282284

285+
/**
286+
* Build Osmosis Swap Tx
287+
* `osmosisSwapProps.transfer.amountInBaseDenom` is the amount in the **base** denom
288+
* e.g. the value of 1 NAM should be BigNumber(1_000_000), not BigNumber(1).
289+
* @async
290+
* @param wrapperTxProps - properties of the transaction
291+
* @param osmosisSwapProps - properties of the osmosis swap tx
292+
* @returns promise that resolves to an TxMsgValue
293+
*/
294+
async buildOsmosisSwap(
295+
wrapperTxProps: WrapperTxProps,
296+
osmosisSwapProps: OsmosisSwapProps
297+
): Promise<TxMsgValue> {
298+
const ibcTransferMsg = new Message<OsmosisSwapProps>();
299+
const encodedWrapperArgs = this.encodeTxArgs(wrapperTxProps);
300+
const encodedIbcTransfer = ibcTransferMsg.encode(
301+
new OsmosisSwapMsgValue(osmosisSwapProps)
302+
);
303+
const serializedTx = await this.sdk.build_osmosis_swap(
304+
encodedIbcTransfer,
305+
encodedWrapperArgs
306+
);
307+
return deserialize(Buffer.from(serializedTx), TxMsgValue);
308+
}
309+
283310
/**
284311
* Build Ethereum Bridge Transfer Tx
285312
* @async

0 commit comments

Comments
 (0)