From 9025ed819f22e4c0a6d734a639314e50743b54dd Mon Sep 17 00:00:00 2001 From: trung2891 Date: Tue, 1 Oct 2024 16:38:42 +0700 Subject: [PATCH 01/29] chore: add osor sdk --- packages/universal-swap/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/universal-swap/package.json b/packages/universal-swap/package.json index a2807e11..f1eb3672 100644 --- a/packages/universal-swap/package.json +++ b/packages/universal-swap/package.json @@ -17,7 +17,8 @@ "ethers": "^5.0.15", "tronweb": "5.3.2", "@oraichain/common": "^1.0.3", - "ts-protoc-gen": "^0.15.0" + "ts-protoc-gen": "^0.15.0", + "@oraichain/osor-api-contracts-sdk": "^1.0.0" }, "devDependencies": { "cosmjs-types": "0.8.0", From def2bd134eadf44a42786697351c810ede3e8f7d Mon Sep 17 00:00:00 2001 From: trung2891 Date: Thu, 3 Oct 2024 08:43:48 +0700 Subject: [PATCH 02/29] feat: build msg swap on oraichain --- packages/universal-swap/package.json | 2 +- packages/universal-swap/src/msg/common.ts | 35 ++ packages/universal-swap/src/msg/index.ts | 3 + packages/universal-swap/src/msg/oraichain.ts | 496 +++++++++++++++++++ packages/universal-swap/src/msg/types.ts | 31 ++ packages/universal-swap/src/types.ts | 9 +- tsconfig.json | 2 +- yarn.lock | 10 + 8 files changed, 585 insertions(+), 3 deletions(-) create mode 100644 packages/universal-swap/src/msg/common.ts create mode 100644 packages/universal-swap/src/msg/index.ts create mode 100644 packages/universal-swap/src/msg/oraichain.ts create mode 100644 packages/universal-swap/src/msg/types.ts diff --git a/packages/universal-swap/package.json b/packages/universal-swap/package.json index f1eb3672..11711b71 100644 --- a/packages/universal-swap/package.json +++ b/packages/universal-swap/package.json @@ -18,7 +18,7 @@ "tronweb": "5.3.2", "@oraichain/common": "^1.0.3", "ts-protoc-gen": "^0.15.0", - "@oraichain/osor-api-contracts-sdk": "^1.0.0" + "@oraichain/osor-api-contracts-sdk": "^1.0.2" }, "devDependencies": { "cosmjs-types": "0.8.0", diff --git a/packages/universal-swap/src/msg/common.ts b/packages/universal-swap/src/msg/common.ts new file mode 100644 index 00000000..10a0c914 --- /dev/null +++ b/packages/universal-swap/src/msg/common.ts @@ -0,0 +1,35 @@ +import { ActionType, Path } from "../types"; +import { SwapOperation } from "@oraichain/osor-api-contracts-sdk/src/types"; +import bech32 from "bech32"; + +/** + * Validates that the path only contains one action bridge, and this action must be the last of the path. + * @param path The path to be validated + */ +export const validatePath = (path: Path) => { + if (path.actions.length == 0) { + throw new Error("Require at least one action"); + } + const numBridgeActions = path.actions.filter((action) => action.type === ActionType.Bridge).length; + + if (numBridgeActions > 1) { + throw new Error("Only one Bridge action is allowed in the path"); + } + + if (numBridgeActions == 1 && path.actions[path.actions.length - 1].type !== ActionType.Bridge) { + throw new Error("Bridge action must be the last in the path"); + } +}; + +// This function checks whether the given address is a CW20 token address +export const isCw20Token = (address: string): boolean => { + try { + // Attempt to decode the address using the bech32 library + bech32.decode(address); + // If decoding is successful, return true as it is a valid CW20 token address + return true; + } catch (err) { + // If an error occurs during decoding, return false indicating that it is not a valid CW20 token address + return false; + } +}; diff --git a/packages/universal-swap/src/msg/index.ts b/packages/universal-swap/src/msg/index.ts new file mode 100644 index 00000000..7a83ef58 --- /dev/null +++ b/packages/universal-swap/src/msg/index.ts @@ -0,0 +1,3 @@ +export * from "./common"; +export * from "./oraichain"; +export * from "./types"; diff --git a/packages/universal-swap/src/msg/oraichain.ts b/packages/universal-swap/src/msg/oraichain.ts new file mode 100644 index 00000000..8ef2327b --- /dev/null +++ b/packages/universal-swap/src/msg/oraichain.ts @@ -0,0 +1,496 @@ +import { BridgeMsgInfo, SwapMsgInfo } from "./types"; +import { ActionType, Path, Wasm } from "../types"; +import { SwapOperation } from "@oraichain/osor-api-contracts-sdk/src/types"; +import { Swap, Action, ExecuteMsg } from "@oraichain/osor-api-contracts-sdk/src/EntryPoint.types"; +import { isCw20Token, validatePath } from "./common"; +import { + calculateTimeoutTimestamp, + CONVERTER_CONTRACT, + generateError, + IBC_TRANSFER_TIMEOUT, + ibcInfos, + INJECTIVE_ORAICHAIN_DENOM, + isEthAddress, + NetworkChainId +} from "@oraichain/oraidex-common"; +import { toBinary } from "@cosmjs/cosmwasm-stargate"; +import { Memo, Memo_PostAction, Memo_UserSwap } from "../proto/universal_swap_memo"; +import { EncodeObject } from "@cosmjs/proto-signing"; +import { MsgExecuteContract } from "cosmjs-types/cosmwasm/wasm/v1/tx"; +import { TransferBackMsg } from "@oraichain/common-contracts-sdk/build/CwIcs20Latest.types"; +import { toUtf8 } from "@cosmjs/encoding"; + +export class OraichainMsg { + SWAP_VENUE_NAME = "oraidex"; + ENTRY_POINT_CONTRACT = ""; + + constructor( + protected path: Path, + protected minimumReceive: string, + protected receiver: string, + protected currentChainAddress: string, + protected memo: string = undefined, + protected destPrefix: string = undefined, + protected obridgeAddress: string = undefined + ) { + // check chainId = "Oraichain" + if (path.chainId !== "Oraichain") { + throw generateError("This path must be on Oraichain"); + } + // validate path + validatePath(path); + } + /** + * Converts the given input and output tokens to a pool ID using the converter contract in the Oraichain ecosystem. + * @param tokenIn The input token to be converted + * @param tokenOut The output token after conversion + * @returns The pool ID generated based on the provided input and output tokens + */ + pasreConverterMsgToPoolId = (tokenIn: string, tokenOut: string) => { + // In Oraichain, conversion from native token to CW20 token always occurs + // TODO: Query the converter contract to determine the appropriate conversion method + + if (isCw20Token(tokenIn)) { + // Convert in reverse + return toBinary({ + contract: CONVERTER_CONTRACT, + msg: toBinary({ + convert_reverse: { + from: { + native_token: { + denom: tokenOut + } + } + } + }) + }); + } else { + // Convert normally + return toBinary({ + contract: CONVERTER_CONTRACT, + msg: toBinary({ + convert: {} + }) + }); + } + }; + + /** + * Function to build msg swap on Oraichain + */ + getSwapAndBridgeInfo(): [SwapOperation[], BridgeMsgInfo] { + let swapOps: SwapOperation[] = []; + let bridgeInfo: BridgeMsgInfo; + + // build swap operations. + // we have 2 main cases: + // - swap + convert + // - bridge (IBC bridge or Ibc wasm bridge) + for (let action of this.path.actions) { + switch (action.type) { + case ActionType.Swap: { + let denomIn = action.tokenIn; + action.swapInfo.forEach((swapInfo) => { + swapOps.push({ + denom_in: denomIn, + denom_out: swapInfo.tokenOut, + pool: swapInfo.poolId + }); + denomIn = swapInfo.tokenOut; + }); + break; + } + case ActionType.Convert: { + swapOps.push({ + denom_in: action.tokenIn, + denom_out: action.tokenOut, + pool: this.pasreConverterMsgToPoolId(action.tokenIn, action.tokenOut) + }); + break; + } + case ActionType.Bridge: { + bridgeInfo = { + sourceChannel: action.bridgeInfo.channel, + sourcePort: action.bridgeInfo.port, + memo: this.memo, + receiver: this.receiver, + timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + fromToken: action.tokenIn, + toToken: action.tokenOut, + fromChain: this.path.chainId as NetworkChainId, + toChain: this.path.tokenOutChainId as NetworkChainId + }; + break; + } + } + } + + return [swapOps, bridgeInfo]; + } + + // function to generate postAction to ibc wasm + getProtoForPostAction(bridgeInfo?: BridgeMsgInfo): Memo_PostAction { + // case 1: transfer to receiver + if (!bridgeInfo) { + return { + transferMsg: { + toAddress: this.receiver + } + }; + } + + // case 2: ibc transfer + if (bridgeInfo.sourcePort == "transfer") { + return { + ibcTransferMsg: { + sourceChannel: bridgeInfo.sourceChannel, + sourcePort: bridgeInfo.sourcePort, + receiver: bridgeInfo.receiver, + memo: bridgeInfo.memo, + recoverAddress: this.currentChainAddress + } + }; + } + + // case 3: ibc wasm transfer + + if (bridgeInfo.sourcePort.startsWith("wasm")) { + // handle noble & evm case + let prefix = ""; + let isBridgeToEvm = isEthAddress(this.receiver); + if (isBridgeToEvm) { + if (!this.destPrefix || !this.obridgeAddress) + throw generateError("Missing prefix os Obridge address for bridge to EVM"); + prefix = this.destPrefix; + } + + return { + ibcWasmTransferMsg: { + localChannelId: bridgeInfo.sourceChannel, + remoteAddress: prefix + isBridgeToEvm ? this.obridgeAddress : this.receiver, + remoteDenom: prefix + bridgeInfo.toToken, + memo: isBridgeToEvm ? prefix + this.receiver : this.memo + } + }; + } + + throw generateError("Missing postAction for ibc wasm memo"); + } + + /** + * Function to generate memo msg for swap through ibc wasm after bridge + * @returns + */ + genMemoForIbcWasm(): string { + let [swapOps, bridgeInfo] = this.getSwapAndBridgeInfo(); + let userSwap: Memo_UserSwap; + if (swapOps.length) { + userSwap = { + swapVenueName: this.SWAP_VENUE_NAME, + swapExactAssetIn: { + operations: swapOps.map((operation) => ({ + poolId: operation.pool, + denomIn: operation.denom_in, + denomOut: operation.denom_out + })) + } + }; + } + let memo: Memo = { + userSwap, + minimumReceive: this.minimumReceive, + timeoutTimestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + postSwapAction: this.getProtoForPostAction(bridgeInfo), + recoveryAddr: this.currentChainAddress + }; + const encodedMemo = Memo.encode(memo).finish(); + return Buffer.from(encodedMemo).toString("base64"); + } + + getPostAction(bridgeInfo?: BridgeMsgInfo): Action { + // case 1: transfer to receiver + if (!bridgeInfo) { + return { + transfer: { + to_address: this.receiver + } + }; + } + + // case 2: ibc transfer + if (bridgeInfo.sourcePort == "transfer") { + return { + ibc_transfer: { + ibc_info: { + source_channel: bridgeInfo.sourceChannel, + receiver: bridgeInfo.receiver, + memo: bridgeInfo.memo, + recover_address: this.currentChainAddress + } + } + }; + } + + // case 3: ibc wasm transfer + if (bridgeInfo.sourcePort.startsWith("wasm")) { + // handle noble & evm case + let prefix = ""; + let isBridgeToEvm = isEthAddress(this.receiver); + if (isBridgeToEvm) { + if (!this.destPrefix || !this.obridgeAddress) + throw generateError("Missing prefix os Obridge address for bridge to EVM"); + prefix = this.destPrefix; + } + + return { + ibc_wasm_transfer: { + ibc_wasm_info: { + local_channel_id: bridgeInfo.sourceChannel, + remote_address: prefix + isBridgeToEvm ? this.obridgeAddress : this.receiver, + remote_denom: prefix + bridgeInfo.toToken, + memo: isBridgeToEvm ? prefix + this.receiver : this.memo + } + } + }; + } + + throw generateError("Missing postAction for postAction in Oraichain"); + } + /** + * Function to generate memo for action on oraichain as middleware + */ + genMemoAsMiddleware(): string { + let [swapOps, bridgeInfo] = this.getSwapAndBridgeInfo(); + + // we have 2 cases: + // - first case: only ibc transfer (build IBC forward middleware msg) + // - second case: swap + action => swap through osor entry point + + if (swapOps.length == 0) { + // bridge only + if (bridgeInfo.sourcePort != "transfer") { + throw generateError("Error on generate memo as middleware: Only support ibc bridge"); + } + + // ibc bridge + return JSON.stringify({ + forward: { + receiver: this.receiver, + port: bridgeInfo.sourcePort, + channel: bridgeInfo.sourceChannel, + timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + retries: 2, + next: this.memo + } + }); + } + + let tokenOutOfSwap = swapOps.slice[swapOps.length - 1].denom_out; + let min_asset = isCw20Token(tokenOutOfSwap) + ? { + cw20: { + amount: this.minimumReceive, + address: tokenOutOfSwap + } + } + : { + native: { + amount: this.minimumReceive, + denom: tokenOutOfSwap + } + }; + + let msg: ExecuteMsg = { + swap_and_action: { + user_swap: { + swap_exact_asset_in: { + swap_venue_name: this.SWAP_VENUE_NAME, + operations: swapOps + } + }, + min_asset: min_asset, + timeout_timestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + post_swap_action: this.getPostAction(bridgeInfo), + affiliates: [] + } + }; + + return JSON.stringify({ + contract: this.ENTRY_POINT_CONTRACT, + msg + }); + } + + /** + * Function to generate execute msg on Oraichain + */ + + genExecuteMsg(): EncodeObject { + let [swapOps, bridgeInfo] = this.getSwapAndBridgeInfo(); + + // we have 3 cases: + // - case 1: ibc transfer + // - case 2; ibc wasm transfer + // - case 3: swap and action + + if (swapOps.length == 0) { + // ibc transfer + if (bridgeInfo.sourcePort == "transfer") { + return { + typeUrl: "/ibc.applications.transfer.v1.MsgTransfer", + value: { + sourcePort: bridgeInfo.sourcePort, + sourceChannel: bridgeInfo.sourceChannel, + receiver: this.receiver, + token: { + amount: this.path.tokenInAmount, + denom: this.path.tokenIn + }, + sender: this.currentChainAddress, + memo: this.memo, + timeoutTimestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT) + } + }; + } + + // ibc wasm transfer + if (bridgeInfo.sourcePort.startsWith("wasm")) { + let prefix = ""; + let isBridgeToEvm = isEthAddress(this.receiver); + if (isBridgeToEvm) { + if (!this.destPrefix || !this.obridgeAddress) + throw generateError("Missing prefix os Obridge address for bridge to EVM"); + prefix = this.destPrefix; + } + + const ibcWasmContractAddress = bridgeInfo.sourceChannel.split(".")[1]; + if (!ibcWasmContractAddress) + throw generateError("IBC Wasm source port is invalid. Cannot transfer to the destination chain"); + + const msg: TransferBackMsg = { + local_channel_id: bridgeInfo.sourceChannel, + remote_address: prefix + isBridgeToEvm ? this.obridgeAddress : this.receiver, + remote_denom: prefix + bridgeInfo.toToken, + timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), // FIXME: should we use nano with an u64 type? -> probably quite big for a u64 + memo: this.memo + }; + + // if asset info is native => send native way, else send cw20 way + if (isCw20Token(bridgeInfo.fromToken)) { + return { + typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract", + value: MsgExecuteContract.fromPartial({ + sender: this.currentChainAddress, + contract: bridgeInfo.fromToken, + msg: toUtf8( + JSON.stringify({ + send: { + send: { + contract: ibcWasmContractAddress, + amount: this.path.tokenInAmount, + msg: toBinary(msg) + } + } + }) + ), + funds: [] + }) + }; + } + // native token + return { + typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract", + value: MsgExecuteContract.fromPartial({ + sender: this.currentChainAddress, + contract: ibcWasmContractAddress, + msg: toUtf8( + JSON.stringify({ + transfer_to_remote: { + msg + } + }) + ), + funds: [ + { + amount: bridgeInfo.fromToken, + denom: this.path.tokenInAmount + } + ] + }) + }; + } + + throw generateError("Error on generate executeMsg on Oraichain: Only support ibc or ibc wasm bridge"); + } + + let tokenOutOfSwap = swapOps.slice[swapOps.length - 1].denom_out; + let min_asset = isCw20Token(tokenOutOfSwap) + ? { + cw20: { + amount: this.minimumReceive, + address: tokenOutOfSwap + } + } + : { + native: { + amount: this.minimumReceive, + denom: tokenOutOfSwap + } + }; + + // swap and action + let msg: ExecuteMsg = { + swap_and_action: { + affiliates: [], + min_asset, + post_swap_action: this.getPostAction(bridgeInfo), + timeout_timestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + user_swap: { + swap_exact_asset_in: { swap_venue_name: this.SWAP_VENUE_NAME, operations: swapOps } + } + } + }; + + // if asset info is native => send native way, else send cw20 way + if (isCw20Token(bridgeInfo.fromToken)) { + return { + typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract", + value: MsgExecuteContract.fromPartial({ + sender: this.currentChainAddress, + contract: bridgeInfo.fromToken, + msg: toUtf8( + JSON.stringify({ + send: { + send: { + contract: this.ENTRY_POINT_CONTRACT, + amount: this.path.tokenInAmount, + msg: toBinary(msg) + } + } + }) + ), + funds: [] + }) + }; + } + // native token + return { + typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract", + value: MsgExecuteContract.fromPartial({ + sender: this.currentChainAddress, + contract: this.ENTRY_POINT_CONTRACT, + msg: toUtf8( + JSON.stringify({ + msg + }) + ), + funds: [ + { + amount: bridgeInfo.fromToken, + denom: this.path.tokenInAmount + } + ] + }) + }; + } +} diff --git a/packages/universal-swap/src/msg/types.ts b/packages/universal-swap/src/msg/types.ts new file mode 100644 index 00000000..8d6944b5 --- /dev/null +++ b/packages/universal-swap/src/msg/types.ts @@ -0,0 +1,31 @@ +import { Coin } from "@cosmjs/amino"; +import { JsonObject } from "@cosmjs/cosmwasm-stargate"; +import { EncodeObject } from "@cosmjs/proto-signing"; +import { NetworkChainId } from "@oraichain/oraidex-common"; + +export interface SwapMsgInfo { + contract: string; + msg: JsonObject; + funds: Coin[]; +} + +export interface BridgeMsgInfo { + sourceChannel: string; + sourcePort: string; + receiver: string; + timeout: number; + memo?: string; + prefix?: string; // use for bridge to evm + fromToken: string; + fromChain: NetworkChainId; + toToken: string; + toChain: NetworkChainId; +} + +export enum PostActionType { + None, + Transfer, + ContractCall, + IbcTransfer, + IbcWasmTransfer +} diff --git a/packages/universal-swap/src/types.ts b/packages/universal-swap/src/types.ts index 4aa41da3..302b8c41 100644 --- a/packages/universal-swap/src/types.ts +++ b/packages/universal-swap/src/types.ts @@ -1,3 +1,4 @@ +import { Coin } from "@cosmjs/amino"; import { AmountDetails, CosmosWallet, EvmWallet, NetworkChainId, TokenItemType } from "@oraichain/oraidex-common"; import { SwapOperation, Uint128 } from "@oraichain/oraidex-contracts-sdk"; import { Affiliate } from "@oraichain/oraidex-contracts-sdk/build/OraiswapMixedRouter.types"; @@ -10,6 +11,12 @@ export type UniversalSwapType = | "cosmos-to-others" | "smart-router"; +export enum ActionType { + Bridge = "Bridge", + Swap = "Swap", + Convert = "Convert" +} + export enum SwapDirection { From, To @@ -206,7 +213,7 @@ export interface Route { paths: Path[]; } -interface Path { +export interface Path { chainId: string; tokenIn: string; tokenInAmount: string; diff --git a/tsconfig.json b/tsconfig.json index 2ecb8479..4d7c5ed5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,6 +12,6 @@ "outDir": "dist", "typeRoots": ["node_modules/@types"], "allowJs": true, - "esModuleInterop": true, + "esModuleInterop": true } } diff --git a/yarn.lock b/yarn.lock index 8612f5c9..7468ade6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3673,6 +3673,16 @@ ethers "^5.0.15" tronweb "5.3.0" +"@oraichain/osor-api-contracts-sdk@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@oraichain/osor-api-contracts-sdk/-/osor-api-contracts-sdk-1.0.2.tgz#8aec43cfdb2df93cfa50538779f8eee550b94738" + integrity sha512-s4ebEgAN34rfnpz3l6qW75F9uKu3gwQTvUNPy+aYtpbkpjanY2Kke1lSt8QS0805Vb47ZfbIumsWLWzsi/4afg== + dependencies: + "@cosmjs/amino" "0.31.3" + "@cosmjs/cosmwasm-stargate" "0.31.3" + "@cosmjs/crypto" "0.31.3" + "@cosmjs/proto-signing" "0.31.3" + "@oraichain/wasm-json-toolkit@^1.0.24": version "1.0.24" resolved "https://registry.yarnpkg.com/@oraichain/wasm-json-toolkit/-/wasm-json-toolkit-1.0.24.tgz#e9a431560e8e946fbb7ec257e5f13d9320ffd23a" From 34e0ebbfd0d55e87a717ec2b288faf70dbd9240f Mon Sep 17 00:00:00 2001 From: trung2891 Date: Thu, 3 Oct 2024 09:00:34 +0700 Subject: [PATCH 03/29] feat: build swap & action on osmosis and cosmos ecosystem --- packages/universal-swap/src/msg/common.ts | 14 +- packages/universal-swap/src/msg/cosmos.ts | 133 +++++++++ packages/universal-swap/src/msg/oraichain.ts | 8 +- packages/universal-swap/src/msg/osmosis.ts | 286 +++++++++++++++++++ 4 files changed, 434 insertions(+), 7 deletions(-) create mode 100644 packages/universal-swap/src/msg/cosmos.ts create mode 100644 packages/universal-swap/src/msg/osmosis.ts diff --git a/packages/universal-swap/src/msg/common.ts b/packages/universal-swap/src/msg/common.ts index 10a0c914..42543916 100644 --- a/packages/universal-swap/src/msg/common.ts +++ b/packages/universal-swap/src/msg/common.ts @@ -1,23 +1,27 @@ import { ActionType, Path } from "../types"; -import { SwapOperation } from "@oraichain/osor-api-contracts-sdk/src/types"; import bech32 from "bech32"; +import { generateError } from "@oraichain/oraidex-common"; /** * Validates that the path only contains one action bridge, and this action must be the last of the path. * @param path The path to be validated */ -export const validatePath = (path: Path) => { +export const validatePath = (path: Path, hasSwap: boolean = true) => { if (path.actions.length == 0) { - throw new Error("Require at least one action"); + throw generateError("Require at least one action"); } const numBridgeActions = path.actions.filter((action) => action.type === ActionType.Bridge).length; if (numBridgeActions > 1) { - throw new Error("Only one Bridge action is allowed in the path"); + throw generateError("Only one Bridge action is allowed in the path"); } if (numBridgeActions == 1 && path.actions[path.actions.length - 1].type !== ActionType.Bridge) { - throw new Error("Bridge action must be the last in the path"); + throw generateError("Bridge action must be the last in the path"); + } + + if (!hasSwap && path.actions.some((action) => action.type == ActionType.Convert || action.type == ActionType.Swap)) { + throw generateError("Don't support swap action"); } }; diff --git a/packages/universal-swap/src/msg/cosmos.ts b/packages/universal-swap/src/msg/cosmos.ts new file mode 100644 index 00000000..49e39525 --- /dev/null +++ b/packages/universal-swap/src/msg/cosmos.ts @@ -0,0 +1,133 @@ +import { BridgeMsgInfo } from "./types"; +import { ActionType, Path } from "../types"; +import { SwapOperation } from "@oraichain/osor-api-contracts-sdk/src/types"; +import { Action, ExecuteMsg } from "@oraichain/osor-api-contracts-sdk/src/EntryPoint.types"; +import { isCw20Token, validatePath } from "./common"; +import { + calculateTimeoutTimestamp, + generateError, + IBC_TRANSFER_TIMEOUT, + NetworkChainId +} from "@oraichain/oraidex-common"; +import { toBinary } from "@cosmjs/cosmwasm-stargate"; +import { EncodeObject } from "@cosmjs/proto-signing"; +import { MsgExecuteContract } from "cosmjs-types/cosmwasm/wasm/v1/tx"; +import { toUtf8 } from "@cosmjs/encoding"; + +export class OraichainMsg { + SWAP_VENUE_NAME = "osmosis-poolmanager"; + ENTRY_POINT_CONTRACT = ""; + + constructor( + protected path: Path, + protected minimumReceive: string, + protected receiver: string, + protected currentChainAddress: string, + protected memo: string = undefined + ) { + // validate path + validatePath(path); + } + + /** + * Function to build msg swap on Oraichain + */ + getBridgeInfo(): BridgeMsgInfo { + let bridgeInfo: BridgeMsgInfo; + + for (let action of this.path.actions) { + switch (action.type) { + case ActionType.Bridge: { + bridgeInfo = { + sourceChannel: action.bridgeInfo.channel, + sourcePort: action.bridgeInfo.port, + memo: this.memo, + receiver: this.receiver, + timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + fromToken: action.tokenIn, + toToken: action.tokenOut, + fromChain: this.path.chainId as NetworkChainId, + toChain: this.path.tokenOutChainId as NetworkChainId + }; + break; + } + default: + throw generateError(`Only support bridge on ${this.path.chainId}`); + } + } + + return bridgeInfo; + } + + getPostAction(bridgeInfo?: BridgeMsgInfo): Action { + // case 1: transfer to receiver + if (!bridgeInfo) { + return { + transfer: { + to_address: this.receiver + } + }; + } + + // case 2: ibc transfer + if (bridgeInfo.sourcePort == "transfer") { + return { + ibc_transfer: { + ibc_info: { + source_channel: bridgeInfo.sourceChannel, + receiver: bridgeInfo.receiver, + memo: bridgeInfo.memo, + recover_address: this.currentChainAddress + } + } + }; + } + + throw generateError(`Missing postAction for universalSwap on ${this.path.chainId}`); + } + /** + * Function to generate memo for action on oraichain as middleware + */ + genMemoAsMiddleware(): string { + let bridgeInfo = this.getBridgeInfo(); + // ibc bridge + return JSON.stringify({ + forward: { + receiver: this.receiver, + port: bridgeInfo.sourcePort, + channel: bridgeInfo.sourceChannel, + timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + retries: 2, + next: this.memo + } + }); + } + + /** + * Function to generate execute msg on Osmosis + */ + + genExecuteMsg(): EncodeObject { + let bridgeInfo = this.getBridgeInfo(); + if (bridgeInfo.sourcePort != "transfer") { + throw generateError("Error on generate executeMsg on Oraichain: Only support ibc transfer"); + } + // ibc transfer + + return { + typeUrl: "/ibc.applications.transfer.v1.MsgTransfer", + value: { + sourcePort: bridgeInfo.sourcePort, + sourceChannel: bridgeInfo.sourceChannel, + receiver: this.receiver, + token: { + amount: this.path.tokenInAmount, + denom: this.path.tokenIn + }, + sender: this.currentChainAddress, + memo: this.memo, + timeoutTimestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT) + } + }; + } +} diff --git a/packages/universal-swap/src/msg/oraichain.ts b/packages/universal-swap/src/msg/oraichain.ts index 8ef2327b..33070756 100644 --- a/packages/universal-swap/src/msg/oraichain.ts +++ b/packages/universal-swap/src/msg/oraichain.ts @@ -122,6 +122,8 @@ export class OraichainMsg { }; break; } + default: + throw generateError("Only support swap + convert + bride on Oraichain"); } } @@ -316,8 +318,10 @@ export class OraichainMsg { }; return JSON.stringify({ - contract: this.ENTRY_POINT_CONTRACT, - msg + wasm: { + contract: this.ENTRY_POINT_CONTRACT, + msg + } }); } diff --git a/packages/universal-swap/src/msg/osmosis.ts b/packages/universal-swap/src/msg/osmosis.ts new file mode 100644 index 00000000..d6d9ef77 --- /dev/null +++ b/packages/universal-swap/src/msg/osmosis.ts @@ -0,0 +1,286 @@ +import { BridgeMsgInfo, SwapMsgInfo } from "./types"; +import { ActionType, Path, Wasm } from "../types"; +import { SwapOperation } from "@oraichain/osor-api-contracts-sdk/src/types"; +import { Swap, Action, ExecuteMsg } from "@oraichain/osor-api-contracts-sdk/src/EntryPoint.types"; +import { isCw20Token, validatePath } from "./common"; +import { + calculateTimeoutTimestamp, + CONVERTER_CONTRACT, + generateError, + IBC_TRANSFER_TIMEOUT, + ibcInfos, + INJECTIVE_ORAICHAIN_DENOM, + isEthAddress, + NetworkChainId +} from "@oraichain/oraidex-common"; +import { toBinary } from "@cosmjs/cosmwasm-stargate"; +import { Memo, Memo_PostAction, Memo_UserSwap } from "../proto/universal_swap_memo"; +import { EncodeObject } from "@cosmjs/proto-signing"; +import { MsgExecuteContract } from "cosmjs-types/cosmwasm/wasm/v1/tx"; +import { TransferBackMsg } from "@oraichain/common-contracts-sdk/build/CwIcs20Latest.types"; +import { toUtf8 } from "@cosmjs/encoding"; + +export class OraichainMsg { + SWAP_VENUE_NAME = "osmosis-poolmanager"; + ENTRY_POINT_CONTRACT = ""; + + constructor( + protected path: Path, + protected minimumReceive: string, + protected receiver: string, + protected currentChainAddress: string, + protected memo: string = undefined + ) { + // check chainId = "osmosis-1" + if (path.chainId !== "osmosis-1") { + throw generateError("This path must be on Osmosis"); + } + // validate path + validatePath(path); + } + + /** + * Function to build msg swap on Oraichain + */ + getSwapAndBridgeInfo(): [SwapOperation[], BridgeMsgInfo] { + let swapOps: SwapOperation[] = []; + let bridgeInfo: BridgeMsgInfo; + + // build swap operations. + // we have 2 main cases: + // - swap + // - IBC BRIDGE + for (let action of this.path.actions) { + switch (action.type) { + case ActionType.Swap: { + let denomIn = action.tokenIn; + action.swapInfo.forEach((swapInfo) => { + swapOps.push({ + denom_in: denomIn, + denom_out: swapInfo.tokenOut, + pool: swapInfo.poolId + }); + denomIn = swapInfo.tokenOut; + }); + break; + } + case ActionType.Bridge: { + bridgeInfo = { + sourceChannel: action.bridgeInfo.channel, + sourcePort: action.bridgeInfo.port, + memo: this.memo, + receiver: this.receiver, + timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + fromToken: action.tokenIn, + toToken: action.tokenOut, + fromChain: this.path.chainId as NetworkChainId, + toChain: this.path.tokenOutChainId as NetworkChainId + }; + break; + } + default: + throw generateError("Only support swap + bride on Osmosis"); + } + } + + return [swapOps, bridgeInfo]; + } + + getPostAction(bridgeInfo?: BridgeMsgInfo): Action { + // case 1: transfer to receiver + if (!bridgeInfo) { + return { + transfer: { + to_address: this.receiver + } + }; + } + + // case 2: ibc transfer + if (bridgeInfo.sourcePort == "transfer") { + return { + ibc_transfer: { + ibc_info: { + source_channel: bridgeInfo.sourceChannel, + receiver: bridgeInfo.receiver, + memo: bridgeInfo.memo, + recover_address: this.currentChainAddress + } + } + }; + } + + throw generateError("Missing postAction for universalSwap on Oraichain"); + } + /** + * Function to generate memo for action on oraichain as middleware + */ + genMemoAsMiddleware(): string { + let [swapOps, bridgeInfo] = this.getSwapAndBridgeInfo(); + + // we have 2 cases: + // - first case: only ibc transfer (build IBC forward middleware msg) + // - second case: swap + action => swap through osor entry point + + if (swapOps.length == 0) { + // bridge only + if (bridgeInfo.sourcePort != "transfer") { + throw generateError("Error on generate memo as middleware: Only support ibc bridge"); + } + + // ibc bridge + return JSON.stringify({ + forward: { + receiver: this.receiver, + port: bridgeInfo.sourcePort, + channel: bridgeInfo.sourceChannel, + timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + retries: 2, + next: this.memo + } + }); + } + + let tokenOutOfSwap = swapOps.slice[swapOps.length - 1].denom_out; + let min_asset = isCw20Token(tokenOutOfSwap) + ? { + cw20: { + amount: this.minimumReceive, + address: tokenOutOfSwap + } + } + : { + native: { + amount: this.minimumReceive, + denom: tokenOutOfSwap + } + }; + + let msg: ExecuteMsg = { + swap_and_action: { + user_swap: { + swap_exact_asset_in: { + swap_venue_name: this.SWAP_VENUE_NAME, + operations: swapOps + } + }, + min_asset: min_asset, + timeout_timestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + post_swap_action: this.getPostAction(bridgeInfo), + affiliates: [] + } + }; + + return JSON.stringify({ + wasm: { + contract: this.ENTRY_POINT_CONTRACT, + msg + } + }); + } + + /** + * Function to generate execute msg on Osmosis + */ + + genExecuteMsg(): EncodeObject { + let [swapOps, bridgeInfo] = this.getSwapAndBridgeInfo(); + + // we have 2 cases: + // - case 1: ibc transfer + // - case 2: swap and action + + if (swapOps.length == 0) { + // ibc transfer + if (bridgeInfo.sourcePort == "transfer") { + return { + typeUrl: "/ibc.applications.transfer.v1.MsgTransfer", + value: { + sourcePort: bridgeInfo.sourcePort, + sourceChannel: bridgeInfo.sourceChannel, + receiver: this.receiver, + token: { + amount: this.path.tokenInAmount, + denom: this.path.tokenIn + }, + sender: this.currentChainAddress, + memo: this.memo, + timeoutTimestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT) + } + }; + } + + throw generateError("Error on generate executeMsg on Oraichain: Only support ibc transfer"); + } + + let tokenOutOfSwap = swapOps.slice[swapOps.length - 1].denom_out; + let min_asset = isCw20Token(tokenOutOfSwap) + ? { + cw20: { + amount: this.minimumReceive, + address: tokenOutOfSwap + } + } + : { + native: { + amount: this.minimumReceive, + denom: tokenOutOfSwap + } + }; + + // swap and action + let msg: ExecuteMsg = { + swap_and_action: { + affiliates: [], + min_asset, + post_swap_action: this.getPostAction(bridgeInfo), + timeout_timestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + user_swap: { + swap_exact_asset_in: { swap_venue_name: this.SWAP_VENUE_NAME, operations: swapOps } + } + } + }; + + // if asset info is native => send native way, else send cw20 way + if (isCw20Token(bridgeInfo.fromToken)) { + return { + typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract", + value: MsgExecuteContract.fromPartial({ + sender: this.currentChainAddress, + contract: bridgeInfo.fromToken, + msg: toUtf8( + JSON.stringify({ + send: { + send: { + contract: this.ENTRY_POINT_CONTRACT, + amount: this.path.tokenInAmount, + msg: toBinary(msg) + } + } + }) + ), + funds: [] + }) + }; + } + // native token + return { + typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract", + value: MsgExecuteContract.fromPartial({ + sender: this.currentChainAddress, + contract: this.ENTRY_POINT_CONTRACT, + msg: toUtf8( + JSON.stringify({ + msg + }) + ), + funds: [ + { + amount: bridgeInfo.fromToken, + denom: this.path.tokenInAmount + } + ] + }) + }; + } +} From 7172104bcde59bd1aed2977054ba424915197f62 Mon Sep 17 00:00:00 2001 From: trung2891 Date: Thu, 3 Oct 2024 14:08:50 +0700 Subject: [PATCH 04/29] chore: refactor --- packages/universal-swap/src/msg/cosmos.ts | 14 ++++---------- packages/universal-swap/src/msg/oraichain.ts | 8 +++----- packages/universal-swap/src/msg/osmosis.ts | 12 +++--------- 3 files changed, 10 insertions(+), 24 deletions(-) diff --git a/packages/universal-swap/src/msg/cosmos.ts b/packages/universal-swap/src/msg/cosmos.ts index 49e39525..0282f86b 100644 --- a/packages/universal-swap/src/msg/cosmos.ts +++ b/packages/universal-swap/src/msg/cosmos.ts @@ -1,23 +1,17 @@ import { BridgeMsgInfo } from "./types"; import { ActionType, Path } from "../types"; -import { SwapOperation } from "@oraichain/osor-api-contracts-sdk/src/types"; -import { Action, ExecuteMsg } from "@oraichain/osor-api-contracts-sdk/src/EntryPoint.types"; -import { isCw20Token, validatePath } from "./common"; +import { Action } from "@oraichain/osor-api-contracts-sdk/src/EntryPoint.types"; +import { validatePath } from "./common"; import { calculateTimeoutTimestamp, generateError, IBC_TRANSFER_TIMEOUT, NetworkChainId } from "@oraichain/oraidex-common"; -import { toBinary } from "@cosmjs/cosmwasm-stargate"; -import { EncodeObject } from "@cosmjs/proto-signing"; -import { MsgExecuteContract } from "cosmjs-types/cosmwasm/wasm/v1/tx"; -import { toUtf8 } from "@cosmjs/encoding"; -export class OraichainMsg { - SWAP_VENUE_NAME = "osmosis-poolmanager"; - ENTRY_POINT_CONTRACT = ""; +import { EncodeObject } from "@cosmjs/proto-signing"; +export class CosmosMsg { constructor( protected path: Path, protected minimumReceive: string, diff --git a/packages/universal-swap/src/msg/oraichain.ts b/packages/universal-swap/src/msg/oraichain.ts index 33070756..d19d4bf0 100644 --- a/packages/universal-swap/src/msg/oraichain.ts +++ b/packages/universal-swap/src/msg/oraichain.ts @@ -1,15 +1,13 @@ -import { BridgeMsgInfo, SwapMsgInfo } from "./types"; -import { ActionType, Path, Wasm } from "../types"; +import { BridgeMsgInfo } from "./types"; +import { ActionType, Path } from "../types"; import { SwapOperation } from "@oraichain/osor-api-contracts-sdk/src/types"; -import { Swap, Action, ExecuteMsg } from "@oraichain/osor-api-contracts-sdk/src/EntryPoint.types"; +import { Action, ExecuteMsg } from "@oraichain/osor-api-contracts-sdk/src/EntryPoint.types"; import { isCw20Token, validatePath } from "./common"; import { calculateTimeoutTimestamp, CONVERTER_CONTRACT, generateError, IBC_TRANSFER_TIMEOUT, - ibcInfos, - INJECTIVE_ORAICHAIN_DENOM, isEthAddress, NetworkChainId } from "@oraichain/oraidex-common"; diff --git a/packages/universal-swap/src/msg/osmosis.ts b/packages/universal-swap/src/msg/osmosis.ts index d6d9ef77..ce88e7fd 100644 --- a/packages/universal-swap/src/msg/osmosis.ts +++ b/packages/universal-swap/src/msg/osmosis.ts @@ -1,26 +1,20 @@ -import { BridgeMsgInfo, SwapMsgInfo } from "./types"; -import { ActionType, Path, Wasm } from "../types"; +import { BridgeMsgInfo } from "./types"; +import { ActionType, Path } from "../types"; import { SwapOperation } from "@oraichain/osor-api-contracts-sdk/src/types"; import { Swap, Action, ExecuteMsg } from "@oraichain/osor-api-contracts-sdk/src/EntryPoint.types"; import { isCw20Token, validatePath } from "./common"; import { calculateTimeoutTimestamp, - CONVERTER_CONTRACT, generateError, IBC_TRANSFER_TIMEOUT, - ibcInfos, - INJECTIVE_ORAICHAIN_DENOM, - isEthAddress, NetworkChainId } from "@oraichain/oraidex-common"; import { toBinary } from "@cosmjs/cosmwasm-stargate"; -import { Memo, Memo_PostAction, Memo_UserSwap } from "../proto/universal_swap_memo"; import { EncodeObject } from "@cosmjs/proto-signing"; import { MsgExecuteContract } from "cosmjs-types/cosmwasm/wasm/v1/tx"; -import { TransferBackMsg } from "@oraichain/common-contracts-sdk/build/CwIcs20Latest.types"; import { toUtf8 } from "@cosmjs/encoding"; -export class OraichainMsg { +export class OsmosisMsg { SWAP_VENUE_NAME = "osmosis-poolmanager"; ENTRY_POINT_CONTRACT = ""; From f45d9300e22ee93e3f77e90607cb3448d3dff51e Mon Sep 17 00:00:00 2001 From: trung2891 Date: Thu, 3 Oct 2024 14:41:15 +0700 Subject: [PATCH 05/29] fix: get last operations --- packages/universal-swap/src/msg/oraichain.ts | 4 ++-- packages/universal-swap/src/msg/osmosis.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/universal-swap/src/msg/oraichain.ts b/packages/universal-swap/src/msg/oraichain.ts index d19d4bf0..c6d2ae30 100644 --- a/packages/universal-swap/src/msg/oraichain.ts +++ b/packages/universal-swap/src/msg/oraichain.ts @@ -285,7 +285,7 @@ export class OraichainMsg { }); } - let tokenOutOfSwap = swapOps.slice[swapOps.length - 1].denom_out; + let tokenOutOfSwap = swapOps[swapOps.length - 1].denom_out; let min_asset = isCw20Token(tokenOutOfSwap) ? { cw20: { @@ -425,7 +425,7 @@ export class OraichainMsg { throw generateError("Error on generate executeMsg on Oraichain: Only support ibc or ibc wasm bridge"); } - let tokenOutOfSwap = swapOps.slice[swapOps.length - 1].denom_out; + let tokenOutOfSwap = swapOps[swapOps.length - 1].denom_out; let min_asset = isCw20Token(tokenOutOfSwap) ? { cw20: { diff --git a/packages/universal-swap/src/msg/osmosis.ts b/packages/universal-swap/src/msg/osmosis.ts index ce88e7fd..3f8745e4 100644 --- a/packages/universal-swap/src/msg/osmosis.ts +++ b/packages/universal-swap/src/msg/osmosis.ts @@ -135,7 +135,7 @@ export class OsmosisMsg { }); } - let tokenOutOfSwap = swapOps.slice[swapOps.length - 1].denom_out; + let tokenOutOfSwap = swapOps[swapOps.length - 1].denom_out; let min_asset = isCw20Token(tokenOutOfSwap) ? { cw20: { @@ -207,7 +207,7 @@ export class OsmosisMsg { throw generateError("Error on generate executeMsg on Oraichain: Only support ibc transfer"); } - let tokenOutOfSwap = swapOps.slice[swapOps.length - 1].denom_out; + let tokenOutOfSwap = swapOps[swapOps.length - 1].denom_out; let min_asset = isCw20Token(tokenOutOfSwap) ? { cw20: { From 967e5e0fa6daf6559802afd2a4dc9e4b0df4144a Mon Sep 17 00:00:00 2001 From: trung2891 Date: Thu, 3 Oct 2024 14:43:10 +0700 Subject: [PATCH 06/29] chore: update enrty point contract for oraichain & osmosis --- packages/universal-swap/src/msg/oraichain.ts | 2 +- packages/universal-swap/src/msg/osmosis.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/universal-swap/src/msg/oraichain.ts b/packages/universal-swap/src/msg/oraichain.ts index c6d2ae30..1f76cfc1 100644 --- a/packages/universal-swap/src/msg/oraichain.ts +++ b/packages/universal-swap/src/msg/oraichain.ts @@ -20,7 +20,7 @@ import { toUtf8 } from "@cosmjs/encoding"; export class OraichainMsg { SWAP_VENUE_NAME = "oraidex"; - ENTRY_POINT_CONTRACT = ""; + ENTRY_POINT_CONTRACT = "orai1yglsm0u2x3xmct9kq3lxa654cshaxj9j5d9rw5enemkkkdjgzj7sr3gwt0"; constructor( protected path: Path, diff --git a/packages/universal-swap/src/msg/osmosis.ts b/packages/universal-swap/src/msg/osmosis.ts index 3f8745e4..8ee89378 100644 --- a/packages/universal-swap/src/msg/osmosis.ts +++ b/packages/universal-swap/src/msg/osmosis.ts @@ -16,7 +16,7 @@ import { toUtf8 } from "@cosmjs/encoding"; export class OsmosisMsg { SWAP_VENUE_NAME = "osmosis-poolmanager"; - ENTRY_POINT_CONTRACT = ""; + ENTRY_POINT_CONTRACT = "osmo1h3jkejkcpthl45xrrm5geed3eq75p5rgfce9taufkwfr89k63muqweu2y7"; constructor( protected path: Path, From 9d5ed54dd452ed34e310855575caa67d895b6d08 Mon Sep 17 00:00:00 2001 From: trung2891 Date: Thu, 3 Oct 2024 15:43:13 +0700 Subject: [PATCH 07/29] fix: wrong first asset on build msg --- packages/universal-swap/src/msg/oraichain.ts | 21 +++++++++----------- packages/universal-swap/src/msg/osmosis.ts | 8 ++++---- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/packages/universal-swap/src/msg/oraichain.ts b/packages/universal-swap/src/msg/oraichain.ts index 1f76cfc1..1218c97b 100644 --- a/packages/universal-swap/src/msg/oraichain.ts +++ b/packages/universal-swap/src/msg/oraichain.ts @@ -20,7 +20,7 @@ import { toUtf8 } from "@cosmjs/encoding"; export class OraichainMsg { SWAP_VENUE_NAME = "oraidex"; - ENTRY_POINT_CONTRACT = "orai1yglsm0u2x3xmct9kq3lxa654cshaxj9j5d9rw5enemkkkdjgzj7sr3gwt0"; + ENTRY_POINT_CONTRACT = "orai13mgxn93pjvd7eermj4ghet8assxdqttxugwk25rasuuqq2g5nczq43eesn"; // FIXME: use mainnet constructor( protected path: Path, @@ -414,8 +414,8 @@ export class OraichainMsg { ), funds: [ { - amount: bridgeInfo.fromToken, - denom: this.path.tokenInAmount + denom: this.path.tokenIn, + amount: this.path.tokenInAmount } ] }) @@ -454,12 +454,12 @@ export class OraichainMsg { }; // if asset info is native => send native way, else send cw20 way - if (isCw20Token(bridgeInfo.fromToken)) { + if (isCw20Token(this.path.tokenIn)) { return { typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract", value: MsgExecuteContract.fromPartial({ sender: this.currentChainAddress, - contract: bridgeInfo.fromToken, + contract: this.path.tokenIn, msg: toUtf8( JSON.stringify({ send: { @@ -476,20 +476,17 @@ export class OraichainMsg { }; } // native token + return { typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract", value: MsgExecuteContract.fromPartial({ sender: this.currentChainAddress, contract: this.ENTRY_POINT_CONTRACT, - msg: toUtf8( - JSON.stringify({ - msg - }) - ), + msg: toUtf8(JSON.stringify(msg)), funds: [ { - amount: bridgeInfo.fromToken, - denom: this.path.tokenInAmount + denom: this.path.tokenIn, + amount: this.path.tokenInAmount } ] }) diff --git a/packages/universal-swap/src/msg/osmosis.ts b/packages/universal-swap/src/msg/osmosis.ts index 8ee89378..cd639ec5 100644 --- a/packages/universal-swap/src/msg/osmosis.ts +++ b/packages/universal-swap/src/msg/osmosis.ts @@ -236,12 +236,12 @@ export class OsmosisMsg { }; // if asset info is native => send native way, else send cw20 way - if (isCw20Token(bridgeInfo.fromToken)) { + if (isCw20Token(this.path.tokenIn)) { return { typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract", value: MsgExecuteContract.fromPartial({ sender: this.currentChainAddress, - contract: bridgeInfo.fromToken, + contract: this.path.tokenIn, msg: toUtf8( JSON.stringify({ send: { @@ -270,8 +270,8 @@ export class OsmosisMsg { ), funds: [ { - amount: bridgeInfo.fromToken, - denom: this.path.tokenInAmount + denom: this.path.tokenIn, + amount: this.path.tokenInAmount } ] }) From 91da4518a34fe3474aa899598e75dfb789c8c63e Mon Sep 17 00:00:00 2001 From: trung2891 Date: Thu, 3 Oct 2024 16:23:25 +0700 Subject: [PATCH 08/29] fix: memo not undefined --- packages/universal-swap/src/msg/cosmos.ts | 2 +- packages/universal-swap/src/msg/oraichain.ts | 2 +- packages/universal-swap/src/msg/osmosis.ts | 8 ++------ 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/packages/universal-swap/src/msg/cosmos.ts b/packages/universal-swap/src/msg/cosmos.ts index 0282f86b..ec11c9ec 100644 --- a/packages/universal-swap/src/msg/cosmos.ts +++ b/packages/universal-swap/src/msg/cosmos.ts @@ -17,7 +17,7 @@ export class CosmosMsg { protected minimumReceive: string, protected receiver: string, protected currentChainAddress: string, - protected memo: string = undefined + protected memo: string = "" ) { // validate path validatePath(path); diff --git a/packages/universal-swap/src/msg/oraichain.ts b/packages/universal-swap/src/msg/oraichain.ts index 1218c97b..a78d7133 100644 --- a/packages/universal-swap/src/msg/oraichain.ts +++ b/packages/universal-swap/src/msg/oraichain.ts @@ -27,7 +27,7 @@ export class OraichainMsg { protected minimumReceive: string, protected receiver: string, protected currentChainAddress: string, - protected memo: string = undefined, + protected memo: string = "", protected destPrefix: string = undefined, protected obridgeAddress: string = undefined ) { diff --git a/packages/universal-swap/src/msg/osmosis.ts b/packages/universal-swap/src/msg/osmosis.ts index cd639ec5..f80eb685 100644 --- a/packages/universal-swap/src/msg/osmosis.ts +++ b/packages/universal-swap/src/msg/osmosis.ts @@ -23,7 +23,7 @@ export class OsmosisMsg { protected minimumReceive: string, protected receiver: string, protected currentChainAddress: string, - protected memo: string = undefined + protected memo: string = "" ) { // check chainId = "osmosis-1" if (path.chainId !== "osmosis-1") { @@ -263,11 +263,7 @@ export class OsmosisMsg { value: MsgExecuteContract.fromPartial({ sender: this.currentChainAddress, contract: this.ENTRY_POINT_CONTRACT, - msg: toUtf8( - JSON.stringify({ - msg - }) - ), + msg: toUtf8(JSON.stringify(msg)), funds: [ { denom: this.path.tokenIn, From 42bdc0604203e6a8f9418a467404fbd4c8da2665 Mon Sep 17 00:00:00 2001 From: trung2891 Date: Thu, 3 Oct 2024 21:15:26 +0700 Subject: [PATCH 09/29] chore: return receiver on build middleware msg --- .../src/msg/{ => chains}/cosmos.ts | 31 ++++++------ .../universal-swap/src/msg/chains/index.ts | 3 ++ .../src/msg/{ => chains}/oraichain.ts | 48 +++++++++++-------- .../src/msg/{ => chains}/osmosis.ts | 46 ++++++++++-------- packages/universal-swap/src/msg/index.ts | 2 +- packages/universal-swap/src/msg/types.ts | 5 ++ 6 files changed, 79 insertions(+), 56 deletions(-) rename packages/universal-swap/src/msg/{ => chains}/cosmos.ts (84%) create mode 100644 packages/universal-swap/src/msg/chains/index.ts rename packages/universal-swap/src/msg/{ => chains}/oraichain.ts (94%) rename packages/universal-swap/src/msg/{ => chains}/osmosis.ts (90%) diff --git a/packages/universal-swap/src/msg/cosmos.ts b/packages/universal-swap/src/msg/chains/cosmos.ts similarity index 84% rename from packages/universal-swap/src/msg/cosmos.ts rename to packages/universal-swap/src/msg/chains/cosmos.ts index ec11c9ec..f7e56c15 100644 --- a/packages/universal-swap/src/msg/cosmos.ts +++ b/packages/universal-swap/src/msg/chains/cosmos.ts @@ -1,7 +1,7 @@ -import { BridgeMsgInfo } from "./types"; -import { ActionType, Path } from "../types"; +import { BridgeMsgInfo, MiddleWareResponse } from "../types"; +import { ActionType, Path } from "../../types"; import { Action } from "@oraichain/osor-api-contracts-sdk/src/EntryPoint.types"; -import { validatePath } from "./common"; +import { validatePath } from "../common"; import { calculateTimeoutTimestamp, generateError, @@ -82,19 +82,22 @@ export class CosmosMsg { /** * Function to generate memo for action on oraichain as middleware */ - genMemoAsMiddleware(): string { + genMemoAsMiddleware(): MiddleWareResponse { let bridgeInfo = this.getBridgeInfo(); // ibc bridge - return JSON.stringify({ - forward: { - receiver: this.receiver, - port: bridgeInfo.sourcePort, - channel: bridgeInfo.sourceChannel, - timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), - retries: 2, - next: this.memo - } - }); + return { + receiver: this.currentChainAddress, + memo: JSON.stringify({ + forward: { + receiver: this.receiver, + port: bridgeInfo.sourcePort, + channel: bridgeInfo.sourceChannel, + timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + retries: 2, + next: this.memo + } + }) + }; } /** diff --git a/packages/universal-swap/src/msg/chains/index.ts b/packages/universal-swap/src/msg/chains/index.ts new file mode 100644 index 00000000..553f9edb --- /dev/null +++ b/packages/universal-swap/src/msg/chains/index.ts @@ -0,0 +1,3 @@ +export * from "./cosmos"; +export * from "./osmosis"; +export * from "./oraichain"; diff --git a/packages/universal-swap/src/msg/oraichain.ts b/packages/universal-swap/src/msg/chains/oraichain.ts similarity index 94% rename from packages/universal-swap/src/msg/oraichain.ts rename to packages/universal-swap/src/msg/chains/oraichain.ts index a78d7133..d2eaae28 100644 --- a/packages/universal-swap/src/msg/oraichain.ts +++ b/packages/universal-swap/src/msg/chains/oraichain.ts @@ -1,8 +1,8 @@ -import { BridgeMsgInfo } from "./types"; -import { ActionType, Path } from "../types"; +import { BridgeMsgInfo, MiddleWareResponse } from "../types"; +import { ActionType, Path } from "../../types"; import { SwapOperation } from "@oraichain/osor-api-contracts-sdk/src/types"; import { Action, ExecuteMsg } from "@oraichain/osor-api-contracts-sdk/src/EntryPoint.types"; -import { isCw20Token, validatePath } from "./common"; +import { isCw20Token, validatePath } from "../common"; import { calculateTimeoutTimestamp, CONVERTER_CONTRACT, @@ -12,7 +12,7 @@ import { NetworkChainId } from "@oraichain/oraidex-common"; import { toBinary } from "@cosmjs/cosmwasm-stargate"; -import { Memo, Memo_PostAction, Memo_UserSwap } from "../proto/universal_swap_memo"; +import { Memo, Memo_PostAction, Memo_UserSwap } from "../../proto/universal_swap_memo"; import { EncodeObject } from "@cosmjs/proto-signing"; import { MsgExecuteContract } from "cosmjs-types/cosmwasm/wasm/v1/tx"; import { TransferBackMsg } from "@oraichain/common-contracts-sdk/build/CwIcs20Latest.types"; @@ -259,7 +259,7 @@ export class OraichainMsg { /** * Function to generate memo for action on oraichain as middleware */ - genMemoAsMiddleware(): string { + genMemoAsMiddleware(): MiddleWareResponse { let [swapOps, bridgeInfo] = this.getSwapAndBridgeInfo(); // we have 2 cases: @@ -273,16 +273,19 @@ export class OraichainMsg { } // ibc bridge - return JSON.stringify({ - forward: { - receiver: this.receiver, - port: bridgeInfo.sourcePort, - channel: bridgeInfo.sourceChannel, - timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), - retries: 2, - next: this.memo - } - }); + return { + receiver: this.currentChainAddress, + memo: JSON.stringify({ + forward: { + receiver: this.receiver, + port: bridgeInfo.sourcePort, + channel: bridgeInfo.sourceChannel, + timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + retries: 2, + next: this.memo + } + }) + }; } let tokenOutOfSwap = swapOps[swapOps.length - 1].denom_out; @@ -315,12 +318,15 @@ export class OraichainMsg { } }; - return JSON.stringify({ - wasm: { - contract: this.ENTRY_POINT_CONTRACT, - msg - } - }); + return { + receiver: this.ENTRY_POINT_CONTRACT, + memo: JSON.stringify({ + wasm: { + contract: this.ENTRY_POINT_CONTRACT, + msg + } + }) + }; } /** diff --git a/packages/universal-swap/src/msg/osmosis.ts b/packages/universal-swap/src/msg/chains/osmosis.ts similarity index 90% rename from packages/universal-swap/src/msg/osmosis.ts rename to packages/universal-swap/src/msg/chains/osmosis.ts index f80eb685..a95913f4 100644 --- a/packages/universal-swap/src/msg/osmosis.ts +++ b/packages/universal-swap/src/msg/chains/osmosis.ts @@ -1,8 +1,8 @@ -import { BridgeMsgInfo } from "./types"; -import { ActionType, Path } from "../types"; +import { BridgeMsgInfo, MiddleWareResponse } from "../types"; +import { ActionType, Path } from "../../types"; import { SwapOperation } from "@oraichain/osor-api-contracts-sdk/src/types"; import { Swap, Action, ExecuteMsg } from "@oraichain/osor-api-contracts-sdk/src/EntryPoint.types"; -import { isCw20Token, validatePath } from "./common"; +import { isCw20Token, validatePath } from "../common"; import { calculateTimeoutTimestamp, generateError, @@ -109,7 +109,7 @@ export class OsmosisMsg { /** * Function to generate memo for action on oraichain as middleware */ - genMemoAsMiddleware(): string { + genMemoAsMiddleware(): MiddleWareResponse { let [swapOps, bridgeInfo] = this.getSwapAndBridgeInfo(); // we have 2 cases: @@ -123,16 +123,19 @@ export class OsmosisMsg { } // ibc bridge - return JSON.stringify({ - forward: { - receiver: this.receiver, - port: bridgeInfo.sourcePort, - channel: bridgeInfo.sourceChannel, - timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), - retries: 2, - next: this.memo - } - }); + return { + receiver: this.currentChainAddress, + memo: JSON.stringify({ + forward: { + receiver: this.receiver, + port: bridgeInfo.sourcePort, + channel: bridgeInfo.sourceChannel, + timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + retries: 2, + next: this.memo + } + }) + }; } let tokenOutOfSwap = swapOps[swapOps.length - 1].denom_out; @@ -165,12 +168,15 @@ export class OsmosisMsg { } }; - return JSON.stringify({ - wasm: { - contract: this.ENTRY_POINT_CONTRACT, - msg - } - }); + return { + receiver: this.ENTRY_POINT_CONTRACT, + memo: JSON.stringify({ + wasm: { + contract: this.ENTRY_POINT_CONTRACT, + msg + } + }) + }; } /** diff --git a/packages/universal-swap/src/msg/index.ts b/packages/universal-swap/src/msg/index.ts index 7a83ef58..db737a77 100644 --- a/packages/universal-swap/src/msg/index.ts +++ b/packages/universal-swap/src/msg/index.ts @@ -1,3 +1,3 @@ export * from "./common"; -export * from "./oraichain"; +export * from "./chains"; export * from "./types"; diff --git a/packages/universal-swap/src/msg/types.ts b/packages/universal-swap/src/msg/types.ts index 8d6944b5..ed48a587 100644 --- a/packages/universal-swap/src/msg/types.ts +++ b/packages/universal-swap/src/msg/types.ts @@ -29,3 +29,8 @@ export enum PostActionType { IbcTransfer, IbcWasmTransfer } + +export interface MiddleWareResponse { + memo: String; + receiver: String; +} From 40e91354c01837f827c27b145c17abde5e40b2cf Mon Sep 17 00:00:00 2001 From: trung2891 Date: Thu, 3 Oct 2024 23:00:47 +0700 Subject: [PATCH 10/29] chore: done logic build msg swap from smart route --- .gitignore | 4 +- .../universal-swap/src/msg/chains/cosmos.ts | 3 +- .../src/msg/chains/oraichain.ts | 10 +- .../universal-swap/src/msg/chains/osmosis.ts | 3 +- packages/universal-swap/src/msg/common.ts | 9 ++ packages/universal-swap/src/msg/msgs.ts | 117 ++++++++++++++++++ packages/universal-swap/src/msg/types.ts | 4 +- 7 files changed, 142 insertions(+), 8 deletions(-) create mode 100644 packages/universal-swap/src/msg/msgs.ts diff --git a/.gitignore b/.gitignore index 7f27c0a6..f46d67dd 100644 --- a/.gitignore +++ b/.gitignore @@ -38,4 +38,6 @@ coverage .nx/cache .nx/workspace-data -.yarn \ No newline at end of file +.yarn + +demo-local \ No newline at end of file diff --git a/packages/universal-swap/src/msg/chains/cosmos.ts b/packages/universal-swap/src/msg/chains/cosmos.ts index f7e56c15..6ee9e7c1 100644 --- a/packages/universal-swap/src/msg/chains/cosmos.ts +++ b/packages/universal-swap/src/msg/chains/cosmos.ts @@ -1,7 +1,7 @@ import { BridgeMsgInfo, MiddleWareResponse } from "../types"; import { ActionType, Path } from "../../types"; import { Action } from "@oraichain/osor-api-contracts-sdk/src/EntryPoint.types"; -import { validatePath } from "../common"; +import { validatePath, validateReceiver } from "../common"; import { calculateTimeoutTimestamp, generateError, @@ -21,6 +21,7 @@ export class CosmosMsg { ) { // validate path validatePath(path); + validateReceiver(receiver, currentChainAddress, path.chainId); } /** diff --git a/packages/universal-swap/src/msg/chains/oraichain.ts b/packages/universal-swap/src/msg/chains/oraichain.ts index d2eaae28..b259e6ff 100644 --- a/packages/universal-swap/src/msg/chains/oraichain.ts +++ b/packages/universal-swap/src/msg/chains/oraichain.ts @@ -2,7 +2,7 @@ import { BridgeMsgInfo, MiddleWareResponse } from "../types"; import { ActionType, Path } from "../../types"; import { SwapOperation } from "@oraichain/osor-api-contracts-sdk/src/types"; import { Action, ExecuteMsg } from "@oraichain/osor-api-contracts-sdk/src/EntryPoint.types"; -import { isCw20Token, validatePath } from "../common"; +import { isCw20Token, validatePath, validateReceiver } from "../common"; import { calculateTimeoutTimestamp, CONVERTER_CONTRACT, @@ -37,6 +37,7 @@ export class OraichainMsg { } // validate path validatePath(path); + validateReceiver(receiver, currentChainAddress, path.chainId); } /** * Converts the given input and output tokens to a pool ID using the converter contract in the Oraichain ecosystem. @@ -181,7 +182,7 @@ export class OraichainMsg { * Function to generate memo msg for swap through ibc wasm after bridge * @returns */ - genMemoForIbcWasm(): string { + genMemoForIbcWasm(): MiddleWareResponse { let [swapOps, bridgeInfo] = this.getSwapAndBridgeInfo(); let userSwap: Memo_UserSwap; if (swapOps.length) { @@ -204,7 +205,10 @@ export class OraichainMsg { recoveryAddr: this.currentChainAddress }; const encodedMemo = Memo.encode(memo).finish(); - return Buffer.from(encodedMemo).toString("base64"); + return { + receiver: this.currentChainAddress, + memo: Buffer.from(encodedMemo).toString("base64") + }; } getPostAction(bridgeInfo?: BridgeMsgInfo): Action { diff --git a/packages/universal-swap/src/msg/chains/osmosis.ts b/packages/universal-swap/src/msg/chains/osmosis.ts index a95913f4..3a41a037 100644 --- a/packages/universal-swap/src/msg/chains/osmosis.ts +++ b/packages/universal-swap/src/msg/chains/osmosis.ts @@ -2,7 +2,7 @@ import { BridgeMsgInfo, MiddleWareResponse } from "../types"; import { ActionType, Path } from "../../types"; import { SwapOperation } from "@oraichain/osor-api-contracts-sdk/src/types"; import { Swap, Action, ExecuteMsg } from "@oraichain/osor-api-contracts-sdk/src/EntryPoint.types"; -import { isCw20Token, validatePath } from "../common"; +import { isCw20Token, validatePath, validateReceiver } from "../common"; import { calculateTimeoutTimestamp, generateError, @@ -31,6 +31,7 @@ export class OsmosisMsg { } // validate path validatePath(path); + validateReceiver(receiver, currentChainAddress, path.chainId); } /** diff --git a/packages/universal-swap/src/msg/common.ts b/packages/universal-swap/src/msg/common.ts index 42543916..19b38754 100644 --- a/packages/universal-swap/src/msg/common.ts +++ b/packages/universal-swap/src/msg/common.ts @@ -25,6 +25,15 @@ export const validatePath = (path: Path, hasSwap: boolean = true) => { } }; +export const validateReceiver = (receiver: string, currentChainAddress: string, chainId: string) => { + if (!receiver || receiver == "") { + throw generateError(`Missing receiver when build msg in ${chainId}`); + } + if (!currentChainAddress || currentChainAddress == "") { + throw generateError(`Missing address of ${chainId}`); + } +}; + // This function checks whether the given address is a CW20 token address export const isCw20Token = (address: string): boolean => { try { diff --git a/packages/universal-swap/src/msg/msgs.ts b/packages/universal-swap/src/msg/msgs.ts new file mode 100644 index 00000000..9fba2019 --- /dev/null +++ b/packages/universal-swap/src/msg/msgs.ts @@ -0,0 +1,117 @@ +import { + generateError, + ORAI_BRIDGE_EVM_DENOM_PREFIX, + ORAI_BRIDGE_EVM_TRON_DENOM_PREFIX +} from "@oraichain/oraidex-common"; +import { Path, Route } from "../types"; +import { CosmosMsg, OraichainMsg, OsmosisMsg } from "./chains"; +import { MiddleWareResponse } from "./types"; +import { EncodeObject } from "@cosmjs/proto-signing"; + +const getDestPrefixForBridgeToEvmOnOrai = (chainId: string): string => { + const prefixMap: { [key: string]: string } = { + "0x01": ORAI_BRIDGE_EVM_DENOM_PREFIX, + "0x38": ORAI_BRIDGE_EVM_DENOM_PREFIX, + "0x2b6653dc": ORAI_BRIDGE_EVM_TRON_DENOM_PREFIX + }; + + const prefix = prefixMap[chainId]; + if (prefix) { + return prefix; + } else if (chainId.startsWith("0x")) { + throw generateError(`Don't support bridge from Oraichain to ${chainId}`); + } + return ""; +}; + +//FIXME: calc minimum receive +const buildMemoSwap = ( + path: Path, + receiver: string, + memo: string, + addresses: { [chainId: string]: string } +): MiddleWareResponse => { + let currentChain = path.chainId; + let currentAddress = addresses[currentChain]; + switch (currentChain) { + case "Oraichain": { + let prefix = getDestPrefixForBridgeToEvmOnOrai(path.tokenOutChainId); + let oBridgeAddress = addresses["OraiBridge"]; + let oraichainMsg = new OraichainMsg(path, "1", receiver, currentAddress, memo, prefix, oBridgeAddress); + let previousChain = path.chainId; + // we have 2 cases: + // - Previous chain use IBC bridge to Oraichain (noble) + // - Previous chain use IBC Wasm bridge to Oraichain + let msgInfo = previousChain == "noble-1" ? oraichainMsg.genMemoForIbcWasm() : oraichainMsg.genMemoAsMiddleware(); + return msgInfo; + } + case "osmosis-1": { + let cosmosMsg = new OsmosisMsg(path, "1", receiver, currentAddress, memo); + let msgInfo = cosmosMsg.genMemoAsMiddleware(); + return msgInfo; + } + + default: { + // currently, we don't support universal swap on EVM + // default cosmos case + if (currentChain.startsWith("0x")) { + throw generateError("Don't support universal swap in EVM"); + } + let cosmosMsg = new CosmosMsg(path, "1", receiver, currentAddress, memo); + let msgInfo = cosmosMsg.genMemoAsMiddleware(); + return msgInfo; + } + } +}; + +//FIXME: calc minimum receive +const buildExecuteMsg = ( + path: Path, + receiver: string, + memo: string, + addresses: { [chainId: string]: string } +): EncodeObject => { + let currentChain = path.chainId; + let currentAddress = addresses[currentChain]; + switch (currentChain) { + case "Oraichain": { + let prefix = getDestPrefixForBridgeToEvmOnOrai(path.tokenOutChainId); + let oBridgeAddress = addresses["OraiBridge"]; + let oraichainMsg = new OraichainMsg(path, "1", receiver, currentAddress, memo, prefix, oBridgeAddress); + return oraichainMsg.genExecuteMsg(); + } + case "osmosis-1": { + let cosmosMsg = new OsmosisMsg(path, "1", receiver, currentAddress, memo); + return cosmosMsg.genExecuteMsg(); + } + + default: { + // currently, we don't support universal swap on EVM + // default cosmos case + if (currentChain.startsWith("0x")) { + throw generateError("Don't support universal swap in EVM"); + } + let cosmosMsg = new CosmosMsg(path, "1", receiver, currentAddress, memo); + return cosmosMsg.genExecuteMsg(); + } + } +}; + +export const generateMsgSwap = (route: Route, slippage: number = 0.01, addresses: { [chainId: string]: string }) => { + if (route.paths.length == 0) { + throw generateError("Require at least 1 action"); + } + let memo: string = ""; + let receiver = addresses[route.paths.at(-1)?.tokenOutChainId]; + + // generate memo for univeral swap + for (let i = route.paths.length - 1; i > 0; i--) { + let swapInfo = buildMemoSwap(route.paths[i], receiver, memo, addresses); + memo = swapInfo.memo; + receiver = swapInfo.receiver; + } + + return buildExecuteMsg(route.paths[0], receiver, memo, addresses); + + // generate execute msg +}; diff --git a/packages/universal-swap/src/msg/types.ts b/packages/universal-swap/src/msg/types.ts index ed48a587..a96d57a0 100644 --- a/packages/universal-swap/src/msg/types.ts +++ b/packages/universal-swap/src/msg/types.ts @@ -31,6 +31,6 @@ export enum PostActionType { } export interface MiddleWareResponse { - memo: String; - receiver: String; + memo: string; + receiver: string; } From fa546625a81fde0453eac417ee1efe38142226cf Mon Sep 17 00:00:00 2001 From: trung2891 Date: Fri, 4 Oct 2024 11:45:38 +0700 Subject: [PATCH 11/29] feat: calc minimum receive --- .../universal-swap/src/msg/chains/chain.ts | 24 +++++++++++++ .../universal-swap/src/msg/chains/cosmos.ts | 29 ++++++++++------ .../src/msg/chains/oraichain.ts | 34 ++++++++++++++----- .../universal-swap/src/msg/chains/osmosis.ts | 31 +++++++++++------ packages/universal-swap/src/msg/msgs.ts | 16 ++++++--- packages/universal-swap/src/msg/types.ts | 1 + 6 files changed, 100 insertions(+), 35 deletions(-) create mode 100644 packages/universal-swap/src/msg/chains/chain.ts diff --git a/packages/universal-swap/src/msg/chains/chain.ts b/packages/universal-swap/src/msg/chains/chain.ts new file mode 100644 index 00000000..1e92800f --- /dev/null +++ b/packages/universal-swap/src/msg/chains/chain.ts @@ -0,0 +1,24 @@ +import { Path } from "../../types"; +import { validatePath, validateReceiver } from "../common"; + +export class ChainMsg { + constructor( + protected path: Path, + protected minimumReceive: string, + protected receiver: string, + protected currentChainAddress: string, + protected memo: string = "" + ) { + // validate path + validatePath(path); + validateReceiver(receiver, currentChainAddress, path.chainId); + } + + setMinimumReceive(minimumReceive: string) { + this.minimumReceive = minimumReceive; + } + + getMinimumReceive() { + return this.minimumReceive; + } +} diff --git a/packages/universal-swap/src/msg/chains/cosmos.ts b/packages/universal-swap/src/msg/chains/cosmos.ts index 6ee9e7c1..76edd175 100644 --- a/packages/universal-swap/src/msg/chains/cosmos.ts +++ b/packages/universal-swap/src/msg/chains/cosmos.ts @@ -3,6 +3,7 @@ import { ActionType, Path } from "../../types"; import { Action } from "@oraichain/osor-api-contracts-sdk/src/EntryPoint.types"; import { validatePath, validateReceiver } from "../common"; import { + BigDecimal, calculateTimeoutTimestamp, generateError, IBC_TRANSFER_TIMEOUT, @@ -10,18 +11,23 @@ import { } from "@oraichain/oraidex-common"; import { EncodeObject } from "@cosmjs/proto-signing"; +import { ChainMsg } from "./chain"; -export class CosmosMsg { - constructor( - protected path: Path, - protected minimumReceive: string, - protected receiver: string, - protected currentChainAddress: string, - protected memo: string = "" - ) { - // validate path - validatePath(path); - validateReceiver(receiver, currentChainAddress, path.chainId); +export class CosmosMsg extends ChainMsg { + constructor(path: Path, minimumReceive: string, receiver: string, currentChainAddress: string, memo: string = "") { + super(path, minimumReceive, receiver, currentChainAddress, memo); + } + + setMinimumReceiveForSwap(slippage: number = 0.01) { + if (slippage > 1) { + throw generateError("Slippage must be less than 1"); + } + let bridgeInfo = this.getBridgeInfo(); + let minimumReceive = new BigDecimal(1 - slippage).mul(bridgeInfo.amount).toString(); + if (minimumReceive.includes(".")) { + minimumReceive = minimumReceive.split(".")[0]; + } + this.minimumReceive = minimumReceive; } /** @@ -34,6 +40,7 @@ export class CosmosMsg { switch (action.type) { case ActionType.Bridge: { bridgeInfo = { + amount: action.tokenInAmount, sourceChannel: action.bridgeInfo.channel, sourcePort: action.bridgeInfo.port, memo: this.memo, diff --git a/packages/universal-swap/src/msg/chains/oraichain.ts b/packages/universal-swap/src/msg/chains/oraichain.ts index b259e6ff..f99e4e01 100644 --- a/packages/universal-swap/src/msg/chains/oraichain.ts +++ b/packages/universal-swap/src/msg/chains/oraichain.ts @@ -4,6 +4,7 @@ import { SwapOperation } from "@oraichain/osor-api-contracts-sdk/src/types"; import { Action, ExecuteMsg } from "@oraichain/osor-api-contracts-sdk/src/EntryPoint.types"; import { isCw20Token, validatePath, validateReceiver } from "../common"; import { + BigDecimal, calculateTimeoutTimestamp, CONVERTER_CONTRACT, generateError, @@ -17,28 +18,42 @@ import { EncodeObject } from "@cosmjs/proto-signing"; import { MsgExecuteContract } from "cosmjs-types/cosmwasm/wasm/v1/tx"; import { TransferBackMsg } from "@oraichain/common-contracts-sdk/build/CwIcs20Latest.types"; import { toUtf8 } from "@cosmjs/encoding"; +import { ChainMsg } from "./chain"; -export class OraichainMsg { +export class OraichainMsg extends ChainMsg { SWAP_VENUE_NAME = "oraidex"; ENTRY_POINT_CONTRACT = "orai13mgxn93pjvd7eermj4ghet8assxdqttxugwk25rasuuqq2g5nczq43eesn"; // FIXME: use mainnet constructor( - protected path: Path, - protected minimumReceive: string, - protected receiver: string, - protected currentChainAddress: string, - protected memo: string = "", + path: Path, + minimumReceive: string, + receiver: string, + currentChainAddress: string, + memo: string = "", protected destPrefix: string = undefined, protected obridgeAddress: string = undefined ) { + super(path, minimumReceive, receiver, currentChainAddress, memo); // check chainId = "Oraichain" if (path.chainId !== "Oraichain") { throw generateError("This path must be on Oraichain"); } - // validate path - validatePath(path); - validateReceiver(receiver, currentChainAddress, path.chainId); } + + setMinimumReceiveForSwap(slippage: number = 0.01) { + if (slippage > 1) { + throw generateError("Slippage must be less than 1"); + } + let [_, bridgeInfo] = this.getSwapAndBridgeInfo(); + + let returnAmount = bridgeInfo ? bridgeInfo.amount : this.path.tokenOutAmount; + let minimumReceive = new BigDecimal(1 - slippage).mul(returnAmount).toString(); + if (minimumReceive.includes(".")) { + minimumReceive = minimumReceive.split(".")[0]; + } + this.minimumReceive = minimumReceive; + } + /** * Converts the given input and output tokens to a pool ID using the converter contract in the Oraichain ecosystem. * @param tokenIn The input token to be converted @@ -109,6 +124,7 @@ export class OraichainMsg { } case ActionType.Bridge: { bridgeInfo = { + amount: action.tokenInAmount, sourceChannel: action.bridgeInfo.channel, sourcePort: action.bridgeInfo.port, memo: this.memo, diff --git a/packages/universal-swap/src/msg/chains/osmosis.ts b/packages/universal-swap/src/msg/chains/osmosis.ts index 3a41a037..8d93989d 100644 --- a/packages/universal-swap/src/msg/chains/osmosis.ts +++ b/packages/universal-swap/src/msg/chains/osmosis.ts @@ -4,6 +4,7 @@ import { SwapOperation } from "@oraichain/osor-api-contracts-sdk/src/types"; import { Swap, Action, ExecuteMsg } from "@oraichain/osor-api-contracts-sdk/src/EntryPoint.types"; import { isCw20Token, validatePath, validateReceiver } from "../common"; import { + BigDecimal, calculateTimeoutTimestamp, generateError, IBC_TRANSFER_TIMEOUT, @@ -13,25 +14,32 @@ import { toBinary } from "@cosmjs/cosmwasm-stargate"; import { EncodeObject } from "@cosmjs/proto-signing"; import { MsgExecuteContract } from "cosmjs-types/cosmwasm/wasm/v1/tx"; import { toUtf8 } from "@cosmjs/encoding"; +import { ChainMsg } from "./chain"; -export class OsmosisMsg { +export class OsmosisMsg extends ChainMsg { SWAP_VENUE_NAME = "osmosis-poolmanager"; ENTRY_POINT_CONTRACT = "osmo1h3jkejkcpthl45xrrm5geed3eq75p5rgfce9taufkwfr89k63muqweu2y7"; - constructor( - protected path: Path, - protected minimumReceive: string, - protected receiver: string, - protected currentChainAddress: string, - protected memo: string = "" - ) { + constructor(path: Path, minimumReceive: string, receiver: string, currentChainAddress: string, memo: string = "") { + super(path, minimumReceive, receiver, currentChainAddress, memo); // check chainId = "osmosis-1" if (path.chainId !== "osmosis-1") { throw generateError("This path must be on Osmosis"); } - // validate path - validatePath(path); - validateReceiver(receiver, currentChainAddress, path.chainId); + } + + setMinimumReceiveForSwap(slippage: number = 0.01) { + if (slippage > 1) { + throw generateError("Slippage must be less than 1"); + } + let [_, bridgeInfo] = this.getSwapAndBridgeInfo(); + + let returnAmount = bridgeInfo ? bridgeInfo.amount : this.path.tokenOutAmount; + let minimumReceive = new BigDecimal(1 - slippage).mul(returnAmount).toString(); + if (minimumReceive.includes(".")) { + minimumReceive = minimumReceive.split(".")[0]; + } + this.minimumReceive = minimumReceive; } /** @@ -61,6 +69,7 @@ export class OsmosisMsg { } case ActionType.Bridge: { bridgeInfo = { + amount: action.tokenInAmount, sourceChannel: action.bridgeInfo.channel, sourcePort: action.bridgeInfo.port, memo: this.memo, diff --git a/packages/universal-swap/src/msg/msgs.ts b/packages/universal-swap/src/msg/msgs.ts index 9fba2019..4fd630bd 100644 --- a/packages/universal-swap/src/msg/msgs.ts +++ b/packages/universal-swap/src/msg/msgs.ts @@ -29,7 +29,8 @@ const buildMemoSwap = ( path: Path, receiver: string, memo: string, - addresses: { [chainId: string]: string } + addresses: { [chainId: string]: string }, + slippage: number = 0.01 ): MiddleWareResponse => { let currentChain = path.chainId; let currentAddress = addresses[currentChain]; @@ -38,6 +39,7 @@ const buildMemoSwap = ( let prefix = getDestPrefixForBridgeToEvmOnOrai(path.tokenOutChainId); let oBridgeAddress = addresses["OraiBridge"]; let oraichainMsg = new OraichainMsg(path, "1", receiver, currentAddress, memo, prefix, oBridgeAddress); + oraichainMsg.setMinimumReceiveForSwap(slippage); let previousChain = path.chainId; // we have 2 cases: // - Previous chain use IBC bridge to Oraichain (noble) @@ -47,6 +49,7 @@ const buildMemoSwap = ( } case "osmosis-1": { let cosmosMsg = new OsmosisMsg(path, "1", receiver, currentAddress, memo); + cosmosMsg.setMinimumReceiveForSwap(slippage); let msgInfo = cosmosMsg.genMemoAsMiddleware(); return msgInfo; } @@ -58,6 +61,7 @@ const buildMemoSwap = ( throw generateError("Don't support universal swap in EVM"); } let cosmosMsg = new CosmosMsg(path, "1", receiver, currentAddress, memo); + cosmosMsg.setMinimumReceiveForSwap(slippage); let msgInfo = cosmosMsg.genMemoAsMiddleware(); return msgInfo; } @@ -69,7 +73,8 @@ const buildExecuteMsg = ( path: Path, receiver: string, memo: string, - addresses: { [chainId: string]: string } + addresses: { [chainId: string]: string }, + slippage: number = 0.01 ): EncodeObject => { let currentChain = path.chainId; let currentAddress = addresses[currentChain]; @@ -78,10 +83,12 @@ const buildExecuteMsg = ( let prefix = getDestPrefixForBridgeToEvmOnOrai(path.tokenOutChainId); let oBridgeAddress = addresses["OraiBridge"]; let oraichainMsg = new OraichainMsg(path, "1", receiver, currentAddress, memo, prefix, oBridgeAddress); + oraichainMsg.setMinimumReceiveForSwap(slippage); return oraichainMsg.genExecuteMsg(); } case "osmosis-1": { let cosmosMsg = new OsmosisMsg(path, "1", receiver, currentAddress, memo); + cosmosMsg.setMinimumReceiveForSwap(slippage); return cosmosMsg.genExecuteMsg(); } @@ -92,6 +99,7 @@ const buildExecuteMsg = ( throw generateError("Don't support universal swap in EVM"); } let cosmosMsg = new CosmosMsg(path, "1", receiver, currentAddress, memo); + cosmosMsg.setMinimumReceiveForSwap(slippage); return cosmosMsg.genExecuteMsg(); } } @@ -106,12 +114,12 @@ export const generateMsgSwap = (route: Route, slippage: number = 0.01, addresses // generate memo for univeral swap for (let i = route.paths.length - 1; i > 0; i--) { - let swapInfo = buildMemoSwap(route.paths[i], receiver, memo, addresses); + let swapInfo = buildMemoSwap(route.paths[i], receiver, memo, addresses, slippage); memo = swapInfo.memo; receiver = swapInfo.receiver; } - return buildExecuteMsg(route.paths[0], receiver, memo, addresses); + return buildExecuteMsg(route.paths[0], receiver, memo, addresses, slippage); // generate execute msg }; diff --git a/packages/universal-swap/src/msg/types.ts b/packages/universal-swap/src/msg/types.ts index a96d57a0..19ce54d1 100644 --- a/packages/universal-swap/src/msg/types.ts +++ b/packages/universal-swap/src/msg/types.ts @@ -10,6 +10,7 @@ export interface SwapMsgInfo { } export interface BridgeMsgInfo { + amount: string; sourceChannel: string; sourcePort: string; receiver: string; From 8e2e16f64f3a77dcd08aef5b0b03ee806b3b059c Mon Sep 17 00:00:00 2001 From: trung2891 Date: Fri, 4 Oct 2024 12:03:01 +0700 Subject: [PATCH 12/29] feat: add msg generate memo for ibc bridge --- .../universal-swap/src/msg/chains/cosmos.ts | 4 +- .../src/msg/chains/oraichain.ts | 6 +-- .../universal-swap/src/msg/chains/osmosis.ts | 4 +- packages/universal-swap/src/msg/msgs.ts | 50 +++++++++++++++---- packages/universal-swap/src/msg/types.ts | 2 +- 5 files changed, 48 insertions(+), 18 deletions(-) diff --git a/packages/universal-swap/src/msg/chains/cosmos.ts b/packages/universal-swap/src/msg/chains/cosmos.ts index 76edd175..c34f0e34 100644 --- a/packages/universal-swap/src/msg/chains/cosmos.ts +++ b/packages/universal-swap/src/msg/chains/cosmos.ts @@ -1,4 +1,4 @@ -import { BridgeMsgInfo, MiddleWareResponse } from "../types"; +import { BridgeMsgInfo, MiddlewareResponse } from "../types"; import { ActionType, Path } from "../../types"; import { Action } from "@oraichain/osor-api-contracts-sdk/src/EntryPoint.types"; import { validatePath, validateReceiver } from "../common"; @@ -90,7 +90,7 @@ export class CosmosMsg extends ChainMsg { /** * Function to generate memo for action on oraichain as middleware */ - genMemoAsMiddleware(): MiddleWareResponse { + genMemoAsMiddleware(): MiddlewareResponse { let bridgeInfo = this.getBridgeInfo(); // ibc bridge return { diff --git a/packages/universal-swap/src/msg/chains/oraichain.ts b/packages/universal-swap/src/msg/chains/oraichain.ts index f99e4e01..3f585cbc 100644 --- a/packages/universal-swap/src/msg/chains/oraichain.ts +++ b/packages/universal-swap/src/msg/chains/oraichain.ts @@ -1,4 +1,4 @@ -import { BridgeMsgInfo, MiddleWareResponse } from "../types"; +import { BridgeMsgInfo, MiddlewareResponse } from "../types"; import { ActionType, Path } from "../../types"; import { SwapOperation } from "@oraichain/osor-api-contracts-sdk/src/types"; import { Action, ExecuteMsg } from "@oraichain/osor-api-contracts-sdk/src/EntryPoint.types"; @@ -198,7 +198,7 @@ export class OraichainMsg extends ChainMsg { * Function to generate memo msg for swap through ibc wasm after bridge * @returns */ - genMemoForIbcWasm(): MiddleWareResponse { + genMemoForIbcWasm(): MiddlewareResponse { let [swapOps, bridgeInfo] = this.getSwapAndBridgeInfo(); let userSwap: Memo_UserSwap; if (swapOps.length) { @@ -279,7 +279,7 @@ export class OraichainMsg extends ChainMsg { /** * Function to generate memo for action on oraichain as middleware */ - genMemoAsMiddleware(): MiddleWareResponse { + genMemoAsMiddleware(): MiddlewareResponse { let [swapOps, bridgeInfo] = this.getSwapAndBridgeInfo(); // we have 2 cases: diff --git a/packages/universal-swap/src/msg/chains/osmosis.ts b/packages/universal-swap/src/msg/chains/osmosis.ts index 8d93989d..6a3debb8 100644 --- a/packages/universal-swap/src/msg/chains/osmosis.ts +++ b/packages/universal-swap/src/msg/chains/osmosis.ts @@ -1,4 +1,4 @@ -import { BridgeMsgInfo, MiddleWareResponse } from "../types"; +import { BridgeMsgInfo, MiddlewareResponse } from "../types"; import { ActionType, Path } from "../../types"; import { SwapOperation } from "@oraichain/osor-api-contracts-sdk/src/types"; import { Swap, Action, ExecuteMsg } from "@oraichain/osor-api-contracts-sdk/src/EntryPoint.types"; @@ -119,7 +119,7 @@ export class OsmosisMsg extends ChainMsg { /** * Function to generate memo for action on oraichain as middleware */ - genMemoAsMiddleware(): MiddleWareResponse { + genMemoAsMiddleware(): MiddlewareResponse { let [swapOps, bridgeInfo] = this.getSwapAndBridgeInfo(); // we have 2 cases: diff --git a/packages/universal-swap/src/msg/msgs.ts b/packages/universal-swap/src/msg/msgs.ts index 4fd630bd..3219d70a 100644 --- a/packages/universal-swap/src/msg/msgs.ts +++ b/packages/universal-swap/src/msg/msgs.ts @@ -5,7 +5,7 @@ import { } from "@oraichain/oraidex-common"; import { Path, Route } from "../types"; import { CosmosMsg, OraichainMsg, OsmosisMsg } from "./chains"; -import { MiddleWareResponse } from "./types"; +import { MiddlewareResponse } from "./types"; import { EncodeObject } from "@cosmjs/proto-signing"; const getDestPrefixForBridgeToEvmOnOrai = (chainId: string): string => { @@ -30,8 +30,9 @@ const buildMemoSwap = ( receiver: string, memo: string, addresses: { [chainId: string]: string }, - slippage: number = 0.01 -): MiddleWareResponse => { + slippage: number = 0.01, + previousChain?: string +): MiddlewareResponse => { let currentChain = path.chainId; let currentAddress = addresses[currentChain]; switch (currentChain) { @@ -40,11 +41,13 @@ const buildMemoSwap = ( let oBridgeAddress = addresses["OraiBridge"]; let oraichainMsg = new OraichainMsg(path, "1", receiver, currentAddress, memo, prefix, oBridgeAddress); oraichainMsg.setMinimumReceiveForSwap(slippage); - let previousChain = path.chainId; // we have 2 cases: - // - Previous chain use IBC bridge to Oraichain (noble) - // - Previous chain use IBC Wasm bridge to Oraichain - let msgInfo = previousChain == "noble-1" ? oraichainMsg.genMemoForIbcWasm() : oraichainMsg.genMemoAsMiddleware(); + // - Previous chain use IBC bridge to Oraichain + // - Previous chain use IBC Wasm bridge to Oraichain (EVM, noble) + let msgInfo = + previousChain && (previousChain == "noble-1" || previousChain.startsWith("0x")) + ? oraichainMsg.genMemoForIbcWasm() + : oraichainMsg.genMemoAsMiddleware(); return msgInfo; } case "osmosis-1": { @@ -105,7 +108,11 @@ const buildExecuteMsg = ( } }; -export const generateMsgSwap = (route: Route, slippage: number = 0.01, addresses: { [chainId: string]: string }) => { +export const generateMsgSwap = ( + route: Route, + slippage: number = 0.01, + addresses: { [chainId: string]: string } +): EncodeObject => { if (route.paths.length == 0) { throw generateError("Require at least 1 action"); } @@ -114,12 +121,35 @@ export const generateMsgSwap = (route: Route, slippage: number = 0.01, addresses // generate memo for univeral swap for (let i = route.paths.length - 1; i > 0; i--) { - let swapInfo = buildMemoSwap(route.paths[i], receiver, memo, addresses, slippage); + let swapInfo = buildMemoSwap(route.paths[i], receiver, memo, addresses, slippage, route.paths[i - 1].chainId); memo = swapInfo.memo; receiver = swapInfo.receiver; } return buildExecuteMsg(route.paths[0], receiver, memo, addresses, slippage); +}; + +export const generateMemoSwap = ( + route: Route, + slippage: number = 0.01, + addresses: { [chainId: string]: string }, + previousChain?: string +): MiddlewareResponse => { + if (route.paths.length == 0) { + return { + memo: "", + receiver: "" + }; + } - // generate execute msg + let memo: string = ""; + let receiver = addresses[route.paths.at(-1)?.tokenOutChainId]; + + // generate memo for univeral swap + for (let i = route.paths.length - 1; i > 0; i--) { + let swapInfo = buildMemoSwap(route.paths[i], receiver, memo, addresses, slippage, route.paths[i - 1].chainId); + memo = swapInfo.memo; + receiver = swapInfo.receiver; + } + return buildMemoSwap(route.paths[0], receiver, memo, addresses, slippage, previousChain); }; diff --git a/packages/universal-swap/src/msg/types.ts b/packages/universal-swap/src/msg/types.ts index 19ce54d1..c6b6e4dc 100644 --- a/packages/universal-swap/src/msg/types.ts +++ b/packages/universal-swap/src/msg/types.ts @@ -31,7 +31,7 @@ export enum PostActionType { IbcWasmTransfer } -export interface MiddleWareResponse { +export interface MiddlewareResponse { memo: string; receiver: string; } From a43efd7d3ed1a4679a7d0b49823227cd555b3051 Mon Sep 17 00:00:00 2001 From: Hau Nguyen Van Date: Fri, 4 Oct 2024 18:12:46 +0700 Subject: [PATCH 13/29] beta universal swap --- packages/oraidex-common/package.json | 2 +- .../oraidex-common/src/celestia-network.ts | 54 ++ packages/oraidex-common/src/constant.ts | 3 +- packages/oraidex-common/src/ibc-info.ts | 7 +- packages/oraidex-common/src/index.ts | 1 + packages/oraidex-common/src/network.ts | 8 +- packages/universal-swap/src/handler.ts | 31 +- packages/universal-swap/src/helper.ts | 89 ++- packages/universal-swap/src/types.ts | 2 + .../src/universal-demos/alpha-smart-router.ts | 559 ++---------------- packages/universal-swap/tests/helper.spec.ts | 15 +- 11 files changed, 230 insertions(+), 541 deletions(-) create mode 100644 packages/oraidex-common/src/celestia-network.ts diff --git a/packages/oraidex-common/package.json b/packages/oraidex-common/package.json index 5f24be55..e3cf54cf 100644 --- a/packages/oraidex-common/package.json +++ b/packages/oraidex-common/package.json @@ -1,6 +1,6 @@ { "name": "@oraichain/oraidex-common", - "version": "1.1.21", + "version": "1.1.22", "main": "build/index.js", "files": [ "build/" diff --git a/packages/oraidex-common/src/celestia-network.ts b/packages/oraidex-common/src/celestia-network.ts new file mode 100644 index 00000000..98a07f6d --- /dev/null +++ b/packages/oraidex-common/src/celestia-network.ts @@ -0,0 +1,54 @@ +import { CustomChainInfo, defaultBech32Config } from "./network"; + +export const celestiaNetwork: CustomChainInfo = { + bech32Config: { + bech32PrefixAccAddr: "celestia", + bech32PrefixAccPub: "celestiapub", + bech32PrefixConsAddr: "celestiavalcons", + bech32PrefixConsPub: "celestiavalconspub", + bech32PrefixValAddr: "celestiavaloper", + bech32PrefixValPub: "celestiavaloperpub" + }, + bip44: { + coinType: 118 + }, + networkType: "cosmos", + chainId: "celestia", + chainName: "Celestia", + chainSymbolImageUrl: + "https://raw.githubusercontent.com/chainapsis/keplr-chain-registry/main/images/celestia/chain.png", + currencies: [ + { + coinDecimals: 6, + coinDenom: "TIA", + coinGeckoId: "celestia", + coinMinimalDenom: "utia", + coinImageUrl: "https://raw.githubusercontent.com/chainapsis/keplr-chain-registry/main/images/celestia/chain.png" + } + ], + features: [], + feeCurrencies: [ + { + coinDecimals: 6, + coinDenom: "TIA", + coinGeckoId: "celestia", + coinMinimalDenom: "utia", + coinImageUrl: "https://raw.githubusercontent.com/chainapsis/keplr-chain-registry/main/images/celestia/chain.png", + gasPriceStep: { + low: 0.01, + average: 0.02, + high: 0.1 + } + } + ], + rpc: "https://rpc-celestia.keplr.app", + rest: "https://lcd-celestia.keplr.app", + stakeCurrency: { + coinDecimals: 6, + coinDenom: "TIA", + coinGeckoId: "celestia", + coinMinimalDenom: "utia", + coinImageUrl: "https://raw.githubusercontent.com/chainapsis/keplr-chain-registry/main/images/celestia/utia.png" + }, + walletUrlForStaking: "https://wallet.keplr.app/chains/celestia" +}; diff --git a/packages/oraidex-common/src/constant.ts b/packages/oraidex-common/src/constant.ts index 4afa67ae..0834e43b 100644 --- a/packages/oraidex-common/src/constant.ts +++ b/packages/oraidex-common/src/constant.ts @@ -179,7 +179,8 @@ export enum COSMOS_CHAIN_ID_COMMON { COSMOSHUB_CHAIN_ID = "cosmoshub-4", INJECTVE_CHAIN_ID = "injective-1", KAWAII_COSMOS_CHAIN_ID = "kawaii_6886-1", - NOBLE_CHAIN_ID = "noble-1" + NOBLE_CHAIN_ID = "noble-1", + CELESTIA_CHAIN_ID = "celestia" } // asset info token diff --git a/packages/oraidex-common/src/ibc-info.ts b/packages/oraidex-common/src/ibc-info.ts index f2e184dd..5c063496 100644 --- a/packages/oraidex-common/src/ibc-info.ts +++ b/packages/oraidex-common/src/ibc-info.ts @@ -41,7 +41,7 @@ const [kwt2oraichain, oraichain2kwt] = KWT_ORAICHAIN_CHANNELS.split(/\s+/); // exclude evm chain -export const ibcInfos: IBCInfoMap = { +export const ibcInfos: Omit = { "cosmoshub-4": { Oraichain: { source: "transfer", @@ -180,7 +180,10 @@ export const ibcInfos: IBCInfoMap = { } }; -export const ibcInfosOld: Omit = { +export const ibcInfosOld: Omit< + IBCInfoMap, + "osmosis-1" | "cosmoshub-4" | "injective-1" | "noble-1" | "Neutaro-1" | "celestia" +> = { Oraichain: { "oraibridge-subnet-2": { source: "transfer", diff --git a/packages/oraidex-common/src/index.ts b/packages/oraidex-common/src/index.ts index 54548c6b..2816b6b5 100644 --- a/packages/oraidex-common/src/index.ts +++ b/packages/oraidex-common/src/index.ts @@ -11,3 +11,4 @@ export * from "./bigdecimal"; export * from "./interface"; export * from "./config/chainInfosWithIcon"; export * from "./axios-request"; +export * from "./celestia-network"; diff --git a/packages/oraidex-common/src/network.ts b/packages/oraidex-common/src/network.ts index 97fc0849..d07d06cd 100644 --- a/packages/oraidex-common/src/network.ts +++ b/packages/oraidex-common/src/network.ts @@ -63,6 +63,7 @@ import { HMSTR_ORAICHAIN_DENOM } from "./constant"; import { listOsmosisToken } from "./alpha-network"; +import { celestiaNetwork } from "./celestia-network"; export type NetworkName = | "Oraichain" @@ -76,7 +77,8 @@ export type NetworkName = | "Tron Network" | "Injective" | "Noble" - | "Neutaro"; + | "Neutaro" + | "Celestia"; export type CosmosChainId = | "Oraichain" // oraichain @@ -86,7 +88,8 @@ export type CosmosChainId = | "injective-1" // injective network | "kawaii_6886-1" // kawaii subnetwork | "noble-1" // noble network - | "Neutaro-1"; //neutaro network; + | "Neutaro-1" // neutaro network; + | "celestia"; // Celestia export type EvmChainId = | "0x38" // bsc @@ -541,6 +544,7 @@ export const oraichainNetwork: CustomChainInfo = { export const chainInfos: CustomChainInfo[] = [ // networks to add on keplr oraichainNetwork, + celestiaNetwork, { rpc: "https://bridge-v2.rpc.orai.io", rest: "https://bridge-v2.lcd.orai.io", diff --git a/packages/universal-swap/src/handler.ts b/packages/universal-swap/src/handler.ts index 0fff3907..15f253fd 100644 --- a/packages/universal-swap/src/handler.ts +++ b/packages/universal-swap/src/handler.ts @@ -60,6 +60,7 @@ import { import { GasPrice } from "@cosmjs/stargate"; import { OraiswapRouterQueryClient } from "@oraichain/oraidex-contracts-sdk"; import { Affiliate } from "@oraichain/oraidex-contracts-sdk/build/OraiswapMixedRouter.types"; +import { COSMOS_CHAIN_IDS } from "@oraichain/common"; const AFFILIATE_DECIMAL = 1e4; // 10_000 export class UniversalSwapHandler { @@ -1093,14 +1094,13 @@ export class UniversalSwapHandler { if (this.config.swapOptions?.isIbcWasm) minimumReceive = await this.caculateMinimumReceive(); const { swapRoute: completeSwapRoute } = await UniversalSwapHelper.addOraiBridgeRoute( - oraiAddress, + { obridgeAddress, sourceReceiver: oraiAddress }, originalFromToken, originalToToken, minimumReceive, destinationReceiver, this.config.swapOptions, - alphaSmartRoutes, - obridgeAddress + alphaSmartRoutes ); const swapRouteSplit = completeSwapRoute.split(":"); const swapRoute = swapRouteSplit.length === 1 ? "" : swapRouteSplit[1]; @@ -1151,7 +1151,8 @@ export class UniversalSwapHandler { async processUniversalSwap() { const { evm, tron } = this.swapData.sender; - const { originalFromToken, originalToToken, simulateAmount, recipientAddress, relayerFee } = this.swapData; + const { originalFromToken, originalToToken, simulateAmount, recipientAddress, relayerFee, alphaSmartRoutes } = + this.swapData; const { swapOptions } = this.config; let toAddress = ""; const currentToNetwork = originalToToken.chainId; @@ -1172,25 +1173,37 @@ export class UniversalSwapHandler { }); } + let injAddress = undefined; const [oraiAddress, obridgeAddress] = await Promise.all([ - this.config.cosmosWallet.getKeplrAddr("Oraichain"), - this.config.cosmosWallet.getKeplrAddr("oraibridge-subnet-2") + this.config.cosmosWallet.getKeplrAddr(COSMOS_CHAIN_IDS.ORAICHAIN), + this.config.cosmosWallet.getKeplrAddr(COSMOS_CHAIN_IDS.ORAIBRIDGE) ]); let minimumReceive = simulateAmount; if (swapOptions?.isIbcWasm) minimumReceive = await this.caculateMinimumReceive(); + if (swapOptions?.isAlphaIbcWasm) { + const routesFlatten = UniversalSwapHelper.flattenSmartRouters(alphaSmartRoutes.routes); + const hasInjectiveAddress = routesFlatten.some((route) => route.chainId === COSMOS_CHAIN_IDS.INJECTVE); + if (hasInjectiveAddress) injAddress = this.config.cosmosWallet.getKeplrAddr(COSMOS_CHAIN_IDS.INJECTVE); + } + const { swapRoute, universalSwapType } = await UniversalSwapHelper.addOraiBridgeRoute( - oraiAddress, + { + obridgeAddress, + injAddress, + sourceReceiver: oraiAddress + }, originalFromToken, originalToToken, minimumReceive, toAddress, this.config.swapOptions, - this.swapData.alphaSmartRoutes, - obridgeAddress + alphaSmartRoutes ); + if (alphaSmartRoutes?.routes?.length && swapOptions.isAlphaIbcWasm) return this.transferAndSwap(swapRoute); + if ( this.swapData?.alphaSmartRoutes?.routes?.length && ["oraichain-to-oraichain", "oraichain-to-cosmos", "cosmos-to-others"].includes(universalSwapType) && diff --git a/packages/universal-swap/src/helper.ts b/packages/universal-swap/src/helper.ts index c28bb355..65608230 100644 --- a/packages/universal-swap/src/helper.ts +++ b/packages/universal-swap/src/helper.ts @@ -47,7 +47,8 @@ import { getAxios, parseAssetInfoFromContractAddrOrDenom, parseAssetInfo, - calculateTimeoutTimestamp + calculateTimeoutTimestamp, + COSMOS_CHAIN_ID_COMMON } from "@oraichain/oraidex-common"; import { ConvertReverse, @@ -82,6 +83,8 @@ import { AXIOS_TIMEOUT, IBC_TRANSFER_TIMEOUT } from "@oraichain/common"; import { TransferBackMsg } from "@oraichain/common-contracts-sdk/build/CwIcs20Latest.types"; import { buildUniversalSwapMemo } from "./proto/universal-swap-memo-proto-handler"; import { Affiliate } from "@oraichain/oraidex-contracts-sdk/build/OraiswapMixedRouter.types"; +import { generateMemoSwap, generateMsgSwap } from "./msg/msgs"; +import { fromBech32, toBech32 } from "@cosmjs/encoding"; const caseSwapNativeAndWrapNative = (fromCoingecko, toCoingecko) => { const arr = ["ethereum", "weth"]; @@ -308,8 +311,17 @@ export class UniversalSwapHelper { return { swapRoute: "", universalSwapType: "other-networks-to-oraichain", isSmartRouter: true }; }; + static getAddress = (prefix: string, { address60, address118 }, coinType: number = 118) => { + const approve = { + 118: address118, + 60: address60 + }; + const { data } = fromBech32(approve[coinType]); + return toBech32(prefix, data); + }; + static addOraiBridgeRoute = async ( - sourceReceiver: string, + addresses: { obridgeAddress?: string; sourceReceiver: string; injAddress?: string }, fromToken: TokenItemType, toToken: TokenItemType, minimumReceive: string, @@ -318,14 +330,14 @@ export class UniversalSwapHelper { isSourceReceiverTest?: boolean; isIbcWasm?: boolean; ibcInfoTestMode?: boolean; + isAlphaIbcWasm?: boolean; }, - alphaSmartRoute?: RouterResponse, - remoteAddressObridge?: string + alphaSmartRoute?: RouterResponse ): Promise => { // TODO: recheck cosmos address undefined (other-chain -> oraichain) - if (!sourceReceiver) throw generateError(`Cannot get source if the sourceReceiver is empty!`); + if (!addresses.sourceReceiver) throw generateError(`Cannot get source if the sourceReceiver is empty!`); const source = UniversalSwapHelper.getSourceReceiver( - sourceReceiver, + addresses.sourceReceiver, fromToken.contractAddress, swapOption?.isSourceReceiverTest ); @@ -340,7 +352,12 @@ export class UniversalSwapHelper { if (!alphaSmartRoute && fromToken.coinGeckoId !== toToken.coinGeckoId) throw generateError(`Missing router !`); swapRoute = await UniversalSwapHelper.getRouteV2( - { minimumReceive, recoveryAddr: sourceReceiver, destReceiver, remoteAddressObridge }, + { + minimumReceive, + recoveryAddr: addresses.sourceReceiver, + destReceiver, + remoteAddressObridge: addresses.obridgeAddress + }, { ...alphaSmartRoute, destAsset: parseTokenInfoRawDenom(toToken), @@ -351,6 +368,48 @@ export class UniversalSwapHelper { ); } + /** + * useAlphaIbcWasm case: (evm -> oraichain -> osmosis -> inj/tia not using wasm) + */ + if (swapOption.isAlphaIbcWasm) { + if (!alphaSmartRoute) throw generateError(`Missing router with alpha ibc wasm!`); + const routes = alphaSmartRoute.routes; + const alphaRoutes = routes[0]; + + if (alphaSmartRoute.routes.length > 1) throw generateError(`Missing router with alpha ibc wasm max length!`); + + const paths = alphaRoutes.paths.filter((_, index) => index > 0); + + const receiverAddresses = { + [COSMOS_CHAIN_ID_COMMON.ORAICHAIN_CHAIN_ID]: addresses.sourceReceiver, + [COSMOS_CHAIN_ID_COMMON.COSMOSHUB_CHAIN_ID]: this.getAddress("cosmos", { + address60: addresses.injAddress, + address118: addresses.obridgeAddress + }), + [COSMOS_CHAIN_ID_COMMON.OSMOSIS_CHAIN_ID]: this.getAddress("osmo", { + address60: addresses.injAddress, + address118: addresses.obridgeAddress + }), + [COSMOS_CHAIN_ID_COMMON.INJECTVE_CHAIN_ID]: addresses.injAddress, + [COSMOS_CHAIN_ID_COMMON.CELESTIA_CHAIN_ID]: this.getAddress("celestia", { + address60: addresses.injAddress, + address118: addresses.obridgeAddress + }) + }; + + const { memo } = generateMemoSwap( + { + ...alphaRoutes, + paths: paths + }, + 0.01, + receiverAddresses, + alphaRoutes.paths[0].chainId + ); + + swapRoute = memo; + } + if (swapRoute.length > 0) return { swapRoute: `${source}:${swapRoute}`, universalSwapType, isSmartRouter }; return { swapRoute: source, universalSwapType, isSmartRouter }; }; @@ -548,7 +607,8 @@ export class UniversalSwapHelper { offerAmount: offerAmount, swapOptions: { protocols: routerConfig.protocols, - dontAlowSwapAfter: routerConfig.dontAllowSwapAfter + dontAlowSwapAfter: routerConfig.dontAllowSwapAfter, + maxSplits: routerConfig.maxSplits } }; const res: { @@ -571,7 +631,8 @@ export class UniversalSwapHelper { url: "https://osor.oraidex.io", path: "/smart-router", protocols: ["Oraidex", "OraidexV3"], - dontAllowSwapAfter: ["Oraidex", "OraidexV3"] + dontAllowSwapAfter: ["Oraidex", "OraidexV3"], + maxSplits: 10 } ): Promise => { const { returnAmount, routes } = await UniversalSwapHelper.querySmartRoute( @@ -720,6 +781,7 @@ export class UniversalSwapHelper { routerOption?: { useAlphaSmartRoute?: boolean; useIbcWasm?: boolean; + useAlphaIbcWasm?: boolean; }; routerConfig?: RouterConfigSmartRoute; }): Promise => { @@ -748,13 +810,18 @@ export class UniversalSwapHelper { url: query?.routerConfig?.url ?? "https://osor.oraidex.io", path: query?.routerConfig?.path ?? "/smart-router/alpha-router", protocols: query?.routerConfig?.protocols ?? ["Oraidex", "OraidexV3"], - dontAllowSwapAfter: query?.routerConfig?.dontAllowSwapAfter ?? ["Oraidex", "OraidexV3"] + dontAllowSwapAfter: query?.routerConfig?.dontAllowSwapAfter ?? ["Oraidex", "OraidexV3"], + maxSplits: query?.routerConfig?.maxSplits ?? 10 }; let fromInfo = getTokenOnOraichain(query.originalFromInfo.coinGeckoId); let toInfo = getTokenOnOraichain(query.originalToInfo.coinGeckoId); - if (!query?.routerOption?.useIbcWasm) { + /** + * useAlphaIbcWasm case: (evm -> oraichain -> osmosis -> inj not using wasm) + * useIbcWasm case: (evm -> cosmos) + */ + if (!query?.routerOption?.useIbcWasm || query?.routerOption?.useAlphaIbcWasm) { fromInfo = query.originalFromInfo; toInfo = query.originalToInfo; } diff --git a/packages/universal-swap/src/types.ts b/packages/universal-swap/src/types.ts index 302b8c41..7a91af32 100644 --- a/packages/universal-swap/src/types.ts +++ b/packages/universal-swap/src/types.ts @@ -96,6 +96,7 @@ export interface SwapOptions { isSourceReceiverTest?: boolean; isAlphaSmartRouter?: boolean; isIbcWasm?: boolean; + isAlphaIbcWasm?: boolean; isCheckBalanceIbc?: boolean; } @@ -265,4 +266,5 @@ export interface RouterConfigSmartRoute { path?: string; protocols?: string[]; dontAllowSwapAfter?: string[]; + maxSplits?: number; } diff --git a/packages/universal-swap/src/universal-demos/alpha-smart-router.ts b/packages/universal-swap/src/universal-demos/alpha-smart-router.ts index c88df52c..17214133 100644 --- a/packages/universal-swap/src/universal-demos/alpha-smart-router.ts +++ b/packages/universal-swap/src/universal-demos/alpha-smart-router.ts @@ -3,534 +3,71 @@ import { CosmosWalletImpl } from "./offline-wallet"; import { UniversalSwapHandler } from "../handler"; import { cosmosTokens, flattenTokens, generateError, getTokenOnOraichain, toAmount } from "@oraichain/oraidex-common"; -// const router = { -// swapAmount: "1000000000", -// returnAmount: "16375767570", -// routes: [ -// { -// swapAmount: "600000000", -// returnAmount: "9825302858", -// paths: [ -// { -// chainId: "Oraichain", -// tokenIn: "orai", -// tokenInAmount: "600000000", -// tokenOut: "uatom", -// tokenOutAmount: "781732133", -// tokenOutChainId: "cosmoshub-4", -// actions: [ -// { -// type: "Swap", -// tokenIn: "orai", -// tokenInAmount: "600000000", -// tokenOut: "ibc/A2E2EEC9057A4A1C2C0A6A4C78B0239118DF5F278830F50B4A6BDD7A66506B78", -// tokenOutAmount: "781732133", -// swapInfo: [ -// { -// poolId: "orai1jf74ry4m0jcy9emsaudkhe7vte9l8qy8enakvs", -// tokenOut: "ibc/A2E2EEC9057A4A1C2C0A6A4C78B0239118DF5F278830F50B4A6BDD7A66506B78" -// } -// ] -// }, -// { -// type: "Bridge", -// tokenIn: "ibc/A2E2EEC9057A4A1C2C0A6A4C78B0239118DF5F278830F50B4A6BDD7A66506B78", -// tokenInAmount: "781732133", -// tokenOut: "uatom", -// tokenOutAmount: "781732133", -// tokenOutChainId: "cosmoshub-4", -// bridgeInfo: { -// port: "transfer", -// channel: "channel-15" -// } -// } -// ] -// }, -// { -// chainId: "cosmoshub-4", -// tokenIn: "uatom", -// tokenInAmount: "781732133", -// tokenOut: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", -// tokenOutAmount: "781732133", -// tokenOutChainId: "osmosis-1", -// actions: [ -// { -// type: "Bridge", -// tokenIn: "uatom", -// tokenInAmount: "781732133", -// tokenOut: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", -// tokenOutAmount: "781732133", -// tokenOutChainId: "osmosis-1", -// bridgeInfo: { -// port: "transfer", -// channel: "channel-141" -// } -// } -// ] -// }, -// { -// chainId: "osmosis-1", -// tokenIn: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", -// tokenInAmount: "781732133", -// tokenOut: "uosmo", -// tokenOutAmount: "9825302858", -// tokenOutChainId: "osmosis-1", -// actions: [ -// { -// type: "Swap", -// tokenIn: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", -// tokenInAmount: "781732133", -// tokenOut: "uosmo", -// tokenOutAmount: "9825302858", -// swapInfo: [ -// { -// poolId: "1135", -// tokenOut: "uosmo" -// } -// ] -// } -// ] -// } -// ] -// }, -// { -// swapAmount: "200000000", -// returnAmount: "3276268429", -// paths: [ -// { -// chainId: "Oraichain", -// tokenIn: "orai", -// tokenInAmount: "200000000", -// tokenOut: "uusdc", -// tokenOutAmount: "1733503520", -// tokenOutChainId: "noble-1", -// actions: [ -// { -// type: "Swap", -// tokenIn: "orai", -// tokenInAmount: "200000000", -// tokenOut: "orai15un8msx3n5zf9ahlxmfeqd2kwa5wm0nrpxer304m9nd5q6qq0g6sku5pdd", -// tokenOutAmount: "1737861370", -// swapInfo: [ -// { -// poolId: "orai19ttg0j7w5kr83js32tmwnwxxdq9rkmw4m3d7mn2j2hkpugwwa4tszwsnkg", -// tokenOut: "orai15un8msx3n5zf9ahlxmfeqd2kwa5wm0nrpxer304m9nd5q6qq0g6sku5pdd" -// } -// ] -// }, -// { -// type: "Bridge", -// tokenIn: "orai15un8msx3n5zf9ahlxmfeqd2kwa5wm0nrpxer304m9nd5q6qq0g6sku5pdd", -// tokenInAmount: "1737861370", -// tokenOut: "uusdc", -// tokenOutAmount: "1733503520", -// tokenOutChainId: "noble-1", -// bridgeInfo: { -// port: "transfer", -// channel: "channel-147" -// } -// } -// ] -// }, -// { -// chainId: "noble-1", -// tokenIn: "uusdc", -// tokenInAmount: "1733503520", -// tokenOut: "ibc/498A0751C798A0D9A389AA3691123DADA57DAA4FE165D5C75894505B876BA6E4", -// tokenOutAmount: "1731770016", -// tokenOutChainId: "osmosis-1", -// actions: [ -// { -// type: "Bridge", -// tokenIn: "uusdc", -// tokenInAmount: "1733503520", -// tokenOut: "ibc/498A0751C798A0D9A389AA3691123DADA57DAA4FE165D5C75894505B876BA6E4", -// tokenOutAmount: "1731770016", -// tokenOutChainId: "osmosis-1", -// bridgeInfo: { -// port: "transfer", -// channel: "channel-1" -// } -// } -// ] -// }, -// { -// chainId: "osmosis-1", -// tokenIn: "ibc/498A0751C798A0D9A389AA3691123DADA57DAA4FE165D5C75894505B876BA6E4", -// tokenInAmount: "1731770016", -// tokenOut: "uosmo", -// tokenOutAmount: "3276268429", -// tokenOutChainId: "osmosis-1", -// actions: [ -// { -// type: "Swap", -// tokenIn: "ibc/498A0751C798A0D9A389AA3691123DADA57DAA4FE165D5C75894505B876BA6E4", -// tokenInAmount: "1731770016", -// tokenOut: "uosmo", -// tokenOutAmount: "3276268429", -// swapInfo: [ -// { -// poolId: "1464", -// tokenOut: "uosmo" -// } -// ] -// } -// ] -// } -// ] -// }, -// { -// swapAmount: "100000000", -// returnAmount: "1637648287", -// paths: [ -// { -// chainId: "Oraichain", -// tokenIn: "orai", -// tokenInAmount: "100000000", -// tokenOut: "uosmo", -// tokenOutAmount: "1637648287", -// tokenOutChainId: "osmosis-1", -// actions: [ -// { -// type: "Swap", -// tokenIn: "orai", -// tokenInAmount: "100000000", -// tokenOut: "ibc/9C4DCD21B48231D0BC2AC3D1B74A864746B37E4292694C93C617324250D002FC", -// tokenOutAmount: "1637648287", -// swapInfo: [ -// { -// poolId: "orai1d37artrk4tkhz2qyjmaulc2jzjkx7206tmpfug", -// tokenOut: "ibc/9C4DCD21B48231D0BC2AC3D1B74A864746B37E4292694C93C617324250D002FC" -// } -// ] -// }, -// { -// type: "Bridge", -// tokenIn: "ibc/9C4DCD21B48231D0BC2AC3D1B74A864746B37E4292694C93C617324250D002FC", -// tokenInAmount: "1637648287", -// tokenOut: "uosmo", -// tokenOutAmount: "1637648287", -// tokenOutChainId: "osmosis-1", -// bridgeInfo: { -// port: "transfer", -// channel: "channel-13" -// } -// } -// ] -// } -// ] -// }, -// { -// swapAmount: "100000000", -// returnAmount: "1636547996", -// paths: [ -// { -// chainId: "Oraichain", -// tokenIn: "orai", -// tokenInAmount: "100000000", -// tokenOut: "uusdc", -// tokenOutAmount: "866506508", -// tokenOutChainId: "noble-1", -// actions: [ -// { -// type: "Swap", -// tokenIn: "orai", -// tokenInAmount: "100000000", -// tokenOut: "orai15un8msx3n5zf9ahlxmfeqd2kwa5wm0nrpxer304m9nd5q6qq0g6sku5pdd", -// tokenOutAmount: "869996493", -// swapInfo: [ -// { -// poolId: "orai1m6q5k5nr2eh8q0rdrf57wr7phk7uvlpg7mwfv5", -// tokenOut: "orai1lus0f0rhx8s03gdllx2n6vhkmf0536dv57wfge" -// }, -// { -// poolId: "orai1n4edv5h86rawzrvhy8lmrmnnmmherxnhuwqnk3yuvt0wgclh75usyn3md6", -// tokenOut: "orai15un8msx3n5zf9ahlxmfeqd2kwa5wm0nrpxer304m9nd5q6qq0g6sku5pdd" -// } -// ] -// }, -// { -// type: "Bridge", -// tokenIn: "orai15un8msx3n5zf9ahlxmfeqd2kwa5wm0nrpxer304m9nd5q6qq0g6sku5pdd", -// tokenInAmount: "869996493", -// tokenOut: "uusdc", -// tokenOutAmount: "866506508", -// tokenOutChainId: "noble-1", -// bridgeInfo: { -// port: "transfer", -// channel: "channel-147" -// } -// } -// ] -// }, -// { -// chainId: "noble-1", -// tokenIn: "uusdc", -// tokenInAmount: "866506508", -// tokenOut: "ibc/498A0751C798A0D9A389AA3691123DADA57DAA4FE165D5C75894505B876BA6E4", -// tokenOutAmount: "865640001", -// tokenOutChainId: "osmosis-1", -// actions: [ -// { -// type: "Bridge", -// tokenIn: "uusdc", -// tokenInAmount: "866506508", -// tokenOut: "ibc/498A0751C798A0D9A389AA3691123DADA57DAA4FE165D5C75894505B876BA6E4", -// tokenOutAmount: "865640001", -// tokenOutChainId: "osmosis-1", -// bridgeInfo: { -// port: "transfer", -// channel: "channel-1" -// } -// } -// ] -// }, -// { -// chainId: "osmosis-1", -// tokenIn: "ibc/498A0751C798A0D9A389AA3691123DADA57DAA4FE165D5C75894505B876BA6E4", -// tokenInAmount: "865640001", -// tokenOut: "uosmo", -// tokenOutAmount: "1636547996", -// tokenOutChainId: "osmosis-1", -// actions: [ -// { -// type: "Swap", -// tokenIn: "ibc/498A0751C798A0D9A389AA3691123DADA57DAA4FE165D5C75894505B876BA6E4", -// tokenInAmount: "865640001", -// tokenOut: "uosmo", -// tokenOutAmount: "1636547996", -// swapInfo: [ -// { -// poolId: "1263", -// tokenOut: "uosmo" -// } -// ] -// } -// ] -// } -// ] -// } -// ] -// }; - -// const router = { -// swapAmount: "1000", -// returnAmount: "45659896949856", -// routes: [ -// { -// swapAmount: "900", -// returnAmount: "41000000000000", -// paths: [ -// { -// chainId: "Oraichain", -// tokenIn: "orai15un8msx3n5zf9ahlxmfeqd2kwa5wm0nrpxer304m9nd5q6qq0g6sku5pdd", -// tokenInAmount: "900", -// tokenOut: "inj", -// tokenOutAmount: "41000000000000", -// tokenOutChainId: "injective-1", -// actions: [ -// { -// type: "Swap", -// tokenIn: "orai15un8msx3n5zf9ahlxmfeqd2kwa5wm0nrpxer304m9nd5q6qq0g6sku5pdd", -// tokenInAmount: "900", -// tokenOut: "orai19rtmkk6sn4tppvjmp5d5zj6gfsdykrl5rw2euu5gwur3luheuuusesqn49", -// tokenOutAmount: "41", -// swapInfo: [ -// { -// poolId: "orai19ttg0j7w5kr83js32tmwnwxxdq9rkmw4m3d7mn2j2hkpugwwa4tszwsnkg", -// tokenOut: "orai" -// }, -// { -// poolId: "orai1le7w5dmd23ky8f6zgtgfnpdv269qs6ezgr839sm8kj24rwaqqnrs58wf4u", -// tokenOut: "orai19rtmkk6sn4tppvjmp5d5zj6gfsdykrl5rw2euu5gwur3luheuuusesqn49" -// } -// ] -// }, -// { -// type: "Convert", -// tokenIn: "orai19rtmkk6sn4tppvjmp5d5zj6gfsdykrl5rw2euu5gwur3luheuuusesqn49", -// tokenInAmount: "41", -// tokenOut: "ibc/49D820DFDE9F885D7081725A58202ABA2F465CAEE4AFBC683DFB79A8E013E83E", -// tokenOutAmount: "41000000000000", -// tokenOutChainId: "Oraichain", -// bridgeInfo: { -// port: "", -// channel: "" -// } -// }, -// { -// type: "Bridge", -// tokenIn: "ibc/49D820DFDE9F885D7081725A58202ABA2F465CAEE4AFBC683DFB79A8E013E83E", -// tokenInAmount: "41000000000000", -// tokenOut: "inj", -// tokenOutAmount: "41000000000000", -// tokenOutChainId: "injective-1", -// bridgeInfo: { -// port: "transfer", -// channel: "channel-146" -// } -// } -// ] -// } -// ] -// }, -// { -// swapAmount: "100", -// returnAmount: "4659896949856", -// paths: [ -// { -// chainId: "Oraichain", -// tokenIn: "orai15un8msx3n5zf9ahlxmfeqd2kwa5wm0nrpxer304m9nd5q6qq0g6sku5pdd", -// tokenInAmount: "100", -// tokenOut: "uosmo", -// tokenOutAmount: "198", -// tokenOutChainId: "osmosis-1", -// actions: [ -// { -// type: "Swap", -// tokenIn: "orai15un8msx3n5zf9ahlxmfeqd2kwa5wm0nrpxer304m9nd5q6qq0g6sku5pdd", -// tokenInAmount: "100", -// tokenOut: "ibc/9C4DCD21B48231D0BC2AC3D1B74A864746B37E4292694C93C617324250D002FC", -// tokenOutAmount: "198", -// swapInfo: [ -// { -// poolId: "orai1n4edv5h86rawzrvhy8lmrmnnmmherxnhuwqnk3yuvt0wgclh75usyn3md6", -// tokenOut: "orai1lus0f0rhx8s03gdllx2n6vhkmf0536dv57wfge" -// }, -// { -// poolId: "orai1m6q5k5nr2eh8q0rdrf57wr7phk7uvlpg7mwfv5", -// tokenOut: "orai" -// }, -// { -// poolId: "orai1d37artrk4tkhz2qyjmaulc2jzjkx7206tmpfug", -// tokenOut: "ibc/9C4DCD21B48231D0BC2AC3D1B74A864746B37E4292694C93C617324250D002FC" -// } -// ] -// }, -// { -// type: "Bridge", -// tokenIn: "ibc/9C4DCD21B48231D0BC2AC3D1B74A864746B37E4292694C93C617324250D002FC", -// tokenInAmount: "198", -// tokenOut: "uosmo", -// tokenOutAmount: "198", -// tokenOutChainId: "osmosis-1", -// bridgeInfo: { -// port: "transfer", -// channel: "channel-13" -// } -// } -// ] -// }, -// { -// chainId: "osmosis-1", -// tokenIn: "uosmo", -// tokenInAmount: "198", -// tokenOut: "inj", -// tokenOutAmount: "4659896949856", -// tokenOutChainId: "injective-1", -// actions: [ -// { -// type: "Swap", -// tokenIn: "uosmo", -// tokenInAmount: "198", -// tokenOut: "ibc/64BA6E31FE887D66C6F8F31C7B1A80C7CA179239677B4088BB55F5EA07DBE273", -// tokenOutAmount: "4659896949856", -// swapInfo: [ -// { -// poolId: "1134", -// tokenOut: "ibc/EA1D43981D5C9A1C4AAEA9C23BB1D4FA126BA9BC7020A25E0AE4AA841EA25DC5" -// }, -// { -// poolId: "1637", -// tokenOut: "ibc/69110FF673D70B39904FF056CFDFD58A90BEC3194303F45C32CB91B8B0A738EA" -// }, -// { -// poolId: "1698", -// tokenOut: "ibc/64BA6E31FE887D66C6F8F31C7B1A80C7CA179239677B4088BB55F5EA07DBE273" -// } -// ] -// }, -// { -// type: "Bridge", -// tokenIn: "ibc/64BA6E31FE887D66C6F8F31C7B1A80C7CA179239677B4088BB55F5EA07DBE273", -// tokenInAmount: "4659896949856", -// tokenOut: "inj", -// tokenOutAmount: "4659896949856", -// tokenOutChainId: "injective-1", -// bridgeInfo: { -// port: "transfer", -// channel: "channel-122" -// } -// } -// ] -// } -// ] -// } -// ] -// }; - const router = { - swapAmount: "88000000", - returnAmount: "521900530", + swapAmount: "10000000", + returnAmount: "633497", routes: [ { - swapAmount: "79200000", - returnAmount: "469713580", + swapAmount: "10000000", + returnAmount: "633497", paths: [ { - chainId: "Oraichain", - tokenIn: "orai", - tokenInAmount: "79200000", + chainId: "0x01", + tokenIn: "0xdAC17F958D2ee523a2206206994597C13D831ec7", + tokenInAmount: "10000000", tokenOut: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh", - tokenOutAmount: "469713580", + tokenOutAmount: "3868034", tokenOutChainId: "Oraichain", actions: [ { - type: "Swap", - protocol: "OraidexV3", - tokenIn: "orai", - tokenInAmount: "79200000", + type: "Bridge", + protocol: "Bridge", + tokenIn: "0xdAC17F958D2ee523a2206206994597C13D831ec7", + tokenInAmount: "10000000", tokenOut: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh", - tokenOutAmount: "469713580", - swapInfo: [ - { - poolId: "orai-orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh-3000000000-100", - tokenOut: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh" - } - ] + tokenOutAmount: "3868034", + tokenOutChainId: "Oraichain", + bridgeInfo: { + port: "transfer", + channel: "channel-1" + } } ] - } - ] - }, - { - swapAmount: "8800000", - returnAmount: "52186950", - paths: [ + }, { chainId: "Oraichain", - tokenIn: "orai", - tokenInAmount: "8800000", - tokenOut: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh", - tokenOutAmount: "52186950", + tokenIn: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh", + tokenInAmount: "3868034", + tokenOut: "orai", + tokenOutAmount: "633497", tokenOutChainId: "Oraichain", actions: [ { type: "Swap", protocol: "OraidexV3", - tokenIn: "orai", - tokenInAmount: "8800000", - tokenOut: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh", - tokenOutAmount: "52186950", + tokenIn: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh", + tokenInAmount: "3868034", + tokenOut: "orai10g6frpysmdgw5tdqke47als6f97aqmr8s3cljsvjce4n5enjftcqtamzsd", + tokenOutAmount: "63", swapInfo: [ - { - poolId: "orai-orai15un8msx3n5zf9ahlxmfeqd2kwa5wm0nrpxer304m9nd5q6qq0g6sku5pdd-3000000000-100", - tokenOut: "orai15un8msx3n5zf9ahlxmfeqd2kwa5wm0nrpxer304m9nd5q6qq0g6sku5pdd" - }, { poolId: - "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh-orai15un8msx3n5zf9ahlxmfeqd2kwa5wm0nrpxer304m9nd5q6qq0g6sku5pdd-500000000-10", - tokenOut: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh" + "orai10g6frpysmdgw5tdqke47als6f97aqmr8s3cljsvjce4n5enjftcqtamzsd-orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh-3000000000-100", + tokenOut: "orai10g6frpysmdgw5tdqke47als6f97aqmr8s3cljsvjce4n5enjftcqtamzsd" + } + ] + }, + { + type: "Swap", + protocol: "Oraidex", + tokenIn: "orai10g6frpysmdgw5tdqke47als6f97aqmr8s3cljsvjce4n5enjftcqtamzsd", + tokenInAmount: "63", + tokenOut: "orai", + tokenOutAmount: "633497", + swapInfo: [ + { + poolId: "orai1fv5kwdv4z0gvp75ht378x8cg2j7prlywa0g35qmctez9q8u4xryspn6lrd", + tokenOut: "orai" } ] } @@ -544,10 +81,10 @@ const router = { const alphaSwapToOraichain = async () => { const wallet = new CosmosWalletImpl(process.env.MNEMONIC); const sender = await wallet.getKeplrAddr("Oraichain"); - const fromAmount = 210; + const fromAmount = 10; console.log("sender: ", sender); - const originalFromToken = flattenTokens.find((t) => t.coinGeckoId === "oraichain-token" && t.chainId === "Oraichain"); - const originalToToken = flattenTokens.find((t) => t.coinGeckoId === "tether" && t.chainId === "0x38"); + const originalFromToken = flattenTokens.find((t) => t.coinGeckoId === "tether" && t.chainId === "0x01"); + const originalToToken = flattenTokens.find((t) => t.coinGeckoId === "oraichain-token" && t.chainId === "Oraichain"); if (!originalToToken) throw generateError("Could not find original to token"); if (!originalFromToken) throw generateError("Could not find original from token"); @@ -566,13 +103,13 @@ const alphaSwapToOraichain = async () => { // recipientAddress: "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2", // recipientAddress: "osmo12zyu8w93h0q2lcnt50g3fn0w3yqnhy4fh4twhr", // recipientAddress: "inj133lq4pqjdxspcz4n388glv70z59ffeuh3ktnaj", - simulatePrice: "5930687", + simulatePrice: "162461", simulateAmount: toAmount(fromAmount, originalToToken.decimals).toString(), alphaSmartRoutes: router }, { cosmosWallet: wallet, - swapOptions: { isAlphaSmartRouter: true, isIbcWasm: true } + swapOptions: { isIbcWasm: false, isAlphaIbcWasm: true } } ); diff --git a/packages/universal-swap/tests/helper.spec.ts b/packages/universal-swap/tests/helper.spec.ts index 759c1474..0277d7cb 100644 --- a/packages/universal-swap/tests/helper.spec.ts +++ b/packages/universal-swap/tests/helper.spec.ts @@ -586,7 +586,7 @@ describe("test helper functions", () => { it("test-addOraiBridgeRoute-empty-swapRoute", async () => { vi.spyOn(UniversalSwapHelper, "getRouteV2").mockResolvedValue(""); const result = await UniversalSwapHelper.addOraiBridgeRoute( - "receiver", + { sourceReceiver: "receiver" }, { contractAddress: "any" } as any, undefined as any, "0", @@ -599,9 +599,16 @@ describe("test helper functions", () => { }); it("test-addOraiBridgeRoute-empty-sourceReceiver", async () => { await expect( - UniversalSwapHelper.addOraiBridgeRoute("", undefined as any, undefined as any, "0", undefined, { - isSourceReceiverTest: false - }) + UniversalSwapHelper.addOraiBridgeRoute( + { sourceReceiver: "" }, + undefined as any, + undefined as any, + "0", + undefined, + { + isSourceReceiverTest: false + } + ) ).rejects.toThrow(); }); From 14883e414ee6be8c2af78861ccd3c5c4ba36ff70 Mon Sep 17 00:00:00 2001 From: Hau Nguyen Van Date: Fri, 4 Oct 2024 18:14:16 +0700 Subject: [PATCH 14/29] 1.1.13 universal swap --- packages/universal-swap/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/universal-swap/package.json b/packages/universal-swap/package.json index 05af6883..34270ce5 100644 --- a/packages/universal-swap/package.json +++ b/packages/universal-swap/package.json @@ -1,6 +1,6 @@ { "name": "@oraichain/oraidex-universal-swap", - "version": "1.1.12", + "version": "1.1.13", "main": "build/index.js", "files": [ "build/" From 3e7112c1ad4b94c18dd6810654156ffd6572cce8 Mon Sep 17 00:00:00 2001 From: trung2891 Date: Sat, 5 Oct 2024 11:51:35 +0700 Subject: [PATCH 15/29] fix: wrong chainID of obridge in build msg swap --- packages/universal-swap/src/msg/msgs.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/universal-swap/src/msg/msgs.ts b/packages/universal-swap/src/msg/msgs.ts index 3219d70a..8a17c651 100644 --- a/packages/universal-swap/src/msg/msgs.ts +++ b/packages/universal-swap/src/msg/msgs.ts @@ -38,7 +38,7 @@ const buildMemoSwap = ( switch (currentChain) { case "Oraichain": { let prefix = getDestPrefixForBridgeToEvmOnOrai(path.tokenOutChainId); - let oBridgeAddress = addresses["OraiBridge"]; + let oBridgeAddress = addresses["oraibridge-subnet-2"]; let oraichainMsg = new OraichainMsg(path, "1", receiver, currentAddress, memo, prefix, oBridgeAddress); oraichainMsg.setMinimumReceiveForSwap(slippage); // we have 2 cases: From bfba1b805705ed7b8523c0e85ee4539d6d1437af Mon Sep 17 00:00:00 2001 From: trung2891 Date: Mon, 7 Oct 2024 08:51:03 +0700 Subject: [PATCH 16/29] chore: update test case for build swap msg --- .../universal-swap/src/msg/chains/cosmos.ts | 9 +- .../src/msg/chains/oraichain.ts | 8 +- .../universal-swap/src/msg/chains/osmosis.ts | 13 +- packages/universal-swap/src/msg/msgs.ts | 2 +- .../tests/msg/comos-msg.spec.ts | 220 ++++++++++ .../tests/msg/oraichain-msg.spec.ts | 375 ++++++++++++++++++ .../tests/msg/osmosis-msg.spec.ts | 375 ++++++++++++++++++ .../universal-swap/tests/msg/test-data.ts | 9 + 8 files changed, 1000 insertions(+), 11 deletions(-) create mode 100644 packages/universal-swap/tests/msg/comos-msg.spec.ts create mode 100644 packages/universal-swap/tests/msg/oraichain-msg.spec.ts create mode 100644 packages/universal-swap/tests/msg/osmosis-msg.spec.ts create mode 100644 packages/universal-swap/tests/msg/test-data.ts diff --git a/packages/universal-swap/src/msg/chains/cosmos.ts b/packages/universal-swap/src/msg/chains/cosmos.ts index c34f0e34..1aed18ca 100644 --- a/packages/universal-swap/src/msg/chains/cosmos.ts +++ b/packages/universal-swap/src/msg/chains/cosmos.ts @@ -31,7 +31,7 @@ export class CosmosMsg extends ChainMsg { } /** - * Function to build msg swap on Oraichain + * Function to get IBC info of Cosmos-base ecosystem */ getBridgeInfo(): BridgeMsgInfo { let bridgeInfo: BridgeMsgInfo; @@ -58,6 +58,11 @@ export class CosmosMsg extends ChainMsg { } } + // check bridge type must be ibc bridge + if (bridgeInfo.sourcePort != "transfer") { + throw generateError(`Only support IBC bridge on ${this.path.chainId}`); + } + return bridgeInfo; } @@ -109,7 +114,7 @@ export class CosmosMsg extends ChainMsg { } /** - * Function to generate execute msg on Osmosis + * Function to generate execute msg on Cosmos-base network */ genExecuteMsg(): EncodeObject { diff --git a/packages/universal-swap/src/msg/chains/oraichain.ts b/packages/universal-swap/src/msg/chains/oraichain.ts index 3f585cbc..95c2cef2 100644 --- a/packages/universal-swap/src/msg/chains/oraichain.ts +++ b/packages/universal-swap/src/msg/chains/oraichain.ts @@ -184,7 +184,7 @@ export class OraichainMsg extends ChainMsg { return { ibcWasmTransferMsg: { localChannelId: bridgeInfo.sourceChannel, - remoteAddress: prefix + isBridgeToEvm ? this.obridgeAddress : this.receiver, + remoteAddress: isBridgeToEvm ? this.obridgeAddress : this.receiver, remoteDenom: prefix + bridgeInfo.toToken, memo: isBridgeToEvm ? prefix + this.receiver : this.memo } @@ -266,7 +266,7 @@ export class OraichainMsg extends ChainMsg { ibc_wasm_transfer: { ibc_wasm_info: { local_channel_id: bridgeInfo.sourceChannel, - remote_address: prefix + isBridgeToEvm ? this.obridgeAddress : this.receiver, + remote_address: isBridgeToEvm ? this.obridgeAddress : this.receiver, remote_denom: prefix + bridgeInfo.toToken, memo: isBridgeToEvm ? prefix + this.receiver : this.memo } @@ -397,10 +397,10 @@ export class OraichainMsg extends ChainMsg { const msg: TransferBackMsg = { local_channel_id: bridgeInfo.sourceChannel, - remote_address: prefix + isBridgeToEvm ? this.obridgeAddress : this.receiver, + remote_address: isBridgeToEvm ? this.obridgeAddress : this.receiver, remote_denom: prefix + bridgeInfo.toToken, timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), // FIXME: should we use nano with an u64 type? -> probably quite big for a u64 - memo: this.memo + memo: isBridgeToEvm ? prefix + this.receiver : this.memo }; // if asset info is native => send native way, else send cw20 way diff --git a/packages/universal-swap/src/msg/chains/osmosis.ts b/packages/universal-swap/src/msg/chains/osmosis.ts index 6a3debb8..fa13e93a 100644 --- a/packages/universal-swap/src/msg/chains/osmosis.ts +++ b/packages/universal-swap/src/msg/chains/osmosis.ts @@ -43,7 +43,7 @@ export class OsmosisMsg extends ChainMsg { } /** - * Function to build msg swap on Oraichain + * Function to build msg swap on Osmosis */ getSwapAndBridgeInfo(): [SwapOperation[], BridgeMsgInfo] { let swapOps: SwapOperation[] = []; @@ -68,6 +68,11 @@ export class OsmosisMsg extends ChainMsg { break; } case ActionType.Bridge: { + // check action is ibc bridge + if (action.bridgeInfo.port != "transfer") { + throw generateError(`Only support IBC bridge on ${this.path.chainId}`); + } + bridgeInfo = { amount: action.tokenInAmount, sourceChannel: action.bridgeInfo.channel, @@ -114,10 +119,10 @@ export class OsmosisMsg extends ChainMsg { }; } - throw generateError("Missing postAction for universalSwap on Oraichain"); + throw generateError(`Missing postAction for universalSwap on ${this.path.chainId}`); } /** - * Function to generate memo for action on oraichain as middleware + * Function to generate memo for action on Osmosis as middleware */ genMemoAsMiddleware(): MiddlewareResponse { let [swapOps, bridgeInfo] = this.getSwapAndBridgeInfo(); @@ -220,7 +225,7 @@ export class OsmosisMsg extends ChainMsg { }; } - throw generateError("Error on generate executeMsg on Oraichain: Only support ibc transfer"); + throw generateError("Error on generate executeMsg on Osmosis: Only support ibc transfer"); } let tokenOutOfSwap = swapOps[swapOps.length - 1].denom_out; diff --git a/packages/universal-swap/src/msg/msgs.ts b/packages/universal-swap/src/msg/msgs.ts index 8a17c651..7ba12298 100644 --- a/packages/universal-swap/src/msg/msgs.ts +++ b/packages/universal-swap/src/msg/msgs.ts @@ -84,7 +84,7 @@ const buildExecuteMsg = ( switch (currentChain) { case "Oraichain": { let prefix = getDestPrefixForBridgeToEvmOnOrai(path.tokenOutChainId); - let oBridgeAddress = addresses["OraiBridge"]; + let oBridgeAddress = addresses["oraibridge-subnet-2"]; let oraichainMsg = new OraichainMsg(path, "1", receiver, currentAddress, memo, prefix, oBridgeAddress); oraichainMsg.setMinimumReceiveForSwap(slippage); return oraichainMsg.genExecuteMsg(); diff --git a/packages/universal-swap/tests/msg/comos-msg.spec.ts b/packages/universal-swap/tests/msg/comos-msg.spec.ts new file mode 100644 index 00000000..370a5ade --- /dev/null +++ b/packages/universal-swap/tests/msg/comos-msg.spec.ts @@ -0,0 +1,220 @@ +import { expect, afterAll, beforeAll, describe, it, vi } from "vitest"; +import { BridgeMsgInfo, CosmosMsg } from "../../src/msg"; +import { calculateTimeoutTimestamp, generateError, IBC_TRANSFER_TIMEOUT } from "@oraichain/oraidex-common"; +import { Action } from "@oraichain/osor-api-contracts-sdk/src/EntryPoint.types"; + +describe("test build cosmos msg", () => { + const validPath = { + chainId: "cosmoshub-4", + tokenIn: "uatom", + tokenInAmount: "217432", + tokenOut: "ibc/A2E2EEC9057A4A1C2C0A6A4C78B0239118DF5F278830F50B4A6BDD7A66506B78", + tokenOutAmount: "217432", + tokenOutChainId: "Oraichain", + actions: [ + { + type: "Bridge", + protocol: "Bridge", + tokenIn: "uatom", + tokenInAmount: "217432", + tokenOut: "ibc/A2E2EEC9057A4A1C2C0A6A4C78B0239118DF5F278830F50B4A6BDD7A66506B78", + tokenOutAmount: "217432", + tokenOutChainId: "Oraichain", + bridgeInfo: { + port: "transfer", + channel: "channel-301" + } + } + ] + }; + + const receiver = "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2"; + const currentAddress = "cosmos1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y0ppr3e"; + + it.each<[BridgeMsgInfo, Action, string]>([ + [ + undefined, + { + transfer: { + to_address: receiver + } + }, + "" + ], + [ + { + amount: "217432", + sourceChannel: "channel-301", + sourcePort: "transfer", + memo: "{}", + receiver: "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2", + timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + fromToken: "uatom", + toToken: "ibc/A2E2EEC9057A4A1C2C0A6A4C78B0239118DF5F278830F50B4A6BDD7A66506B78", + fromChain: "cosmoshub-4", + toChain: "Oraichain" + }, + { + ibc_transfer: { + ibc_info: { + source_channel: "channel-301", + receiver: receiver, + memo: "{}", + recover_address: currentAddress + } + } + }, + "" + ], + [ + { + amount: "217432", + sourceChannel: "channel-301", + sourcePort: "wasm.orai123", + memo: "{}", + receiver: "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2", + timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + fromToken: "uatom", + toToken: "ibc/A2E2EEC9057A4A1C2C0A6A4C78B0239118DF5F278830F50B4A6BDD7A66506B78", + fromChain: "cosmoshub-4", + toChain: "Oraichain" + }, + { + ibc_transfer: { + ibc_info: { + source_channel: "channel-301", + receiver: receiver, + memo: "{}", + recover_address: currentAddress + } + } + }, + "" + ] + ])("cosmos test get post action", (bridgeInfo, expectedAction, expectedError) => { + let cosmos = new CosmosMsg(validPath, "1", receiver, currentAddress); + try { + let postAction = cosmos.getPostAction(bridgeInfo); + expect(postAction).toEqual(expectedAction); + } catch (err) { + expect(err).toEqual(generateError(`Missing postAction for universalSwap on ${validPath.chainId}`)); + } + }); + + it("Valid path", () => { + const nextMemo = "{}"; + let cosmos = new CosmosMsg(validPath, "1", receiver, currentAddress, nextMemo); + + let bridgeInfo = cosmos.getBridgeInfo(); + expect(bridgeInfo).toEqual({ + amount: "217432", + sourceChannel: "channel-301", + sourcePort: "transfer", + memo: "{}", + receiver: "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2", + timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + fromToken: "uatom", + toToken: "ibc/A2E2EEC9057A4A1C2C0A6A4C78B0239118DF5F278830F50B4A6BDD7A66506B78", + fromChain: "cosmoshub-4", + toChain: "Oraichain" + }); + + let memoAsMiddleware = cosmos.genMemoAsMiddleware(); + expect(memoAsMiddleware).toEqual({ + receiver: currentAddress, + memo: JSON.stringify({ + forward: { + receiver: receiver, + port: bridgeInfo.sourcePort, + channel: bridgeInfo.sourceChannel, + timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + retries: 2, + next: nextMemo + } + }) + }); + + let executeMsg = cosmos.genExecuteMsg(); + expect(executeMsg).toEqual({ + typeUrl: "/ibc.applications.transfer.v1.MsgTransfer", + value: { + sourcePort: bridgeInfo.sourcePort, + sourceChannel: bridgeInfo.sourceChannel, + receiver: receiver, + token: { + amount: validPath.tokenInAmount, + denom: validPath.tokenIn + }, + sender: currentAddress, + memo: nextMemo, + timeoutTimestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT) + } + }); + }); + + it("Invalid path", () => { + const nextMemo = "{}"; + const invalidPathNotBridgeAction = { + chainId: "cosmoshub-4", + tokenIn: "uatom", + tokenInAmount: "217432", + tokenOut: "ibc/A2E2EEC9057A4A1C2C0A6A4C78B0239118DF5F278830F50B4A6BDD7A66506B78", + tokenOutAmount: "217432", + tokenOutChainId: "Oraichain", + actions: [ + { + type: "Swap", + protocol: "Oraidex", + tokenIn: "ibc/A2E2EEC9057A4A1C2C0A6A4C78B0239118DF5F278830F50B4A6BDD7A66506B78", + tokenInAmount: "217432", + tokenOut: "orai", + tokenOutAmount: "159680", + swapInfo: [ + { + poolId: "orai1jf74ry4m0jcy9emsaudkhe7vte9l8qy8enakvs", + tokenOut: "orai" + } + ] + } + ] + }; + + let cosmos = new CosmosMsg(invalidPathNotBridgeAction, "1", receiver, currentAddress, nextMemo); + + try { + cosmos.genMemoAsMiddleware(); + } catch (err) { + expect(err).toEqual(generateError(`Only support bridge on ${invalidPathNotBridgeAction.chainId}`)); + } + + const invalidPathNotIbcTransfer = { + chainId: "cosmoshub-4", + tokenIn: "uatom", + tokenInAmount: "217432", + tokenOut: "ibc/A2E2EEC9057A4A1C2C0A6A4C78B0239118DF5F278830F50B4A6BDD7A66506B78", + tokenOutAmount: "217432", + tokenOutChainId: "Oraichain", + actions: [ + { + type: "Bridge", + protocol: "Bridge", + tokenIn: "uatom", + tokenInAmount: "217432", + tokenOut: "ibc/A2E2EEC9057A4A1C2C0A6A4C78B0239118DF5F278830F50B4A6BDD7A66506B78", + tokenOutAmount: "217432", + tokenOutChainId: "Oraichain", + bridgeInfo: { + port: "wasm.cosmos123", + channel: "channel-301" + } + } + ] + }; + try { + cosmos = new CosmosMsg(invalidPathNotIbcTransfer, "1", receiver, currentAddress, nextMemo); + cosmos.genExecuteMsg(); + } catch (err) { + expect(err).toEqual(generateError(`Only support IBC bridge on ${invalidPathNotBridgeAction.chainId}`)); + } + }); +}); diff --git a/packages/universal-swap/tests/msg/oraichain-msg.spec.ts b/packages/universal-swap/tests/msg/oraichain-msg.spec.ts new file mode 100644 index 00000000..4b3dfaae --- /dev/null +++ b/packages/universal-swap/tests/msg/oraichain-msg.spec.ts @@ -0,0 +1,375 @@ +import { expect, afterAll, beforeAll, describe, it, vi } from "vitest"; +import { BridgeMsgInfo, OraichainMsg } from "../../src/msg"; +import { calculateTimeoutTimestamp, generateError, IBC_TRANSFER_TIMEOUT } from "@oraichain/oraidex-common"; +import { Action } from "@oraichain/osor-api-contracts-sdk/src/EntryPoint.types"; +import { OsmosisMsg } from "../../build/msg"; + +describe("test build oraichain msg", () => { + const validPath = { + chainId: "Oraichain", + tokenIn: "orai", + tokenInAmount: "1000000", + tokenOut: "uatom", + tokenOutAmount: "1359212", + tokenOutChainId: "cosmoshub-4", + actions: [ + { + type: "Swap", + protocol: "Oraidex", + tokenIn: "orai", + tokenInAmount: "1000000", + tokenOut: "ibc/A2E2EEC9057A4A1C2C0A6A4C78B0239118DF5F278830F50B4A6BDD7A66506B78", + tokenOutAmount: "1359212", + swapInfo: [ + { + poolId: "orai1jf74ry4m0jcy9emsaudkhe7vte9l8qy8enakvs", + tokenOut: "ibc/A2E2EEC9057A4A1C2C0A6A4C78B0239118DF5F278830F50B4A6BDD7A66506B78" + } + ] + }, + { + type: "Bridge", + protocol: "Bridge", + tokenIn: "ibc/A2E2EEC9057A4A1C2C0A6A4C78B0239118DF5F278830F50B4A6BDD7A66506B78", + tokenInAmount: "1359212", + tokenOut: "uatom", + tokenOutAmount: "1359212", + tokenOutChainId: "cosmoshub-4", + bridgeInfo: { + port: "transfer", + channel: "channel-15" + } + } + ] + }; + let receiver = "cosmos1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y0ppr3e"; + const currentAddress = "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2"; + + it.each<[BridgeMsgInfo, Action, string]>([ + [ + undefined, + { + transfer: { + to_address: receiver + } + }, + "" + ], + [ + { + amount: "217432", + sourceChannel: "channel-301", + sourcePort: "transfer", + memo: "{}", + receiver: receiver, + timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + fromToken: "ibc/A2E2EEC9057A4A1C2C0A6A4C78B0239118DF5F278830F50B4A6BDD7A66506B78", + toToken: "uatom", + fromChain: "Oraichain", + toChain: "cosmoshub-4" + }, + { + ibc_transfer: { + ibc_info: { + source_channel: "channel-301", + receiver: receiver, + memo: "{}", + recover_address: currentAddress + } + } + }, + "" + ], + [ + { + amount: "217432", + sourceChannel: "channel-301", + sourcePort: "wasm.orai123", + memo: "{}", + receiver: receiver, + timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + fromToken: "ibc/A2E2EEC9057A4A1C2C0A6A4C78B0239118DF5F278830F50B4A6BDD7A66506B78", + toToken: "uusdc", + fromChain: "Oraichain", + toChain: "noble-1" + }, + { + ibc_wasm_transfer: { + ibc_wasm_info: { + local_channel_id: "channel-301", + remote_address: "cosmos1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y0ppr3e", + remote_denom: "uusdc", + memo: "" + } + } + }, + "" + ] + ])("oraichain test get post action", (bridgeInfo, expectedAction, expectedError) => { + let oraichain = new OraichainMsg(validPath, "1", receiver, currentAddress); + try { + let postAction = oraichain.getPostAction(bridgeInfo); + expect(postAction).toEqual(expectedAction); + } catch (err) { + expect(err).toEqual(generateError(`Missing postAction for universalSwap on ${validPath.chainId}`)); + } + }); + + // it("Valid path with swap + bridge", () => { + // const nextMemo = "{}"; + // let osmosis = new OsmosisMsg(validPath, "1", receiver, currentAddress, nextMemo); + + // let [swapOps, bridgeInfo] = osmosis.getSwapAndBridgeInfo(); + // expect(bridgeInfo).toEqual({ + // amount: "217432", + // sourceChannel: "channel-0", + // sourcePort: "transfer", + // memo: nextMemo, + // receiver: receiver, + // timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + // fromToken: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + // toToken: "uatom", + // fromChain: "osmosis-1", + // toChain: "cosmoshub-4" + // }); + // expect(swapOps).toEqual([ + // { + // denom_in: "ibc/498A0751C798A0D9A389AA3691123DADA57DAA4FE165D5C75894505B876BA6E4", + // denom_out: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + // pool: "1282" + // } + // ]); + + // let memoAsMiddleware = osmosis.genMemoAsMiddleware(); + // expect(memoAsMiddleware).toEqual({ + // receiver: osmosis.ENTRY_POINT_CONTRACT, + // memo: JSON.stringify({ + // wasm: { + // contract: osmosis.ENTRY_POINT_CONTRACT, + // msg: { + // swap_and_action: { + // user_swap: { + // swap_exact_asset_in: { + // swap_venue_name: osmosis.SWAP_VENUE_NAME, + // operations: [ + // { + // denom_in: "ibc/498A0751C798A0D9A389AA3691123DADA57DAA4FE165D5C75894505B876BA6E4", + // denom_out: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + // pool: "1282" + // } + // ] + // } + // }, + // min_asset: { + // native: { amount: "1", denom: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2" } + // }, + // timeout_timestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + // post_swap_action: { + // ibc_transfer: { + // ibc_info: { + // source_channel: "channel-0", + // receiver: receiver, + // memo: "{}", + // recover_address: currentAddress + // } + // } + // }, + // affiliates: [] + // } + // } + // } + // }) + // }); + + // let executeMsg = osmosis.genExecuteMsg(); + // expect(executeMsg.typeUrl).toEqual("/cosmwasm.wasm.v1.MsgExecuteContract"); + // }); + + // it("Valid path with bridge only", () => { + // const nextMemo = "{}"; + // let validPathBridgeOnly = { + // chainId: "osmosis-1", + // tokenIn: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + // tokenInAmount: "999000", + // tokenOut: "uatom", + // tokenOutAmount: "999000", + // tokenOutChainId: "cosmoshub-4", + // actions: [ + // { + // type: "Bridge", + // protocol: "Bridge", + // tokenIn: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + // tokenInAmount: "999000", + // tokenOut: "uatom", + // tokenOutAmount: "999000", + // tokenOutChainId: "cosmoshub-4", + // bridgeInfo: { + // port: "transfer", + // channel: "channel-0" + // } + // } + // ] + // }; + // let osmosis = new OsmosisMsg(validPathBridgeOnly, "1", receiver, currentAddress, nextMemo); + + // let [swapOps, bridgeInfo] = osmosis.getSwapAndBridgeInfo(); + // expect(bridgeInfo).toEqual({ + // amount: "999000", + // sourceChannel: "channel-0", + // sourcePort: "transfer", + // memo: nextMemo, + // receiver: receiver, + // timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + // fromToken: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + // toToken: "uatom", + // fromChain: "osmosis-1", + // toChain: "cosmoshub-4" + // }); + // expect(swapOps).toEqual([]); + + // let memoAsMiddleware = osmosis.genMemoAsMiddleware(); + // expect(memoAsMiddleware).toEqual({ + // receiver: currentAddress, + // memo: JSON.stringify({ + // forward: { + // receiver: "cosmos1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y0ppr3e", + // port: "transfer", + // channel: "channel-0", + // timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + // retries: 2, + // next: "{}" + // } + // }) + // }); + + // let executeMsg = osmosis.genExecuteMsg(); + // expect(executeMsg).toEqual({ + // typeUrl: "/ibc.applications.transfer.v1.MsgTransfer", + // value: { + // sourcePort: bridgeInfo.sourcePort, + // sourceChannel: bridgeInfo.sourceChannel, + // receiver: receiver, + // token: { + // amount: validPathBridgeOnly.tokenInAmount, + // denom: validPathBridgeOnly.tokenIn + // }, + // sender: currentAddress, + // memo: nextMemo, + // timeoutTimestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT) + // } + // }); + // }); + + // it("Valid path with swap only", () => { + // const nextMemo = "{}"; + // receiver = currentAddress; + // const validPathSwapOnly = { + // chainId: "osmosis-1", + // tokenIn: "ibc/498A0751C798A0D9A389AA3691123DADA57DAA4FE165D5C75894505B876BA6E4", + // tokenInAmount: "999000", + // tokenOut: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + // tokenOutAmount: "217432", + // tokenOutChainId: "osmosis-1", + // actions: [ + // { + // type: "Swap", + // protocol: "Osmosis", + // tokenIn: "ibc/498A0751C798A0D9A389AA3691123DADA57DAA4FE165D5C75894505B876BA6E4", + // tokenInAmount: "999000", + // tokenOut: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + // tokenOutAmount: "217432", + // swapInfo: [ + // { + // poolId: "1282", + // tokenOut: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2" + // } + // ] + // } + // ] + // }; + // let osmosis = new OsmosisMsg(validPathSwapOnly, "1", receiver, currentAddress, nextMemo); + + // let [swapOps, bridgeInfo] = osmosis.getSwapAndBridgeInfo(); + // expect(bridgeInfo).toEqual(undefined); + // expect(swapOps).toEqual([ + // { + // denom_in: "ibc/498A0751C798A0D9A389AA3691123DADA57DAA4FE165D5C75894505B876BA6E4", + // denom_out: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + // pool: "1282" + // } + // ]); + + // let memoAsMiddleware = osmosis.genMemoAsMiddleware(); + // expect(memoAsMiddleware).toEqual({ + // receiver: osmosis.ENTRY_POINT_CONTRACT, + // memo: JSON.stringify({ + // wasm: { + // contract: osmosis.ENTRY_POINT_CONTRACT, + // msg: { + // swap_and_action: { + // user_swap: { + // swap_exact_asset_in: { + // swap_venue_name: osmosis.SWAP_VENUE_NAME, + // operations: [ + // { + // denom_in: "ibc/498A0751C798A0D9A389AA3691123DADA57DAA4FE165D5C75894505B876BA6E4", + // denom_out: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + // pool: "1282" + // } + // ] + // } + // }, + // min_asset: { + // native: { amount: "1", denom: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2" } + // }, + // timeout_timestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + // post_swap_action: { + // transfer: { + // to_address: receiver + // } + // }, + // affiliates: [] + // } + // } + // } + // }) + // }); + + // let executeMsg = osmosis.genExecuteMsg(); + // expect(executeMsg.typeUrl).toEqual("/cosmwasm.wasm.v1.MsgExecuteContract"); + // }); + + // it("Invalid path not ibc transfer", () => { + // const nextMemo = "{}"; + + // const invalidPathNotIbcTransfer = { + // chainId: "osmosis-1", + // tokenIn: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + // tokenInAmount: "999000", + // tokenOut: "uatom", + // tokenOutAmount: "999000", + // tokenOutChainId: "cosmoshub-4", + // actions: [ + // { + // type: "Bridge", + // protocol: "Bridge", + // tokenIn: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + // tokenInAmount: "999000", + // tokenOut: "uatom", + // tokenOutAmount: "999000", + // tokenOutChainId: "cosmoshub-4", + // bridgeInfo: { + // port: "wasm.osmo123", + // channel: "channel-0" + // } + // } + // ] + // }; + // let osmosis = new OsmosisMsg(invalidPathNotIbcTransfer, "1", receiver, currentAddress, nextMemo); + // try { + // osmosis.genMemoAsMiddleware(); + // } catch (err) { + // expect(err).toEqual(generateError(`Only support IBC bridge on ${invalidPathNotIbcTransfer.chainId}`)); + // } + // }); +}); diff --git a/packages/universal-swap/tests/msg/osmosis-msg.spec.ts b/packages/universal-swap/tests/msg/osmosis-msg.spec.ts new file mode 100644 index 00000000..371308b9 --- /dev/null +++ b/packages/universal-swap/tests/msg/osmosis-msg.spec.ts @@ -0,0 +1,375 @@ +import { expect, afterAll, beforeAll, describe, it, vi } from "vitest"; +import { BridgeMsgInfo } from "../../src/msg"; +import { calculateTimeoutTimestamp, generateError, IBC_TRANSFER_TIMEOUT } from "@oraichain/oraidex-common"; +import { Action } from "@oraichain/osor-api-contracts-sdk/src/EntryPoint.types"; +import { OsmosisMsg } from "../../build/msg"; + +describe("test build osmosis msg", () => { + const validPath = { + chainId: "osmosis-1", + tokenIn: "ibc/498A0751C798A0D9A389AA3691123DADA57DAA4FE165D5C75894505B876BA6E4", + tokenInAmount: "999000", + tokenOut: "uatom", + tokenOutAmount: "217432", + tokenOutChainId: "cosmoshub-4", + actions: [ + { + type: "Swap", + protocol: "Osmosis", + tokenIn: "ibc/498A0751C798A0D9A389AA3691123DADA57DAA4FE165D5C75894505B876BA6E4", + tokenInAmount: "999000", + tokenOut: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + tokenOutAmount: "217432", + swapInfo: [ + { + poolId: "1282", + tokenOut: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2" + } + ] + }, + { + type: "Bridge", + protocol: "Bridge", + tokenIn: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + tokenInAmount: "217432", + tokenOut: "uatom", + tokenOutAmount: "217432", + tokenOutChainId: "cosmoshub-4", + bridgeInfo: { + port: "transfer", + channel: "channel-0" + } + } + ] + }; + let receiver = "cosmos1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y0ppr3e"; + const currentAddress = "osmo1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y86jn8t"; + + it.each<[BridgeMsgInfo, Action, string]>([ + [ + undefined, + { + transfer: { + to_address: receiver + } + }, + "" + ], + [ + { + amount: "217432", + sourceChannel: "channel-301", + sourcePort: "transfer", + memo: "{}", + receiver: receiver, + timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + fromToken: "osmo", + toToken: "ibc/A2E2EEC9057A4A1C2C0A6A4C78B0239118DF5F278830F50B4A6BDD7A66506B78", + fromChain: "osmosis-1", + toChain: "cosmoshub-4" + }, + { + ibc_transfer: { + ibc_info: { + source_channel: "channel-301", + receiver: receiver, + memo: "{}", + recover_address: currentAddress + } + } + }, + "" + ], + [ + { + amount: "217432", + sourceChannel: "channel-301", + sourcePort: "wasm.orai123", + memo: "{}", + receiver: receiver, + timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + fromToken: "osmos", + toToken: "ibc/A2E2EEC9057A4A1C2C0A6A4C78B0239118DF5F278830F50B4A6BDD7A66506B78", + fromChain: "osmosis-1", + toChain: "cosmoshub-4" + }, + { + ibc_transfer: { + ibc_info: { + source_channel: "channel-301", + receiver: receiver, + memo: "{}", + recover_address: currentAddress + } + } + }, + "" + ] + ])("osmosis test get post action", (bridgeInfo, expectedAction, expectedError) => { + let osmosis = new OsmosisMsg(validPath, "1", receiver, currentAddress); + try { + let postAction = osmosis.getPostAction(bridgeInfo); + expect(postAction).toEqual(expectedAction); + } catch (err) { + expect(err).toEqual(generateError(`Missing postAction for universalSwap on ${validPath.chainId}`)); + } + }); + + it("Valid path with swap + bridge", () => { + const nextMemo = "{}"; + let osmosis = new OsmosisMsg(validPath, "1", receiver, currentAddress, nextMemo); + + let [swapOps, bridgeInfo] = osmosis.getSwapAndBridgeInfo(); + expect(bridgeInfo).toEqual({ + amount: "217432", + sourceChannel: "channel-0", + sourcePort: "transfer", + memo: nextMemo, + receiver: receiver, + timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + fromToken: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + toToken: "uatom", + fromChain: "osmosis-1", + toChain: "cosmoshub-4" + }); + expect(swapOps).toEqual([ + { + denom_in: "ibc/498A0751C798A0D9A389AA3691123DADA57DAA4FE165D5C75894505B876BA6E4", + denom_out: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + pool: "1282" + } + ]); + + let memoAsMiddleware = osmosis.genMemoAsMiddleware(); + expect(memoAsMiddleware).toEqual({ + receiver: osmosis.ENTRY_POINT_CONTRACT, + memo: JSON.stringify({ + wasm: { + contract: osmosis.ENTRY_POINT_CONTRACT, + msg: { + swap_and_action: { + user_swap: { + swap_exact_asset_in: { + swap_venue_name: osmosis.SWAP_VENUE_NAME, + operations: [ + { + denom_in: "ibc/498A0751C798A0D9A389AA3691123DADA57DAA4FE165D5C75894505B876BA6E4", + denom_out: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + pool: "1282" + } + ] + } + }, + min_asset: { + native: { amount: "1", denom: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2" } + }, + timeout_timestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + post_swap_action: { + ibc_transfer: { + ibc_info: { + source_channel: "channel-0", + receiver: receiver, + memo: "{}", + recover_address: currentAddress + } + } + }, + affiliates: [] + } + } + } + }) + }); + + let executeMsg = osmosis.genExecuteMsg(); + expect(executeMsg.typeUrl).toEqual("/cosmwasm.wasm.v1.MsgExecuteContract"); + }); + + it("Valid path with bridge only", () => { + const nextMemo = "{}"; + let validPathBridgeOnly = { + chainId: "osmosis-1", + tokenIn: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + tokenInAmount: "999000", + tokenOut: "uatom", + tokenOutAmount: "999000", + tokenOutChainId: "cosmoshub-4", + actions: [ + { + type: "Bridge", + protocol: "Bridge", + tokenIn: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + tokenInAmount: "999000", + tokenOut: "uatom", + tokenOutAmount: "999000", + tokenOutChainId: "cosmoshub-4", + bridgeInfo: { + port: "transfer", + channel: "channel-0" + } + } + ] + }; + let osmosis = new OsmosisMsg(validPathBridgeOnly, "1", receiver, currentAddress, nextMemo); + + let [swapOps, bridgeInfo] = osmosis.getSwapAndBridgeInfo(); + expect(bridgeInfo).toEqual({ + amount: "999000", + sourceChannel: "channel-0", + sourcePort: "transfer", + memo: nextMemo, + receiver: receiver, + timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + fromToken: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + toToken: "uatom", + fromChain: "osmosis-1", + toChain: "cosmoshub-4" + }); + expect(swapOps).toEqual([]); + + let memoAsMiddleware = osmosis.genMemoAsMiddleware(); + expect(memoAsMiddleware).toEqual({ + receiver: currentAddress, + memo: JSON.stringify({ + forward: { + receiver: "cosmos1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y0ppr3e", + port: "transfer", + channel: "channel-0", + timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + retries: 2, + next: "{}" + } + }) + }); + + let executeMsg = osmosis.genExecuteMsg(); + expect(executeMsg).toEqual({ + typeUrl: "/ibc.applications.transfer.v1.MsgTransfer", + value: { + sourcePort: bridgeInfo.sourcePort, + sourceChannel: bridgeInfo.sourceChannel, + receiver: receiver, + token: { + amount: validPathBridgeOnly.tokenInAmount, + denom: validPathBridgeOnly.tokenIn + }, + sender: currentAddress, + memo: nextMemo, + timeoutTimestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT) + } + }); + }); + + it("Valid path with swap only", () => { + const nextMemo = "{}"; + receiver = currentAddress; + const validPathSwapOnly = { + chainId: "osmosis-1", + tokenIn: "ibc/498A0751C798A0D9A389AA3691123DADA57DAA4FE165D5C75894505B876BA6E4", + tokenInAmount: "999000", + tokenOut: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + tokenOutAmount: "217432", + tokenOutChainId: "osmosis-1", + actions: [ + { + type: "Swap", + protocol: "Osmosis", + tokenIn: "ibc/498A0751C798A0D9A389AA3691123DADA57DAA4FE165D5C75894505B876BA6E4", + tokenInAmount: "999000", + tokenOut: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + tokenOutAmount: "217432", + swapInfo: [ + { + poolId: "1282", + tokenOut: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2" + } + ] + } + ] + }; + let osmosis = new OsmosisMsg(validPathSwapOnly, "1", receiver, currentAddress, nextMemo); + + let [swapOps, bridgeInfo] = osmosis.getSwapAndBridgeInfo(); + expect(bridgeInfo).toEqual(undefined); + expect(swapOps).toEqual([ + { + denom_in: "ibc/498A0751C798A0D9A389AA3691123DADA57DAA4FE165D5C75894505B876BA6E4", + denom_out: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + pool: "1282" + } + ]); + + let memoAsMiddleware = osmosis.genMemoAsMiddleware(); + expect(memoAsMiddleware).toEqual({ + receiver: osmosis.ENTRY_POINT_CONTRACT, + memo: JSON.stringify({ + wasm: { + contract: osmosis.ENTRY_POINT_CONTRACT, + msg: { + swap_and_action: { + user_swap: { + swap_exact_asset_in: { + swap_venue_name: osmosis.SWAP_VENUE_NAME, + operations: [ + { + denom_in: "ibc/498A0751C798A0D9A389AA3691123DADA57DAA4FE165D5C75894505B876BA6E4", + denom_out: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + pool: "1282" + } + ] + } + }, + min_asset: { + native: { amount: "1", denom: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2" } + }, + timeout_timestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + post_swap_action: { + transfer: { + to_address: receiver + } + }, + affiliates: [] + } + } + } + }) + }); + + let executeMsg = osmosis.genExecuteMsg(); + expect(executeMsg.typeUrl).toEqual("/cosmwasm.wasm.v1.MsgExecuteContract"); + }); + + it("Invalid path not ibc transfer", () => { + const nextMemo = "{}"; + + const invalidPathNotIbcTransfer = { + chainId: "osmosis-1", + tokenIn: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + tokenInAmount: "999000", + tokenOut: "uatom", + tokenOutAmount: "999000", + tokenOutChainId: "cosmoshub-4", + actions: [ + { + type: "Bridge", + protocol: "Bridge", + tokenIn: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + tokenInAmount: "999000", + tokenOut: "uatom", + tokenOutAmount: "999000", + tokenOutChainId: "cosmoshub-4", + bridgeInfo: { + port: "wasm.osmo123", + channel: "channel-0" + } + } + ] + }; + let osmosis = new OsmosisMsg(invalidPathNotIbcTransfer, "1", receiver, currentAddress, nextMemo); + try { + osmosis.genMemoAsMiddleware(); + } catch (err) { + expect(err).toEqual(generateError(`Only support IBC bridge on ${invalidPathNotIbcTransfer.chainId}`)); + } + }); +}); diff --git a/packages/universal-swap/tests/msg/test-data.ts b/packages/universal-swap/tests/msg/test-data.ts new file mode 100644 index 00000000..1611b6d9 --- /dev/null +++ b/packages/universal-swap/tests/msg/test-data.ts @@ -0,0 +1,9 @@ +export const cosmosTestData = [ + { + type: "Valid Path", + path: "", + nextMemo: "{}", + expectedMemo: "" + // expected + } +]; From 267ffcfb3fb8a4473bf0d6404afbf06ea31d85da Mon Sep 17 00:00:00 2001 From: trung2891 Date: Mon, 7 Oct 2024 09:26:39 +0700 Subject: [PATCH 17/29] chore: test build oraichain msg with valid path with swap + ibc bridge --- .../tests/msg/oraichain-msg.spec.ts | 164 +++++++++++------- 1 file changed, 98 insertions(+), 66 deletions(-) diff --git a/packages/universal-swap/tests/msg/oraichain-msg.spec.ts b/packages/universal-swap/tests/msg/oraichain-msg.spec.ts index 4b3dfaae..f5d63e2b 100644 --- a/packages/universal-swap/tests/msg/oraichain-msg.spec.ts +++ b/packages/universal-swap/tests/msg/oraichain-msg.spec.ts @@ -3,6 +3,7 @@ import { BridgeMsgInfo, OraichainMsg } from "../../src/msg"; import { calculateTimeoutTimestamp, generateError, IBC_TRANSFER_TIMEOUT } from "@oraichain/oraidex-common"; import { Action } from "@oraichain/osor-api-contracts-sdk/src/EntryPoint.types"; import { OsmosisMsg } from "../../build/msg"; +import { Memo } from "../../src/proto/universal_swap_memo"; describe("test build oraichain msg", () => { const validPath = { @@ -115,75 +116,106 @@ describe("test build oraichain msg", () => { } }); - // it("Valid path with swap + bridge", () => { - // const nextMemo = "{}"; - // let osmosis = new OsmosisMsg(validPath, "1", receiver, currentAddress, nextMemo); + it("Valid path with swap + ibc bridge", () => { + const nextMemo = "{}"; + let oraichainMsg = new OraichainMsg(validPath, "1", receiver, currentAddress, nextMemo); - // let [swapOps, bridgeInfo] = osmosis.getSwapAndBridgeInfo(); - // expect(bridgeInfo).toEqual({ - // amount: "217432", - // sourceChannel: "channel-0", - // sourcePort: "transfer", - // memo: nextMemo, - // receiver: receiver, - // timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), - // fromToken: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", - // toToken: "uatom", - // fromChain: "osmosis-1", - // toChain: "cosmoshub-4" - // }); - // expect(swapOps).toEqual([ - // { - // denom_in: "ibc/498A0751C798A0D9A389AA3691123DADA57DAA4FE165D5C75894505B876BA6E4", - // denom_out: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", - // pool: "1282" - // } - // ]); + let [swapOps, bridgeInfo] = oraichainMsg.getSwapAndBridgeInfo(); + expect(bridgeInfo).toEqual({ + amount: "1359212", + sourceChannel: "channel-15", + sourcePort: "transfer", + memo: "{}", + receiver: receiver, + timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + fromToken: "ibc/A2E2EEC9057A4A1C2C0A6A4C78B0239118DF5F278830F50B4A6BDD7A66506B78", + toToken: "uatom", + fromChain: "Oraichain", + toChain: "cosmoshub-4" + }); + expect(swapOps).toEqual([ + { + denom_in: "orai", + denom_out: "ibc/A2E2EEC9057A4A1C2C0A6A4C78B0239118DF5F278830F50B4A6BDD7A66506B78", + pool: "orai1jf74ry4m0jcy9emsaudkhe7vte9l8qy8enakvs" + } + ]); - // let memoAsMiddleware = osmosis.genMemoAsMiddleware(); - // expect(memoAsMiddleware).toEqual({ - // receiver: osmosis.ENTRY_POINT_CONTRACT, - // memo: JSON.stringify({ - // wasm: { - // contract: osmosis.ENTRY_POINT_CONTRACT, - // msg: { - // swap_and_action: { - // user_swap: { - // swap_exact_asset_in: { - // swap_venue_name: osmosis.SWAP_VENUE_NAME, - // operations: [ - // { - // denom_in: "ibc/498A0751C798A0D9A389AA3691123DADA57DAA4FE165D5C75894505B876BA6E4", - // denom_out: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", - // pool: "1282" - // } - // ] - // } - // }, - // min_asset: { - // native: { amount: "1", denom: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2" } - // }, - // timeout_timestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), - // post_swap_action: { - // ibc_transfer: { - // ibc_info: { - // source_channel: "channel-0", - // receiver: receiver, - // memo: "{}", - // recover_address: currentAddress - // } - // } - // }, - // affiliates: [] - // } - // } - // } - // }) - // }); + let memoAsMiddleware = oraichainMsg.genMemoAsMiddleware(); + expect(memoAsMiddleware).toEqual({ + receiver: oraichainMsg.ENTRY_POINT_CONTRACT, + memo: JSON.stringify({ + wasm: { + contract: oraichainMsg.ENTRY_POINT_CONTRACT, + msg: { + swap_and_action: { + user_swap: { + swap_exact_asset_in: { + swap_venue_name: oraichainMsg.SWAP_VENUE_NAME, + operations: [ + { + denom_in: "orai", + denom_out: "ibc/A2E2EEC9057A4A1C2C0A6A4C78B0239118DF5F278830F50B4A6BDD7A66506B78", + pool: "orai1jf74ry4m0jcy9emsaudkhe7vte9l8qy8enakvs" + } + ] + } + }, + min_asset: { + native: { amount: "1", denom: "ibc/A2E2EEC9057A4A1C2C0A6A4C78B0239118DF5F278830F50B4A6BDD7A66506B78" } + }, + timeout_timestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + post_swap_action: { + ibc_transfer: { + ibc_info: { + source_channel: "channel-15", + receiver: receiver, + memo: "{}", + recover_address: currentAddress + } + } + }, + affiliates: [] + } + } + } + }) + }); - // let executeMsg = osmosis.genExecuteMsg(); - // expect(executeMsg.typeUrl).toEqual("/cosmwasm.wasm.v1.MsgExecuteContract"); - // }); + let executeMsg = oraichainMsg.genExecuteMsg(); + expect(executeMsg.typeUrl).toEqual("/cosmwasm.wasm.v1.MsgExecuteContract"); + + let memoForIbcWasm = oraichainMsg.genMemoForIbcWasm(); + expect(memoForIbcWasm).toEqual({ + receiver: currentAddress, + memo: Buffer.from( + Memo.encode({ + userSwap: { + swapVenueName: oraichainMsg.SWAP_VENUE_NAME, + swapExactAssetIn: { + operations: swapOps.map((operation) => ({ + poolId: operation.pool, + denomIn: operation.denom_in, + denomOut: operation.denom_out + })) + } + }, + minimumReceive: "1", + timeoutTimestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + postSwapAction: { + ibcTransferMsg: { + sourceChannel: bridgeInfo.sourceChannel, + sourcePort: bridgeInfo.sourcePort, + receiver: bridgeInfo.receiver, + memo: bridgeInfo.memo, + recoverAddress: currentAddress + } + }, + recoveryAddr: currentAddress + }).finish() + ).toString("base64") + }); + }); // it("Valid path with bridge only", () => { // const nextMemo = "{}"; From 09ad0e0a0a844556ea70498063097638c5c343c3 Mon Sep 17 00:00:00 2001 From: trung2891 Date: Mon, 7 Oct 2024 10:40:31 +0700 Subject: [PATCH 18/29] chore: test gen msg on oraichain --- .../tests/msg/oraichain-msg.spec.ts | 520 +++++++++++++----- 1 file changed, 374 insertions(+), 146 deletions(-) diff --git a/packages/universal-swap/tests/msg/oraichain-msg.spec.ts b/packages/universal-swap/tests/msg/oraichain-msg.spec.ts index f5d63e2b..ddcea33b 100644 --- a/packages/universal-swap/tests/msg/oraichain-msg.spec.ts +++ b/packages/universal-swap/tests/msg/oraichain-msg.spec.ts @@ -217,159 +217,387 @@ describe("test build oraichain msg", () => { }); }); - // it("Valid path with bridge only", () => { - // const nextMemo = "{}"; - // let validPathBridgeOnly = { - // chainId: "osmosis-1", - // tokenIn: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", - // tokenInAmount: "999000", - // tokenOut: "uatom", - // tokenOutAmount: "999000", - // tokenOutChainId: "cosmoshub-4", - // actions: [ - // { - // type: "Bridge", - // protocol: "Bridge", - // tokenIn: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", - // tokenInAmount: "999000", - // tokenOut: "uatom", - // tokenOutAmount: "999000", - // tokenOutChainId: "cosmoshub-4", - // bridgeInfo: { - // port: "transfer", - // channel: "channel-0" - // } - // } - // ] - // }; - // let osmosis = new OsmosisMsg(validPathBridgeOnly, "1", receiver, currentAddress, nextMemo); + it("Valid path with ibc bridge only", () => { + const nextMemo = "{}"; + let validPathBridgeOnly = { + chainId: "Oraichain", + tokenIn: "ibc/A2E2EEC9057A4A1C2C0A6A4C78B0239118DF5F278830F50B4A6BDD7A66506B78", + tokenInAmount: "999000", + tokenOut: "uatom", + tokenOutAmount: "999000", + tokenOutChainId: "cosmoshub-4", + actions: [ + { + type: "Bridge", + protocol: "Bridge", + tokenIn: "ibc/A2E2EEC9057A4A1C2C0A6A4C78B0239118DF5F278830F50B4A6BDD7A66506B78", + tokenInAmount: "999000", + tokenOut: "uatom", + tokenOutAmount: "999000", + tokenOutChainId: "cosmoshub-4", + bridgeInfo: { + port: "transfer", + channel: "channel-15" + } + } + ] + }; + let oraichainMsg = new OraichainMsg(validPathBridgeOnly, "1", receiver, currentAddress, nextMemo); - // let [swapOps, bridgeInfo] = osmosis.getSwapAndBridgeInfo(); - // expect(bridgeInfo).toEqual({ - // amount: "999000", - // sourceChannel: "channel-0", - // sourcePort: "transfer", - // memo: nextMemo, - // receiver: receiver, - // timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), - // fromToken: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", - // toToken: "uatom", - // fromChain: "osmosis-1", - // toChain: "cosmoshub-4" - // }); - // expect(swapOps).toEqual([]); + let [swapOps, bridgeInfo] = oraichainMsg.getSwapAndBridgeInfo(); + expect(bridgeInfo).toEqual({ + amount: "999000", + sourceChannel: "channel-15", + sourcePort: "transfer", + memo: nextMemo, + receiver: receiver, + timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + fromToken: "ibc/A2E2EEC9057A4A1C2C0A6A4C78B0239118DF5F278830F50B4A6BDD7A66506B78", + toToken: "uatom", + fromChain: "Oraichain", + toChain: "cosmoshub-4" + }); + expect(swapOps).toEqual([]); - // let memoAsMiddleware = osmosis.genMemoAsMiddleware(); - // expect(memoAsMiddleware).toEqual({ - // receiver: currentAddress, - // memo: JSON.stringify({ - // forward: { - // receiver: "cosmos1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y0ppr3e", - // port: "transfer", - // channel: "channel-0", - // timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), - // retries: 2, - // next: "{}" - // } - // }) - // }); + let memoAsMiddleware = oraichainMsg.genMemoAsMiddleware(); + expect(memoAsMiddleware).toEqual({ + receiver: currentAddress, + memo: JSON.stringify({ + forward: { + receiver: "cosmos1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y0ppr3e", + port: "transfer", + channel: "channel-15", + timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + retries: 2, + next: "{}" + } + }) + }); - // let executeMsg = osmosis.genExecuteMsg(); - // expect(executeMsg).toEqual({ - // typeUrl: "/ibc.applications.transfer.v1.MsgTransfer", - // value: { - // sourcePort: bridgeInfo.sourcePort, - // sourceChannel: bridgeInfo.sourceChannel, - // receiver: receiver, - // token: { - // amount: validPathBridgeOnly.tokenInAmount, - // denom: validPathBridgeOnly.tokenIn - // }, - // sender: currentAddress, - // memo: nextMemo, - // timeoutTimestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT) - // } - // }); - // }); + let executeMsg = oraichainMsg.genExecuteMsg(); + expect(executeMsg).toEqual({ + typeUrl: "/ibc.applications.transfer.v1.MsgTransfer", + value: { + sourcePort: bridgeInfo.sourcePort, + sourceChannel: bridgeInfo.sourceChannel, + receiver: receiver, + token: { + amount: validPathBridgeOnly.tokenInAmount, + denom: validPathBridgeOnly.tokenIn + }, + sender: currentAddress, + memo: nextMemo, + timeoutTimestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT) + } + }); - // it("Valid path with swap only", () => { - // const nextMemo = "{}"; - // receiver = currentAddress; - // const validPathSwapOnly = { - // chainId: "osmosis-1", - // tokenIn: "ibc/498A0751C798A0D9A389AA3691123DADA57DAA4FE165D5C75894505B876BA6E4", - // tokenInAmount: "999000", - // tokenOut: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", - // tokenOutAmount: "217432", - // tokenOutChainId: "osmosis-1", - // actions: [ - // { - // type: "Swap", - // protocol: "Osmosis", - // tokenIn: "ibc/498A0751C798A0D9A389AA3691123DADA57DAA4FE165D5C75894505B876BA6E4", - // tokenInAmount: "999000", - // tokenOut: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", - // tokenOutAmount: "217432", - // swapInfo: [ - // { - // poolId: "1282", - // tokenOut: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2" - // } - // ] - // } - // ] - // }; - // let osmosis = new OsmosisMsg(validPathSwapOnly, "1", receiver, currentAddress, nextMemo); + let memoForIbcWasm = oraichainMsg.genMemoForIbcWasm(); + expect(memoForIbcWasm).toEqual({ + receiver: currentAddress, + memo: Buffer.from( + Memo.encode({ + userSwap: undefined, + minimumReceive: "1", + timeoutTimestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + postSwapAction: { + ibcTransferMsg: { + sourceChannel: bridgeInfo.sourceChannel, + sourcePort: bridgeInfo.sourcePort, + receiver: bridgeInfo.receiver, + memo: bridgeInfo.memo, + recoverAddress: currentAddress + } + }, + recoveryAddr: currentAddress + }).finish() + ).toString("base64") + }); + }); - // let [swapOps, bridgeInfo] = osmosis.getSwapAndBridgeInfo(); - // expect(bridgeInfo).toEqual(undefined); - // expect(swapOps).toEqual([ - // { - // denom_in: "ibc/498A0751C798A0D9A389AA3691123DADA57DAA4FE165D5C75894505B876BA6E4", - // denom_out: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", - // pool: "1282" - // } - // ]); + it("Valid path with swap only", () => { + const nextMemo = "{}"; + receiver = currentAddress; + const validPathSwapOnly = { + chainId: "Oraichain", + tokenIn: "orai", + tokenInAmount: "999000", + tokenOut: "ibc/A2E2EEC9057A4A1C2C0A6A4C78B0239118DF5F278830F50B4A6BDD7A66506B78", + tokenOutAmount: "217432", + tokenOutChainId: "Oraichain", + actions: [ + { + type: "Swap", + protocol: "Oraidex", + tokenIn: "orai", + tokenInAmount: "1000000", + tokenOut: "ibc/A2E2EEC9057A4A1C2C0A6A4C78B0239118DF5F278830F50B4A6BDD7A66506B78", + tokenOutAmount: "1359212", + swapInfo: [ + { + poolId: "orai1jf74ry4m0jcy9emsaudkhe7vte9l8qy8enakvs", + tokenOut: "ibc/A2E2EEC9057A4A1C2C0A6A4C78B0239118DF5F278830F50B4A6BDD7A66506B78" + } + ] + } + ] + }; + let oraichainMsg = new OraichainMsg(validPathSwapOnly, "1", receiver, currentAddress, nextMemo); - // let memoAsMiddleware = osmosis.genMemoAsMiddleware(); - // expect(memoAsMiddleware).toEqual({ - // receiver: osmosis.ENTRY_POINT_CONTRACT, - // memo: JSON.stringify({ - // wasm: { - // contract: osmosis.ENTRY_POINT_CONTRACT, - // msg: { - // swap_and_action: { - // user_swap: { - // swap_exact_asset_in: { - // swap_venue_name: osmosis.SWAP_VENUE_NAME, - // operations: [ - // { - // denom_in: "ibc/498A0751C798A0D9A389AA3691123DADA57DAA4FE165D5C75894505B876BA6E4", - // denom_out: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", - // pool: "1282" - // } - // ] - // } - // }, - // min_asset: { - // native: { amount: "1", denom: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2" } - // }, - // timeout_timestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), - // post_swap_action: { - // transfer: { - // to_address: receiver - // } - // }, - // affiliates: [] - // } - // } - // } - // }) - // }); + let [swapOps, bridgeInfo] = oraichainMsg.getSwapAndBridgeInfo(); + expect(bridgeInfo).toEqual(undefined); + expect(swapOps).toEqual([ + { + denom_in: "orai", + denom_out: "ibc/A2E2EEC9057A4A1C2C0A6A4C78B0239118DF5F278830F50B4A6BDD7A66506B78", + pool: "orai1jf74ry4m0jcy9emsaudkhe7vte9l8qy8enakvs" + } + ]); - // let executeMsg = osmosis.genExecuteMsg(); - // expect(executeMsg.typeUrl).toEqual("/cosmwasm.wasm.v1.MsgExecuteContract"); - // }); + let memoAsMiddleware = oraichainMsg.genMemoAsMiddleware(); + expect(memoAsMiddleware).toEqual({ + receiver: oraichainMsg.ENTRY_POINT_CONTRACT, + memo: JSON.stringify({ + wasm: { + contract: oraichainMsg.ENTRY_POINT_CONTRACT, + msg: { + swap_and_action: { + user_swap: { + swap_exact_asset_in: { + swap_venue_name: oraichainMsg.SWAP_VENUE_NAME, + operations: [ + { + denom_in: "orai", + denom_out: "ibc/A2E2EEC9057A4A1C2C0A6A4C78B0239118DF5F278830F50B4A6BDD7A66506B78", + pool: "orai1jf74ry4m0jcy9emsaudkhe7vte9l8qy8enakvs" + } + ] + } + }, + min_asset: { + native: { amount: "1", denom: "ibc/A2E2EEC9057A4A1C2C0A6A4C78B0239118DF5F278830F50B4A6BDD7A66506B78" } + }, + timeout_timestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + post_swap_action: { + transfer: { + to_address: receiver + } + }, + affiliates: [] + } + } + } + }) + }); + + let executeMsg = oraichainMsg.genExecuteMsg(); + expect(executeMsg.typeUrl).toEqual("/cosmwasm.wasm.v1.MsgExecuteContract"); + + let memoForIbcWasm = oraichainMsg.genMemoForIbcWasm(); + expect(memoForIbcWasm).toEqual({ + receiver: currentAddress, + memo: Buffer.from( + Memo.encode({ + userSwap: { + swapVenueName: oraichainMsg.SWAP_VENUE_NAME, + swapExactAssetIn: { + operations: swapOps.map((operation) => ({ + poolId: operation.pool, + denomIn: operation.denom_in, + denomOut: operation.denom_out + })) + } + }, + minimumReceive: "1", + timeoutTimestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + postSwapAction: { + transferMsg: { + toAddress: receiver + } + }, + recoveryAddr: currentAddress + }).finish() + ).toString("base64") + }); + }); + + it("Valid path with swap + ibc wasm bridge", () => { + const nextMemo = "{}"; + const validPath = { + chainId: "Oraichain", + tokenIn: "orai", + tokenInAmount: "150000", + tokenOut: "0x55d398326f99059fF775485246999027B3197955", + tokenOutAmount: "306106", + tokenOutChainId: "0x38", + actions: [ + { + type: "Swap", + protocol: "Oraidex", + tokenIn: "orai", + tokenInAmount: "150000", + tokenOut: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh", + tokenOutAmount: "936043", + swapInfo: [ + { + poolId: "orai1c5s03c3l336dgesne7dylnmhszw8554tsyy9yt", + tokenOut: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh" + } + ] + }, + { + type: "Bridge", + protocol: "Bridge", + tokenIn: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh", + tokenInAmount: "936043", + tokenOut: "0x55d398326f99059fF775485246999027B3197955", + tokenOutAmount: "306106", + tokenOutChainId: "0x38", + bridgeInfo: { + port: "wasm.orai195269awwnt5m6c843q6w7hp8rt0k7syfu9de4h0wz384slshuzps8y7ccm", + channel: "channel-29" + } + } + ] + }; + + let receiver = "0x0000000000000000000000000000000000000000"; + const currentAddress = "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2"; + const oraiBridgeAddr = "oraib1hvr9d72r5um9lvt0rpkd4r75vrsqtw6ytnnvpf"; + let oraichainMsg = new OraichainMsg(validPath, "1", receiver, currentAddress, nextMemo, "oraib", oraiBridgeAddr); + + let [swapOps, bridgeInfo] = oraichainMsg.getSwapAndBridgeInfo(); + expect(bridgeInfo).toEqual({ + amount: "936043", + sourceChannel: "channel-29", + sourcePort: "wasm.orai195269awwnt5m6c843q6w7hp8rt0k7syfu9de4h0wz384slshuzps8y7ccm", + memo: "{}", + receiver: receiver, + timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + fromToken: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh", + toToken: "0x55d398326f99059fF775485246999027B3197955", + fromChain: "Oraichain", + toChain: "0x38" + }); + expect(swapOps).toEqual([ + { + denom_in: "orai", + denom_out: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh", + pool: "orai1c5s03c3l336dgesne7dylnmhszw8554tsyy9yt" + } + ]); + + let memoAsMiddleware = oraichainMsg.genMemoAsMiddleware(); + expect(memoAsMiddleware).toEqual({ + receiver: oraichainMsg.ENTRY_POINT_CONTRACT, + memo: JSON.stringify({ + wasm: { + contract: oraichainMsg.ENTRY_POINT_CONTRACT, + msg: { + swap_and_action: { + user_swap: { + swap_exact_asset_in: { + swap_venue_name: oraichainMsg.SWAP_VENUE_NAME, + operations: [ + { + denom_in: "orai", + denom_out: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh", + pool: "orai1c5s03c3l336dgesne7dylnmhszw8554tsyy9yt" + } + ] + } + }, + min_asset: { + cw20: { amount: "1", address: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh" } + }, + timeout_timestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + post_swap_action: { + ibc_wasm_transfer: { + ibc_wasm_info: { + local_channel_id: "channel-29", + remote_address: oraiBridgeAddr, + remote_denom: "oraib0x55d398326f99059fF775485246999027B3197955", + memo: "oraib0x0000000000000000000000000000000000000000" + } + } + }, + affiliates: [] + } + } + } + }) + }); + + let executeMsg = oraichainMsg.genExecuteMsg(); + expect(executeMsg.typeUrl).toEqual("/cosmwasm.wasm.v1.MsgExecuteContract"); + + let memoForIbcWasm = oraichainMsg.genMemoForIbcWasm(); + expect(memoForIbcWasm).toEqual({ + receiver: currentAddress, + memo: Buffer.from( + Memo.encode({ + userSwap: { + swapVenueName: oraichainMsg.SWAP_VENUE_NAME, + swapExactAssetIn: { + operations: swapOps.map((operation) => ({ + poolId: operation.pool, + denomIn: operation.denom_in, + denomOut: operation.denom_out + })) + } + }, + minimumReceive: "1", + timeoutTimestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + postSwapAction: { + ibcWasmTransferMsg: { + localChannelId: bridgeInfo.sourceChannel, + remoteAddress: oraiBridgeAddr, + remoteDenom: "oraib0x55d398326f99059fF775485246999027B3197955", + memo: "oraib0x0000000000000000000000000000000000000000" + } + }, + recoveryAddr: currentAddress + }).finish() + ).toString("base64") + }); + }); + + it("Invalid path in gen memo as middleware with ibc wasm bridge", () => { + const nextMemo = "{}"; + const invalidPath = { + chainId: "Oraichain", + tokenIn: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh", + tokenInAmount: "936043", + tokenOut: "0x55d398326f99059fF775485246999027B3197955", + tokenOutAmount: "936043", + tokenOutChainId: "0x38", + actions: [ + { + type: "Bridge", + protocol: "Bridge", + tokenIn: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh", + tokenInAmount: "936043", + tokenOut: "0x55d398326f99059fF775485246999027B3197955", + tokenOutAmount: "306106", + tokenOutChainId: "0x38", + bridgeInfo: { + port: "wasm.orai195269awwnt5m6c843q6w7hp8rt0k7syfu9de4h0wz384slshuzps8y7ccm", + channel: "channel-29" + } + } + ] + }; + + let receiver = "0x0000000000000000000000000000000000000000"; + const currentAddress = "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2"; + const oraiBridgeAddr = "oraib1hvr9d72r5um9lvt0rpkd4r75vrsqtw6ytnnvpf"; + let oraichainMsg = new OraichainMsg(invalidPath, "1", receiver, currentAddress, nextMemo, "oraib", oraiBridgeAddr); + + try { + oraichainMsg.genMemoAsMiddleware(); + } catch (err) { + expect(err).toEqual(generateError(`Error on generate memo as middleware: Only support ibc bridge`)); + } + }); // it("Invalid path not ibc transfer", () => { // const nextMemo = "{}"; From 10dfe29c7d2d8033e203ea91e502c1ec0af8a4f0 Mon Sep 17 00:00:00 2001 From: trung2891 Date: Mon, 7 Oct 2024 10:56:10 +0700 Subject: [PATCH 19/29] fix: wrong parse ibcWasmContractAddress from source port --- .../src/msg/chains/oraichain.ts | 2 +- .../tests/msg/oraichain-msg.spec.ts | 71 +++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/packages/universal-swap/src/msg/chains/oraichain.ts b/packages/universal-swap/src/msg/chains/oraichain.ts index 95c2cef2..a83ca9ce 100644 --- a/packages/universal-swap/src/msg/chains/oraichain.ts +++ b/packages/universal-swap/src/msg/chains/oraichain.ts @@ -391,7 +391,7 @@ export class OraichainMsg extends ChainMsg { prefix = this.destPrefix; } - const ibcWasmContractAddress = bridgeInfo.sourceChannel.split(".")[1]; + const ibcWasmContractAddress = bridgeInfo.sourcePort.split(".")[1]; if (!ibcWasmContractAddress) throw generateError("IBC Wasm source port is invalid. Cannot transfer to the destination chain"); diff --git a/packages/universal-swap/tests/msg/oraichain-msg.spec.ts b/packages/universal-swap/tests/msg/oraichain-msg.spec.ts index ddcea33b..9bc44680 100644 --- a/packages/universal-swap/tests/msg/oraichain-msg.spec.ts +++ b/packages/universal-swap/tests/msg/oraichain-msg.spec.ts @@ -599,6 +599,77 @@ describe("test build oraichain msg", () => { } }); + it("Valid path in genMemoForIbcWasm with ibc wasm bridge only", () => { + const nextMemo = "{}"; + const validPath = { + chainId: "Oraichain", + tokenIn: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh", + tokenInAmount: "936043", + tokenOut: "0x55d398326f99059fF775485246999027B3197955", + tokenOutAmount: "306106", + tokenOutChainId: "0x38", + actions: [ + { + type: "Bridge", + protocol: "Bridge", + tokenIn: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh", + tokenInAmount: "936043", + tokenOut: "0x55d398326f99059fF775485246999027B3197955", + tokenOutAmount: "306106", + tokenOutChainId: "0x38", + bridgeInfo: { + port: "wasm.orai195269awwnt5m6c843q6w7hp8rt0k7syfu9de4h0wz384slshuzps8y7ccm", + channel: "channel-29" + } + } + ] + }; + + let receiver = "0x0000000000000000000000000000000000000000"; + const currentAddress = "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2"; + const oraiBridgeAddr = "oraib1hvr9d72r5um9lvt0rpkd4r75vrsqtw6ytnnvpf"; + let oraichainMsg = new OraichainMsg(validPath, "1", receiver, currentAddress, nextMemo, "oraib", oraiBridgeAddr); + + let [swapOps, bridgeInfo] = oraichainMsg.getSwapAndBridgeInfo(); + expect(bridgeInfo).toEqual({ + amount: "936043", + sourceChannel: "channel-29", + sourcePort: "wasm.orai195269awwnt5m6c843q6w7hp8rt0k7syfu9de4h0wz384slshuzps8y7ccm", + memo: "{}", + receiver: receiver, + timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + fromToken: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh", + toToken: "0x55d398326f99059fF775485246999027B3197955", + fromChain: "Oraichain", + toChain: "0x38" + }); + expect(swapOps).toEqual([]); + + let executeMsg = oraichainMsg.genExecuteMsg(); + expect(executeMsg.typeUrl).toEqual("/cosmwasm.wasm.v1.MsgExecuteContract"); + + let memoForIbcWasm = oraichainMsg.genMemoForIbcWasm(); + expect(memoForIbcWasm).toEqual({ + receiver: currentAddress, + memo: Buffer.from( + Memo.encode({ + userSwap: undefined, + minimumReceive: "1", + timeoutTimestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + postSwapAction: { + ibcWasmTransferMsg: { + localChannelId: bridgeInfo.sourceChannel, + remoteAddress: oraiBridgeAddr, + remoteDenom: "oraib0x55d398326f99059fF775485246999027B3197955", + memo: "oraib0x0000000000000000000000000000000000000000" + } + }, + recoveryAddr: currentAddress + }).finish() + ).toString("base64") + }); + }); + // it("Invalid path not ibc transfer", () => { // const nextMemo = "{}"; From 7badad25a75e3bd4125955848fedda95313f7445 Mon Sep 17 00:00:00 2001 From: Hau Nguyen Van Date: Mon, 7 Oct 2024 11:04:54 +0700 Subject: [PATCH 20/29] update params query smart route --- packages/universal-swap/src/helper.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/universal-swap/src/helper.ts b/packages/universal-swap/src/helper.ts index 65608230..a7835aab 100644 --- a/packages/universal-swap/src/helper.ts +++ b/packages/universal-swap/src/helper.ts @@ -607,8 +607,10 @@ export class UniversalSwapHelper { offerAmount: offerAmount, swapOptions: { protocols: routerConfig.protocols, - dontAlowSwapAfter: routerConfig.dontAllowSwapAfter, - maxSplits: routerConfig.maxSplits + maxSplits: routerConfig.maxSplits, + swapConfig: { + dontAlowSwapAfter: routerConfig.dontAllowSwapAfter + } } }; const res: { From 8ccebb39c1190039afa0b3a69dff089191c4e767 Mon Sep 17 00:00:00 2001 From: trung2891 Date: Mon, 7 Oct 2024 11:08:51 +0700 Subject: [PATCH 21/29] chore: test build msg bridge to evm missing params --- .../src/msg/chains/oraichain.ts | 6 +- .../tests/msg/oraichain-msg.spec.ts | 86 ++++++++++++------- 2 files changed, 56 insertions(+), 36 deletions(-) diff --git a/packages/universal-swap/src/msg/chains/oraichain.ts b/packages/universal-swap/src/msg/chains/oraichain.ts index a83ca9ce..7e7969be 100644 --- a/packages/universal-swap/src/msg/chains/oraichain.ts +++ b/packages/universal-swap/src/msg/chains/oraichain.ts @@ -177,7 +177,7 @@ export class OraichainMsg extends ChainMsg { let isBridgeToEvm = isEthAddress(this.receiver); if (isBridgeToEvm) { if (!this.destPrefix || !this.obridgeAddress) - throw generateError("Missing prefix os Obridge address for bridge to EVM"); + throw generateError("Missing prefix or Obridge address for bridge to EVM"); prefix = this.destPrefix; } @@ -258,7 +258,7 @@ export class OraichainMsg extends ChainMsg { let isBridgeToEvm = isEthAddress(this.receiver); if (isBridgeToEvm) { if (!this.destPrefix || !this.obridgeAddress) - throw generateError("Missing prefix os Obridge address for bridge to EVM"); + throw generateError("Missing prefix or Obridge address for bridge to EVM"); prefix = this.destPrefix; } @@ -387,7 +387,7 @@ export class OraichainMsg extends ChainMsg { let isBridgeToEvm = isEthAddress(this.receiver); if (isBridgeToEvm) { if (!this.destPrefix || !this.obridgeAddress) - throw generateError("Missing prefix os Obridge address for bridge to EVM"); + throw generateError("Missing prefix or Obridge address for bridge to EVM"); prefix = this.destPrefix; } diff --git a/packages/universal-swap/tests/msg/oraichain-msg.spec.ts b/packages/universal-swap/tests/msg/oraichain-msg.spec.ts index 9bc44680..5f33908d 100644 --- a/packages/universal-swap/tests/msg/oraichain-msg.spec.ts +++ b/packages/universal-swap/tests/msg/oraichain-msg.spec.ts @@ -670,37 +670,57 @@ describe("test build oraichain msg", () => { }); }); - // it("Invalid path not ibc transfer", () => { - // const nextMemo = "{}"; - - // const invalidPathNotIbcTransfer = { - // chainId: "osmosis-1", - // tokenIn: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", - // tokenInAmount: "999000", - // tokenOut: "uatom", - // tokenOutAmount: "999000", - // tokenOutChainId: "cosmoshub-4", - // actions: [ - // { - // type: "Bridge", - // protocol: "Bridge", - // tokenIn: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", - // tokenInAmount: "999000", - // tokenOut: "uatom", - // tokenOutAmount: "999000", - // tokenOutChainId: "cosmoshub-4", - // bridgeInfo: { - // port: "wasm.osmo123", - // channel: "channel-0" - // } - // } - // ] - // }; - // let osmosis = new OsmosisMsg(invalidPathNotIbcTransfer, "1", receiver, currentAddress, nextMemo); - // try { - // osmosis.genMemoAsMiddleware(); - // } catch (err) { - // expect(err).toEqual(generateError(`Only support IBC bridge on ${invalidPathNotIbcTransfer.chainId}`)); - // } - // }); + it("Valid path but missing obridge address or destPrefix on bridge to evm", () => { + const nextMemo = "{}"; + const validPath = { + chainId: "Oraichain", + tokenIn: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh", + tokenInAmount: "936043", + tokenOut: "0x55d398326f99059fF775485246999027B3197955", + tokenOutAmount: "306106", + tokenOutChainId: "0x38", + actions: [ + { + type: "Bridge", + protocol: "Bridge", + tokenIn: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh", + tokenInAmount: "936043", + tokenOut: "0x55d398326f99059fF775485246999027B3197955", + tokenOutAmount: "306106", + tokenOutChainId: "0x38", + bridgeInfo: { + port: "wasm.orai195269awwnt5m6c843q6w7hp8rt0k7syfu9de4h0wz384slshuzps8y7ccm", + channel: "channel-29" + } + } + ] + }; + let receiver = "0x0000000000000000000000000000000000000000"; + const currentAddress = "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2"; + const oraiBridgeAddr = "oraib1hvr9d72r5um9lvt0rpkd4r75vrsqtw6ytnnvpf"; + const destPrefix = "oraib"; + // missing oraibridge address + try { + let oraichainMsg = new OraichainMsg(validPath, "1", receiver, currentAddress, nextMemo, destPrefix); + oraichainMsg.genExecuteMsg(); + } catch (err) { + expect(err).toEqual(generateError(`Missing prefix or Obridge address for bridge to EVM`)); + } + + // missing destPrefix + try { + let oraichainMsg = new OraichainMsg( + validPath, + "1", + receiver, + currentAddress, + nextMemo, + undefined, + oraiBridgeAddr + ); + oraichainMsg.genExecuteMsg(); + } catch (err) { + expect(err).toEqual(generateError(`Missing prefix or Obridge address for bridge to EVM`)); + } + }); }); From 43fca1f78361d3153d868f572519dec22d7b1f44 Mon Sep 17 00:00:00 2001 From: Hau Nguyen Van Date: Mon, 7 Oct 2024 16:59:22 +0700 Subject: [PATCH 22/29] support swap evm -> inj or tia --- packages/universal-swap/src/handler.ts | 28 ++- packages/universal-swap/src/helper.ts | 15 +- .../src/universal-demos/alpha-ibc-new.ts | 176 ++++++++++++++++++ packages/universal-swap/tests/helper.spec.ts | 8 +- 4 files changed, 206 insertions(+), 21 deletions(-) create mode 100644 packages/universal-swap/src/universal-demos/alpha-ibc-new.ts diff --git a/packages/universal-swap/src/handler.ts b/packages/universal-swap/src/handler.ts index 3873b446..3baba193 100644 --- a/packages/universal-swap/src/handler.ts +++ b/packages/universal-swap/src/handler.ts @@ -1063,8 +1063,16 @@ export class UniversalSwapHandler { // Oraichain will be use as a proxy // TODO: write test cases async swapCosmosToOtherNetwork(destinationReceiver: string) { - const { originalFromToken, originalToToken, sender, fromAmount, simulateAmount, alphaSmartRoutes, relayerFee } = - this.swapData; + const { + originalFromToken, + originalToToken, + sender, + fromAmount, + simulateAmount, + alphaSmartRoutes, + userSlippage, + relayerFee + } = this.swapData; // guard check to see if from token has a pool on Oraichain or not. If not then return error const { client } = await this.config.cosmosWallet.getCosmWasmClient( @@ -1094,11 +1102,11 @@ export class UniversalSwapHandler { if (this.config.swapOptions?.isIbcWasm) minimumReceive = await this.caculateMinimumReceive(); const { swapRoute: completeSwapRoute } = await UniversalSwapHelper.addOraiBridgeRoute( - { obridgeAddress, sourceReceiver: oraiAddress }, + { obridgeAddress, sourceReceiver: oraiAddress, destReceiver: destinationReceiver }, originalFromToken, originalToToken, minimumReceive, - destinationReceiver, + userSlippage, this.config.swapOptions, alphaSmartRoutes ); @@ -1151,7 +1159,7 @@ export class UniversalSwapHandler { async processUniversalSwap() { const { evm, tron } = this.swapData.sender; - const { originalFromToken, originalToToken, simulateAmount, recipientAddress, relayerFee, alphaSmartRoutes } = + const { originalFromToken, originalToToken, simulateAmount, recipientAddress, userSlippage, alphaSmartRoutes } = this.swapData; const { swapOptions } = this.config; let toAddress = ""; @@ -1184,26 +1192,26 @@ export class UniversalSwapHandler { if (swapOptions?.isAlphaIbcWasm) { const routesFlatten = UniversalSwapHelper.flattenSmartRouters(alphaSmartRoutes.routes); - const hasInjectiveAddress = routesFlatten.some((route) => route.chainId === COSMOS_CHAIN_IDS.INJECTVE); - if (hasInjectiveAddress) injAddress = this.config.cosmosWallet.getKeplrAddr(COSMOS_CHAIN_IDS.INJECTVE); + const hasInjectiveAddress = routesFlatten.some((route) => route.tokenOutChainId === COSMOS_CHAIN_IDS.INJECTVE); + if (hasInjectiveAddress) injAddress = await this.config.cosmosWallet.getKeplrAddr(COSMOS_CHAIN_IDS.INJECTVE); } const { swapRoute, universalSwapType } = await UniversalSwapHelper.addOraiBridgeRoute( { obridgeAddress, injAddress, - sourceReceiver: oraiAddress + sourceReceiver: oraiAddress, + destReceiver: toAddress }, originalFromToken, originalToToken, minimumReceive, - toAddress, + userSlippage, this.config.swapOptions, alphaSmartRoutes ); if (alphaSmartRoutes?.routes?.length && swapOptions.isAlphaIbcWasm) return this.transferAndSwap(swapRoute); - if ( this.swapData?.alphaSmartRoutes?.routes?.length && ["oraichain-to-oraichain", "oraichain-to-cosmos", "cosmos-to-others"].includes(universalSwapType) && diff --git a/packages/universal-swap/src/helper.ts b/packages/universal-swap/src/helper.ts index a7835aab..58385040 100644 --- a/packages/universal-swap/src/helper.ts +++ b/packages/universal-swap/src/helper.ts @@ -321,11 +321,11 @@ export class UniversalSwapHelper { }; static addOraiBridgeRoute = async ( - addresses: { obridgeAddress?: string; sourceReceiver: string; injAddress?: string }, + addresses: { obridgeAddress?: string; sourceReceiver: string; injAddress?: string; destReceiver?: string }, fromToken: TokenItemType, toToken: TokenItemType, minimumReceive: string, - destReceiver?: string, + userSlippage: number, swapOption?: { isSourceReceiverTest?: boolean; isIbcWasm?: boolean; @@ -345,17 +345,17 @@ export class UniversalSwapHelper { let { swapRoute, universalSwapType, isSmartRouter } = UniversalSwapHelper.getRoute( fromToken, toToken, - destReceiver + addresses.destReceiver ); - if (isSmartRouter && swapOption.isIbcWasm) { + if (isSmartRouter && swapOption.isIbcWasm && !swapOption?.isAlphaIbcWasm) { if (!alphaSmartRoute && fromToken.coinGeckoId !== toToken.coinGeckoId) throw generateError(`Missing router !`); swapRoute = await UniversalSwapHelper.getRouteV2( { minimumReceive, recoveryAddr: addresses.sourceReceiver, - destReceiver, + destReceiver: addresses.destReceiver, remoteAddressObridge: addresses.obridgeAddress }, { @@ -402,7 +402,7 @@ export class UniversalSwapHelper { ...alphaRoutes, paths: paths }, - 0.01, + userSlippage / 100, receiverAddresses, alphaRoutes.paths[0].chainId ); @@ -819,6 +819,7 @@ export class UniversalSwapHelper { let fromInfo = getTokenOnOraichain(query.originalFromInfo.coinGeckoId); let toInfo = getTokenOnOraichain(query.originalToInfo.coinGeckoId); + const amountSimulate = toAmount(query.originalAmount, fromInfo.decimals).toString(); /** * useAlphaIbcWasm case: (evm -> oraichain -> osmosis -> inj not using wasm) * useIbcWasm case: (evm -> cosmos) @@ -834,7 +835,7 @@ export class UniversalSwapHelper { const simulateRes: SmartRouterResponse = await UniversalSwapHelper.simulateSwapUsingSmartRoute({ fromInfo, toInfo, - amount: toAmount(query.originalAmount, fromInfo.decimals).toString(), + amount: amountSimulate, routerConfig: routerConfigDefault }); diff --git a/packages/universal-swap/src/universal-demos/alpha-ibc-new.ts b/packages/universal-swap/src/universal-demos/alpha-ibc-new.ts new file mode 100644 index 00000000..82776404 --- /dev/null +++ b/packages/universal-swap/src/universal-demos/alpha-ibc-new.ts @@ -0,0 +1,176 @@ +import "dotenv/config"; +import { CosmosWalletImpl } from "./offline-wallet"; +import { UniversalSwapHandler } from "../handler"; +import { cosmosTokens, flattenTokens, generateError, getTokenOnOraichain, toAmount } from "@oraichain/oraidex-common"; + +const router = { + swapAmount: "2000000", + returnAmount: "241755", + routes: [ + { + swapAmount: "2000000", + returnAmount: "241755", + paths: [ + { + chainId: "0x38", + tokenIn: "0x55d398326f99059fF775485246999027B3197955", + tokenInAmount: "2000000", + tokenOut: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh", + tokenOutAmount: "1366092", + tokenOutChainId: "Oraichain", + actions: [ + { + type: "Bridge", + protocol: "Bridge", + tokenIn: "0x55d398326f99059fF775485246999027B3197955", + tokenInAmount: "2000000", + tokenOut: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh", + tokenOutAmount: "1366092", + tokenOutChainId: "Oraichain", + bridgeInfo: { + port: "transfer", + channel: "channel-1" + } + } + ] + }, + { + chainId: "Oraichain", + tokenIn: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh", + tokenInAmount: "1366092", + tokenOut: "uosmo", + tokenOutAmount: "2445755", + tokenOutChainId: "osmosis-1", + actions: [ + { + type: "Swap", + protocol: "OraidexV3", + tokenIn: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh", + tokenInAmount: "1366092", + tokenOut: "orai", + tokenOutAmount: "214856", + swapInfo: [ + { + poolId: "orai-orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh-3000000000-100", + tokenOut: "orai" + } + ] + }, + { + type: "Swap", + protocol: "Oraidex", + tokenIn: "orai", + tokenInAmount: "214856", + tokenOut: "ibc/9C4DCD21B48231D0BC2AC3D1B74A864746B37E4292694C93C617324250D002FC", + tokenOutAmount: "2445755", + swapInfo: [ + { + poolId: "orai1d37artrk4tkhz2qyjmaulc2jzjkx7206tmpfug", + tokenOut: "ibc/9C4DCD21B48231D0BC2AC3D1B74A864746B37E4292694C93C617324250D002FC" + } + ] + }, + { + type: "Bridge", + protocol: "Bridge", + tokenIn: "ibc/9C4DCD21B48231D0BC2AC3D1B74A864746B37E4292694C93C617324250D002FC", + tokenInAmount: "2445755", + tokenOut: "uosmo", + tokenOutAmount: "2445755", + tokenOutChainId: "osmosis-1", + bridgeInfo: { + port: "transfer", + channel: "channel-13" + } + } + ] + }, + { + chainId: "osmosis-1", + tokenIn: "uosmo", + tokenInAmount: "2445755", + tokenOut: "utia", + tokenOutAmount: "241755", + tokenOutChainId: "celestia", + actions: [ + { + type: "Swap", + protocol: "Osmosis", + tokenIn: "uosmo", + tokenInAmount: "2445755", + tokenOut: "ibc/D79E7D83AB399BFFF93433E54FAA480C191248FC556924A2A8351AE2638B3877", + tokenOutAmount: "241755", + swapInfo: [ + { + poolId: "1263", + tokenOut: "ibc/498A0751C798A0D9A389AA3691123DADA57DAA4FE165D5C75894505B876BA6E4" + }, + { + poolId: "1247", + tokenOut: "ibc/D79E7D83AB399BFFF93433E54FAA480C191248FC556924A2A8351AE2638B3877" + } + ] + }, + { + type: "Bridge", + protocol: "Bridge", + tokenIn: "ibc/D79E7D83AB399BFFF93433E54FAA480C191248FC556924A2A8351AE2638B3877", + tokenInAmount: "241755", + tokenOut: "utia", + tokenOutAmount: "241755", + tokenOutChainId: "celestia", + bridgeInfo: { + port: "transfer", + channel: "channel-6994" + } + } + ] + } + ] + } + ] +}; +const alphaSwapToOraichain = async () => { + const wallet = new CosmosWalletImpl(process.env.MNEMONIC); + const sender = await wallet.getKeplrAddr("Oraichain"); + + const fromAmount = 2; + console.log("sender: ", sender); + const originalFromToken = flattenTokens.find((t) => t.coinGeckoId === "tether" && t.chainId === "0x38"); + const originalToToken = flattenTokens.find((t) => t.coinGeckoId === "celestia" && t.chainId === "celestia"); + + if (!originalToToken) throw generateError("Could not find original to token"); + if (!originalFromToken) throw generateError("Could not find original from token"); + + const universalHandler = new UniversalSwapHandler( + { + originalFromToken, + originalToToken, + sender: { cosmos: sender, evm: "0x8c7E0A841269a01c0Ab389Ce8Fb3Cf150A94E797" }, + fromAmount, + userSlippage: 1, + relayerFee: { + relayerAmount: "100000", + relayerDecimals: 6 + }, + simulatePrice: "147161", + simulateAmount: toAmount(fromAmount, originalToToken.decimals).toString(), + alphaSmartRoutes: router + }, + { + cosmosWallet: wallet, + swapOptions: { isIbcWasm: false, isAlphaIbcWasm: true } + } + ); + + try { + const result = await universalHandler.processUniversalSwap(); + console.log("result: ", result); + } catch (error) { + console.trace("error: ", error); + } +}; + +(() => { + alphaSwapToOraichain(); +})(); diff --git a/packages/universal-swap/tests/helper.spec.ts b/packages/universal-swap/tests/helper.spec.ts index 0277d7cb..16970ede 100644 --- a/packages/universal-swap/tests/helper.spec.ts +++ b/packages/universal-swap/tests/helper.spec.ts @@ -586,11 +586,11 @@ describe("test helper functions", () => { it("test-addOraiBridgeRoute-empty-swapRoute", async () => { vi.spyOn(UniversalSwapHelper, "getRouteV2").mockResolvedValue(""); const result = await UniversalSwapHelper.addOraiBridgeRoute( - { sourceReceiver: "receiver" }, + { sourceReceiver: "receiver", destReceiver: undefined }, { contractAddress: "any" } as any, undefined as any, "0", - undefined, + 1, { isSourceReceiverTest: false } @@ -600,11 +600,11 @@ describe("test helper functions", () => { it("test-addOraiBridgeRoute-empty-sourceReceiver", async () => { await expect( UniversalSwapHelper.addOraiBridgeRoute( - { sourceReceiver: "" }, + { sourceReceiver: "", destReceiver: undefined }, undefined as any, undefined as any, "0", - undefined, + 1, { isSourceReceiverTest: false } From e09ac6e13487e9cbeefc37147d11a0250a649780 Mon Sep 17 00:00:00 2001 From: trung2891 Date: Mon, 7 Oct 2024 17:10:10 +0700 Subject: [PATCH 23/29] chore: test universal swap from cosmos-base ecosystem --- .../universal-swap/src/msg/chains/cosmos.ts | 1 - .../src/msg/chains/oraichain.ts | 2 +- .../universal-swap/src/msg/chains/osmosis.ts | 2 +- .../universal-swap/tests/msg/msgs.spec.ts | 155 ++++++++++++++++++ 4 files changed, 157 insertions(+), 3 deletions(-) create mode 100644 packages/universal-swap/tests/msg/msgs.spec.ts diff --git a/packages/universal-swap/src/msg/chains/cosmos.ts b/packages/universal-swap/src/msg/chains/cosmos.ts index 1aed18ca..20237eac 100644 --- a/packages/universal-swap/src/msg/chains/cosmos.ts +++ b/packages/universal-swap/src/msg/chains/cosmos.ts @@ -1,7 +1,6 @@ import { BridgeMsgInfo, MiddlewareResponse } from "../types"; import { ActionType, Path } from "../../types"; import { Action } from "@oraichain/osor-api-contracts-sdk/src/EntryPoint.types"; -import { validatePath, validateReceiver } from "../common"; import { BigDecimal, calculateTimeoutTimestamp, diff --git a/packages/universal-swap/src/msg/chains/oraichain.ts b/packages/universal-swap/src/msg/chains/oraichain.ts index 7e7969be..90be5ac1 100644 --- a/packages/universal-swap/src/msg/chains/oraichain.ts +++ b/packages/universal-swap/src/msg/chains/oraichain.ts @@ -2,7 +2,7 @@ import { BridgeMsgInfo, MiddlewareResponse } from "../types"; import { ActionType, Path } from "../../types"; import { SwapOperation } from "@oraichain/osor-api-contracts-sdk/src/types"; import { Action, ExecuteMsg } from "@oraichain/osor-api-contracts-sdk/src/EntryPoint.types"; -import { isCw20Token, validatePath, validateReceiver } from "../common"; +import { isCw20Token } from "../common"; import { BigDecimal, calculateTimeoutTimestamp, diff --git a/packages/universal-swap/src/msg/chains/osmosis.ts b/packages/universal-swap/src/msg/chains/osmosis.ts index fa13e93a..86cd968d 100644 --- a/packages/universal-swap/src/msg/chains/osmosis.ts +++ b/packages/universal-swap/src/msg/chains/osmosis.ts @@ -2,7 +2,7 @@ import { BridgeMsgInfo, MiddlewareResponse } from "../types"; import { ActionType, Path } from "../../types"; import { SwapOperation } from "@oraichain/osor-api-contracts-sdk/src/types"; import { Swap, Action, ExecuteMsg } from "@oraichain/osor-api-contracts-sdk/src/EntryPoint.types"; -import { isCw20Token, validatePath, validateReceiver } from "../common"; +import { isCw20Token } from "../common"; import { BigDecimal, calculateTimeoutTimestamp, diff --git a/packages/universal-swap/tests/msg/msgs.spec.ts b/packages/universal-swap/tests/msg/msgs.spec.ts new file mode 100644 index 00000000..ff43f4b8 --- /dev/null +++ b/packages/universal-swap/tests/msg/msgs.spec.ts @@ -0,0 +1,155 @@ +import { expect, afterAll, beforeAll, describe, it, vi } from "vitest"; +import { calculateTimeoutTimestamp, generateError, IBC_TRANSFER_TIMEOUT } from "@oraichain/oraidex-common"; +import { Action } from "@oraichain/osor-api-contracts-sdk/src/EntryPoint.types"; +import { generateMsgSwap } from "../../src/msg/msgs"; + +describe("test build swap msg", () => { + it("Test universal swap from cosmos-base ecosystem", () => { + let route = { + swapAmount: "10000000", + returnAmount: "7340955", + paths: [ + { + chainId: "cosmoshub-4", + tokenIn: "uatom", + tokenInAmount: "10000000", + tokenOut: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + tokenOutAmount: "10000000", + tokenOutChainId: "osmosis-1", + actions: [ + { + type: "Bridge", + protocol: "Bridge", + tokenIn: "uatom", + tokenInAmount: "10000000", + tokenOut: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + tokenOutAmount: "10000000", + tokenOutChainId: "osmosis-1", + bridgeInfo: { + port: "transfer", + channel: "channel-141" + } + } + ] + }, + { + chainId: "osmosis-1", + tokenIn: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + tokenInAmount: "10000000", + tokenOut: "orai", + tokenOutAmount: "7340955", + tokenOutChainId: "Oraichain", + actions: [ + { + type: "Swap", + protocol: "Osmosis", + tokenIn: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + tokenInAmount: "10000000", + tokenOut: "ibc/161D7D62BAB3B9C39003334F1671208F43C06B643CC9EDBBE82B64793C857F1D", + tokenOutAmount: "7340955", + swapInfo: [ + { + poolId: "1135", + tokenOut: "uosmo" + }, + { + poolId: "2173", + tokenOut: "ibc/161D7D62BAB3B9C39003334F1671208F43C06B643CC9EDBBE82B64793C857F1D" + } + ] + }, + { + type: "Bridge", + protocol: "Bridge", + tokenIn: "ibc/161D7D62BAB3B9C39003334F1671208F43C06B643CC9EDBBE82B64793C857F1D", + tokenInAmount: "7340955", + tokenOut: "orai", + tokenOutAmount: "7340955", + tokenOutChainId: "Oraichain", + bridgeInfo: { + port: "transfer", + channel: "channel-216" + } + } + ] + } + ] + }; + + // case 1: missing receiver address + try { + let res = generateMsgSwap(route, 0.1, {}); + } catch (err) { + expect(err).toEqual(generateError(`Missing receiver when build msg in osmosis-1`)); + } + + // case 2: missing current chain address + + try { + let res = generateMsgSwap(route, 0.1, { Oraichain: "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2" }); + } catch (err) { + expect(err).toEqual(generateError(`Missing address of osmosis-1`)); + } + + let res = generateMsgSwap(route, 0.1, { + Oraichain: "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2", + "cosmoshub-4": "cosmos1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y0ppr3e", + "osmosis-1": "osmo1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y86jn8t" + }); + expect(res).toEqual({ + typeUrl: "/ibc.applications.transfer.v1.MsgTransfer", + value: { + sourcePort: "transfer", + sourceChannel: "channel-141", + receiver: "osmo1h3jkejkcpthl45xrrm5geed3eq75p5rgfce9taufkwfr89k63muqweu2y7", + token: { amount: "10000000", denom: "uatom" }, + sender: "cosmos1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y0ppr3e", + memo: JSON.stringify({ + wasm: { + contract: "osmo1h3jkejkcpthl45xrrm5geed3eq75p5rgfce9taufkwfr89k63muqweu2y7", + msg: { + swap_and_action: { + user_swap: { + swap_exact_asset_in: { + swap_venue_name: "osmosis-poolmanager", + operations: [ + { + denom_in: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + denom_out: "uosmo", + pool: "1135" + }, + { + denom_in: "uosmo", + denom_out: "ibc/161D7D62BAB3B9C39003334F1671208F43C06B643CC9EDBBE82B64793C857F1D", + pool: "2173" + } + ] + } + }, + min_asset: { + native: { + amount: "6606859", + denom: "ibc/161D7D62BAB3B9C39003334F1671208F43C06B643CC9EDBBE82B64793C857F1D" + } + }, + timeout_timestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + post_swap_action: { + ibc_transfer: { + ibc_info: { + source_channel: "channel-216", + receiver: "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2", + memo: "", + recover_address: "osmo1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y86jn8t" + } + } + }, + affiliates: [] + } + } + } + }), + timeoutTimestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT) + } + }); + }); +}); From 1e235832f4e58174c33f6503142f633ddd7c830e Mon Sep 17 00:00:00 2001 From: trung2891 Date: Mon, 7 Oct 2024 18:09:41 +0700 Subject: [PATCH 24/29] chore: test build msg swap from boble --- .../universal-swap/tests/msg/msgs.spec.ts | 444 +++++++++++++++++- 1 file changed, 443 insertions(+), 1 deletion(-) diff --git a/packages/universal-swap/tests/msg/msgs.spec.ts b/packages/universal-swap/tests/msg/msgs.spec.ts index ff43f4b8..f9b3a9d2 100644 --- a/packages/universal-swap/tests/msg/msgs.spec.ts +++ b/packages/universal-swap/tests/msg/msgs.spec.ts @@ -2,9 +2,10 @@ import { expect, afterAll, beforeAll, describe, it, vi } from "vitest"; import { calculateTimeoutTimestamp, generateError, IBC_TRANSFER_TIMEOUT } from "@oraichain/oraidex-common"; import { Action } from "@oraichain/osor-api-contracts-sdk/src/EntryPoint.types"; import { generateMsgSwap } from "../../src/msg/msgs"; +import { Memo } from "../../src/proto/universal_swap_memo"; describe("test build swap msg", () => { - it("Test universal swap from cosmos-base ecosystem", () => { + it("Test build universal swap msg from cosmos-base ecosystem", () => { let route = { swapAmount: "10000000", returnAmount: "7340955", @@ -152,4 +153,445 @@ describe("test build swap msg", () => { } }); }); + + it("Test build universal swap msg from cosmos to evm", () => { + let route = { + swapAmount: "1000000", + returnAmount: "634236000000000000", + paths: [ + { + chainId: "cosmoshub-4", + tokenIn: "uatom", + tokenInAmount: "1000000", + tokenOut: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + tokenOutAmount: "1000000", + tokenOutChainId: "osmosis-1", + actions: [ + { + type: "Bridge", + protocol: "Bridge", + tokenIn: "uatom", + tokenInAmount: "1000000", + tokenOut: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + tokenOutAmount: "1000000", + tokenOutChainId: "osmosis-1", + bridgeInfo: { + port: "transfer", + channel: "channel-141" + } + } + ] + }, + { + chainId: "osmosis-1", + tokenIn: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + tokenInAmount: "1000000", + tokenOut: "ibc/9C4DCD21B48231D0BC2AC3D1B74A864746B37E4292694C93C617324250D002FC", + tokenOutAmount: "8405405", + tokenOutChainId: "Oraichain", + actions: [ + { + type: "Swap", + protocol: "Osmosis", + tokenIn: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + tokenInAmount: "1000000", + tokenOut: "uosmo", + tokenOutAmount: "8405405", + swapInfo: [ + { + poolId: "1282", + tokenOut: "ibc/498A0751C798A0D9A389AA3691123DADA57DAA4FE165D5C75894505B876BA6E4" + }, + { + poolId: "1464", + tokenOut: "uosmo" + } + ] + }, + { + type: "Bridge", + protocol: "Bridge", + tokenIn: "uosmo", + tokenInAmount: "8405405", + tokenOut: "ibc/9C4DCD21B48231D0BC2AC3D1B74A864746B37E4292694C93C617324250D002FC", + tokenOutAmount: "8405405", + tokenOutChainId: "Oraichain", + bridgeInfo: { + port: "transfer", + channel: "channel-216" + } + } + ] + }, + { + chainId: "Oraichain", + tokenIn: "ibc/9C4DCD21B48231D0BC2AC3D1B74A864746B37E4292694C93C617324250D002FC", + tokenInAmount: "8405405", + tokenOut: "0xA325Ad6D9c92B55A3Fc5aD7e412B1518F96441C0", + tokenOutAmount: "634236000000000000", + tokenOutChainId: "0x38", + actions: [ + { + type: "Swap", + protocol: "Oraidex", + tokenIn: "ibc/9C4DCD21B48231D0BC2AC3D1B74A864746B37E4292694C93C617324250D002FC", + tokenInAmount: "8405405", + tokenOut: "orai", + tokenOutAmount: "734971", + swapInfo: [ + { + poolId: "orai1d37artrk4tkhz2qyjmaulc2jzjkx7206tmpfug", + tokenOut: "orai" + } + ] + }, + { + type: "Bridge", + protocol: "Bridge", + tokenIn: "orai", + tokenInAmount: "734971", + tokenOut: "0xA325Ad6D9c92B55A3Fc5aD7e412B1518F96441C0", + tokenOutAmount: "634236000000000000", + tokenOutChainId: "0x38", + bridgeInfo: { + port: "wasm.orai195269awwnt5m6c843q6w7hp8rt0k7syfu9de4h0wz384slshuzps8y7ccm", + channel: "channel-29" + } + } + ] + } + ] + }; + + let res = generateMsgSwap(route, 0.1, { + "0x38": "0x8c7E0A841269a01c0Ab389Ce8Fb3Cf150A94E797", + "oraibridge-subnet-2": "oraib1hvr9d72r5um9lvt0rpkd4r75vrsqtw6ytnnvpf", + Oraichain: "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2", + "cosmoshub-4": "cosmos1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y0ppr3e", + "osmosis-1": "osmo1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y86jn8t" + }); + + expect(res).toEqual({ + typeUrl: "/ibc.applications.transfer.v1.MsgTransfer", + value: { + sourcePort: "transfer", + sourceChannel: "channel-141", + receiver: "osmo1h3jkejkcpthl45xrrm5geed3eq75p5rgfce9taufkwfr89k63muqweu2y7", + token: { amount: "1000000", denom: "uatom" }, + sender: "cosmos1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y0ppr3e", + memo: JSON.stringify({ + wasm: { + contract: "osmo1h3jkejkcpthl45xrrm5geed3eq75p5rgfce9taufkwfr89k63muqweu2y7", + msg: { + swap_and_action: { + user_swap: { + swap_exact_asset_in: { + swap_venue_name: "osmosis-poolmanager", + operations: [ + { + denom_in: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + denom_out: "ibc/498A0751C798A0D9A389AA3691123DADA57DAA4FE165D5C75894505B876BA6E4", + pool: "1282" + }, + { + denom_in: "ibc/498A0751C798A0D9A389AA3691123DADA57DAA4FE165D5C75894505B876BA6E4", + denom_out: "uosmo", + pool: "1464" + } + ] + } + }, + min_asset: { native: { amount: "7564864", denom: "uosmo" } }, + timeout_timestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + post_swap_action: { + ibc_transfer: { + ibc_info: { + source_channel: "channel-216", + receiver: "orai13mgxn93pjvd7eermj4ghet8assxdqttxugwk25rasuuqq2g5nczq43eesn", + memo: JSON.stringify({ + wasm: { + contract: "orai13mgxn93pjvd7eermj4ghet8assxdqttxugwk25rasuuqq2g5nczq43eesn", + msg: { + swap_and_action: { + user_swap: { + swap_exact_asset_in: { + swap_venue_name: "oraidex", + operations: [ + { + denom_in: "ibc/9C4DCD21B48231D0BC2AC3D1B74A864746B37E4292694C93C617324250D002FC", + denom_out: "orai", + pool: "orai1d37artrk4tkhz2qyjmaulc2jzjkx7206tmpfug" + } + ] + } + }, + min_asset: { native: { amount: "661473", denom: "orai" } }, + timeout_timestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + post_swap_action: { + ibc_wasm_transfer: { + ibc_wasm_info: { + local_channel_id: "channel-29", + remote_address: "oraib1hvr9d72r5um9lvt0rpkd4r75vrsqtw6ytnnvpf", + remote_denom: "oraib0xA325Ad6D9c92B55A3Fc5aD7e412B1518F96441C0", + memo: "oraib0x8c7E0A841269a01c0Ab389Ce8Fb3Cf150A94E797" + } + } + }, + affiliates: [] + } + } + } + }), + recover_address: "osmo1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y86jn8t" + } + } + }, + affiliates: [] + } + } + } + }), + timeoutTimestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT) + } + }); + }); + + it("Test build universal swap msg from noble", () => { + let route = { + swapAmount: "1000000000", + returnAmount: "996112805", + paths: [ + { + chainId: "noble-1", + tokenIn: "uusdc", + tokenInAmount: "1000000000", + tokenOut: "orai15un8msx3n5zf9ahlxmfeqd2kwa5wm0nrpxer304m9nd5q6qq0g6sku5pdd", + tokenOutAmount: "997121725", + tokenOutChainId: "Oraichain", + actions: [ + { + type: "Bridge", + protocol: "Bridge", + tokenIn: "uusdc", + tokenInAmount: "1000000000", + tokenOut: "orai15un8msx3n5zf9ahlxmfeqd2kwa5wm0nrpxer304m9nd5q6qq0g6sku5pdd", + tokenOutAmount: "997121725", + tokenOutChainId: "Oraichain", + bridgeInfo: { + port: "transfer", + channel: "channel-34" + } + } + ] + }, + { + chainId: "Oraichain", + tokenIn: "orai15un8msx3n5zf9ahlxmfeqd2kwa5wm0nrpxer304m9nd5q6qq0g6sku5pdd", + tokenInAmount: "997121725", + tokenOut: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh", + tokenOutAmount: "996112805", + tokenOutChainId: "Oraichain", + actions: [ + { + type: "Swap", + protocol: "OraidexV3", + tokenIn: "orai15un8msx3n5zf9ahlxmfeqd2kwa5wm0nrpxer304m9nd5q6qq0g6sku5pdd", + tokenInAmount: "997121725", + tokenOut: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh", + tokenOutAmount: "996112805", + swapInfo: [ + { + poolId: + "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh-orai15un8msx3n5zf9ahlxmfeqd2kwa5wm0nrpxer304m9nd5q6qq0g6sku5pdd-500000000-10", + tokenOut: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh" + } + ] + } + ] + } + ] + }; + + let res = generateMsgSwap(route, 0.1, { + "0x38": "0x8c7E0A841269a01c0Ab389Ce8Fb3Cf150A94E797", + "oraibridge-subnet-2": "oraib1hvr9d72r5um9lvt0rpkd4r75vrsqtw6ytnnvpf", + Oraichain: "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2", + "cosmoshub-4": "cosmos1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y0ppr3e", + "osmosis-1": "osmo1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y86jn8t", + "noble-1": "noble1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y8z5tfh" + }); + + expect(res).toEqual({ + typeUrl: "/ibc.applications.transfer.v1.MsgTransfer", + value: { + sourcePort: "transfer", + sourceChannel: "channel-34", + receiver: "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2", + token: { amount: "1000000000", denom: "uusdc" }, + sender: "noble1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y8z5tfh", + memo: Buffer.from( + Memo.encode({ + userSwap: { + swapVenueName: "oraidex", + swapExactAssetIn: { + operations: [ + { + poolId: + "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh-orai15un8msx3n5zf9ahlxmfeqd2kwa5wm0nrpxer304m9nd5q6qq0g6sku5pdd-500000000-10", + denomIn: "orai15un8msx3n5zf9ahlxmfeqd2kwa5wm0nrpxer304m9nd5q6qq0g6sku5pdd", + denomOut: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh" + } + ] + } + }, + minimumReceive: "896501524", + timeoutTimestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + postSwapAction: { + transferMsg: { + toAddress: "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2" + } + }, + recoveryAddr: "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2" + }).finish() + ).toString("base64"), + timeoutTimestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT) + } + }); + }); + + it("Test build universal swap msg from noble to evm", () => { + let route = { + swapAmount: "1000000000", + returnAmount: "886558424", + paths: [ + { + chainId: "noble-1", + tokenIn: "uusdc", + tokenInAmount: "1000000000", + tokenOut: "orai15un8msx3n5zf9ahlxmfeqd2kwa5wm0nrpxer304m9nd5q6qq0g6sku5pdd", + tokenOutAmount: "997121083", + tokenOutChainId: "Oraichain", + actions: [ + { + type: "Bridge", + protocol: "Bridge", + tokenIn: "uusdc", + tokenInAmount: "1000000000", + tokenOut: "orai15un8msx3n5zf9ahlxmfeqd2kwa5wm0nrpxer304m9nd5q6qq0g6sku5pdd", + tokenOutAmount: "997121083", + tokenOutChainId: "Oraichain", + bridgeInfo: { + port: "transfer", + channel: "channel-34" + } + } + ] + }, + { + chainId: "Oraichain", + tokenIn: "orai15un8msx3n5zf9ahlxmfeqd2kwa5wm0nrpxer304m9nd5q6qq0g6sku5pdd", + tokenInAmount: "997121083", + tokenOut: "0x55d398326f99059fF775485246999027B3197955", + tokenOutAmount: "886558424", + tokenOutChainId: "0x38", + actions: [ + { + type: "Swap", + protocol: "Oraidex", + tokenIn: "orai15un8msx3n5zf9ahlxmfeqd2kwa5wm0nrpxer304m9nd5q6qq0g6sku5pdd", + tokenInAmount: "997121083", + tokenOut: "orai", + tokenOutAmount: "141853616", + swapInfo: [ + { + poolId: "orai19ttg0j7w5kr83js32tmwnwxxdq9rkmw4m3d7mn2j2hkpugwwa4tszwsnkg", + tokenOut: "orai" + } + ] + }, + { + type: "Swap", + protocol: "OraidexV3", + tokenIn: "orai", + tokenInAmount: "141853616", + tokenOut: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh", + tokenOutAmount: "888072940", + swapInfo: [ + { + poolId: "orai-orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh-3000000000-100", + tokenOut: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh" + } + ] + }, + { + type: "Bridge", + protocol: "Bridge", + tokenIn: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh", + tokenInAmount: "888072940", + tokenOut: "0x55d398326f99059fF775485246999027B3197955", + tokenOutAmount: "886558424", + tokenOutChainId: "0x38", + bridgeInfo: { + port: "wasm.orai195269awwnt5m6c843q6w7hp8rt0k7syfu9de4h0wz384slshuzps8y7ccm", + channel: "channel-29" + } + } + ] + } + ] + }; + + let res = generateMsgSwap(route, 0.1, { + "0x38": "0x8c7E0A841269a01c0Ab389Ce8Fb3Cf150A94E797", + "oraibridge-subnet-2": "oraib1hvr9d72r5um9lvt0rpkd4r75vrsqtw6ytnnvpf", + Oraichain: "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2", + "cosmoshub-4": "cosmos1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y0ppr3e", + "osmosis-1": "osmo1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y86jn8t", + "noble-1": "noble1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y8z5tfh" + }); + + expect(res).toEqual({ + typeUrl: "/ibc.applications.transfer.v1.MsgTransfer", + value: { + sourcePort: "transfer", + sourceChannel: "channel-34", + receiver: "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2", + token: { amount: "1000000000", denom: "uusdc" }, + sender: "noble1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y8z5tfh", + memo: Buffer.from( + Memo.encode({ + userSwap: { + swapVenueName: "oraidex", + swapExactAssetIn: { + operations: [ + { + poolId: "orai19ttg0j7w5kr83js32tmwnwxxdq9rkmw4m3d7mn2j2hkpugwwa4tszwsnkg", + denomIn: "orai15un8msx3n5zf9ahlxmfeqd2kwa5wm0nrpxer304m9nd5q6qq0g6sku5pdd", + denomOut: "orai" + }, + { + poolId: "orai-orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh-3000000000-100", + denomIn: "orai", + denomOut: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh" + } + ] + } + }, + minimumReceive: "799265646", + timeoutTimestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + postSwapAction: { + ibcWasmTransferMsg: { + localChannelId: "channel-29", + remoteAddress: "oraib1hvr9d72r5um9lvt0rpkd4r75vrsqtw6ytnnvpf", + remoteDenom: "oraib0x55d398326f99059fF775485246999027B3197955", + memo: "oraib0x8c7E0A841269a01c0Ab389Ce8Fb3Cf150A94E797" + } + }, + recoveryAddr: "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2" + }).finish() + ).toString("base64"), + timeoutTimestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT) + } + }); + }); }); From 1ad1cb6704426cd818128bed56e4bc248e07f03a Mon Sep 17 00:00:00 2001 From: Hau Nguyen Van Date: Tue, 8 Oct 2024 13:45:01 +0700 Subject: [PATCH 25/29] update network celestia --- packages/oraidex-common/package.json | 2 +- packages/oraidex-common/src/celestia-network.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/oraidex-common/package.json b/packages/oraidex-common/package.json index e3cf54cf..2e055632 100644 --- a/packages/oraidex-common/package.json +++ b/packages/oraidex-common/package.json @@ -1,6 +1,6 @@ { "name": "@oraichain/oraidex-common", - "version": "1.1.22", + "version": "1.1.23", "main": "build/index.js", "files": [ "build/" diff --git a/packages/oraidex-common/src/celestia-network.ts b/packages/oraidex-common/src/celestia-network.ts index 98a07f6d..ea2629d3 100644 --- a/packages/oraidex-common/src/celestia-network.ts +++ b/packages/oraidex-common/src/celestia-network.ts @@ -41,8 +41,8 @@ export const celestiaNetwork: CustomChainInfo = { } } ], - rpc: "https://rpc-celestia.keplr.app", - rest: "https://lcd-celestia.keplr.app", + rpc: "https://celestia.rpc.orai.io", + rest: "https://celestia.lcd.orai.io", stakeCurrency: { coinDecimals: 6, coinDenom: "TIA", From a438bc6f485a3bf9d5aa6116036adc966d9fae87 Mon Sep 17 00:00:00 2001 From: trung2891 Date: Tue, 8 Oct 2024 14:54:44 +0700 Subject: [PATCH 26/29] chore: use mainnet contract Oraidex entrypoint --- packages/universal-swap/src/msg/chains/oraichain.ts | 2 +- packages/universal-swap/src/msg/msgs.ts | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/universal-swap/src/msg/chains/oraichain.ts b/packages/universal-swap/src/msg/chains/oraichain.ts index 90be5ac1..7bc0bf0d 100644 --- a/packages/universal-swap/src/msg/chains/oraichain.ts +++ b/packages/universal-swap/src/msg/chains/oraichain.ts @@ -22,7 +22,7 @@ import { ChainMsg } from "./chain"; export class OraichainMsg extends ChainMsg { SWAP_VENUE_NAME = "oraidex"; - ENTRY_POINT_CONTRACT = "orai13mgxn93pjvd7eermj4ghet8assxdqttxugwk25rasuuqq2g5nczq43eesn"; // FIXME: use mainnet + ENTRY_POINT_CONTRACT = "orai1yglsm0u2x3xmct9kq3lxa654cshaxj9j5d9rw5enemkkkdjgzj7sr3gwt0"; constructor( path: Path, diff --git a/packages/universal-swap/src/msg/msgs.ts b/packages/universal-swap/src/msg/msgs.ts index 7ba12298..cb4d2653 100644 --- a/packages/universal-swap/src/msg/msgs.ts +++ b/packages/universal-swap/src/msg/msgs.ts @@ -24,7 +24,6 @@ const getDestPrefixForBridgeToEvmOnOrai = (chainId: string): string => { return ""; }; -//FIXME: calc minimum receive const buildMemoSwap = ( path: Path, receiver: string, @@ -71,7 +70,6 @@ const buildMemoSwap = ( } }; -//FIXME: calc minimum receive const buildExecuteMsg = ( path: Path, receiver: string, From 34ce3514369bf6594625e54da98f19adee3fd23b Mon Sep 17 00:00:00 2001 From: trung2891 Date: Tue, 8 Oct 2024 14:57:44 +0700 Subject: [PATCH 27/29] fix: test build msg swap --- packages/universal-swap/src/msg/chains/oraichain.ts | 2 +- packages/universal-swap/tests/msg/msgs.spec.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/universal-swap/src/msg/chains/oraichain.ts b/packages/universal-swap/src/msg/chains/oraichain.ts index 7bc0bf0d..987afc09 100644 --- a/packages/universal-swap/src/msg/chains/oraichain.ts +++ b/packages/universal-swap/src/msg/chains/oraichain.ts @@ -399,7 +399,7 @@ export class OraichainMsg extends ChainMsg { local_channel_id: bridgeInfo.sourceChannel, remote_address: isBridgeToEvm ? this.obridgeAddress : this.receiver, remote_denom: prefix + bridgeInfo.toToken, - timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), // FIXME: should we use nano with an u64 type? -> probably quite big for a u64 + timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), memo: isBridgeToEvm ? prefix + this.receiver : this.memo }; diff --git a/packages/universal-swap/tests/msg/msgs.spec.ts b/packages/universal-swap/tests/msg/msgs.spec.ts index f9b3a9d2..fc93cb18 100644 --- a/packages/universal-swap/tests/msg/msgs.spec.ts +++ b/packages/universal-swap/tests/msg/msgs.spec.ts @@ -307,10 +307,10 @@ describe("test build swap msg", () => { ibc_transfer: { ibc_info: { source_channel: "channel-216", - receiver: "orai13mgxn93pjvd7eermj4ghet8assxdqttxugwk25rasuuqq2g5nczq43eesn", + receiver: "orai1yglsm0u2x3xmct9kq3lxa654cshaxj9j5d9rw5enemkkkdjgzj7sr3gwt0", memo: JSON.stringify({ wasm: { - contract: "orai13mgxn93pjvd7eermj4ghet8assxdqttxugwk25rasuuqq2g5nczq43eesn", + contract: "orai1yglsm0u2x3xmct9kq3lxa654cshaxj9j5d9rw5enemkkkdjgzj7sr3gwt0", msg: { swap_and_action: { user_swap: { From 00ab35436d75fa86e61cb3c23ba97f5051075c7e Mon Sep 17 00:00:00 2001 From: Hau Nguyen Van Date: Tue, 8 Oct 2024 15:14:06 +0700 Subject: [PATCH 28/29] update simulate --- packages/universal-swap/src/helper.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/universal-swap/src/helper.ts b/packages/universal-swap/src/helper.ts index 58385040..4f0264bc 100644 --- a/packages/universal-swap/src/helper.ts +++ b/packages/universal-swap/src/helper.ts @@ -819,7 +819,6 @@ export class UniversalSwapHelper { let fromInfo = getTokenOnOraichain(query.originalFromInfo.coinGeckoId); let toInfo = getTokenOnOraichain(query.originalToInfo.coinGeckoId); - const amountSimulate = toAmount(query.originalAmount, fromInfo.decimals).toString(); /** * useAlphaIbcWasm case: (evm -> oraichain -> osmosis -> inj not using wasm) * useIbcWasm case: (evm -> cosmos) @@ -835,7 +834,7 @@ export class UniversalSwapHelper { const simulateRes: SmartRouterResponse = await UniversalSwapHelper.simulateSwapUsingSmartRoute({ fromInfo, toInfo, - amount: amountSimulate, + amount: toAmount(query.originalAmount, fromInfo.decimals).toString(), routerConfig: routerConfigDefault }); From d6bca873330ec93258c0512af8a6453b19e0119d Mon Sep 17 00:00:00 2001 From: Hau Nguyen Van Date: Tue, 8 Oct 2024 17:33:38 +0700 Subject: [PATCH 29/29] add swap cosmos to cosmos with new msg --- packages/universal-swap/src/handler.ts | 56 ++++++--- packages/universal-swap/src/helper.ts | 19 +++ .../src/universal-demos/alpha-ibc-new.ts | 117 +++--------------- 3 files changed, 79 insertions(+), 113 deletions(-) diff --git a/packages/universal-swap/src/handler.ts b/packages/universal-swap/src/handler.ts index 3baba193..12ddbdf3 100644 --- a/packages/universal-swap/src/handler.ts +++ b/packages/universal-swap/src/handler.ts @@ -61,6 +61,7 @@ import { GasPrice } from "@cosmjs/stargate"; import { OraiswapRouterQueryClient } from "@oraichain/oraidex-contracts-sdk"; import { Affiliate } from "@oraichain/oraidex-contracts-sdk/build/OraiswapMixedRouter.types"; import { COSMOS_CHAIN_IDS } from "@oraichain/common"; +import { generateMsgSwap } from "./msg/msgs"; const AFFILIATE_DECIMAL = 1e4; // 10_000 export class UniversalSwapHandler { @@ -314,15 +315,6 @@ export class UniversalSwapHandler { return [...msgExecuteSwap, ...msgExecuteTransfer]; } - private getAddress = (prefix: string, { address60, address118 }, coinType: number = 118) => { - const approve = { - 118: address118, - 60: address60 - }; - const { data } = fromBech32(approve[coinType]); - return toBech32(prefix, data); - }; - private getReceiverIBCHooks = (chainId: string, receiver?: string) => { if (chainId === "osmosis-1") return OSMOSIS_ROUTER_CONTRACT; return receiver; @@ -340,7 +332,7 @@ export class UniversalSwapHandler { if (isOnlySwap) { post_swap_action = { transfer: { - to_address: this.getAddress( + to_address: UniversalSwapHelper.getAddress( prefixReceiver, { address60: injAddress, address118: oraiAddress }, chainInfoReceiver.bip44.coinType @@ -424,13 +416,13 @@ export class UniversalSwapHandler { ibc_transfer: { ibc_info: { source_channel: route.bridgeInfo.channel, - receiver: this.getAddress( + receiver: UniversalSwapHelper.getAddress( prefixReceiver, { address60: injAddress, address118: oraiAddress }, chainInfoReceiver.bip44.coinType ), memo: "", - recover_address: this.getAddress( + recover_address: UniversalSwapHelper.getAddress( prefixRecover, { address60: injAddress, address118: oraiAddress }, chainInfoRecover.bip44.coinType @@ -443,7 +435,7 @@ export class UniversalSwapHandler { public createForwardObject = (route: Routes, { oraiAddress, injAddress }, isLastRoute?: boolean) => { const { prefixReceiver, chainInfoReceiver } = this.getPrefixCosmos(route); - const addressReceiver = this.getAddress( + const addressReceiver = UniversalSwapHelper.getAddress( prefixReceiver, { address60: injAddress, address118: oraiAddress }, chainInfoReceiver.bip44.coinType @@ -481,7 +473,7 @@ export class UniversalSwapHandler { flagAffiliate?: boolean ) => { const { prefixReceiver, prefixRecover, chainInfoRecover, chainInfoReceiver } = this.getPrefixCosmos(route); - const addressReceiver = this.getAddress( + const addressReceiver = UniversalSwapHelper.getAddress( prefixReceiver, { address60: injAddress, address118: oraiAddress }, chainInfoReceiver.bip44.coinType @@ -505,7 +497,7 @@ export class UniversalSwapHandler { amount: tokenInAmount, denom: route.tokenIn }, - sender: this.getAddress( + sender: UniversalSwapHelper.getAddress( prefixRecover, { address60: injAddress, address118: oraiAddress }, chainInfoRecover.bip44.coinType @@ -1157,6 +1149,31 @@ export class UniversalSwapHandler { return client.signAndBroadcast(sender.cosmos, [msgTransferEncodeObj], "auto"); } + async alphaSmartRouterSwapNewMsg(swapRoute, universalSwapType, receiverAddresses) { + const { sender, originalFromToken, alphaSmartRoutes, userSlippage } = this.swapData; + if ( + universalSwapType === "cosmos-to-others" || + universalSwapType === "oraichain-to-oraichain" || + universalSwapType === "oraichain-to-cosmos" + ) { + const msgs = alphaSmartRoutes.routes.map((route) => { + return generateMsgSwap(route, userSlippage / 100, receiverAddresses); + }); + const { client } = await this.config.cosmosWallet.getCosmWasmClient( + { + chainId: originalFromToken.chainId as CosmosChainId, + rpc: originalFromToken.rpc + }, + { + gasPrice: this.getGasPriceFromToken() + } + ); + return await client.signAndBroadcast(sender.cosmos, msgs, "auto"); + } + + return this.transferAndSwap(swapRoute); + } + async processUniversalSwap() { const { evm, tron } = this.swapData.sender; const { originalFromToken, originalToToken, simulateAmount, recipientAddress, userSlippage, alphaSmartRoutes } = @@ -1211,7 +1228,14 @@ export class UniversalSwapHandler { alphaSmartRoutes ); - if (alphaSmartRoutes?.routes?.length && swapOptions.isAlphaIbcWasm) return this.transferAndSwap(swapRoute); + if (alphaSmartRoutes?.routes?.length && swapOptions.isAlphaIbcWasm) { + return this.alphaSmartRouterSwapNewMsg( + swapRoute, + universalSwapType, + UniversalSwapHelper.generateAddress({ oraiAddress, injAddress }) + ); + } + if ( this.swapData?.alphaSmartRoutes?.routes?.length && ["oraichain-to-oraichain", "oraichain-to-cosmos", "cosmos-to-others"].includes(universalSwapType) && diff --git a/packages/universal-swap/src/helper.ts b/packages/universal-swap/src/helper.ts index 4f0264bc..84c3ab91 100644 --- a/packages/universal-swap/src/helper.ts +++ b/packages/universal-swap/src/helper.ts @@ -320,6 +320,25 @@ export class UniversalSwapHelper { return toBech32(prefix, data); }; + static generateAddress = ({ oraiAddress, injAddress }) => { + return { + [COSMOS_CHAIN_ID_COMMON.ORAICHAIN_CHAIN_ID]: oraiAddress, + [COSMOS_CHAIN_ID_COMMON.COSMOSHUB_CHAIN_ID]: UniversalSwapHelper.getAddress("cosmos", { + address60: injAddress, + address118: oraiAddress + }), + [COSMOS_CHAIN_ID_COMMON.OSMOSIS_CHAIN_ID]: UniversalSwapHelper.getAddress("osmo", { + address60: injAddress, + address118: oraiAddress + }), + [COSMOS_CHAIN_ID_COMMON.INJECTVE_CHAIN_ID]: injAddress, + [COSMOS_CHAIN_ID_COMMON.CELESTIA_CHAIN_ID]: UniversalSwapHelper.getAddress("celestia", { + address60: injAddress, + address118: oraiAddress + }) + }; + }; + static addOraiBridgeRoute = async ( addresses: { obridgeAddress?: string; sourceReceiver: string; injAddress?: string; destReceiver?: string }, fromToken: TokenItemType, diff --git a/packages/universal-swap/src/universal-demos/alpha-ibc-new.ts b/packages/universal-swap/src/universal-demos/alpha-ibc-new.ts index 82776404..a57ad01e 100644 --- a/packages/universal-swap/src/universal-demos/alpha-ibc-new.ts +++ b/packages/universal-swap/src/universal-demos/alpha-ibc-new.ts @@ -4,109 +4,31 @@ import { UniversalSwapHandler } from "../handler"; import { cosmosTokens, flattenTokens, generateError, getTokenOnOraichain, toAmount } from "@oraichain/oraidex-common"; const router = { - swapAmount: "2000000", - returnAmount: "241755", + swapAmount: "100000", + returnAmount: "9983", routes: [ { - swapAmount: "2000000", - returnAmount: "241755", + swapAmount: "100000", + returnAmount: "9983", paths: [ - { - chainId: "0x38", - tokenIn: "0x55d398326f99059fF775485246999027B3197955", - tokenInAmount: "2000000", - tokenOut: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh", - tokenOutAmount: "1366092", - tokenOutChainId: "Oraichain", - actions: [ - { - type: "Bridge", - protocol: "Bridge", - tokenIn: "0x55d398326f99059fF775485246999027B3197955", - tokenInAmount: "2000000", - tokenOut: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh", - tokenOutAmount: "1366092", - tokenOutChainId: "Oraichain", - bridgeInfo: { - port: "transfer", - channel: "channel-1" - } - } - ] - }, - { - chainId: "Oraichain", - tokenIn: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh", - tokenInAmount: "1366092", - tokenOut: "uosmo", - tokenOutAmount: "2445755", - tokenOutChainId: "osmosis-1", - actions: [ - { - type: "Swap", - protocol: "OraidexV3", - tokenIn: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh", - tokenInAmount: "1366092", - tokenOut: "orai", - tokenOutAmount: "214856", - swapInfo: [ - { - poolId: "orai-orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh-3000000000-100", - tokenOut: "orai" - } - ] - }, - { - type: "Swap", - protocol: "Oraidex", - tokenIn: "orai", - tokenInAmount: "214856", - tokenOut: "ibc/9C4DCD21B48231D0BC2AC3D1B74A864746B37E4292694C93C617324250D002FC", - tokenOutAmount: "2445755", - swapInfo: [ - { - poolId: "orai1d37artrk4tkhz2qyjmaulc2jzjkx7206tmpfug", - tokenOut: "ibc/9C4DCD21B48231D0BC2AC3D1B74A864746B37E4292694C93C617324250D002FC" - } - ] - }, - { - type: "Bridge", - protocol: "Bridge", - tokenIn: "ibc/9C4DCD21B48231D0BC2AC3D1B74A864746B37E4292694C93C617324250D002FC", - tokenInAmount: "2445755", - tokenOut: "uosmo", - tokenOutAmount: "2445755", - tokenOutChainId: "osmosis-1", - bridgeInfo: { - port: "transfer", - channel: "channel-13" - } - } - ] - }, { chainId: "osmosis-1", tokenIn: "uosmo", - tokenInAmount: "2445755", + tokenInAmount: "100000", tokenOut: "utia", - tokenOutAmount: "241755", + tokenOutAmount: "9983", tokenOutChainId: "celestia", actions: [ { type: "Swap", protocol: "Osmosis", tokenIn: "uosmo", - tokenInAmount: "2445755", + tokenInAmount: "100000", tokenOut: "ibc/D79E7D83AB399BFFF93433E54FAA480C191248FC556924A2A8351AE2638B3877", - tokenOutAmount: "241755", + tokenOutAmount: "9983", swapInfo: [ { - poolId: "1263", - tokenOut: "ibc/498A0751C798A0D9A389AA3691123DADA57DAA4FE165D5C75894505B876BA6E4" - }, - { - poolId: "1247", + poolId: "1347", tokenOut: "ibc/D79E7D83AB399BFFF93433E54FAA480C191248FC556924A2A8351AE2638B3877" } ] @@ -115,9 +37,9 @@ const router = { type: "Bridge", protocol: "Bridge", tokenIn: "ibc/D79E7D83AB399BFFF93433E54FAA480C191248FC556924A2A8351AE2638B3877", - tokenInAmount: "241755", + tokenInAmount: "9983", tokenOut: "utia", - tokenOutAmount: "241755", + tokenOutAmount: "9983", tokenOutChainId: "celestia", bridgeInfo: { port: "transfer", @@ -130,13 +52,14 @@ const router = { } ] }; + const alphaSwapToOraichain = async () => { const wallet = new CosmosWalletImpl(process.env.MNEMONIC); - const sender = await wallet.getKeplrAddr("Oraichain"); + const sender = await wallet.getKeplrAddr("osmosis-1"); - const fromAmount = 2; + const fromAmount = 0.1; console.log("sender: ", sender); - const originalFromToken = flattenTokens.find((t) => t.coinGeckoId === "tether" && t.chainId === "0x38"); + const originalFromToken = flattenTokens.find((t) => t.coinGeckoId === "osmosis" && t.chainId === "osmosis-1"); const originalToToken = flattenTokens.find((t) => t.coinGeckoId === "celestia" && t.chainId === "celestia"); if (!originalToToken) throw generateError("Could not find original to token"); @@ -149,11 +72,11 @@ const alphaSwapToOraichain = async () => { sender: { cosmos: sender, evm: "0x8c7E0A841269a01c0Ab389Ce8Fb3Cf150A94E797" }, fromAmount, userSlippage: 1, - relayerFee: { - relayerAmount: "100000", - relayerDecimals: 6 - }, - simulatePrice: "147161", + // relayerFee: { + // relayerAmount: "100000", + // relayerDecimals: 6 + // }, + simulatePrice: "99956", simulateAmount: toAmount(fromAmount, originalToToken.decimals).toString(), alphaSmartRoutes: router },