diff --git a/package.json b/package.json index eefeb3fe..5a967510 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,13 @@ "@cosmjs/stargate": "^0.32.4", "@cosmjs/tendermint-rpc": "^0.32.4", "@oraichain/common-contracts-build": "1.0.35", - "@oraichain/common-contracts-sdk": "1.0.31" + "@oraichain/common-contracts-sdk": "1.0.31", + "@oraichain/ton-bridge-contracts": "^0.15.9", + "@oraichain/tonbridge-sdk": "^1.4.2", + "@orbs-network/ton-access": "^2.3.3", + "@ton/core": "0.56.3", + "@ton/crypto": "3.3.0", + "@ton/ton": "14.0.0" }, "devDependencies": { "@babel/traverse": "7.24.1", diff --git a/packages/oraidex-common/package.json b/packages/oraidex-common/package.json index 2f381f0c..c4af789f 100644 --- a/packages/oraidex-common/package.json +++ b/packages/oraidex-common/package.json @@ -1,6 +1,6 @@ { "name": "@oraichain/oraidex-common", - "version": "1.1.39", + "version": "1.1.40", "main": "build/index.js", "files": [ "build/" diff --git a/packages/oraidex-common/src/alpha-network.ts b/packages/oraidex-common/src/alpha-network.ts index 8db72250..92011158 100644 --- a/packages/oraidex-common/src/alpha-network.ts +++ b/packages/oraidex-common/src/alpha-network.ts @@ -1,8 +1,17 @@ +import { + ATOM_OSMOSIS_CONTRACT, + INJ_OSMOSIS_CONTRACT, + ORAI_OSMOSIS_CONTRACT, + TIA_OSMOSIS_CONTRACT, + TON_ALL_OSMOSIS_CONTRACT, + TON_OSMOSIS_CONTRACT, + USDC_OSMOSIS_CONTRACT +} from "./constant"; import { BridgeAppCurrency } from "./network"; export const AtomOsmosisToken: BridgeAppCurrency = { coinDenom: "ATOM", - coinMinimalDenom: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + coinMinimalDenom: ATOM_OSMOSIS_CONTRACT, coinDecimals: 6, coinGeckoId: "cosmos", coinImageUrl: "https://dhj8dql1kzq2v.cloudfront.net/white/atom.png", @@ -15,7 +24,7 @@ export const AtomOsmosisToken: BridgeAppCurrency = { export const UsdcOsmosisToken: BridgeAppCurrency = { coinDenom: "USDC", - coinMinimalDenom: "ibc/498A0751C798A0D9A389AA3691123DADA57DAA4FE165D5C75894505B876BA6E4", + coinMinimalDenom: USDC_OSMOSIS_CONTRACT, coinDecimals: 6, coinGeckoId: "usd-coin", coinImageUrl: "https://raw.githubusercontent.com/cosmos/chain-registry/master/noble/images/USDCoin.png", @@ -28,7 +37,7 @@ export const UsdcOsmosisToken: BridgeAppCurrency = { export const OraiOsmosisToken: BridgeAppCurrency = { coinDenom: "ORAI", - coinMinimalDenom: "ibc/161D7D62BAB3B9C39003334F1671208F43C06B643CC9EDBBE82B64793C857F1D", + coinMinimalDenom: ORAI_OSMOSIS_CONTRACT, coinDecimals: 6, coinGeckoId: "oraichain-token", coinImageUrl: "https://s2.coinmarketcap.com/static/img/coins/64x64/7533.png", @@ -41,7 +50,7 @@ export const OraiOsmosisToken: BridgeAppCurrency = { export const TiaOsmosisToken: BridgeAppCurrency = { coinDenom: "TIA", - coinMinimalDenom: "ibc/D79E7D83AB399BFFF93433E54FAA480C191248FC556924A2A8351AE2638B3877", + coinMinimalDenom: TIA_OSMOSIS_CONTRACT, coinDecimals: 6, coinGeckoId: "celestia", coinImageUrl: "https://raw.githubusercontent.com/cosmos/chain-registry/master/celestia/images/celestia.png", @@ -54,7 +63,7 @@ export const TiaOsmosisToken: BridgeAppCurrency = { export const InjOsmosisToken: BridgeAppCurrency = { coinDenom: "INJ", - coinMinimalDenom: "ibc/64BA6E31FE887D66C6F8F31C7B1A80C7CA179239677B4088BB55F5EA07DBE273", + coinMinimalDenom: INJ_OSMOSIS_CONTRACT, coinDecimals: 18, coinGeckoId: "injective-protocol", coinImageUrl: "https://raw.githubusercontent.com/cosmos/chain-registry/master/injective/images/inj.png", @@ -65,4 +74,39 @@ export const InjOsmosisToken: BridgeAppCurrency = { } }; -export const listOsmosisToken = [AtomOsmosisToken, OraiOsmosisToken, TiaOsmosisToken, InjOsmosisToken]; +export const TonOsmosisToken: BridgeAppCurrency = { + coinDenom: "TON", + coinMinimalDenom: TON_ALL_OSMOSIS_CONTRACT, + coinDecimals: 9, + bridgeTo: ["ton", "Oraichain"], + coinGeckoId: "the-open-network", + coinImageUrl: "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/ton/images/ton.png", + gasPriceStep: { + low: 0, + average: 0.025, + high: 0.04 + } +}; + +export const TonOraiOsmosisToken: BridgeAppCurrency = { + coinDenom: "TON.orai", + coinMinimalDenom: TON_OSMOSIS_CONTRACT, + coinDecimals: 9, + bridgeTo: ["ton", "Oraichain"], + coinGeckoId: "the-open-network", + coinImageUrl: "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/ton/images/ton.png", + gasPriceStep: { + low: 0, + average: 0.025, + high: 0.04 + } +}; + +export const listOsmosisToken = [ + AtomOsmosisToken, + OraiOsmosisToken, + TiaOsmosisToken, + TonOsmosisToken, + TonOraiOsmosisToken, + InjOsmosisToken +]; diff --git a/packages/oraidex-common/src/constant.ts b/packages/oraidex-common/src/constant.ts index e7c7acf0..0d05804c 100644 --- a/packages/oraidex-common/src/constant.ts +++ b/packages/oraidex-common/src/constant.ts @@ -142,6 +142,36 @@ export const WETH_CONTRACT = "orai1dqa52a7hxxuv8ghe7q5v0s36ra0cthea960q2cukznleq export const BTC_CONTRACT = "orai10g6frpysmdgw5tdqke47als6f97aqmr8s3cljsvjce4n5enjftcqtamzsd"; export const OCH_CONTRACT = "orai1hn8w33cqvysun2aujk5sv33tku4pgcxhhnsxmvnkfvdxagcx0p8qa4l98q"; +// config for Ton Token +export const TON20_USDT_CONTRACT = "EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs"; +export const HMSTR_TON_CONTRACT = "EQAJ8uWd7EBqsmpSWaRdf_I-8R8-XHwh3gsNKhy-UrdrPcUo"; +export const jUSDC_TON_CONTRACT = "EQB-MPwrd1G6WKNkLz_VnV6WqBDd142KMQv-g1O-8QUA3728"; +export const TON_CONTRACT = "EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c"; +export const TON_BRIDGE_ADAPTER = "EQC-aFP0rJXwTgKZQJPbPfTSpBFc8wxOgKHWD9cPvOl_DnaY"; +export const TON_LIGHT_CLIENT = "EQDzy_POlimFDyzrHd3OQsb9sZCngyG3O7Za4GRFzM-rrO93"; +export const TON_WHITE_LIST = "EQATDM6mfPZjPDMD9TVa6D9dlbmAKY5w6xOJiTXJ9Nqj_dsu"; +/** + * TODO: This is the object containing the hardcoded addresses of cw20 TON Network. They are obtained from the formula below. We need to add more when we support new TON20. + * const client = await getTonClient(); + * const jettonMinter = JettonMinter.createFromAddress( + * Address.parse(TON20 contractAddress) // exam: "EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs" => TON20_USDT_CONTRACT + * ); + * const jettonMinterContract = client.open(jettonMinter); + * const jettonWalletAddress = await jettonMinterContract.getWalletAddress( + * Address.parse("orai159l8l9c5ckhqpuwdfgs9p4v599nqt3cjlfahalmtrhfuncnec2ms5mz60e") + * ); + */ +export const JETTONS_ADDRESS = { + [TON_CONTRACT]: TON_CONTRACT, + [TON20_USDT_CONTRACT]: "EQANiP_ggkS0lJbR12xmDNT7hgqphdnVG0cCiJN6BN35Z498", + [HMSTR_TON_CONTRACT]: "EQC6hNI8R02lWfTV7wKXo9zueMoUP7ZBY4KZp5fkvN_Ad9Um", + [jUSDC_TON_CONTRACT]: "EQAacZPtQpnIHS1PlQgVaceb_I4v2HE3rvrZC91ynSRqXd9d" +}; +export const TON_NATIVE_DENOM = "ton"; + +export const TON_BRIDGE_ADAPTER_ORAICHAIN = "orai159l8l9c5ckhqpuwdfgs9p4v599nqt3cjlfahalmtrhfuncnec2ms5mz60e"; +export const TOKEN_FACTORY = "orai1wuvhex9xqs3r539mvc6mtm7n20fcj3qr2m0y9khx6n5vtlngfzes3k0rq9"; + // config for oraichain contract export const FACTORY_CONTRACT = "orai1hemdkz4xx9kukgrunxu3yw0nvpyxf34v82d2c8"; export const FACTORY_V2_CONTRACT = "orai167r4ut7avvgpp3rlzksz6vw5spmykluzagvmj3ht845fjschwugqjsqhst"; @@ -180,6 +210,14 @@ export const BASE_API_URL = "https://api.oraidex.io"; // alpha smart router export const OSMOSIS_ROUTER_CONTRACT = "osmo1h3jkejkcpthl45xrrm5geed3eq75p5rgfce9taufkwfr89k63muqweu2y7"; +export const ATOM_OSMOSIS_CONTRACT = "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2"; +export const USDC_OSMOSIS_CONTRACT = "ibc/498A0751C798A0D9A389AA3691123DADA57DAA4FE165D5C75894505B876BA6E4"; +export const ORAI_OSMOSIS_CONTRACT = "ibc/161D7D62BAB3B9C39003334F1671208F43C06B643CC9EDBBE82B64793C857F1D"; +export const TIA_OSMOSIS_CONTRACT = "ibc/D79E7D83AB399BFFF93433E54FAA480C191248FC556924A2A8351AE2638B3877"; +export const INJ_OSMOSIS_CONTRACT = "ibc/64BA6E31FE887D66C6F8F31C7B1A80C7CA179239677B4088BB55F5EA07DBE273"; +export const TON_ALL_OSMOSIS_CONTRACT = + "factory/osmo12lnwf54yd30p6amzaged2atln8k0l32n7ncxf04ctg7u7ymnsy7qkqgsw4/alloyed/allTON"; +export const TON_OSMOSIS_CONTRACT = "ibc/905889A7F0B94F1CE1506D9BADF13AE9141E4CBDBCD565E1DFC7AE418B3E3E98"; // websocket consts export const WEBSOCKET_RECONNECT_ATTEMPTS = 5; diff --git a/packages/oraidex-common/src/helper.ts b/packages/oraidex-common/src/helper.ts index 9729c5df..e9f55b18 100644 --- a/packages/oraidex-common/src/helper.ts +++ b/packages/oraidex-common/src/helper.ts @@ -19,7 +19,7 @@ import { MULTIPLIER, CW20_DECIMALS } from "./constant"; -import { CoinGeckoId, NetworkChainId, cosmosChains } from "./network"; +import { CoinGeckoId, NetworkChainId, cosmosChainIds, cosmosChains, evmChainIds, tonChainId } from "./network"; import { AmountDetails, TokenInfo, @@ -34,6 +34,7 @@ import { StargateMsg, Tx } from "./tx"; import { BigDecimal } from "./bigdecimal"; import { TextProposal } from "cosmjs-types/cosmos/gov/v1beta1/gov"; import { defaultRegistryTypes as defaultStargateTypes, IndexedTx, logs, StargateClient } from "@cosmjs/stargate"; +import { Address } from "@ton/core"; export const getEvmAddress = (bech32Address: string) => { if (!bech32Address) throw new Error("bech32 address is empty"); @@ -572,19 +573,30 @@ export const validateEvmAddress = (address: string, network: string) => { } }; -export const validateTronAddress = (address: string, network: string) => { +const isValidTronAddress = (address: string): boolean => /T[a-zA-Z0-9]{32}/.test(address); +const isValidTonAddress = (address: string): boolean => !!Address.parse(address); + +export const validateAddressTonTron = (address: string, network: string) => { try { - if (!/T[a-zA-Z0-9]{32}/.test(address)) { - throw new Error("Invalid tron address"); + let isValid: boolean; + switch (network) { + case "0x2b6653dc": + isValid = isValidTronAddress(address); + break; + case "ton": + isValid = isValidTonAddress(address); + break; + default: + throw new Error("Unsupported network"); } - return { - isValid: true, + isValid, network }; } catch (error) { return { - isValid: false + isValid: false, + error: error.message }; } }; @@ -595,11 +607,27 @@ export const checkValidateAddressWithNetwork = (address: string, network: Networ case "0x38": return validateEvmAddress(address, network); - // tron + // tron & ton case "0x2b6653dc": - return validateTronAddress(address, network); + case "ton": + return validateAddressTonTron(address, network); default: return validateAndIdentifyCosmosAddress(address, network); } }; + +export const isCosmosChain = (chainId: string): boolean => { + const hasValue = cosmosChainIds.find((id) => id === chainId); + return Boolean(hasValue); +}; + +export const isEvmChain = (chainId: string): boolean => { + const hasValue = evmChainIds.find((id) => id === chainId); + return Boolean(hasValue); +}; + +export const isTonChain = (chainId: string): boolean => { + const hasValue = tonChainId.find((id) => id === chainId); + return Boolean(hasValue); +}; diff --git a/packages/oraidex-common/src/interface/wallet.ts b/packages/oraidex-common/src/interface/wallet.ts index 78615bb4..5bb03eeb 100644 --- a/packages/oraidex-common/src/interface/wallet.ts +++ b/packages/oraidex-common/src/interface/wallet.ts @@ -17,5 +17,7 @@ export enum ChainIdEnum { BNBChainTestNet = "0x61", TRON = "0x2b6653dc", BitcoinTestnet = "bitcoinTestnet", - Bitcoin = "bitcoin" + Bitcoin = "bitcoin", + Ton = "ton", + TonTestnet = "ton_testnet" } diff --git a/packages/oraidex-common/src/network.ts b/packages/oraidex-common/src/network.ts index 8ecbe594..5ad2d04a 100644 --- a/packages/oraidex-common/src/network.ts +++ b/packages/oraidex-common/src/network.ts @@ -61,9 +61,13 @@ import { PEPE_BSC_CONTRACT, CAT_BSC_CONTRACT, HMSTR_ORAICHAIN_DENOM, + TON20_USDT_CONTRACT, + TON_CONTRACT, OBTC_ORAICHAIN_EXT_DENOM, DOGE_BNB_ORAICHAIN_DENOM, DOGE_BSC_CONTRACT, + jUSDC_TON_CONTRACT, + HMSTR_TON_CONTRACT, WSOL_WORMHOLE_BNB_ORAICHAIN_DENOM, WSOL_WORMHOLE_BSC_CONTRACT, solChainId, @@ -88,28 +92,30 @@ export type NetworkName = | "Noble" | "Neutaro" | "Celestia" + | "TON" | "Solana"; -export type CosmosChainId = - | "Oraichain" // oraichain - | "oraibridge-subnet-2" // oraibridge - | "osmosis-1" // osmosis - | "cosmoshub-4" // cosmos hub - | "injective-1" // injective network - | "kawaii_6886-1" // kawaii subnetwork - | "noble-1" // noble network - | "Neutaro-1" // neutaro network; - | "celestia"; // Celestia +export const cosmosChainIds = [ + "Oraichain", // oraichain + "oraibridge-subnet-2", // oraibridge + "osmosis-1", // osmosis + "cosmoshub-4", // cosmos hub + "injective-1", // injective network + "kawaii_6886-1", // kawaii subnetwork + "noble-1", // noble network + "Neutaro-1", // neutaro network; + "celestia" // Celestia +] as const; +export type CosmosChainId = (typeof cosmosChainIds)[number]; -export type EvmChainId = - | "0x38" // bsc - | "0x01" // ethereum - | "0x1ae6" // kawaii - | "0x2b6653dc"; // tron +export const evmChainIds = ["0x38", "0x01", "0x1ae6", "0x2b6653dc"] as const; +export type EvmChainId = (typeof evmChainIds)[number]; -export type SolChainId = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp"; +export const tonChainId = ["ton"] as const; // FIXME: don;t know ton chainID +export type TonChainId = (typeof tonChainId)[number]; -export type NetworkChainId = CosmosChainId | EvmChainId | SolChainId; +export type SolChainId = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp"; +export type NetworkChainId = CosmosChainId | EvmChainId | TonChainId | SolChainId; export type CoinGeckoId = | "oraichain-token" @@ -142,7 +148,7 @@ export type CoinGeckoId = | "solana" | "max-2"; -export type NetworkType = "cosmos" | "evm" | "svm"; +export type NetworkType = "cosmos" | "evm" | "ton" | "svm"; export interface NetworkConfig { coinType?: number; explorer: string; @@ -176,7 +182,7 @@ export type BridgeAppCurrency = FeeCurrency & { readonly prefixToken?: string; }; -export type CoinType = 118 | 60 | 195 | 501; +export type CoinType = 118 | 60 | 195 | 501 | 607; /** * A list of Cosmos chain infos. If we need to add / remove any chains, just directly update this variable. @@ -323,6 +329,77 @@ export const OsmoToken: BridgeAppCurrency = { } }; +export const tonNetworkMainnet: CustomChainInfo = { + rest: "https://toncenter.com/api/v2/jsonRPC", + rpc: "https://toncenter.com/api/v2/jsonRPC", + chainId: "ton", + chainName: "TON", + bip44: { + coinType: 607 + }, + coinType: 607, + stakeCurrency: { + coinDenom: "TON", + coinMinimalDenom: "ton", + coinDecimals: 9, + coinGeckoId: "the-open-network", + coinImageUrl: "https://assets.coingecko.com/coins/images/17980/standard/ton_symbol.png" + }, + bech32Config: defaultBech32Config("ton"), + networkType: "ton", + currencies: [ + { + coinDenom: "TON", + coinMinimalDenom: "ton", + coinDecimals: 9, + bridgeTo: ["Oraichain"], + prefixToken: "ton20_", + contractAddress: TON_CONTRACT, + coinGeckoId: "the-open-network", + coinImageUrl: "https://assets.coingecko.com/coins/images/17980/standard/ton_symbol.png" + }, + { + coinDenom: "USDT", + coinMinimalDenom: "ton20_usdt", + coinDecimals: 6, + bridgeTo: ["Oraichain"], + contractAddress: TON20_USDT_CONTRACT, + prefixToken: "ton20_", + coinGeckoId: "tether", + coinImageUrl: "https://s2.coinmarketcap.com/static/img/coins/64x64/825.png" + }, + { + coinDenom: "HMSTR", + coinMinimalDenom: "ton20_hamster_kombat", + coinDecimals: 9, + bridgeTo: ["Oraichain"], + contractAddress: HMSTR_TON_CONTRACT, + prefixToken: "ton20_", + coinGeckoId: "hamster-kombat", + coinImageUrl: "https://assets.coingecko.com/coins/images/39102/standard/hamster-removebg-preview.png?1720514486" + }, + { + coinDenom: "jUSDC", + coinMinimalDenom: "ton20_usdc", + coinDecimals: 6, + bridgeTo: ["Oraichain"], + contractAddress: jUSDC_TON_CONTRACT, + prefixToken: "ton20_", + coinGeckoId: "usd-coin", + coinImageUrl: "https://s2.coinmarketcap.com/static/img/coins/64x64/3408.png" + } + ], + get feeCurrencies() { + return this.currencies; + }, + features: ["isTon"], + txExplorer: { + name: "BlockStream", + txUrl: "https://tonviewer.com/transaction/{txHash}", + accountUrl: `https://tonviewer.com/transaction/{address}` + } +}; + export const oraichainNetwork: CustomChainInfo = { rpc: "https://rpc.orai.io", rest: "https://lcd.orai.io", @@ -415,7 +492,7 @@ export const oraichainNetwork: CustomChainInfo = { coinMinimalDenom: "usdt", type: "cw20", contractAddress: USDT_CONTRACT, - bridgeTo: ["0x38", "0x2b6653dc", "0x01"], + bridgeTo: ["0x38", "0x2b6653dc", "0x01", "ton"], coinDecimals: 6, coinImageUrl: "https://s2.coinmarketcap.com/static/img/coins/64x64/825.png" }, @@ -425,7 +502,7 @@ export const oraichainNetwork: CustomChainInfo = { coinMinimalDenom: "usdc", type: "cw20", contractAddress: USDC_CONTRACT, - bridgeTo: ["0x01", "noble-1"], + bridgeTo: ["0x01", "noble-1", "ton"], coinDecimals: 6, coinImageUrl: "https://s2.coinmarketcap.com/static/img/coins/64x64/3408.png" }, @@ -567,12 +644,14 @@ export const oraichainNetwork: CustomChainInfo = { coinDenom: "TON", coinMinimalDenom: TON_ORAICHAIN_DENOM, coinDecimals: 9, + bridgeTo: ["ton", "osmosis-1"], coinGeckoId: "the-open-network", coinImageUrl: "https://assets.coingecko.com/coins/images/17980/standard/ton_symbol.png?1696517498" }, { coinDenom: "HMSTR", coinMinimalDenom: HMSTR_ORAICHAIN_DENOM, + bridgeTo: ["ton"], coinDecimals: 9, coinGeckoId: "hamster-kombat", coinImageUrl: "https://assets.coingecko.com/coins/images/39102/standard/hamster-removebg-preview.png?1720514486" @@ -656,6 +735,7 @@ export const solanaMainnet: CustomChainInfo = { export const chainInfos: CustomChainInfo[] = [ // networks to add on keplr oraichainNetwork, + tonNetworkMainnet, celestiaNetwork, solanaMainnet, { diff --git a/packages/oraidex-common/tests/helper.spec.ts b/packages/oraidex-common/tests/helper.spec.ts index 24ab35d1..90934a03 100644 --- a/packages/oraidex-common/tests/helper.spec.ts +++ b/packages/oraidex-common/tests/helper.spec.ts @@ -33,7 +33,7 @@ import { validateAndIdentifyCosmosAddress, validateEvmAddress, validateNumber, - validateTronAddress, + validateAddressTonTron, parseAssetInfoFromContractAddrOrDenom } from "../src/helper"; import { CoinGeckoId, NetworkChainId } from "../src/network"; @@ -633,11 +633,12 @@ describe("should helper functions in helper run exactly", () => { ["TEu6u8JLCFs6x1w5s8WosNqYqVx2JMC5hQ", "0x2b6653dc", true], ["0x1CE09E54A5d7432ecabf3b085BAda7920aeb7dab", "0x01", false], ["TEu6u8JLCFs6x1w5s8WosNqYqVx2JMC5hQ", "0x01", false], + ["UQDOAHXyCPFOXAXm9c1P_NeNEeSWy6IaRHqJRnBUp0jMZ6i3", "ton", true], ["TE", "0x2b6653dc", false], ["", "0x2b6653dc", false] ])("test-validateTronAddress", (value, network, expectation) => { try { - const { isValid } = validateTronAddress(value, network); + const { isValid } = validateAddressTonTron(value, network); expect(isValid).toEqual(expectation); } catch (error) { expect(expectation).toEqual(false); diff --git a/packages/universal-swap/package.json b/packages/universal-swap/package.json index 72d9d97a..7d4a69d0 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.23", + "version": "1.1.24", "main": "build/index.js", "files": [ "build/" @@ -16,12 +16,12 @@ "bech32": "1.1.4", "ethers": "^5.0.15", "tronweb": "5.3.2", - "@oraichain/common": "^1.0.3", + "@oraichain/common": "^1.2.4", "ts-protoc-gen": "^0.15.0", - "@oraichain/osor-api-contracts-sdk": "^1.0.2" + "@oraichain/osor-api-contracts-sdk": "^1.0.2", + "cosmjs-types": "^0.9.0" }, "devDependencies": { - "cosmjs-types": "^0.9.0", "ts-results": "^3.3.0", "protobufjs": "7.2.6" } diff --git a/packages/universal-swap/src/handler.ts b/packages/universal-swap/src/handler.ts index bddc473d..a1adccb8 100644 --- a/packages/universal-swap/src/handler.ts +++ b/packages/universal-swap/src/handler.ts @@ -41,7 +41,10 @@ import { OSMOSIS_ROUTER_CONTRACT, cosmosChains, toDisplay, - ChainIdEnum + ChainIdEnum, + isCosmosChain, + TON_BRIDGE_ADAPTER_ORAICHAIN, + TON_BRIDGE_ADAPTER } from "@oraichain/oraidex-common"; import { ethers } from "ethers"; import { UniversalSwapHelper } from "./helper"; @@ -62,6 +65,9 @@ import { OraiswapRouterQueryClient } from "@oraichain/oraidex-contracts-sdk"; import { Affiliate } from "@oraichain/oraidex-contracts-sdk/build/OraiswapMixedRouter.types"; import { COSMOS_CHAIN_IDS, EVM_CHAIN_IDS } from "@oraichain/common"; import { generateMsgSwap } from "./msg/msgs"; +// import { calculateTimeoutTimestampTon, createTonBridgeHandler } from "@oraichain/tonbridge-sdk"; +import { toNano } from "@ton/core"; +import { getHttpEndpoint } from "@orbs-network/ton-access"; const AFFILIATE_DECIMAL = 1e4; // 10_000 export class UniversalSwapHandler { @@ -89,12 +95,18 @@ export class UniversalSwapHandler { async getUniversalSwapToAddress( toChainId: NetworkChainId, - address: { metamaskAddress?: string; tronAddress?: string } + address: { metamaskAddress?: string; tronAddress?: string; tonAddress?: string } ): Promise { // evm based if (toChainId === "0x01" || toChainId === "0x1ae6" || toChainId === "0x38") { return address.metamaskAddress ?? (await this.config.evmWallet.getEthAddress()); } + + // ton + if (toChainId === "ton") { + return address.tonAddress ?? (await this.config.tonWallet.sender.address.toString()); + } + // tron if (toChainId === "0x2b6653dc") { if (address.tronAddress) return tronToEthAddress(address.tronAddress); @@ -102,7 +114,9 @@ export class UniversalSwapHandler { if (tronWeb && tronWeb.defaultAddress?.base58) return tronToEthAddress(tronWeb.defaultAddress.base58); throw generateError("Cannot find tron web to nor tron address to send to Tron network"); } - return this.config.cosmosWallet.getKeplrAddr(toChainId as CosmosChainId); + + if (isCosmosChain(toChainId.toString())) return this.config.cosmosWallet.getKeplrAddr(toChainId as CosmosChainId); + throw generateError(`Cannot not get address for chain: ${toChainId}`); } /** @@ -747,10 +761,14 @@ export class UniversalSwapHandler { "oraichain-to-oraichain", "oraichain-to-cosmos", "oraichain-to-evm", + "oraichain-to-ton", "cosmos-to-others" ].includes(universalSwapType); if (universalSwapTypeFromCosmos) { + if (!alphaSmartRoutes.routes?.length) + throw generateError(`Missing router universalSwapTypeFromCosmos: ${universalSwapType}!`); + const isCheckBalance = ["oraichain-to-evm", "oraichain-to-cosmos"].includes(universalSwapType); if (!this.config?.swapOptions?.isCheckBalanceIbc && isCheckBalance) { const { client } = await this.config.cosmosWallet.getCosmWasmClient( @@ -776,6 +794,7 @@ export class UniversalSwapHandler { const msgs = alphaSmartRoutes.routes.map((route) => { return generateMsgSwap(route, userSlippage / 100, receiverAddresses); }); + const { client } = await this.config.cosmosWallet.getCosmWasmClient( { chainId: originalFromToken.chainId as CosmosChainId, @@ -788,10 +807,98 @@ export class UniversalSwapHandler { return await client.signAndBroadcast(sender.cosmos, msgs, "auto"); } + if (universalSwapType === "ton-to-others") return this.transferAndSwapTon(swapRoute); return this.transferAndSwap(swapRoute); } + async transferAndSwapTon(swapRoute: string) { + const { + sender, + originalFromToken, + originalToToken, + fromAmount, + userSlippage, + simulatePrice, + relayerFee, + simulateAmount + } = this.swapData; + const { ton: tonAddress } = sender; + if (!tonAddress) throw generateError("Cannot call ton swap if the ton address is empty"); + if (!this.config.cosmosWallet) throw generateError("Cannot transfer and swap if cosmos wallet is not initialized"); + // we get cosmwasm client on Oraichain because this is checking channel balance on Oraichain + const { client } = await this.config.cosmosWallet.getCosmWasmClient( + { rpc: network.rpc, chainId: network.chainId as CosmosChainId }, + {} + ); + + if (!this.config?.swapOptions?.isCheckBalanceIbc) { + await UniversalSwapHelper.checkBalanceIBCOraichain( + originalToToken, + originalFromToken, + fromAmount, + simulateAmount, + client, + this.getCwIcs20ContractAddr() + ); + + const routerClient = new OraiswapRouterQueryClient(client, network.mixer_router); + const isSufficient = await UniversalSwapHelper.checkFeeRelayer({ + originalFromToken, + fromAmount, + relayerFee, + routerClient + }); + if (!isSufficient) + throw generateError( + `Your swap amount ${fromAmount} cannot cover the fees for this transaction. Please try again with a higher swap amount` + ); + } + + const { createTonBridgeHandler, calculateTimeoutTimestampTon } = await import( + "@oraichain/tonbridge-sdk/build/utils" + ); + + const tonCenterUrl = await getHttpEndpoint({ + network: "mainnet" + }); + const handler = await createTonBridgeHandler( + this.config.cosmosWallet, + this.config.tonWallet, + { + rpc: originalToToken.rpc, + chainId: COSMOS_CHAIN_IDS.ORAICHAIN + }, + { + overrideConfig: { + tonCenterUrl + } + } + ); + + const swapRouteSplit = swapRoute.split(":"); + const memo = swapRouteSplit[1] || ""; + + await handler.sendToCosmos( + handler.wasmBridge.sender, + toNano(fromAmount), + originalFromToken.denom, + { + queryId: 0, + value: toNano(0) + }, + calculateTimeoutTimestampTon(3600), + memo + ); + + return { + code: 0, + status: true, + sender: sender.ton, + transactionHash: sender.ton, + receiver: handler.wasmBridge.sender + }; + } - async getToAddressUniversalSwap(evm, tron, recipientAddress, originalToToken) { + async getToAddressUniversalSwap({ evm, tron, ton }, recipientAddress, originalToToken) { if (this.swapData.recipientAddress) { const isValidRecipient = checkValidateAddressWithNetwork(recipientAddress, originalToToken.chainId); @@ -803,30 +910,38 @@ export class UniversalSwapHandler { } else { return await this.getUniversalSwapToAddress(originalToToken.chainId, { metamaskAddress: evm, - tronAddress: tron + tronAddress: tron, + tonAddress: ton }); } } async processUniversalSwap() { - const { evm, tron } = this.swapData.sender; + const { evm, tron, ton } = this.swapData.sender; const { originalFromToken, originalToToken, simulateAmount, recipientAddress, userSlippage, alphaSmartRoutes } = this.swapData; const { swapOptions } = this.config; - const toAddress = await this.getToAddressUniversalSwap(evm, tron, this.swapData.recipientAddress, originalToToken); + const toAddress = await this.getToAddressUniversalSwap( + { evm, tron, ton }, + this.swapData.recipientAddress, + originalToToken + ); const oraiAddress = await this.config.cosmosWallet.getKeplrAddr(COSMOS_CHAIN_IDS.ORAICHAIN); if (!oraiAddress) throw generateError("orai address and obridge address invalid!"); const isValidRecipientOraichain = checkValidateAddressWithNetwork(oraiAddress, COSMOS_CHAIN_IDS.ORAICHAIN); if (!isValidRecipientOraichain.isValid) throw generateError("orai get address invalid!"); let injAddress = undefined; - let evmAddress = undefined; let tronAddress = undefined; let addressParams = { oraiAddress, injAddress, - evmInfo: {} + evmInfo: { + ton: ton, + [EVM_CHAIN_IDS.ETH]: evm, + [EVM_CHAIN_IDS.BSC]: evm + } as any }; const isAlphaIbcWasmHasRoute = swapOptions?.isAlphaIbcWasm && alphaSmartRoutes?.routes?.length; @@ -835,35 +950,21 @@ export class UniversalSwapHandler { if (swapOptions?.isIbcWasm) minimumReceive = await this.calculateMinimumReceive(); if (isAlphaIbcWasmHasRoute) { const routesFlatten = UniversalSwapHelper.flattenSmartRouters(alphaSmartRoutes.routes); - const [hasEVMChains, hasTron, hasInjectiveAddress] = [ - routesFlatten.some((route) => - [route.chainId, route.tokenOutChainId].some((id) => - [EVM_CHAIN_IDS.ETH, EVM_CHAIN_IDS.BSC].includes(id as any) - ) - ), + const [hasTron, hasInj] = [ routesFlatten.some((route) => [route.chainId, route.tokenOutChainId].includes(EVM_CHAIN_IDS.TRON)), routesFlatten.some((route) => [route.chainId, route.tokenOutChainId].includes(COSMOS_CHAIN_IDS.INJECTVE)) ]; - if (hasInjectiveAddress) { + if (hasInj) { injAddress = await this.config.cosmosWallet.getKeplrAddr(COSMOS_CHAIN_IDS.INJECTVE); addressParams.injAddress = injAddress; } - if (hasEVMChains) { - evmAddress = this.swapData.sender.evm; - addressParams.evmInfo = { - ...addressParams.evmInfo, - [EVM_CHAIN_IDS.ETH]: this.swapData.sender.evm, - [EVM_CHAIN_IDS.BSC]: this.swapData.sender.evm - }; - } - if (hasTron) { - tronAddress = tronToEthAddress(this.swapData.sender.tron); + tronAddress = tronToEthAddress(tron); addressParams.evmInfo = { ...addressParams.evmInfo, - [EVM_CHAIN_IDS.TRON]: tronToEthAddress(this.swapData.sender.tron) + [EVM_CHAIN_IDS.TRON]: tronToEthAddress(tron) }; } } @@ -874,8 +975,9 @@ export class UniversalSwapHandler { sourceReceiver: oraiAddress, destReceiver: toAddress, recipientAddress, - evmAddress, - tronAddress + evmAddress: evm, + tronAddress, + tonAddress: ton }, originalFromToken, originalToToken, @@ -885,7 +987,7 @@ export class UniversalSwapHandler { alphaSmartRoutes ); - if (isAlphaIbcWasmHasRoute) { + if (swapOptions?.isAlphaIbcWasm) { let receiverAddresses = UniversalSwapHelper.generateAddress(addressParams); if (recipientAddress) receiverAddresses[originalToToken.chainId] = toAddress; return this.alphaSmartRouterSwapNewMsg(swapRoute, universalSwapType, receiverAddresses); diff --git a/packages/universal-swap/src/helper.ts b/packages/universal-swap/src/helper.ts index fff116d0..9525be91 100644 --- a/packages/universal-swap/src/helper.ts +++ b/packages/universal-swap/src/helper.ts @@ -251,11 +251,16 @@ export class UniversalSwapHelper { if (!fromToken || !toToken || !destReceiver) return { swapRoute: "", universalSwapType: "other-networks-to-oraichain", isSmartRouter: false }; // this is the simplest case. Both tokens on the same Oraichain network => simple swap with to token denom + if (fromToken.chainId === "ton") { + return { swapRoute: "", universalSwapType: "ton-to-others", isSmartRouter: true }; + } if (fromToken.chainId === "Oraichain" && toToken.chainId === "Oraichain") { return { swapRoute: "", universalSwapType: "oraichain-to-oraichain", isSmartRouter: false }; } // we dont need to have any swapRoute for this case if (fromToken.chainId === "Oraichain") { + if (toToken.chainId === "ton") + return { swapRoute: "", universalSwapType: "oraichain-to-ton", isSmartRouter: false }; if (cosmosTokens.some((t) => t.chainId === toToken.chainId)) return { swapRoute: "", universalSwapType: "oraichain-to-cosmos", isSmartRouter: false }; return { swapRoute: "", universalSwapType: "oraichain-to-evm", isSmartRouter: false }; @@ -385,6 +390,7 @@ export class UniversalSwapHelper { recipientAddress?: string; evmAddress?: string; tronAddress?: string; + tonAddress?: string; }, fromToken: TokenItemType, toToken: TokenItemType, @@ -438,9 +444,15 @@ export class UniversalSwapHelper { /** * useAlphaIbcWasm case: - * evm -> oraichain -> osmosis -> inj/tia + * evm -> oraichain -> osmosis -> cosmos + * ton -> others */ - if (swapOption.isAlphaIbcWasm && !fromToken.cosmosBased && fromToken.chainId !== toToken.chainId) { + if ( + swapOption.isAlphaIbcWasm && + alphaSmartRoute?.routes?.length && + !fromToken.cosmosBased && + fromToken.chainId !== toToken.chainId + ) { if (!alphaSmartRoute) throw generateError(`Missing router with alpha ibc wasm!`); const routes = alphaSmartRoute.routes; const alphaRoutes = routes[0]; @@ -456,14 +468,18 @@ export class UniversalSwapHelper { paths = alphaRoutes.paths.filter((_, index) => index > 0); } + let evmInfo = {}; + if (!toToken.cosmosBased) { + const addressMapping = { + [EVM_CHAIN_IDS.TRON]: addresses.tronAddress, + ton: addresses.tonAddress + }; + evmInfo[toToken.chainId] = addressMapping[toToken.chainId] || addresses.evmAddress; + } let receiverAddresses = UniversalSwapHelper.generateAddress({ injAddress: addresses.injAddress, oraiAddress: addresses.sourceReceiver, - evmInfo: !toToken.cosmosBased - ? { - [toToken.chainId]: toToken.chainId === EVM_CHAIN_IDS.TRON ? addresses.tronAddress : addresses.evmAddress - } - : {} + evmInfo }); if (addresses?.recipientAddress) receiverAddresses[toToken.chainId] = addresses?.recipientAddress; diff --git a/packages/universal-swap/src/msg/chains/oraichain.ts b/packages/universal-swap/src/msg/chains/oraichain.ts index 221c5371..1a4fc0a8 100644 --- a/packages/universal-swap/src/msg/chains/oraichain.ts +++ b/packages/universal-swap/src/msg/chains/oraichain.ts @@ -9,7 +9,11 @@ import { CONVERTER_CONTRACT, generateError, IBC_TRANSFER_TIMEOUT, + IBC_WASM_CONTRACT, + isCosmosChain, isEthAddress, + isTonChain, + JETTONS_ADDRESS, NetworkChainId } from "@oraichain/oraidex-common"; import { toBinary } from "@cosmjs/cosmwasm-stargate"; @@ -23,6 +27,7 @@ import { ChainMsg } from "./chain"; export class OraichainMsg extends ChainMsg { SWAP_VENUE_NAME = "oraidex"; ENTRY_POINT_CONTRACT = "orai1yglsm0u2x3xmct9kq3lxa654cshaxj9j5d9rw5enemkkkdjgzj7sr3gwt0"; + TON_BRIDGE_ADAPTER = "orai159l8l9c5ckhqpuwdfgs9p4v599nqt3cjlfahalmtrhfuncnec2ms5mz60e"; constructor( path: Path, @@ -162,7 +167,38 @@ export class OraichainMsg extends ChainMsg { }; } - // case 2: ibc transfer + // case 2: bridge to ton + if (isTonChain(bridgeInfo.toChain)) { + const jettonToToken = JETTONS_ADDRESS[bridgeInfo.toToken]; + if (!jettonToToken) throw `getProtoForPostAction: jetton address of ${bridgeInfo.toToken} not found!`; + + let msg: unknown = { + bridge_to_ton: { + to: bridgeInfo.receiver, + denom: jettonToToken, + timeout: Math.floor(new Date().getTime() / 1000) + IBC_TRANSFER_TIMEOUT, + recovery_addr: this.currentChainAddress + } + }; + + if (isCw20Token(bridgeInfo.fromToken)) { + msg = { + to: bridgeInfo.receiver, + denom: jettonToToken, + timeout: Math.floor(new Date().getTime() / 1000) + IBC_TRANSFER_TIMEOUT, + recovery_addr: this.currentChainAddress + }; + } + + return { + contractCall: { + contractAddress: this.TON_BRIDGE_ADAPTER, + msg: toBinary(msg) + } + }; + } + + // case 3: ibc transfer if (bridgeInfo.sourcePort == "transfer") { return { ibcTransferMsg: { @@ -175,8 +211,7 @@ export class OraichainMsg extends ChainMsg { }; } - // case 3: ibc wasm transfer - + // case 4: ibc wasm transfer if (bridgeInfo.sourcePort.startsWith("wasm")) { // handle noble & evm case let prefix = ""; @@ -249,7 +284,37 @@ export class OraichainMsg extends ChainMsg { }; } - // case 2: ibc transfer + // case 2: bridge to ton + if (isTonChain(bridgeInfo.toChain)) { + const jettonToToken = JETTONS_ADDRESS[bridgeInfo.toToken]; + if (!jettonToToken) throw `getPostAction: jetton address of ${bridgeInfo.toToken} not found!`; + + let msg: unknown = { + bridge_to_ton: { + to: bridgeInfo.receiver, + denom: jettonToToken, + timeout: Math.floor(new Date().getTime() / 1000) + IBC_TRANSFER_TIMEOUT, + recovery_addr: this.currentChainAddress + } + }; + + if (isCw20Token(bridgeInfo.fromToken)) { + msg = { + to: bridgeInfo.receiver, + denom: jettonToToken, + timeout: Math.floor(new Date().getTime() / 1000) + IBC_TRANSFER_TIMEOUT, + recovery_addr: this.currentChainAddress + }; + } + return { + contract_call: { + contract_address: this.TON_BRIDGE_ADAPTER, + msg: toBinary(msg) + } + }; + } + + // case 3: ibc transfer if (bridgeInfo.sourcePort == "transfer") { return { ibc_transfer: { @@ -263,7 +328,7 @@ export class OraichainMsg extends ChainMsg { }; } - // case 3: ibc wasm transfer + // case 4: ibc wasm transfer if (bridgeInfo.sourcePort.startsWith("wasm")) { // handle noble & evm case let prefix = ""; @@ -295,29 +360,61 @@ export class OraichainMsg extends ChainMsg { let [swapOps, bridgeInfo] = this.getSwapAndBridgeInfo(); // we have 2 cases: - // - first case: only ibc transfer (build IBC forward middleware msg) + // - first case: only bridge via Oraichain ibc transfer or transfer to TON (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"); + // we have 2 case: + // - first case: bridge to TON (use tonBridge) + // - ibc bridge: bridge to other cosmos chain (ibc) + // TODO: support bridge to EVM + + // case 1: bridge to ton + // we will use IBC hooks with ibcWasm contract + if (isTonChain(bridgeInfo.toChain)) { + let memo: Memo = { + userSwap: undefined, // no swap action + minimumReceive: this.minimumReceive, + timeoutTimestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + postSwapAction: this.getProtoForPostAction(bridgeInfo), + recoveryAddr: this.currentChainAddress + }; + return { + receiver: IBC_WASM_CONTRACT, + memo: JSON.stringify({ + wasm: { + contract: IBC_WASM_CONTRACT, + msg: { + ibc_hooks_receive: { + func: "universal_swap", + orai_receiver: this.currentChainAddress, + args: Buffer.from(Memo.encode(memo).finish()).toString("base64") + } + } + } + }) + }; } - // ibc bridge - 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 || undefined - } - }) - }; + // case 2: ibc bridge + // bridge only + if (bridgeInfo.sourcePort == "transfer") { + // ibc bridge + 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 || undefined + } + }) + }; + } + throw generateError("Error on generate memo as middleware: Only support ibc bridge & ton bridge"); } let tokenOutOfSwap = swapOps[swapOps.length - 1].denom_out; @@ -361,107 +458,144 @@ export class OraichainMsg extends ChainMsg { }; } - /** - * Function to generate execute msg on Oraichain - */ + private genBridgeMsg(bridgeInfo: BridgeMsgInfo): EncodeObject { + // 3 cases: + // ibc transfer + // ibc wasm transfer + // ton bridge - genExecuteMsg(): EncodeObject { - let [swapOps, bridgeInfo] = this.getSwapAndBridgeInfo(); + // ibc transfer + if (bridgeInfo.sourcePort == "transfer" && isCosmosChain(bridgeInfo.toChain)) { + 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) + } + }; + } - // we have 3 cases: - // - case 1: ibc transfer - // - case 2; ibc wasm transfer - // - case 3: swap and action + let msg; + let contractAddr: string; - 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 or Obridge address for bridge to EVM"); + prefix = this.destPrefix; } - // 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 or Obridge address for bridge to EVM"); - prefix = this.destPrefix; - } - - const ibcWasmContractAddress = bridgeInfo.sourcePort.split(".")[1]; - if (!ibcWasmContractAddress) - throw generateError("IBC Wasm source port is invalid. Cannot transfer to the destination chain"); + const ibcWasmContractAddress = bridgeInfo.sourcePort.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: isBridgeToEvm ? this.obridgeAddress : this.receiver, - remote_denom: prefix + bridgeInfo.toToken, - timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), - memo: isBridgeToEvm ? prefix + this.receiver : this.memo + msg = { + local_channel_id: bridgeInfo.sourceChannel, + remote_address: isBridgeToEvm ? this.obridgeAddress : this.receiver, + remote_denom: prefix + bridgeInfo.toToken, + timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + memo: isBridgeToEvm ? prefix + this.receiver : this.memo + }; + if (!isCw20Token(bridgeInfo.fromToken)) { + msg = { + transfer_to_remote: msg }; + } + contractAddr = ibcWasmContractAddress; + } else if (isTonChain(bridgeInfo.toChain)) { + contractAddr = this.TON_BRIDGE_ADAPTER; + const jettonToToken = JETTONS_ADDRESS[bridgeInfo.toToken]; + if (!jettonToToken) throw `jetton address of ${bridgeInfo.toToken} not found!`; + + msg = { + to: bridgeInfo.receiver, + denom: bridgeInfo.toToken, + timeout: Math.floor(new Date().getTime() / 1000) + IBC_TRANSFER_TIMEOUT, + recovery_addr: this.currentChainAddress + }; - // 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: { - 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: [ - { - denom: this.path.tokenIn, - amount: this.path.tokenInAmount - } - ] - }) + if (!isCw20Token(bridgeInfo.fromToken)) { + msg = { + bridge_to_ton: { + to: bridgeInfo.receiver, + denom: bridgeInfo.toToken, + timeout: Math.floor(new Date().getTime() / 1000) + IBC_TRANSFER_TIMEOUT, + recovery_addr: this.currentChainAddress + } }; } + } + + if (!msg || !contractAddr) { + throw generateError("Error on generate executeMsg on Oraichain: Only support ibc, ibc wasm bridge, ton bridge"); + } + + // 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: { + contract: contractAddr, + amount: this.path.tokenInAmount, + msg: toBinary(msg) + } + }) + ), + funds: [] + }) + }; + } + + // native token + return { + typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract", + value: MsgExecuteContract.fromPartial({ + sender: this.currentChainAddress, + contract: contractAddr, + msg: toUtf8(JSON.stringify(msg)), + funds: [ + { + denom: this.path.tokenIn, + amount: this.path.tokenInAmount + } + ] + }) + }; + } + /** + * Function to generate execute msg on Oraichain + */ + + genExecuteMsg(): EncodeObject { + let [swapOps, bridgeInfo] = this.getSwapAndBridgeInfo(); + console.log({ swapOps, bridgeInfo }); + + // we have 2 cases: + // - case 1: bridge only + // - case 2; swap and action - throw generateError("Error on generate executeMsg on Oraichain: Only support ibc or ibc wasm bridge"); + if (swapOps.length == 0) { + return this.genBridgeMsg(bridgeInfo); } let tokenOutOfSwap = swapOps[swapOps.length - 1].denom_out; + console.log({ tokenOutOfSwap }); let min_asset = isCw20Token(tokenOutOfSwap) ? { cw20: { diff --git a/packages/universal-swap/src/msg/msgs.ts b/packages/universal-swap/src/msg/msgs.ts index ccfb4400..7eeaab0f 100644 --- a/packages/universal-swap/src/msg/msgs.ts +++ b/packages/universal-swap/src/msg/msgs.ts @@ -1,5 +1,7 @@ import { generateError, + isEvmChain, + isTonChain, ORAI_BRIDGE_EVM_DENOM_PREFIX, ORAI_BRIDGE_EVM_TRON_DENOM_PREFIX, ORAI_BRIDGE_EVM_ETH_DENOM_PREFIX @@ -35,12 +37,13 @@ const buildMemoSwap = ( ): MiddlewareResponse => { let currentChain = path.chainId; let currentAddress = addresses[currentChain]; + switch (currentChain) { case "Oraichain": { let prefix = getDestPrefixForBridgeToEvmOnOrai(path.tokenOutChainId); const ORAIBRIDGE_SUBNET = "oraibridge-subnet-2"; let oBridgeAddress = addresses[ORAIBRIDGE_SUBNET]; - if (!oBridgeAddress) { + if (!oBridgeAddress && !isEvmChain(path.tokenOutChainId)) { throw generateError(`Missing oBridge address for ${ORAIBRIDGE_SUBNET}`); } @@ -48,9 +51,9 @@ const buildMemoSwap = ( oraichainMsg.setMinimumReceiveForSwap(slippage); // we have 2 cases: // - Previous chain use IBC bridge to Oraichain - // - Previous chain use IBC Wasm bridge to Oraichain (EVM, noble) + // - Previous chain use IBC Wasm bridge to Oraichain (EVM, noble, ton) let msgInfo = - previousChain && (previousChain == "noble-1" || previousChain.startsWith("0x")) + previousChain && (previousChain == "noble-1" || isEvmChain(previousChain) || isTonChain(previousChain)) ? oraichainMsg.genMemoForIbcWasm() : oraichainMsg.genMemoAsMiddleware(); return msgInfo; diff --git a/packages/universal-swap/src/proto/universal_swap_memo.ts b/packages/universal-swap/src/proto/universal_swap_memo.ts index 7a580f97..2069e295 100644 --- a/packages/universal-swap/src/proto/universal_swap_memo.ts +++ b/packages/universal-swap/src/proto/universal_swap_memo.ts @@ -11,9 +11,7 @@ import _m0 from "protobufjs/minimal"; export const protobufPackage = ""; export interface Memo { - userSwap: - | Memo_UserSwap - | undefined; + userSwap: Memo_UserSwap | undefined; /** string because the minimum receive may be very high due to decimal points */ minimumReceive: string; timeoutTimestamp: number; @@ -91,9 +89,7 @@ export interface Memo_IbcWasmTransfer { * / How long the packet lives in seconds. If not specified, use * / default_timeout */ - timeout?: - | number - | undefined; + timeout?: number | undefined; /** / metadata of the transfer to suit the new fungible token transfer */ memo?: string | undefined; } @@ -188,7 +184,7 @@ export const Memo = { minimumReceive: isSet(object.minimumReceive) ? globalThis.String(object.minimumReceive) : "", timeoutTimestamp: isSet(object.timeoutTimestamp) ? globalThis.Number(object.timeoutTimestamp) : 0, postSwapAction: isSet(object.postSwapAction) ? Memo_PostAction.fromJSON(object.postSwapAction) : undefined, - recoveryAddr: isSet(object.recoveryAddr) ? globalThis.String(object.recoveryAddr) : "", + recoveryAddr: isSet(object.recoveryAddr) ? globalThis.String(object.recoveryAddr) : "" }; }, @@ -217,17 +213,19 @@ export const Memo = { }, fromPartial, I>>(object: I): Memo { const message = createBaseMemo(); - message.userSwap = (object.userSwap !== undefined && object.userSwap !== null) - ? Memo_UserSwap.fromPartial(object.userSwap) - : undefined; + message.userSwap = + object.userSwap !== undefined && object.userSwap !== null + ? Memo_UserSwap.fromPartial(object.userSwap) + : undefined; message.minimumReceive = object.minimumReceive ?? ""; message.timeoutTimestamp = object.timeoutTimestamp ?? 0; - message.postSwapAction = (object.postSwapAction !== undefined && object.postSwapAction !== null) - ? Memo_PostAction.fromPartial(object.postSwapAction) - : undefined; + message.postSwapAction = + object.postSwapAction !== undefined && object.postSwapAction !== null + ? Memo_PostAction.fromPartial(object.postSwapAction) + : undefined; message.recoveryAddr = object.recoveryAddr ?? ""; return message; - }, + } }; function createBaseMemo_SwapExactAssetIn(): Memo_SwapExactAssetIn { @@ -269,7 +267,7 @@ export const Memo_SwapExactAssetIn = { return { operations: globalThis.Array.isArray(object?.operations) ? object.operations.map((e: any) => Memo_SwapOperation.fromJSON(e)) - : [], + : [] }; }, @@ -288,7 +286,7 @@ export const Memo_SwapExactAssetIn = { const message = createBaseMemo_SwapExactAssetIn(); message.operations = object.operations?.map((e) => Memo_SwapOperation.fromPartial(e)) || []; return message; - }, + } }; function createBaseMemo_SmartSwapExactAssetIn(): Memo_SmartSwapExactAssetIn { @@ -328,7 +326,7 @@ export const Memo_SmartSwapExactAssetIn = { fromJSON(object: any): Memo_SmartSwapExactAssetIn { return { - routes: globalThis.Array.isArray(object?.routes) ? object.routes.map((e: any) => Memo_Route.fromJSON(e)) : [], + routes: globalThis.Array.isArray(object?.routes) ? object.routes.map((e: any) => Memo_Route.fromJSON(e)) : [] }; }, @@ -347,7 +345,7 @@ export const Memo_SmartSwapExactAssetIn = { const message = createBaseMemo_SmartSwapExactAssetIn(); message.routes = object.routes?.map((e) => Memo_Route.fromPartial(e)) || []; return message; - }, + } }; function createBaseMemo_Route(): Memo_Route { @@ -400,7 +398,7 @@ export const Memo_Route = { offerAmount: isSet(object.offerAmount) ? globalThis.String(object.offerAmount) : "", operations: globalThis.Array.isArray(object?.operations) ? object.operations.map((e: any) => Memo_SwapOperation.fromJSON(e)) - : [], + : [] }; }, @@ -423,7 +421,7 @@ export const Memo_Route = { message.offerAmount = object.offerAmount ?? ""; message.operations = object.operations?.map((e) => Memo_SwapOperation.fromPartial(e)) || []; return message; - }, + } }; function createBaseMemo_SwapOperation(): Memo_SwapOperation { @@ -485,7 +483,7 @@ export const Memo_SwapOperation = { return { poolId: isSet(object.poolId) ? globalThis.String(object.poolId) : "", denomIn: isSet(object.denomIn) ? globalThis.String(object.denomIn) : "", - denomOut: isSet(object.denomOut) ? globalThis.String(object.denomOut) : "", + denomOut: isSet(object.denomOut) ? globalThis.String(object.denomOut) : "" }; }, @@ -512,7 +510,7 @@ export const Memo_SwapOperation = { message.denomIn = object.denomIn ?? ""; message.denomOut = object.denomOut ?? ""; return message; - }, + } }; function createBaseMemo_UserSwap(): Memo_UserSwap { @@ -578,7 +576,7 @@ export const Memo_UserSwap = { : undefined, smartSwapExactAssetIn: isSet(object.smartSwapExactAssetIn) ? Memo_SmartSwapExactAssetIn.fromJSON(object.smartSwapExactAssetIn) - : undefined, + : undefined }; }, @@ -602,15 +600,16 @@ export const Memo_UserSwap = { fromPartial, I>>(object: I): Memo_UserSwap { const message = createBaseMemo_UserSwap(); message.swapVenueName = object.swapVenueName ?? ""; - message.swapExactAssetIn = (object.swapExactAssetIn !== undefined && object.swapExactAssetIn !== null) - ? Memo_SwapExactAssetIn.fromPartial(object.swapExactAssetIn) - : undefined; + message.swapExactAssetIn = + object.swapExactAssetIn !== undefined && object.swapExactAssetIn !== null + ? Memo_SwapExactAssetIn.fromPartial(object.swapExactAssetIn) + : undefined; message.smartSwapExactAssetIn = - (object.smartSwapExactAssetIn !== undefined && object.smartSwapExactAssetIn !== null) + object.smartSwapExactAssetIn !== undefined && object.smartSwapExactAssetIn !== null ? Memo_SmartSwapExactAssetIn.fromPartial(object.smartSwapExactAssetIn) : undefined; return message; - }, + } }; function createBaseMemo_PostAction(): Memo_PostAction { @@ -685,7 +684,7 @@ export const Memo_PostAction = { ? Memo_IbcWasmTransfer.fromJSON(object.ibcWasmTransferMsg) : undefined, contractCall: isSet(object.contractCall) ? Memo_ContractCall.fromJSON(object.contractCall) : undefined, - transferMsg: isSet(object.transferMsg) ? Memo_Transfer.fromJSON(object.transferMsg) : undefined, + transferMsg: isSet(object.transferMsg) ? Memo_Transfer.fromJSON(object.transferMsg) : undefined }; }, @@ -711,20 +710,24 @@ export const Memo_PostAction = { }, fromPartial, I>>(object: I): Memo_PostAction { const message = createBaseMemo_PostAction(); - message.ibcTransferMsg = (object.ibcTransferMsg !== undefined && object.ibcTransferMsg !== null) - ? Memo_IbcTransfer.fromPartial(object.ibcTransferMsg) - : undefined; - message.ibcWasmTransferMsg = (object.ibcWasmTransferMsg !== undefined && object.ibcWasmTransferMsg !== null) - ? Memo_IbcWasmTransfer.fromPartial(object.ibcWasmTransferMsg) - : undefined; - message.contractCall = (object.contractCall !== undefined && object.contractCall !== null) - ? Memo_ContractCall.fromPartial(object.contractCall) - : undefined; - message.transferMsg = (object.transferMsg !== undefined && object.transferMsg !== null) - ? Memo_Transfer.fromPartial(object.transferMsg) - : undefined; + message.ibcTransferMsg = + object.ibcTransferMsg !== undefined && object.ibcTransferMsg !== null + ? Memo_IbcTransfer.fromPartial(object.ibcTransferMsg) + : undefined; + message.ibcWasmTransferMsg = + object.ibcWasmTransferMsg !== undefined && object.ibcWasmTransferMsg !== null + ? Memo_IbcWasmTransfer.fromPartial(object.ibcWasmTransferMsg) + : undefined; + message.contractCall = + object.contractCall !== undefined && object.contractCall !== null + ? Memo_ContractCall.fromPartial(object.contractCall) + : undefined; + message.transferMsg = + object.transferMsg !== undefined && object.transferMsg !== null + ? Memo_Transfer.fromPartial(object.transferMsg) + : undefined; return message; - }, + } }; function createBaseMemo_IbcTransfer(): Memo_IbcTransfer { @@ -808,7 +811,7 @@ export const Memo_IbcTransfer = { sourcePort: isSet(object.sourcePort) ? globalThis.String(object.sourcePort) : "", receiver: isSet(object.receiver) ? globalThis.String(object.receiver) : "", memo: isSet(object.memo) ? globalThis.String(object.memo) : "", - recoverAddress: isSet(object.recoverAddress) ? globalThis.String(object.recoverAddress) : "", + recoverAddress: isSet(object.recoverAddress) ? globalThis.String(object.recoverAddress) : "" }; }, @@ -843,7 +846,7 @@ export const Memo_IbcTransfer = { message.memo = object.memo ?? ""; message.recoverAddress = object.recoverAddress ?? ""; return message; - }, + } }; function createBaseMemo_IbcWasmTransfer(): Memo_IbcWasmTransfer { @@ -927,7 +930,7 @@ export const Memo_IbcWasmTransfer = { remoteAddress: isSet(object.remoteAddress) ? globalThis.String(object.remoteAddress) : "", remoteDenom: isSet(object.remoteDenom) ? globalThis.String(object.remoteDenom) : "", timeout: isSet(object.timeout) ? globalThis.Number(object.timeout) : undefined, - memo: isSet(object.memo) ? globalThis.String(object.memo) : undefined, + memo: isSet(object.memo) ? globalThis.String(object.memo) : undefined }; }, @@ -962,7 +965,7 @@ export const Memo_IbcWasmTransfer = { message.timeout = object.timeout ?? undefined; message.memo = object.memo ?? undefined; return message; - }, + } }; function createBaseMemo_ContractCall(): Memo_ContractCall { @@ -1013,7 +1016,7 @@ export const Memo_ContractCall = { fromJSON(object: any): Memo_ContractCall { return { contractAddress: isSet(object.contractAddress) ? globalThis.String(object.contractAddress) : "", - msg: isSet(object.msg) ? globalThis.String(object.msg) : "", + msg: isSet(object.msg) ? globalThis.String(object.msg) : "" }; }, @@ -1036,7 +1039,7 @@ export const Memo_ContractCall = { message.contractAddress = object.contractAddress ?? ""; message.msg = object.msg ?? ""; return message; - }, + } }; function createBaseMemo_Transfer(): Memo_Transfer { @@ -1093,19 +1096,24 @@ export const Memo_Transfer = { const message = createBaseMemo_Transfer(); message.toAddress = object.toAddress ?? ""; return message; - }, + } }; type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; -export type DeepPartial = T extends Builtin ? T - : T extends globalThis.Array ? globalThis.Array> - : T extends ReadonlyArray ? ReadonlyArray> - : T extends {} ? { [K in keyof T]?: DeepPartial } +export type DeepPartial = T extends Builtin + ? T + : T extends globalThis.Array + ? globalThis.Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} + ? { [K in keyof T]?: DeepPartial } : Partial; type KeysOfUnion = T extends T ? keyof T : never; -export type Exact = P extends Builtin ? P +export type Exact = P extends Builtin + ? P : P & { [K in keyof P]: Exact } & { [K in Exclude>]: never }; function longToNumber(long: Long): number { diff --git a/packages/universal-swap/src/types.ts b/packages/universal-swap/src/types.ts index f9ef97e9..e77d2f61 100644 --- a/packages/universal-swap/src/types.ts +++ b/packages/universal-swap/src/types.ts @@ -2,14 +2,16 @@ 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"; +import TonWallet from "@oraichain/tonbridge-sdk/build/wallet"; export type UniversalSwapType = | "other-networks-to-oraichain" | "oraichain-to-oraichain" | "oraichain-to-evm" | "oraichain-to-cosmos" + | "oraichain-to-ton" | "cosmos-to-others" - | "smart-router"; + | "ton-to-others"; export enum ActionType { Bridge = "Bridge", @@ -38,6 +40,7 @@ export interface Sender { cosmos: string; evm?: string; tron?: string; + ton?: string; } export interface RelayerFeeData { @@ -74,6 +77,7 @@ export interface UniversalSwapData { export interface UniversalSwapConfig { readonly cosmosWallet?: CosmosWallet; readonly evmWallet?: EvmWallet; + readonly tonWallet?: TonWallet; readonly swapOptions?: SwapOptions; } 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 a1ce2042..54d424e6 100644 --- a/packages/universal-swap/src/universal-demos/alpha-ibc-new.ts +++ b/packages/universal-swap/src/universal-demos/alpha-ibc-new.ts @@ -4,73 +4,32 @@ import { UniversalSwapHandler } from "../handler"; import { cosmosTokens, flattenTokens, generateError, getTokenOnOraichain, toAmount } from "@oraichain/oraidex-common"; const router = { - swapAmount: "79297000000000000", - returnAmount: "310968", + swapAmount: "16000000", + returnAmount: "1000000", routes: [ { - swapAmount: "79297000000000000", - returnAmount: "310968", + swapAmount: "16000000", + returnAmount: "1000000", paths: [ { - chainId: "injective-1", - tokenIn: "inj", - tokenInAmount: "79297000000000000", - tokenOut: "ibc/64BA6E31FE887D66C6F8F31C7B1A80C7CA179239677B4088BB55F5EA07DBE273", - tokenOutAmount: "79297000000000000", - tokenOutChainId: "osmosis-1", + chainId: "Oraichain", + tokenIn: "orai15un8msx3n5zf9ahlxmfeqd2kwa5wm0nrpxer304m9nd5q6qq0g6sku5pdd", + tokenInAmount: "16000000", + tokenOut: "EQB-MPwrd1G6WKNkLz_VnV6WqBDd142KMQv-g1O-8QUA3728", + tokenOutAmount: "1000000", + tokenOutChainId: "ton", actions: [ { type: "Bridge", protocol: "Bridge", - tokenIn: "inj", - tokenInAmount: "79297000000000000", - tokenOut: "ibc/64BA6E31FE887D66C6F8F31C7B1A80C7CA179239677B4088BB55F5EA07DBE273", - tokenOutAmount: "79297000000000000", - tokenOutChainId: "osmosis-1", + tokenIn: "orai15un8msx3n5zf9ahlxmfeqd2kwa5wm0nrpxer304m9nd5q6qq0g6sku5pdd", + tokenInAmount: "16000000", + tokenOut: "EQB-MPwrd1G6WKNkLz_VnV6WqBDd142KMQv-g1O-8QUA3728", + tokenOutAmount: "1000000", + tokenOutChainId: "ton", bridgeInfo: { port: "transfer", - channel: "channel-8" - } - } - ] - }, - { - chainId: "osmosis-1", - tokenIn: "ibc/64BA6E31FE887D66C6F8F31C7B1A80C7CA179239677B4088BB55F5EA07DBE273", - tokenInAmount: "79297000000000000", - tokenOut: "utia", - tokenOutAmount: "310968", - tokenOutChainId: "celestia", - actions: [ - { - type: "Swap", - protocol: "Osmosis", - tokenIn: "ibc/64BA6E31FE887D66C6F8F31C7B1A80C7CA179239677B4088BB55F5EA07DBE273", - tokenInAmount: "79297000000000000", - tokenOut: "ibc/D79E7D83AB399BFFF93433E54FAA480C191248FC556924A2A8351AE2638B3877", - tokenOutAmount: "310968", - swapInfo: [ - { - poolId: "725", - tokenOut: "uosmo" - }, - { - poolId: "1249", - tokenOut: "ibc/D79E7D83AB399BFFF93433E54FAA480C191248FC556924A2A8351AE2638B3877" - } - ] - }, - { - type: "Bridge", - protocol: "Bridge", - tokenIn: "ibc/D79E7D83AB399BFFF93433E54FAA480C191248FC556924A2A8351AE2638B3877", - tokenInAmount: "310968", - tokenOut: "utia", - tokenOutAmount: "310968", - tokenOutChainId: "celestia", - bridgeInfo: { - port: "transfer", - channel: "channel-6994" + channel: "channel-1" } } ] @@ -79,17 +38,15 @@ const router = { } ] }; - const alphaSwapToOraichain = async () => { const wallet = new CosmosWalletImpl(process.env.MNEMONIC); - const sender = await wallet.getKeplrAddr("injective-1"); + const chainId = "Oraichain"; + const sender = await wallet.getKeplrAddr(chainId); - const fromAmount = 0.079297; + const fromAmount = 16; console.log("sender: ", sender); - const originalFromToken = flattenTokens.find( - (t) => t.coinGeckoId === "injective-protocol" && t.chainId === "injective-1" - ); - const originalToToken = flattenTokens.find((t) => t.coinGeckoId === "celestia" && t.chainId === "celestia"); + const originalFromToken = flattenTokens.find((t) => t.coinGeckoId === "usd-coin" && t.chainId === chainId); + const originalToToken = flattenTokens.find((t) => t.coinGeckoId === "usd-coin" && t.chainId === "ton"); if (!originalToToken) throw generateError("Could not find original to token"); if (!originalFromToken) throw generateError("Could not find original from token"); @@ -98,15 +55,19 @@ const alphaSwapToOraichain = async () => { { originalFromToken, originalToToken, - sender: { cosmos: sender, evm: "0x8c7E0A841269a01c0Ab389Ce8Fb3Cf150A94E797" }, + sender: { + cosmos: sender, + // evm: "0x8c7E0A841269a01c0Ab389Ce8Fb3Cf150A94E797", + ton: "UQDOAHXyCPFOXAXm9c1P_NeNEeSWy6IaRHqJRnBUp0jMZ6i3" + }, fromAmount, userSlippage: 1, // relayerFee: { // relayerAmount: "100000", // relayerDecimals: 6 // }, - recipientAddress: "celestia13lpgsy2dk9ftwac2uagw7fc2fw35cdp00xucfz", - simulatePrice: "3921560", + // recipientAddress: "celestia13lpgsy2dk9ftwac2uagw7fc2fw35cdp00xucfz", + simulatePrice: "1000000", simulateAmount: toAmount(fromAmount, originalToToken.decimals).toString(), alphaSmartRoutes: router }, 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 e9cd0676..f9cb1987 100644 --- a/packages/universal-swap/src/universal-demos/alpha-smart-router.ts +++ b/packages/universal-swap/src/universal-demos/alpha-smart-router.ts @@ -5,32 +5,33 @@ import { cosmosTokens, flattenTokens, generateError, getTokenOnOraichain, toAmou const router = { swapAmount: "1000000", - returnAmount: "339961000000000000", + returnAmount: "98932", routes: [ { swapAmount: "1000000", - returnAmount: "339961000000000000", + returnAmount: "98932", paths: [ { chainId: "Oraichain", tokenIn: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh", tokenInAmount: "1000000", - tokenOut: "0x55d398326f99059fF775485246999027B3197955", - tokenOutAmount: "339961000000000000", - tokenOutChainId: "0x38", + tokenOut: "orai", + tokenOutAmount: "98932", + tokenOutChainId: "Oraichain", actions: [ { - type: "Bridge", - protocol: "Bridge", + type: "Swap", + protocol: "OraidexV3", tokenIn: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh", tokenInAmount: "1000000", - tokenOut: "0x55d398326f99059fF775485246999027B3197955", - tokenOutAmount: "339961000000000000", - tokenOutChainId: "0x38", - bridgeInfo: { - port: "wasm.orai195269awwnt5m6c843q6w7hp8rt0k7syfu9de4h0wz384slshuzps8y7ccm", - channel: "channel-29" - } + tokenOut: "orai", + tokenOutAmount: "98932", + swapInfo: [ + { + poolId: "orai-orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh-3000000000-100", + tokenOut: "orai" + } + ] } ] } @@ -45,7 +46,7 @@ const alphaSwapToOraichain = async () => { const fromAmount = 1; console.log("sender: ", sender); const originalFromToken = flattenTokens.find((t) => t.coinGeckoId === "tether" && t.chainId === "Oraichain"); - const originalToToken = flattenTokens.find((t) => t.coinGeckoId === "tether" && t.chainId === "0x38"); + 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"); @@ -54,7 +55,7 @@ const alphaSwapToOraichain = async () => { { originalFromToken, originalToToken, - sender: { cosmos: sender, evm: "0x8c7E0A841269a01c0Ab389Ce8Fb3Cf150A94E797" }, + sender: { cosmos: sender }, fromAmount, userSlippage: 1, relayerFee: { @@ -64,7 +65,7 @@ const alphaSwapToOraichain = async () => { // recipientAddress: "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2", // recipientAddress: "osmo12zyu8w93h0q2lcnt50g3fn0w3yqnhy4fh4twhr", // recipientAddress: "inj133lq4pqjdxspcz4n388glv70z59ffeuh3ktnaj", - simulatePrice: "1000000", + simulatePrice: "98932", simulateAmount: router.returnAmount, alphaSmartRoutes: router }, diff --git a/packages/universal-swap/src/universal-demos/decode-memo.ts b/packages/universal-swap/src/universal-demos/decode-memo.ts index f4343aae..dfbf56f5 100644 --- a/packages/universal-swap/src/universal-demos/decode-memo.ts +++ b/packages/universal-swap/src/universal-demos/decode-memo.ts @@ -2,12 +2,17 @@ import { Memo } from "../proto/universal_swap_memo"; (() => { const memo = - "Co0BCgdvcmFpZGV4GoEBCn8KBzEwMDAwMDASdAo/b3JhaS1vcmFpMTJoemp4Zmg3N3dsNTcyZ2R6Y3QyZnh2MmFyeGN3aDZneWtjN3FoLTMwMDAwMDAwMDAtMTAwEitvcmFpMTJoemp4Zmg3N3dsNTcyZ2R6Y3QyZnh2MmFyeGN3aDZneWtjN3FoGgRvcmFpEgY4ODAwMDAYgJDwj+nYgP0XIi8iLQorb3JhaTFodnI5ZDcycjV1bTlsdnQwcnBrZDRyNzV2cnNxdHc2eXVqaHFzMiorb3JhaTFodnI5ZDcycjV1bTlsdnQwcnBrZDRyNzV2cnNxdHc2eXVqaHFzMg=="; - const uint8Array = Buffer.from(memo, "base64"); - const encodedMemo = Memo.decode(uint8Array); + "CoEBCgdvcmFpZGV4EnYKdAo/b3JhaS1vcmFpMTJoemp4Zmg3N3dsNTcyZ2R6Y3QyZnh2MmFyeGN3aDZneWtjN3FoLTMwMDAwMDAwMDAtMTAwEitvcmFpMTJoemp4Zmg3N3dsNTcyZ2R6Y3QyZnh2MmFyeGN3aDZneWtjN3FoGgRvcmFpEgk1MDUzODAwMjAYgJzA9dmxioMYIi8iLQorb3JhaTF1czR0c3gzcTVwNWdnand0MG5mdHNrdndueWNyMHFwdmdyYXNjbCorb3JhaTF1czR0c3gzcTVwNWdnand0MG5mdHNrdndueWNyMHFwdmdyYXNjbA=="; + // "CtMDCgdvcmFpZGV4GscDCsQDCgYyMzczNjcS9AEKf2liYy9BMkUyRUVDOTA1N0E0QTFDMkMwQTZBNEM3OEIwMjM5MTE4REY1RjI3ODgzMEY1MEI0QTZCREQ3QTY2NTA2Qjc4LW9yYWkxbHVzMGYwcmh4OHMwM2dkbGx4Mm42dmhrbWYwNTM2ZHY1N3dmZ2UtMzAwMDAwMDAwMC0xMDASRGliYy9BMkUyRUVDOTA1N0E0QTFDMkMwQTZBNEM3OEIwMjM5MTE4REY1RjI3ODgzMEY1MEI0QTZCREQ3QTY2NTA2Qjc4GitvcmFpMWx1czBmMHJoeDhzMDNnZGxseDJuNnZoa21mMDUzNmR2NTd3ZmdlEsIBCmZvcmFpMTJoemp4Zmg3N3dsNTcyZ2R6Y3QyZnh2MmFyeGN3aDZneWtjN3FoLW9yYWkxbHVzMGYwcmh4OHMwM2dkbGx4Mm42dmhrbWYwNTM2ZHY1N3dmZ2UtMzAwMDAwMDAwMC0xMDASK29yYWkxbHVzMGYwcmh4OHMwM2dkbGx4Mm42dmhrbWYwNTM2ZHY1N3dmZ2UaK29yYWkxMmh6anhmaDc3d2w1NzJnZHpjdDJmeHYyYXJ4Y3doNmd5a2M3cWgSBjQ3MzcyNRiAnLyrmIr9ghgiqQESpgEKCmNoYW5uZWwtMjkSLG9yYWliMWh2cjlkNzJyNXVtOWx2dDBycGtkNHI3NXZyc3F0dzZ5dG5udnBmGi9vcmFpYjB4NTVkMzk4MzI2Zjk5MDU5ZkY3NzU0ODUyNDY5OTkwMjdCMzE5Nzk1NSCAuI/AlYr9ghgqL29yYWliMHg4YzdFMEE4NDEyNjlhMDFjMEFiMzg5Q2U4RmIzQ2YxNTBBOTRFNzk3KitvcmFpMWh2cjlkNzJyNXVtOWx2dDBycGtkNHI3NXZyc3F0dzZ5dWpocXMy"; + const memo1 = + "CpQICgdvcmFpZGV4GogICtkFCgg1NDMyNjI4MhL0AQp/aWJjL0EyRTJFRUM5MDU3QTRBMUMyQzBBNkE0Qzc4QjAyMzkxMThERjVGMjc4ODMwRjUwQjRBNkJERDdBNjY1MDZCNzgtb3JhaTFsdXMwZjByaHg4czAzZ2RsbHgybjZ2aGttZjA1MzZkdjU3d2ZnZS0zMDAwMDAwMDAwLTEwMBJEaWJjL0EyRTJFRUM5MDU3QTRBMUMyQzBBNkE0Qzc4QjAyMzkxMThERjVGMjc4ODMwRjUwQjRBNkJERDdBNjY1MDZCNzgaK29yYWkxbHVzMGYwcmh4OHMwM2dkbGx4Mm42dmhrbWYwNTM2ZHY1N3dmZ2US6gEKem9yYWkxNXVuOG1zeDNuNXpmOWFobHhtZmVxZDJrd2E1d20wbnJweGVyMzA0bTluZDVxNnFxMGc2c2t1NXBkZC1vcmFpMWx1czBmMHJoeDhzMDNnZGxseDJuNnZoa21mMDUzNmR2NTd3ZmdlLTMwMDAwMDAwMDAtMTAwEitvcmFpMWx1czBmMHJoeDhzMDNnZGxseDJuNnZoa21mMDUzNmR2NTd3ZmdlGj9vcmFpMTV1bjhtc3gzbjV6ZjlhaGx4bWZlcWQya3dhNXdtMG5ycHhlcjMwNG05bmQ1cTZxcTBnNnNrdTVwZGQS6AEKeG9yYWkxMmh6anhmaDc3d2w1NzJnZHpjdDJmeHYyYXJ4Y3doNmd5a2M3cWgtb3JhaTE1dW44bXN4M241emY5YWhseG1mZXFkMmt3YTV3bTBucnB4ZXIzMDRtOW5kNXE2cXEwZzZza3U1cGRkLTUwMDAwMDAwMC0xMBI/b3JhaTE1dW44bXN4M241emY5YWhseG1mZXFkMmt3YTV3bTBucnB4ZXIzMDRtOW5kNXE2cXEwZzZza3U1cGRkGitvcmFpMTJoemp4Zmg3N3dsNTcyZ2R6Y3QyZnh2MmFyeGN3aDZneWtjN3FoCqkCCgg1NDMyNjI4MxKmAQpYaWJjL0EyRTJFRUM5MDU3QTRBMUMyQzBBNkE0Qzc4QjAyMzkxMThERjVGMjc4ODMwRjUwQjRBNkJERDdBNjY1MDZCNzgtb3JhaS0zMDAwMDAwMDAwLTEwMBJEaWJjL0EyRTJFRUM5MDU3QTRBMUMyQzBBNkE0Qzc4QjAyMzkxMThERjVGMjc4ODMwRjUwQjRBNkJERDdBNjY1MDZCNzgaBG9yYWkSdAo/b3JhaS1vcmFpMTJoemp4Zmg3N3dsNTcyZ2R6Y3QyZnh2MmFyeGN3aDZneWtjN3FoLTMwMDAwMDAwMDAtMTAwEgRvcmFpGitvcmFpMTJoemp4Zmg3N3dsNTcyZ2R6Y3QyZnh2MmFyeGN3aDZneWtjN3FoEhLbtNu427nbttuz27TbsNu427gYgICLirX+5YIYIqkBEqYBCgpjaGFubmVsLTI5EixvcmFpYjFmdmpsZXluMDVwczJscmwyajM2cHQ1djIzenRnZnAyNjUwNWx2Nxovb3JhaWIweDU1ZDM5ODMyNmY5OTA1OWZGNzc1NDg1MjQ2OTk5MDI3QjMxOTc5NTUggOyV37T+5YIYKi9vcmFpYjB4NjA5RWE2YzQ0MDkyZTYwODM4ZDhEOTZDQzVCYjljOURiMzFiMzE1YSorb3JhaTFmdmpsZXluMDVwczJscmwyajM2cHQ1djIzenRnZnAyNnJ3c25hYQ=="; + + const encodedMemo = Memo.decode(Buffer.from(memo, "base64")); + const encodedMemo1 = Memo.decode(Buffer.from(memo1, "base64")); console.dir( { - encodedMemo + encodedMemo, + encodedMemo1 }, { depth: null } ); diff --git a/packages/universal-swap/src/universal-demos/from-cosmos-to-cosmos.ts b/packages/universal-swap/src/universal-demos/from-cosmos-to-cosmos.ts new file mode 100644 index 00000000..a5504500 --- /dev/null +++ b/packages/universal-swap/src/universal-demos/from-cosmos-to-cosmos.ts @@ -0,0 +1,54 @@ +import "dotenv/config"; +import { CosmosWalletImpl } from "./offline-wallet"; +import { UniversalSwapHandler } from "../handler"; +import { + ORAI_ETH_CONTRACT, + cosmosTokens, + flattenTokens, + generateError, + toAmount, + USDT_CONTRACT +} from "@oraichain/oraidex-common"; + +const cosmosToCosmos = async (chainId: "osmosis-1") => { + const wallet = new CosmosWalletImpl(process.env.MNEMONIC); + + const sender = await wallet.getKeplrAddr(chainId); + const fromAmount = 1; + let originalFromToken = cosmosTokens.find((t) => t.chainId === chainId && t.coinGeckoId === "osmosis"); + + let originalToToken = flattenTokens.find((t) => t.chainId === "Oraichain" && t.coinGeckoId === "osmosis"); + if (!originalFromToken) throw generateError("Could not find original from token"); + if (!originalToToken) throw generateError("Could not find original to token"); + const universalHandler = new UniversalSwapHandler( + { + originalFromToken, + originalToToken, + sender: { cosmos: sender }, + relayerFee: { + relayerAmount: "1000000", + relayerDecimals: 6 + }, + fromAmount, + simulateAmount: toAmount(fromAmount, originalToToken.decimals).toString() + }, + { + cosmosWallet: wallet, + swapOptions: { + isIbcWasm: false, + isAlphaSmartRouter: false + } + } + ); + + try { + const result = await universalHandler.processUniversalSwap(); + console.log("result: ", result); + } catch (error) { + console.log("error: ", error); + } +}; + +(() => { + return cosmosToCosmos("osmosis-1"); +})(); diff --git a/packages/universal-swap/src/universal-demos/from-oraichain-to-evm.ts b/packages/universal-swap/src/universal-demos/from-oraichain-to-evm.ts index 4d495861..9a1a0737 100644 --- a/packages/universal-swap/src/universal-demos/from-oraichain-to-evm.ts +++ b/packages/universal-swap/src/universal-demos/from-oraichain-to-evm.ts @@ -10,11 +10,9 @@ const oraichainToEvm = async () => { const sender = await wallet.getKeplrAddr(chainId); const fromAmount = 10000; - let originalToToken = flattenTokens.find((t) => t.chainId === chainId && t.coinGeckoId === "pepe"); + let originalToToken = flattenTokens.find((t) => t.chainId === chainId && t.coinGeckoId === "tether"); - let originalFromToken = flattenTokens.find((t) => t.chainId === "0x38" && t.coinGeckoId === "pepe"); - - console.log({ originalFromToken, originalToToken }); + let originalFromToken = flattenTokens.find((t) => t.chainId === "0x38" && t.coinGeckoId === "tether"); if (!originalFromToken) throw generateError("Could not find original from token"); if (!originalToToken) throw generateError("Could not find original to token"); diff --git a/packages/universal-swap/src/universal-demos/from-oraichain-to-oraichain.ts b/packages/universal-swap/src/universal-demos/from-oraichain-to-oraichain.ts index 5f8722ac..43362e7d 100644 --- a/packages/universal-swap/src/universal-demos/from-oraichain-to-oraichain.ts +++ b/packages/universal-swap/src/universal-demos/from-oraichain-to-oraichain.ts @@ -35,7 +35,6 @@ const oraichainToOraichain = async (chainId: "Oraichain") => { amount: toAmount(fromAmount, originalToToken.decimals).toString() }); - console.log("expected amount: ", smartRoutes.returnAmount); const universalHandler = new UniversalSwapHandler( { originalFromToken, @@ -45,12 +44,18 @@ const oraichainToOraichain = async (chainId: "Oraichain") => { relayerAmount: "0", relayerDecimals: 6 }, - simulatePrice: "1", + simulatePrice: "1000000", fromAmount, simulateAmount: toAmount(fromAmount, originalToToken.decimals).toString(), userSlippage: 0.01 }, - { cosmosWallet: wallet, swapOptions: {} } + { + cosmosWallet: wallet, + swapOptions: { + isIbcWasm: false, + isAlphaIbcWasm: true + } + } ); try { diff --git a/packages/universal-swap/src/universal-demos/from-oraichain-to-ton.ts b/packages/universal-swap/src/universal-demos/from-oraichain-to-ton.ts new file mode 100644 index 00000000..c3c1e756 --- /dev/null +++ b/packages/universal-swap/src/universal-demos/from-oraichain-to-ton.ts @@ -0,0 +1,110 @@ +import "dotenv/config"; +import { CosmosWalletImpl } from "./offline-wallet"; +import { UniversalSwapHandler } from "../handler"; +import { + cosmosTokens, + flattenTokens, + generateError, + getTokenOnOraichain, + toAmount, + jUSDC_TON_CONTRACT +} from "@oraichain/oraidex-common"; + +const router = { + swapAmount: "1300000", + returnAmount: "81199", + routes: [ + { + swapAmount: "1300000", + returnAmount: "81199", + paths: [ + { + chainId: "Oraichain", + tokenIn: "orai", + tokenInAmount: "1300000", + tokenOut: "EQB-MPwrd1G6WKNkLz_VnV6WqBDd142KMQv-g1O-8QUA3728", + tokenOutAmount: "81199", + tokenOutChainId: "ton", + actions: [ + { + type: "Swap", + protocol: "OraidexV3", + tokenIn: "orai", + tokenInAmount: "1300000", + tokenOut: "orai15un8msx3n5zf9ahlxmfeqd2kwa5wm0nrpxer304m9nd5q6qq0g6sku5pdd", + tokenOutAmount: "15081199", + swapInfo: [ + { + poolId: "orai-orai15un8msx3n5zf9ahlxmfeqd2kwa5wm0nrpxer304m9nd5q6qq0g6sku5pdd-3000000000-100", + tokenOut: "orai15un8msx3n5zf9ahlxmfeqd2kwa5wm0nrpxer304m9nd5q6qq0g6sku5pdd" + } + ] + }, + { + type: "Bridge", + protocol: "Bridge", + tokenIn: "orai15un8msx3n5zf9ahlxmfeqd2kwa5wm0nrpxer304m9nd5q6qq0g6sku5pdd", + tokenInAmount: "15081199", + tokenOut: "EQB-MPwrd1G6WKNkLz_VnV6WqBDd142KMQv-g1O-8QUA3728", + tokenOutAmount: "81199", + tokenOutChainId: "ton", + bridgeInfo: { + port: "transfer", + channel: "channel-1" + } + } + ] + } + ] + } + ] +}; + +const swapOraichainToTon = async () => { + const wallet = new CosmosWalletImpl(process.env.MNEMONIC); + const sender = await wallet.getKeplrAddr("Oraichain"); + const fromAmount = 1.3; + console.log("sender: ", sender); + const originalFromToken = flattenTokens.find((t) => t.coinGeckoId === "oraichain-token" && t.chainId === "Oraichain"); + const originalToToken = flattenTokens.find((t) => t.coinGeckoId === "usd-coin" && t.chainId === "ton"); + + 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", + // ton: "UQD3zsGYoDGgpambcp7SquM3wJqo6Yc-ksEtCGCDS8JwGQpp" + ton: "UQDOAHXyCPFOXAXm9c1P_NeNEeSWy6IaRHqJRnBUp0jMZ6i3" + }, + fromAmount, + userSlippage: 1, + relayerFee: { + relayerAmount: "100000", + relayerDecimals: 6 + }, + simulatePrice: "1100000", + 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); + } +}; + +(async () => { + await swapOraichainToTon(); +})(); diff --git a/packages/universal-swap/src/universal-demos/from-ton-to-oraichain.ts b/packages/universal-swap/src/universal-demos/from-ton-to-oraichain.ts new file mode 100644 index 00000000..7d54c4bf --- /dev/null +++ b/packages/universal-swap/src/universal-demos/from-ton-to-oraichain.ts @@ -0,0 +1,100 @@ +import "dotenv/config"; +import { CosmosWalletImpl } from "./offline-wallet"; +import { UniversalSwapHandler } from "../handler"; +import { cosmosTokens, flattenTokens, generateError, getTokenOnOraichain, toAmount } from "@oraichain/oraidex-common"; +import TonWallet from "@oraichain/tonbridge-sdk/build/wallet"; + +const router = { + swapAmount: "3000000000", + returnAmount: "1000000000", + routes: [ + { + swapAmount: "3000000000", + returnAmount: "1000000000", + paths: [ + { + chainId: "ton", + tokenIn: "EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c", + tokenInAmount: "3000000000", + tokenOut: "factory/orai1wuvhex9xqs3r539mvc6mtm7n20fcj3qr2m0y9khx6n5vtlngfzes3k0rq9/ton", + tokenOutAmount: "1000000000", + tokenOutChainId: "Oraichain", + actions: [ + { + type: "Bridge", + protocol: "Bridge", + tokenIn: "EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c", + tokenInAmount: "3000000000", + tokenOut: "factory/orai1wuvhex9xqs3r539mvc6mtm7n20fcj3qr2m0y9khx6n5vtlngfzes3k0rq9/ton", + tokenOutAmount: "1000000000", + tokenOutChainId: "Oraichain", + bridgeInfo: { + port: "transfer", + channel: "channel-1" + } + } + ] + } + ] + } + ] +}; + +const swapTonToOraichain = async () => { + const wallet = new CosmosWalletImpl(process.env.MNEMONIC); + + const tonWallet = await TonWallet.create("mainnet", { + mnemonicData: { + mnemonic: process.env.TON_MNEMONIC.split(" "), + tonWalletVersion: "V4" + } + }); + + const sender = await wallet.getKeplrAddr("Oraichain"); + const tonAddr = await tonWallet.sender.address.toString(); + const fromAmount = 3; + console.log("sender: ", sender); + console.log("tonAddr: ", tonAddr); + const originalFromToken = flattenTokens.find((t) => t.coinGeckoId === "the-open-network" && t.chainId === "ton"); + const originalToToken = flattenTokens.find((t) => t.coinGeckoId === "the-open-network" && t.chainId === "Oraichain"); + 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", + // ton: "UQD3zsGYoDGgpambcp7SquM3wJqo6Yc-ksEtCGCDS8JwGQpp" + ton: "EQDOAHXyCPFOXAXm9c1P_NeNEeSWy6IaRHqJRnBUp0jMZ_Vy" + }, + fromAmount, + userSlippage: 1, + relayerFee: { + relayerAmount: "100000", + relayerDecimals: 6 + }, + simulatePrice: "1000000000", + simulateAmount: toAmount(fromAmount, originalToToken.decimals).toString(), + alphaSmartRoutes: router + }, + { + cosmosWallet: wallet, + tonWallet, + swapOptions: { isIbcWasm: false, isAlphaIbcWasm: true } + } + ); + + try { + const result = await universalHandler.processUniversalSwap(); + console.log("result: ", result); + } catch (error) { + console.trace("error: ", error); + } +}; + +(() => { + swapTonToOraichain(); +})(); diff --git a/packages/universal-swap/tests/msg/oraichain-msg.spec.ts b/packages/universal-swap/tests/msg/oraichain-msg.spec.ts index 5f33908d..a983d14a 100644 --- a/packages/universal-swap/tests/msg/oraichain-msg.spec.ts +++ b/packages/universal-swap/tests/msg/oraichain-msg.spec.ts @@ -1,9 +1,18 @@ 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 { + calculateTimeoutTimestamp, + generateError, + IBC_TRANSFER_TIMEOUT, + IBC_WASM_CONTRACT, + TON_CONTRACT +} 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"; +import { toBinary } from "@cosmjs/cosmwasm-stargate"; +import { MsgExecuteContract } from "cosmjs-types/cosmwasm/wasm/v1/tx"; +import { toUtf8 } from "@cosmjs/encoding"; describe("test build oraichain msg", () => { const validPath = { @@ -48,7 +57,7 @@ describe("test build oraichain msg", () => { it.each<[BridgeMsgInfo, Action, string]>([ [ - undefined, + undefined as any, { transfer: { to_address: receiver @@ -207,7 +216,7 @@ describe("test build oraichain msg", () => { sourceChannel: bridgeInfo.sourceChannel, sourcePort: bridgeInfo.sourcePort, receiver: bridgeInfo.receiver, - memo: bridgeInfo.memo, + memo: bridgeInfo.memo as string, recoverAddress: currentAddress } }, @@ -304,7 +313,7 @@ describe("test build oraichain msg", () => { sourceChannel: bridgeInfo.sourceChannel, sourcePort: bridgeInfo.sourcePort, receiver: bridgeInfo.receiver, - memo: bridgeInfo.memo, + memo: bridgeInfo.memo as string, recoverAddress: currentAddress } }, @@ -314,6 +323,136 @@ describe("test build oraichain msg", () => { }); }); + it("Valid path with ton bridge only", () => { + const nextMemo = "{}"; + let validPathTonBridgeOnly = { + chainId: "Oraichain", + tokenIn: "factory/orai1wuvhex9xqs3r539mvc6mtm7n20fcj3qr2m0y9khx6n5vtlngfzes3k0rq9/ton", + tokenInAmount: "999000", + tokenOut: TON_CONTRACT, + tokenOutAmount: "999000", + tokenOutChainId: "ton", + actions: [ + { + type: "Bridge", + protocol: "Bridge", + tokenIn: "factory/orai1wuvhex9xqs3r539mvc6mtm7n20fcj3qr2m0y9khx6n5vtlngfzes3k0rq9/ton", + tokenInAmount: "999000", + tokenOut: TON_CONTRACT, + tokenOutAmount: "999000", + tokenOutChainId: "ton", + bridgeInfo: { + port: "transfer", // fake port + channel: "ton-channel" // fake channel + } + } + ] + }; + let oraichainMsg = new OraichainMsg(validPathTonBridgeOnly, "1", receiver, currentAddress, nextMemo); + + let [swapOps, bridgeInfo] = oraichainMsg.getSwapAndBridgeInfo(); + expect(bridgeInfo).toEqual({ + amount: "999000", + sourceChannel: "ton-channel", + sourcePort: "transfer", + memo: nextMemo, + receiver: receiver, + timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + fromToken: "factory/orai1wuvhex9xqs3r539mvc6mtm7n20fcj3qr2m0y9khx6n5vtlngfzes3k0rq9/ton", + toToken: TON_CONTRACT, + fromChain: "Oraichain", + toChain: "ton" + }); + expect(swapOps).toEqual([]); + + let memoAsMiddleware = oraichainMsg.genMemoAsMiddleware(); + let memo: Memo = { + userSwap: undefined, // no swap action + minimumReceive: "1", + timeoutTimestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + postSwapAction: { + contractCall: { + contractAddress: oraichainMsg.TON_BRIDGE_ADAPTER, + msg: toBinary({ + bridge_to_ton: { + to: bridgeInfo.receiver, + denom: bridgeInfo.toToken, + timeout: Math.floor(new Date().getTime() / 1000) + IBC_TRANSFER_TIMEOUT, + recovery_addr: currentAddress + } + }) + } + }, + recoveryAddr: currentAddress + }; + expect(memoAsMiddleware).toEqual({ + receiver: IBC_WASM_CONTRACT, + memo: JSON.stringify({ + wasm: { + contract: IBC_WASM_CONTRACT, + msg: { + ibc_hooks_receive: { + func: "universal_swap", + orai_receiver: currentAddress, + args: Buffer.from(Memo.encode(memo).finish()).toString("base64") + } + } + } + }) + }); + + let executeMsg = oraichainMsg.genExecuteMsg(); + expect(executeMsg).toEqual({ + typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract", + value: MsgExecuteContract.fromPartial({ + sender: currentAddress, + contract: oraichainMsg.TON_BRIDGE_ADAPTER, + msg: toUtf8( + JSON.stringify({ + bridge_to_ton: { + to: bridgeInfo.receiver, + denom: bridgeInfo.toToken, + timeout: Math.floor(new Date().getTime() / 1000) + IBC_TRANSFER_TIMEOUT, + recovery_addr: currentAddress + } + }) + ), + funds: [ + { + denom: validPathTonBridgeOnly.tokenIn, + amount: validPathTonBridgeOnly.tokenInAmount + } + ] + }) + }); + + let memoForIbcWasm = oraichainMsg.genMemoForIbcWasm(); + expect(memoForIbcWasm).toEqual({ + receiver: currentAddress, + memo: Buffer.from( + Memo.encode({ + userSwap: undefined, + minimumReceive: "1", + timeoutTimestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + postSwapAction: { + contractCall: { + contractAddress: oraichainMsg.TON_BRIDGE_ADAPTER, + msg: toBinary({ + bridge_to_ton: { + to: bridgeInfo.receiver, + denom: bridgeInfo.toToken, + timeout: Math.floor(new Date().getTime() / 1000) + IBC_TRANSFER_TIMEOUT, + recovery_addr: currentAddress + } + }) + } + }, + recoveryAddr: currentAddress + }).finish() + ).toString("base64") + }); + }); + it("Valid path with swap only", () => { const nextMemo = "{}"; receiver = currentAddress; @@ -595,11 +734,11 @@ describe("test build oraichain msg", () => { try { oraichainMsg.genMemoAsMiddleware(); } catch (err) { - expect(err).toEqual(generateError(`Error on generate memo as middleware: Only support ibc bridge`)); + expect(err).toEqual(generateError(`Error on generate memo as middleware: Only support ibc bridge & ton bridge`)); } }); - it("Valid path in genMemoForIbcWasm with ibc wasm bridge only", () => { + it("Valid path with ibc wasm bridge only - cw20 token", () => { const nextMemo = "{}"; const validPath = { chainId: "Oraichain", @@ -628,7 +767,8 @@ describe("test build oraichain msg", () => { let receiver = "0x0000000000000000000000000000000000000000"; const currentAddress = "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2"; const oraiBridgeAddr = "oraib1hvr9d72r5um9lvt0rpkd4r75vrsqtw6ytnnvpf"; - let oraichainMsg = new OraichainMsg(validPath, "1", receiver, currentAddress, nextMemo, "oraib", oraiBridgeAddr); + let destPrefix = "oraib"; + let oraichainMsg = new OraichainMsg(validPath, "1", receiver, currentAddress, nextMemo, destPrefix, oraiBridgeAddr); let [swapOps, bridgeInfo] = oraichainMsg.getSwapAndBridgeInfo(); expect(bridgeInfo).toEqual({ @@ -646,7 +786,30 @@ describe("test build oraichain msg", () => { expect(swapOps).toEqual([]); let executeMsg = oraichainMsg.genExecuteMsg(); - expect(executeMsg.typeUrl).toEqual("/cosmwasm.wasm.v1.MsgExecuteContract"); + + expect(executeMsg).toEqual({ + typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract", + value: MsgExecuteContract.fromPartial({ + sender: currentAddress, + contract: validPath.tokenIn, + msg: toUtf8( + JSON.stringify({ + send: { + contract: "orai195269awwnt5m6c843q6w7hp8rt0k7syfu9de4h0wz384slshuzps8y7ccm", + amount: validPath.tokenInAmount, + msg: toBinary({ + local_channel_id: bridgeInfo.sourceChannel, + remote_address: oraiBridgeAddr, + remote_denom: destPrefix + bridgeInfo.toToken, + timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), + memo: destPrefix + receiver + }) + } + }) + ), + funds: [] + }) + }); let memoForIbcWasm = oraichainMsg.genMemoForIbcWasm(); expect(memoForIbcWasm).toEqual({ diff --git a/yarn.lock b/yarn.lock index 569e58d0..4b13ec9d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2988,6 +2988,13 @@ resolved "https://registry.yarnpkg.com/@juggle/resize-observer/-/resize-observer-3.4.0.tgz#08d6c5e20cf7e4cc02fd181c4b0c225cd31dbb60" integrity sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA== +"@keplr-wallet/types@0.12.141": + version "0.12.141" + resolved "https://registry.yarnpkg.com/@keplr-wallet/types/-/types-0.12.141.tgz#341cc45882a86a39701d726edc8febdc42cafcb3" + integrity sha512-och1uUeqL6bUgWGLBSlV87rwXUDZMDUOJaARue65mRzfD6ohp9WBVFZfFgpyxU3M6S6UnzQQ6HuKUnWh4WuDjw== + dependencies: + long "^4.0.0" + "@keplr-wallet/types@^0.11.38": version "0.11.64" resolved "https://registry.yarnpkg.com/@keplr-wallet/types/-/types-0.11.64.tgz#5a308c8c019b4e18f894e0f35f0904b60134d605" @@ -3732,22 +3739,21 @@ dependencies: "@oraichain/common-contracts-sdk" "1.0.31" -"@oraichain/common-contracts-sdk@1.0.31": +"@oraichain/common-contracts-sdk@1.0.31", "@oraichain/common-contracts-sdk@^1.0.31": version "1.0.31" resolved "https://registry.yarnpkg.com/@oraichain/common-contracts-sdk/-/common-contracts-sdk-1.0.31.tgz#595f93b168438d69d64896909b37855c9afc92fb" integrity sha512-s8H20RXy5gCnu3DnM7L5ClQyj2mdQpbSBpZrXCpIAX9qY0LKsDdZG3sYaDQ8+VN333jz9Pp/qGWdFSYD+6PBsg== -"@oraichain/common@^1.0.3": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@oraichain/common/-/common-1.0.3.tgz#c6746473deed39622a1368a8f8f733a13f628a7e" - integrity sha512-4Rwwl8DF3bMNh5XSlP44LjUIKCOglDc/R1bUCeVFifZlrTGaz6Zo7Fdw6xwych7b4doxcuQBBh2gr7AZE6jQyQ== +"@oraichain/common@^1.1.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@oraichain/common/-/common-1.2.4.tgz#1a28dc288f3ada9d802541454585839e5c275deb" + integrity sha512-/2TIpqDrJKgh/gb50s+sz5lhsffsj6QOVrJh1QrlIsRlup3yQnahm06vwsiVOX75rbdAMO5c7tJiXjKkHbfkww== dependencies: - "@cosmjs/cosmwasm-stargate" "0.31.3" - "@cosmjs/tendermint-rpc" "^0.31.3" + "@keplr-wallet/types" "0.12.141" axios "0.21.4" axios-extensions "3.1.6" bech32 "^1.1.4" - cosmjs-types "^0.8.0" + cosmjs-types "^0.9.0" ethers "^5.5.1" lodash "^4.17.21" @@ -3812,11 +3818,49 @@ "@cosmjs/crypto" "0.31.3" "@cosmjs/proto-signing" "0.31.3" +"@oraichain/ton-bridge-contracts@^0.15.8", "@oraichain/ton-bridge-contracts@^0.15.9": + version "0.15.9" + resolved "https://registry.yarnpkg.com/@oraichain/ton-bridge-contracts/-/ton-bridge-contracts-0.15.9.tgz#279f78ac434d5df69ccf2e51c01e57484789e91e" + integrity sha512-M4ukzlCkPRS+3S+05g/P+82fXjBCe05kBqpJiaZkUDAN7UDbRQJH480AZWl8HotPiM1Ae2TKNl8GPivNmzmwRA== + dependencies: + "@cosmjs/tendermint-rpc" "^0.32.4" + "@orbs-network/ton-access" "^2.3.3" + "@types/varstruct" "^6.1.3" + cosmjs-types "^0.9.0" + dotenv "^16.4.5" + varstruct "^6.1.3" + +"@oraichain/tonbridge-contracts-sdk@^1.3.6": + version "1.3.6" + resolved "https://registry.yarnpkg.com/@oraichain/tonbridge-contracts-sdk/-/tonbridge-contracts-sdk-1.3.6.tgz#1ee1309e6bb8e186eaa5d706c7d32276ef976402" + integrity sha512-k1DgzfYg3nQNPn2dLiXbkDHXd1WREQA9Dl0ezQ5kPtnlbNK9XjmxSco2gmaJwql0tE3cbBExUflfG0MaRF0dWw== + +"@oraichain/tonbridge-sdk@^1.4.2": + version "1.4.2" + resolved "https://registry.yarnpkg.com/@oraichain/tonbridge-sdk/-/tonbridge-sdk-1.4.2.tgz#05722298dfd0bdbc4feaa45eb652d98361619638" + integrity sha512-pMj/y76S87b/GT4cNjp0oZ+J86tb/vPE0Kcm9vfi9SIMMYeWsk/H5AVlzoYevPrZflkzIA6l/vx+oHkMxpoVyQ== + dependencies: + "@oraichain/common-contracts-sdk" "^1.0.31" + "@oraichain/oraidex-common" "^1.1.6" + "@oraichain/ton-bridge-contracts" "^0.15.8" + "@oraichain/tonbridge-contracts-sdk" "^1.3.6" + "@orbs-network/ton-access" "^2.3.3" + "@ton/core" "^0.56.3" + "@ton/ton" "^14.0.0" + "@tonconnect/ui-react" "^2.0.9" + "@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" integrity sha512-j+8gN3dE3rqaiEUVVblt0dfJrE6RIsSkfqF08ISxRvHkH9Pe9exIOgxpCyd2Qn3liHj27hwth6R0ELw7y3QcLg== +"@orbs-network/ton-access@^2.3.3": + version "2.3.3" + resolved "https://registry.yarnpkg.com/@orbs-network/ton-access/-/ton-access-2.3.3.tgz#20292f83b52a75f451f185821180a7d9b829bcd3" + integrity sha512-b1miCPts7wBG9JKYgzXIRZQm/LMy5Uk1mNK8NzlcXHL3HRHJkkFbuYJGuj3IkWCiIicW3Ipp4sYnn3Fwo4oB0g== + dependencies: + isomorphic-fetch "^3.0.0" + "@pkgjs/parseargs@^0.11.0": version "0.11.0" resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" @@ -5316,6 +5360,88 @@ dependencies: "@swc/counter" "^0.1.3" +"@ton/core@0.56.3", "@ton/core@^0.56.3": + version "0.56.3" + resolved "https://registry.yarnpkg.com/@ton/core/-/core-0.56.3.tgz#1162764573abb76032eba70f8497e5cb2ea532ee" + integrity sha512-HVkalfqw8zqLLPehtq0CNhu5KjVzc7IrbDwDHPjGoOSXmnqSobiWj8a5F+YuWnZnEbQKtrnMGNOOjVw4LG37rg== + dependencies: + symbol.inspect "1.0.1" + +"@ton/crypto-primitives@2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@ton/crypto-primitives/-/crypto-primitives-2.1.0.tgz#8c9277c250b59aae3c819e0d6bd61e44d998e9ca" + integrity sha512-PQesoyPgqyI6vzYtCXw4/ZzevePc4VGcJtFwf08v10OevVJHVfW238KBdpj1kEDQkxWLeuNHEpTECNFKnP6tow== + dependencies: + jssha "3.2.0" + +"@ton/crypto@3.3.0": + version "3.3.0" + resolved "https://registry.yarnpkg.com/@ton/crypto/-/crypto-3.3.0.tgz#019103df6540fbc1d8102979b4587bc85ff9779e" + integrity sha512-/A6CYGgA/H36OZ9BbTaGerKtzWp50rg67ZCH2oIjV1NcrBaCK9Z343M+CxedvM7Haf3f/Ee9EhxyeTp0GKMUpA== + dependencies: + "@ton/crypto-primitives" "2.1.0" + jssha "3.2.0" + tweetnacl "1.0.3" + +"@ton/ton@14.0.0", "@ton/ton@^14.0.0": + version "14.0.0" + resolved "https://registry.yarnpkg.com/@ton/ton/-/ton-14.0.0.tgz#a66fbbfb159200892557442039a0341cda71fc2d" + integrity sha512-xb2CY6U0AlHUKc7DV7xK/K4Gqn6YoR253yUrM2E7L5WegVFsDF0CQRUIfpYACCuj1oUywQc5J2oMolYNu/uGkA== + dependencies: + axios "^1.6.7" + dataloader "^2.0.0" + symbol.inspect "1.0.1" + teslabot "^1.3.0" + zod "^3.21.4" + +"@tonconnect/isomorphic-eventsource@^0.0.2": + version "0.0.2" + resolved "https://registry.yarnpkg.com/@tonconnect/isomorphic-eventsource/-/isomorphic-eventsource-0.0.2.tgz#e58c44cf9953e090f2c35da9a638946ddb614be5" + integrity sha512-B4UoIjPi0QkvIzZH5fV3BQLWrqSYABdrzZQSI9sJA9aA+iC0ohOzFwVVGXanlxeDAy1bcvPbb29f6sVUk0UnnQ== + dependencies: + eventsource "^2.0.2" + +"@tonconnect/isomorphic-fetch@^0.0.3": + version "0.0.3" + resolved "https://registry.yarnpkg.com/@tonconnect/isomorphic-fetch/-/isomorphic-fetch-0.0.3.tgz#31978e04ddc4428eff532c23d20229ed5ddb6417" + integrity sha512-jIg5nTrDwnite4fXao3dD83eCpTvInTjZon/rZZrIftIegh4XxyVb5G2mpMqXrVGk1e8SVXm3Kj5OtfMplQs0w== + dependencies: + node-fetch "^2.6.9" + +"@tonconnect/protocol@^2.2.6": + version "2.2.6" + resolved "https://registry.yarnpkg.com/@tonconnect/protocol/-/protocol-2.2.6.tgz#24b3fbcde6003e65fb5840a190072db5378699db" + integrity sha512-kyoDz5EqgsycYP+A+JbVsAUYHNT059BCrK+m0pqxykMODwpziuSAXfwAZmHcg8v7NB9VKYbdFY55xKeXOuEd0w== + dependencies: + tweetnacl "^1.0.3" + tweetnacl-util "^0.15.1" + +"@tonconnect/sdk@3.0.5": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@tonconnect/sdk/-/sdk-3.0.5.tgz#08a202bdc8ea897c37221fd69925c35cd2106323" + integrity sha512-ow0qnN4s3iQ/r2uXobZ7YzdQBtan/36CgCT9IP35G07g38UxsUXwzw8ANmJTDj/JPiQcIKuYBMfIwIX9zLM0wg== + dependencies: + "@tonconnect/isomorphic-eventsource" "^0.0.2" + "@tonconnect/isomorphic-fetch" "^0.0.3" + "@tonconnect/protocol" "^2.2.6" + +"@tonconnect/ui-react@^2.0.9": + version "2.0.9" + resolved "https://registry.yarnpkg.com/@tonconnect/ui-react/-/ui-react-2.0.9.tgz#c226b81110e05b7e259bbf965ad5da42318a5dd3" + integrity sha512-wN7tEZpQiRYSUcdNAxFsDkk5TYo8krIu00ZLE1R5kXyr+XpO120jOmTEweBSXvIzTgEVkD/PxDZbBQQxRTXsUw== + dependencies: + "@tonconnect/ui" "2.0.9" + +"@tonconnect/ui@2.0.9": + version "2.0.9" + resolved "https://registry.yarnpkg.com/@tonconnect/ui/-/ui-2.0.9.tgz#589285c9b8f4b0d94c10b3feadfae266bf086503" + integrity sha512-ZxofTBf81NqrxyD0ybI8AuFHN11uKVg/00xTDFhP5FoPB8rYC7En9qE2VJ6IvwvtTpmh8jspi2ancOHUMBoCQA== + dependencies: + "@tonconnect/sdk" "3.0.5" + classnames "^2.3.2" + deepmerge "^4.2.2" + ua-parser-js "^1.0.35" + "@tootallnate/once@2": version "2.0.0" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" @@ -5836,6 +5962,13 @@ resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.10.tgz#04ffa7f406ab628f7f7e97ca23e290cd8ab15efc" integrity sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA== +"@types/varstruct@^6.1.3": + version "6.1.3" + resolved "https://registry.yarnpkg.com/@types/varstruct/-/varstruct-6.1.3.tgz#dad39ad875eef0a9b9d63ae4e503cc3d5c456955" + integrity sha512-LgivtaEn0RHmVk+CHnIS95CtkV0xMAT6u1hPlr6qXNmYnDUU7ZhXM5fM7/byytaqXk7fNX8B/ibN1xpxe+X+hg== + dependencies: + "@types/node" "*" + "@types/ws@*", "@types/ws@^8.5.4": version "8.5.10" resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.10.tgz#4acfb517970853fa6574a3a6886791d04a396787" @@ -6794,7 +6927,7 @@ axios@^0.27.2: follow-redirects "^1.14.9" form-data "^4.0.0" -axios@^1.7.4: +axios@^1.6.7, axios@^1.7.4: version "1.7.7" resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.7.tgz#2f554296f9892a72ac8d8e4c5b79c14a91d0a47f" integrity sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q== @@ -7552,6 +7685,11 @@ cjs-module-lexer@^1.0.0: resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz#6c370ab19f8a3394e318fe682686ec0ac684d107" integrity sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ== +classnames@^2.3.2: + version "2.5.1" + resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.5.1.tgz#ba774c614be0f016da105c858e7159eae8e7687b" + integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow== + clean-css@^5.2.2: version "5.3.3" resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-5.3.3.tgz#b330653cd3bd6b75009cc25c714cae7b93351ccd" @@ -8361,6 +8499,11 @@ data-view-byte-offset@^1.0.0: es-errors "^1.3.0" is-data-view "^1.0.1" +dataloader@^2.0.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-2.2.2.tgz#216dc509b5abe39d43a9b9d97e6e5e473dfbe3e0" + integrity sha512-8YnDaaf7N3k/q5HnTJVuzSyLETjoZjVmHc4AeKAzOvKHEFQKcn64OKBfzHYtE9zGjctNM7V9I0MfnUVLpi7M5g== + dateformat@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" @@ -9539,6 +9682,11 @@ events@^3.2.0: resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== +eventsource@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-2.0.2.tgz#76dfcc02930fb2ff339520b6d290da573a9e8508" + integrity sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA== + evp_bytestokey@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" @@ -11083,6 +11231,11 @@ inquirer@^8.2.4: through "^2.3.6" wrap-ansi "^6.0.1" +int53@^0.2.4: + version "0.2.4" + resolved "https://registry.yarnpkg.com/int53/-/int53-0.2.4.tgz#5ed8d7aad6c5c6567cae69aa7ffc4a109ee80f86" + integrity sha512-a5jlKftS7HUOhkUyYD7j2sJ/ZnvWiNlZS1ldR+g1ifQ+/UuZXIE+YTc/lK1qGj/GwAU5F8Z0e1eVq2t1J5Ob2g== + internal-slot@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.7.tgz#c06dcca3ed874249881007b0a5523b172a190802" @@ -11452,6 +11605,14 @@ isobject@^3.0.1: resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== +isomorphic-fetch@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz#0267b005049046d2421207215d45d6a262b8b8b4" + integrity sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA== + dependencies: + node-fetch "^2.6.1" + whatwg-fetch "^3.4.1" + isomorphic-ws@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz#55fd4cd6c5e6491e76dc125938dd863f5cd4f2dc" @@ -12135,6 +12296,11 @@ jsonschema@^1.4.0: resolved "https://registry.yarnpkg.com/jsonschema/-/jsonschema-1.4.1.tgz#cc4c3f0077fb4542982973d8a083b6b34f482dab" integrity sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ== +jssha@3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/jssha/-/jssha-3.2.0.tgz#88ec50b866dd1411deaddbe6b3e3692e4c710f16" + integrity sha512-QuruyBENDWdN4tZwJbQq7/eAK85FqrI4oDbXjy5IBhYD+2pTJyBUWZe8ctWaCkrV0gy6AaelgOZZBMeswEa/6Q== + keccak256@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/keccak256/-/keccak256-1.0.6.tgz#dd32fb771558fed51ce4e45a035ae7515573da58" @@ -13297,7 +13463,7 @@ node-fetch@2.6.7: dependencies: whatwg-url "^5.0.0" -node-fetch@^2.0.0, node-fetch@^2.6.12, node-fetch@^2.6.7: +node-fetch@^2.0.0, node-fetch@^2.6.1, node-fetch@^2.6.12, node-fetch@^2.6.7, node-fetch@^2.6.9: version "2.7.0" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== @@ -16355,6 +16521,11 @@ symbol-observable@^4.0.0: resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-4.0.0.tgz#5b425f192279e87f2f9b937ac8540d1984b39205" integrity sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ== +symbol.inspect@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/symbol.inspect/-/symbol.inspect-1.0.1.tgz#e13125b8038c4996eb0dfa1567332ad4dcd0763f" + integrity sha512-YQSL4duoHmLhsTD1Pw8RW6TZ5MaTX5rXJnqacJottr2P2LZBF/Yvrc3ku4NUpMOm8aM0KOCqM+UAkMA5HWQCzQ== + synchronous-promise@^2.0.15: version "2.0.17" resolved "https://registry.yarnpkg.com/synchronous-promise/-/synchronous-promise-2.0.17.tgz#38901319632f946c982152586f2caf8ddc25c032" @@ -16490,6 +16661,11 @@ terser@^5.10.0, terser@^5.26.0: commander "^2.20.0" source-map-support "~0.5.20" +teslabot@^1.3.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/teslabot/-/teslabot-1.5.0.tgz#70f544516699ca5f696d8ae94f3d12cd495d5cd6" + integrity sha512-e2MmELhCgrgZEGo7PQu/6bmYG36IDH+YrBI1iGm6jovXkeDIGa3pZ2WSqRjzkuw2vt1EqfkZoV5GpXgqL8QJVg== + test-exclude@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" @@ -16858,7 +17034,7 @@ tweetnacl-util@^0.15.1: resolved "https://registry.yarnpkg.com/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz#b80fcdb5c97bcc508be18c44a4be50f022eea00b" integrity sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw== -tweetnacl@^1.0.3: +tweetnacl@1.0.3, tweetnacl@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== @@ -17021,6 +17197,11 @@ uWebSockets.js@uNetworking/uWebSockets.js#v20.32.0: version "20.32.0" resolved "https://codeload.github.com/uNetworking/uWebSockets.js/tar.gz/21c0e41a662c27bcc29b64f3e9d7f0bdaa6d7ee7" +ua-parser-js@^1.0.35: + version "1.0.39" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-1.0.39.tgz#bfc07f361549bf249bd8f4589a4cccec18fd2018" + integrity sha512-k24RCVWlEcjkdOxYmVJgeD/0a1TiSpqLg+ZalVGV9lsnr4yqu0w7tX/x2xX6G4zpkgQnRf89lxuZ1wsbjXM8lw== + ufo@^1.4.0: version "1.5.3" resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.5.3.tgz#3325bd3c977b6c6cd3160bf4ff52989adc9d3344" @@ -17300,6 +17481,14 @@ value-or-promise@1.0.11: resolved "https://registry.yarnpkg.com/value-or-promise/-/value-or-promise-1.0.11.tgz#3e90299af31dd014fe843fe309cefa7c1d94b140" integrity sha512-41BrgH+dIbCFXClcSapVs5M6GkENd3gQOJpEfPDNa71LsUGMXDL0jMWpI/Rh7WhX+Aalfz2TTS3Zt5pUsbnhLg== +varstruct@^6.1.3: + version "6.1.3" + resolved "https://registry.yarnpkg.com/varstruct/-/varstruct-6.1.3.tgz#45a8a073e41fb88adb10bc71158c945e20c57fbe" + integrity sha512-4l1Q7uxrVUBZXsMcb2cakrZL6gd4G+Ykn/m9cGnT4EY8iRBPkxOxKVDwOnL9AsIPKmREBx5BDqjfNMKKP6Zy2w== + dependencies: + int53 "^0.2.4" + safe-buffer "^5.1.1" + vary@^1, vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" @@ -17517,6 +17706,11 @@ webpack@5.89.0: watchpack "^2.4.0" webpack-sources "^3.2.3" +whatwg-fetch@^3.4.1: + version "3.6.20" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz#580ce6d791facec91d37c72890995a0b48d31c70" + integrity sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg== + whatwg-url@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" @@ -17863,6 +18057,11 @@ zen-observable@0.8.15: resolved "https://registry.yarnpkg.com/zen-observable/-/zen-observable-0.8.15.tgz#96415c512d8e3ffd920afd3889604e30b9eaac15" integrity sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ== +zod@^3.21.4: + version "3.23.8" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.23.8.tgz#e37b957b5d52079769fb8097099b592f0ef4067d" + integrity sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g== + zstd-codec@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/zstd-codec/-/zstd-codec-0.1.4.tgz#6abb311b63cfacbd06e72797ee6c6e1c7c65248c"