diff --git a/package.json b/package.json index de5372ae8a..ab747ee41f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rubic-sdk", - "version": "5.31.11", + "version": "5.32.0", "description": "Simplify dApp creation", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -73,7 +73,7 @@ "@1inch/limit-order-protocol-utils": "3.0.1", "@arbitrum/sdk": "^3.1.3", "@debridge-finance/debridge-external-call": "^1.0.7", - "@layerzerolabs/scan-client": "^0.0.0-beta.6", + "@layerzerolabs/scan-client": "0.0.8", "@noble/secp256k1": "^1.7.1", "@pancakeswap/sdk": "^5.1.0", "@pancakeswap/smart-router": "4.2.1", diff --git a/src/features/cross-chain/calculation-manager/constants/cross-chain-providers.ts b/src/features/cross-chain/calculation-manager/constants/cross-chain-providers.ts index 24ce2169ab..0204614316 100644 --- a/src/features/cross-chain/calculation-manager/constants/cross-chain-providers.ts +++ b/src/features/cross-chain/calculation-manager/constants/cross-chain-providers.ts @@ -16,12 +16,14 @@ import { MesonCrossChainProvider } from '../providers/meson-provider/meson-cross import { OrbiterBridgeProvider } from '../providers/orbiter-bridge/orbiter-bridge-provider'; import { OwlToBridgeProvider } from '../providers/owl-to-bridge/owl-to-bridge-provider'; import { RangoCrossChainProvider } from '../providers/rango-provider/rango-cross-chain-provider'; -// import { StargateCrossChainProvider } from '../providers/stargate-provider/stargate-cross-chain-provider'; +import { StargateV2CrossChainProvider } from '../providers/stargate-v2-provider/stargate-v2-cross-chain-provider'; +//import { StargateCrossChainProvider } from '../providers/stargate-provider/stargate-cross-chain-provider'; import { TaikoBridgeProvider } from '../providers/taiko-bridge/taiko-bridge-provider'; const proxyProviders = [ SymbiosisCrossChainProvider, - // StargateCrossChainProvider, + StargateV2CrossChainProvider, + //StargateCrossChainProvider, XyCrossChainProvider, CbridgeCrossChainProvider, // Possibly Compromised diff --git a/src/features/cross-chain/calculation-manager/models/cross-chain-trade-type.ts b/src/features/cross-chain/calculation-manager/models/cross-chain-trade-type.ts index 6a3ad75f3a..278d5fb742 100644 --- a/src/features/cross-chain/calculation-manager/models/cross-chain-trade-type.ts +++ b/src/features/cross-chain/calculation-manager/models/cross-chain-trade-type.ts @@ -8,6 +8,7 @@ export const CROSS_CHAIN_TRADE_TYPE = { CELER_BRIDGE: 'celer_bridge', CHANGENOW: 'changenow', STARGATE: 'stargate', + STARGATE_V2: 'stargate_v2', ARBITRUM: 'arbitrum', SQUIDROUTER: 'squidrouter', SCROLL_BRIDGE: 'scroll_bridge', diff --git a/src/features/cross-chain/calculation-manager/providers/stargate-v2-provider/constants/stargate-v2-blockchain-supported-pools.ts b/src/features/cross-chain/calculation-manager/providers/stargate-v2-provider/constants/stargate-v2-blockchain-supported-pools.ts new file mode 100644 index 0000000000..c810e93b6e --- /dev/null +++ b/src/features/cross-chain/calculation-manager/providers/stargate-v2-provider/constants/stargate-v2-blockchain-supported-pools.ts @@ -0,0 +1,72 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +import { stargateV2BridgeToken } from './stargate-v2-bridge-token'; +import { StargateV2SupportedBlockchains } from './stargate-v2-cross-chain-supported-blockchains'; +import { stargateV2PoolId } from './stargate-v2-pool-id'; + +export const stargateV2BlockchainSupportedPools: Record = + { + [BLOCKCHAIN_NAME.ETHEREUM]: [ + stargateV2PoolId[stargateV2BridgeToken.USDC], + stargateV2PoolId[stargateV2BridgeToken.USDT], + stargateV2PoolId[stargateV2BridgeToken.ETH], + // stargateV2PoolId[stargateV2BridgeToken.METIS], + stargateV2PoolId[stargateV2BridgeToken.METH] + ], + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: [stargateV2PoolId[stargateV2BridgeToken.USDT]], + [BLOCKCHAIN_NAME.AVALANCHE]: [ + stargateV2PoolId[stargateV2BridgeToken.USDC], + stargateV2PoolId[stargateV2BridgeToken.USDT] + ], + [BLOCKCHAIN_NAME.POLYGON]: [ + stargateV2PoolId[stargateV2BridgeToken.USDC], + stargateV2PoolId[stargateV2BridgeToken.USDT] + ], + [BLOCKCHAIN_NAME.ARBITRUM]: [ + stargateV2PoolId[stargateV2BridgeToken.USDC], + stargateV2PoolId[stargateV2BridgeToken.USDT], + stargateV2PoolId[stargateV2BridgeToken.ETH] + ], + [BLOCKCHAIN_NAME.OPTIMISM]: [ + stargateV2PoolId[stargateV2BridgeToken.USDC], + stargateV2PoolId[stargateV2BridgeToken.USDT], + stargateV2PoolId[stargateV2BridgeToken.ETH] + ], + [BLOCKCHAIN_NAME.METIS]: [ + stargateV2PoolId[stargateV2BridgeToken.mUSD], + stargateV2PoolId[stargateV2BridgeToken.WETH] + // stargateV2PoolId[stargateV2BridgeToken.METIS] + ], + [BLOCKCHAIN_NAME.LINEA]: [stargateV2PoolId[stargateV2BridgeToken.ETH]], + [BLOCKCHAIN_NAME.MANTLE]: [ + stargateV2PoolId[stargateV2BridgeToken.USDC], + stargateV2PoolId[stargateV2BridgeToken.USDT], + stargateV2PoolId[stargateV2BridgeToken.WETH], + stargateV2PoolId[stargateV2BridgeToken.METH] + ], + [BLOCKCHAIN_NAME.BASE]: [ + stargateV2PoolId[stargateV2BridgeToken.USDC], + stargateV2PoolId[stargateV2BridgeToken.ETH] + ], + [BLOCKCHAIN_NAME.KAVA]: [stargateV2PoolId[stargateV2BridgeToken.USDT]], + [BLOCKCHAIN_NAME.SCROLL]: [ + stargateV2PoolId[stargateV2BridgeToken.USDCe], + stargateV2PoolId[stargateV2BridgeToken.ETH] + ], + [BLOCKCHAIN_NAME.AURORA]: [stargateV2PoolId[stargateV2BridgeToken.USDC]] + // [BLOCKCHAIN_NAME.KLAYTN]: [ + // stargateV2PoolId[stargateV2BridgeToken.USDC], + // stargateV2PoolId[stargateV2BridgeToken.USDT], + // stargateV2PoolId[stargateV2BridgeToken.WETH] + // ], + // [BLOCKCHAIN_NAME.IOTA]: [ + // stargateV2PoolId[stargateV2BridgeToken.USDC], + // stargateV2PoolId[stargateV2BridgeToken.USDT], + // stargateV2PoolId[stargateV2BridgeToken.WETH] + // ], + // [BLOCKCHAIN_NAME.TAIKO]: [ + // stargateV2PoolId[stargateV2BridgeToken.USDC], + // stargateV2PoolId[stargateV2BridgeToken.USDT] + // ], + // [BLOCKCHAIN_NAME.SEI]: [stargateV2PoolId[stargateV2BridgeToken.WETH]] + }; diff --git a/src/features/cross-chain/calculation-manager/providers/stargate-v2-provider/constants/stargate-v2-bridge-token.ts b/src/features/cross-chain/calculation-manager/providers/stargate-v2-provider/constants/stargate-v2-bridge-token.ts new file mode 100644 index 0000000000..350f0d0eee --- /dev/null +++ b/src/features/cross-chain/calculation-manager/providers/stargate-v2-provider/constants/stargate-v2-bridge-token.ts @@ -0,0 +1,13 @@ +export const stargateV2BridgeToken = { + USDC: 'USDC', + USDT: 'USDT', + METIS: 'METIS', + mUSD: 'm.USDT', + ETH: 'ETH', + METH: 'mETH', + WETH: 'WETH', + USDCe: 'USDC.e' +} as const; + +export type StargateV2BridgeToken = + (typeof stargateV2BridgeToken)[keyof typeof stargateV2BridgeToken]; diff --git a/src/features/cross-chain/calculation-manager/providers/stargate-v2-provider/constants/stargate-v2-chain-id.ts b/src/features/cross-chain/calculation-manager/providers/stargate-v2-provider/constants/stargate-v2-chain-id.ts new file mode 100644 index 0000000000..b885d195a9 --- /dev/null +++ b/src/features/cross-chain/calculation-manager/providers/stargate-v2-provider/constants/stargate-v2-chain-id.ts @@ -0,0 +1,23 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +import { StargateV2SupportedBlockchains } from './stargate-v2-cross-chain-supported-blockchains'; + +export const stargateV2ChainIds: Record = { + [BLOCKCHAIN_NAME.ETHEREUM]: 30101, + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: 30102, + [BLOCKCHAIN_NAME.AVALANCHE]: 30106, + [BLOCKCHAIN_NAME.POLYGON]: 30109, + [BLOCKCHAIN_NAME.ARBITRUM]: 30110, + [BLOCKCHAIN_NAME.OPTIMISM]: 30111, + [BLOCKCHAIN_NAME.METIS]: 30151, + [BLOCKCHAIN_NAME.LINEA]: 30183, + [BLOCKCHAIN_NAME.MANTLE]: 30181, + [BLOCKCHAIN_NAME.BASE]: 30184, + [BLOCKCHAIN_NAME.KAVA]: 30177, + [BLOCKCHAIN_NAME.SCROLL]: 30214, + [BLOCKCHAIN_NAME.AURORA]: 30211 + // [BLOCKCHAIN_NAME.KLAYTN]: 30150, + // [BLOCKCHAIN_NAME.IOTA]: 30284, + // [BLOCKCHAIN_NAME.TAIKO]: 30290, + // [BLOCKCHAIN_NAME.SEI]: 30280 +}; diff --git a/src/features/cross-chain/calculation-manager/providers/stargate-v2-provider/constants/stargate-v2-contract-address.ts b/src/features/cross-chain/calculation-manager/providers/stargate-v2-provider/constants/stargate-v2-contract-address.ts new file mode 100644 index 0000000000..f3801a3768 --- /dev/null +++ b/src/features/cross-chain/calculation-manager/providers/stargate-v2-provider/constants/stargate-v2-contract-address.ts @@ -0,0 +1,85 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +import { StargateV2BridgeToken, stargateV2BridgeToken } from './stargate-v2-bridge-token'; +import { StargateV2SupportedBlockchains } from './stargate-v2-cross-chain-supported-blockchains'; + +type StargateV2ContractAddress = Record< + StargateV2SupportedBlockchains, + Partial> +>; + +export const stargateV2ContractAddress: StargateV2ContractAddress = { + [BLOCKCHAIN_NAME.ETHEREUM]: { + [stargateV2BridgeToken.ETH]: '0x77b2043768d28E9C9aB44E1aBfC95944bcE57931', + [stargateV2BridgeToken.USDC]: '0xc026395860Db2d07ee33e05fE50ed7bD583189C7', + [stargateV2BridgeToken.USDT]: '0x933597a323Eb81cAe705C5bC29985172fd5A3973', + [stargateV2BridgeToken.METIS]: '0xcDafB1b2dB43f366E48e6F614b8DCCBFeeFEEcD3', + [stargateV2BridgeToken.METH]: '0x268Ca24DAefF1FaC2ed883c598200CcbB79E931D' + }, + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: { + [stargateV2BridgeToken.USDT]: '0x138EB30f73BC423c6455C53df6D89CB01d9eBc63' + }, + [BLOCKCHAIN_NAME.AVALANCHE]: { + [stargateV2BridgeToken.USDC]: '0x5634c4a5FEd09819E3c46D86A965Dd9447d86e47', + [stargateV2BridgeToken.USDT]: '0x12dC9256Acc9895B076f6638D628382881e62CeE' + }, + [BLOCKCHAIN_NAME.POLYGON]: { + [stargateV2BridgeToken.USDC]: '0x9Aa02D4Fae7F58b8E8f34c66E756cC734DAc7fe4', + [stargateV2BridgeToken.USDT]: '0xd47b03ee6d86Cf251ee7860FB2ACf9f91B9fD4d7' + }, + [BLOCKCHAIN_NAME.ARBITRUM]: { + [stargateV2BridgeToken.ETH]: '0xA45B5130f36CDcA45667738e2a258AB09f4A5f7F', + [stargateV2BridgeToken.USDC]: '0xe8CDF27AcD73a434D661C84887215F7598e7d0d3', + [stargateV2BridgeToken.USDT]: '0xcE8CcA271Ebc0533920C83d39F417ED6A0abB7D0' + }, + [BLOCKCHAIN_NAME.OPTIMISM]: { + [stargateV2BridgeToken.ETH]: '0xe8CDF27AcD73a434D661C84887215F7598e7d0d3', + [stargateV2BridgeToken.USDC]: '0xcE8CcA271Ebc0533920C83d39F417ED6A0abB7D0', + [stargateV2BridgeToken.USDT]: '0x19cFCE47eD54a88614648DC3f19A5980097007dD' + }, + [BLOCKCHAIN_NAME.METIS]: { + [stargateV2BridgeToken.METIS]: '0xD9050e7043102a0391F81462a3916326F86331F0', + [stargateV2BridgeToken.WETH]: '0x36ed193dc7160D3858EC250e69D12B03Ca087D08', + [stargateV2BridgeToken.mUSD]: '0x4dCBFC0249e8d5032F89D6461218a9D2eFff5125' + }, + [BLOCKCHAIN_NAME.LINEA]: { + [stargateV2BridgeToken.ETH]: '0x81F6138153d473E8c5EcebD3DC8Cd4903506B075' + }, + [BLOCKCHAIN_NAME.MANTLE]: { + [stargateV2BridgeToken.WETH]: '0x4c1d3Fc3fC3c177c3b633427c2F769276c547463', + [stargateV2BridgeToken.USDC]: '0xAc290Ad4e0c891FDc295ca4F0a6214cf6dC6acDC', + [stargateV2BridgeToken.USDT]: '0xB715B85682B731dB9D5063187C450095c91C57FC', + [stargateV2BridgeToken.METH]: '0xF7628d84a2BbD9bb9c8E686AC95BB5d55169F3F1' + }, + [BLOCKCHAIN_NAME.BASE]: { + [stargateV2BridgeToken.ETH]: '0xdc181Bd607330aeeBEF6ea62e03e5e1Fb4B6F7C7', + [stargateV2BridgeToken.USDC]: '0x27a16dc786820B16E5c9028b75B99F6f604b5d26' + }, + [BLOCKCHAIN_NAME.KAVA]: { + [stargateV2BridgeToken.USDT]: '0x41A5b0470D96656Fb3e8f68A218b39AdBca3420b' + }, + [BLOCKCHAIN_NAME.SCROLL]: { + [stargateV2BridgeToken.ETH]: '0xC2b638Cb5042c1B3c5d5C969361fB50569840583', + [stargateV2BridgeToken.USDCe]: '0x3Fc69CC4A842838bCDC9499178740226062b14E4' + }, + [BLOCKCHAIN_NAME.AURORA]: { + [stargateV2BridgeToken.USDC]: '0x81F6138153d473E8c5EcebD3DC8Cd4903506B075' + } + // [BLOCKCHAIN_NAME.KLAYTN]: { + // [stargateV2BridgeToken.ETH]: '0xBB4957E44401a31ED81Cab33539d9e8993FA13Ce', + // [stargateV2BridgeToken.USDC]: '0x01A7c805cc47AbDB254CD8AaD29dE5e447F59224', + // [stargateV2BridgeToken.USDT]: '0x8619bA1B324e099CB2227060c4BC5bDEe14456c6' + // }, + // [BLOCKCHAIN_NAME.IOTA]: { + // [stargateV2BridgeToken.ETH]: '0x9c2dc7377717603eB92b2655c5f2E7997a4945BD', + // [stargateV2BridgeToken.USDC]: '0x8e8539e4CcD69123c623a106773F2b0cbbc58746', + // [stargateV2BridgeToken.USDT]: '0x77C71633C34C3784ede189d74223122422492a0f' + // }, + // [BLOCKCHAIN_NAME.TAIKO]: { + // [stargateV2BridgeToken.USDC]: '0x77C71633C34C3784ede189d74223122422492a0f', + // [stargateV2BridgeToken.USDT]: '0x1C10CC06DC6D35970d1D53B2A23c76ef370d4135' + // }, + // [BLOCKCHAIN_NAME.SEI]: { + // [stargateV2BridgeToken.WETH]: '0x5c386D85b1B82FD9Db681b9176C8a4248bb6345B' + // } +}; diff --git a/src/features/cross-chain/calculation-manager/providers/stargate-v2-provider/constants/stargate-v2-cross-chain-supported-blockchains.ts b/src/features/cross-chain/calculation-manager/providers/stargate-v2-provider/constants/stargate-v2-cross-chain-supported-blockchains.ts new file mode 100644 index 0000000000..05ee121687 --- /dev/null +++ b/src/features/cross-chain/calculation-manager/providers/stargate-v2-provider/constants/stargate-v2-cross-chain-supported-blockchains.ts @@ -0,0 +1,23 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +export const stargateV2SupportedBlockchains = [ + BLOCKCHAIN_NAME.ETHEREUM, + BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN, + BLOCKCHAIN_NAME.POLYGON, + BLOCKCHAIN_NAME.AVALANCHE, + BLOCKCHAIN_NAME.ARBITRUM, + BLOCKCHAIN_NAME.OPTIMISM, + BLOCKCHAIN_NAME.METIS, + BLOCKCHAIN_NAME.LINEA, + BLOCKCHAIN_NAME.MANTLE, + BLOCKCHAIN_NAME.BASE, + BLOCKCHAIN_NAME.KAVA, + BLOCKCHAIN_NAME.SCROLL, + BLOCKCHAIN_NAME.AURORA + // BLOCKCHAIN_NAME.KLAYTN, + // BLOCKCHAIN_NAME.IOTA, + // BLOCKCHAIN_NAME.TAIKO, + // BLOCKCHAIN_NAME.SEI +]; + +export type StargateV2SupportedBlockchains = (typeof stargateV2SupportedBlockchains)[number]; diff --git a/src/features/cross-chain/calculation-manager/providers/stargate-v2-provider/constants/stargate-v2-pool-abi.ts b/src/features/cross-chain/calculation-manager/providers/stargate-v2-provider/constants/stargate-v2-pool-abi.ts new file mode 100644 index 0000000000..ca8b03a643 --- /dev/null +++ b/src/features/cross-chain/calculation-manager/providers/stargate-v2-provider/constants/stargate-v2-pool-abi.ts @@ -0,0 +1,182 @@ +import { AbiItem } from 'web3-utils'; + +export const stargateV2PoolAbi: AbiItem[] = [ + { + inputs: [ + { + components: [ + { internalType: 'uint32', name: 'dstEid', type: 'uint32' }, + { internalType: 'bytes32', name: 'to', type: 'bytes32' }, + { internalType: 'uint256', name: 'amountLD', type: 'uint256' }, + { internalType: 'uint256', name: 'minAmountLD', type: 'uint256' }, + { internalType: 'bytes', name: 'extraOptions', type: 'bytes' }, + { internalType: 'bytes', name: 'composeMsg', type: 'bytes' }, + { internalType: 'bytes', name: 'oftCmd', type: 'bytes' } + ], + internalType: 'struct SendParam', + name: '_sendParam', + type: 'tuple' + } + ], + name: 'quoteOFT', + outputs: [ + { + components: [ + { internalType: 'uint256', name: 'minAmountLD', type: 'uint256' }, + { internalType: 'uint256', name: 'maxAmountLD', type: 'uint256' } + ], + internalType: 'struct OFTLimit', + name: 'limit', + type: 'tuple' + }, + { + components: [ + { internalType: 'int256', name: 'feeAmountLD', type: 'int256' }, + { internalType: 'string', name: 'description', type: 'string' } + ], + internalType: 'struct OFTFeeDetail[]', + name: 'oftFeeDetails', + type: 'tuple[]' + }, + { + components: [ + { internalType: 'uint256', name: 'amountSentLD', type: 'uint256' }, + { internalType: 'uint256', name: 'amountReceivedLD', type: 'uint256' } + ], + internalType: 'struct OFTReceipt', + name: 'receipt', + type: 'tuple' + } + ], + stateMutability: 'view', + type: 'function' + } +]; + +export const stargateV2SendQuoteAbi: AbiItem[] = [ + { + inputs: [ + { + components: [ + { internalType: 'uint32', name: 'dstEid', type: 'uint32' }, + { internalType: 'bytes32', name: 'to', type: 'bytes32' }, + { internalType: 'uint256', name: 'amountLD', type: 'uint256' }, + { internalType: 'uint256', name: 'minAmountLD', type: 'uint256' }, + { internalType: 'bytes', name: 'extraOptions', type: 'bytes' }, + { internalType: 'bytes', name: 'composeMsg', type: 'bytes' }, + { internalType: 'bytes', name: 'oftCmd', type: 'bytes' } + ], + internalType: 'struct SendParam', + name: '_sendParam', + type: 'tuple' + }, + { + internalType: 'bool', + name: '_payInLzToken', + type: 'bool' + } + ], + name: 'quoteSend', + outputs: [ + { + components: [ + { internalType: 'uint256', name: 'nativeFee', type: 'uint256' }, + { internalType: 'uint256', name: 'lzTokenFee', type: 'uint256' } + ], + internalType: 'struct MessagingFee', + name: 'fee', + type: 'tuple' + } + ], + stateMutability: 'view', + type: 'function' + } +]; + +export const stargateV2SendTokenAbi: AbiItem[] = [ + { + inputs: [ + { + components: [ + { internalType: 'uint32', name: 'dstEid', type: 'uint32' }, + { internalType: 'bytes32', name: 'to', type: 'bytes32' }, + { internalType: 'uint256', name: 'amountLD', type: 'uint256' }, + { internalType: 'uint256', name: 'minAmountLD', type: 'uint256' }, + { internalType: 'bytes', name: 'extraOptions', type: 'bytes' }, + { internalType: 'bytes', name: 'composeMsg', type: 'bytes' }, + { internalType: 'bytes', name: 'oftCmd', type: 'bytes' } + ], + internalType: 'struct SendParam', + name: '_sendParam', + type: 'tuple' + }, + { + components: [ + { internalType: 'uint256', name: 'nativeFee', type: 'uint256' }, + { internalType: 'uint256', name: 'lzTokenFee', type: 'uint256' } + ], + internalType: 'struct MessagingFee', + name: '_fee', + type: 'tuple' + }, + { internalType: 'address', name: '_refundAddress', type: 'address' } + ], + name: 'sendToken', + outputs: [ + { + components: [ + { internalType: 'bytes32', name: 'guid', type: 'bytes32' }, + { internalType: 'uint64', name: 'nonce', type: 'uint64' }, + { + components: [ + { internalType: 'uint256', name: 'nativeFee', type: 'uint256' }, + { internalType: 'uint256', name: 'lzTokenFee', type: 'uint256' } + ], + internalType: 'struct MessagingFee', + name: 'fee', + type: 'tuple' + } + ], + internalType: 'struct MessagingReceipt', + name: 'msgReceipt', + type: 'tuple' + }, + { + components: [ + { internalType: 'uint256', name: 'amountSentLD', type: 'uint256' }, + { internalType: 'uint256', name: 'amountReceivedLD', type: 'uint256' } + ], + internalType: 'struct OFTReceipt', + name: 'oftReceipt', + type: 'tuple' + }, + { + components: [ + { internalType: 'uint72', name: 'ticketId', type: 'uint72' }, + { internalType: 'bytes', name: 'passengerBytes', type: 'bytes' } + ], + internalType: 'struct Ticket', + name: 'ticket', + type: 'tuple' + } + ], + stateMutability: 'payable', + type: 'function' + } +]; + +export const stargateV2PoolBalanceAbi: AbiItem[] = [ + { + inputs: [], + name: 'poolBalance', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256' + } + ], + stateMutability: 'view', + type: 'function' + } +]; diff --git a/src/features/cross-chain/calculation-manager/providers/stargate-v2-provider/constants/stargate-v2-pool-id.ts b/src/features/cross-chain/calculation-manager/providers/stargate-v2-provider/constants/stargate-v2-pool-id.ts new file mode 100644 index 0000000000..dabf7c90cc --- /dev/null +++ b/src/features/cross-chain/calculation-manager/providers/stargate-v2-provider/constants/stargate-v2-pool-id.ts @@ -0,0 +1,29 @@ +import { StargateV2BridgeToken, stargateV2BridgeToken } from './stargate-v2-bridge-token'; +import { StargateV2SupportedBlockchains } from './stargate-v2-cross-chain-supported-blockchains'; +import { stargateV2TokenAddress } from './stargate-v2-token-address'; + +const pools = { + [stargateV2BridgeToken.USDC]: 1, + [stargateV2BridgeToken.USDCe]: 1, + [stargateV2BridgeToken.USDT]: 2, + [stargateV2BridgeToken.mUSD]: 2, + [stargateV2BridgeToken.ETH]: 13, + [stargateV2BridgeToken.WETH]: 13, + [stargateV2BridgeToken.METIS]: 17, + [stargateV2BridgeToken.METH]: 22 +} as const; + +export const stargateV2PoolId: Record = pools; +export type StargateV2PoolId = (typeof pools)[keyof typeof pools]; + +export function getTokenPoolByAddress( + blockchain: StargateV2SupportedBlockchains, + contractAddress: string +): number | null { + const tokenAddress = contractAddress.toLowerCase(); + const tokenSymbol = stargateV2TokenAddress?.[blockchain]?.[tokenAddress]; + if (tokenSymbol) { + return stargateV2PoolId[tokenSymbol]; + } + return null; +} diff --git a/src/features/cross-chain/calculation-manager/providers/stargate-v2-provider/constants/stargate-v2-token-address.ts b/src/features/cross-chain/calculation-manager/providers/stargate-v2-provider/constants/stargate-v2-token-address.ts new file mode 100644 index 0000000000..9a25e0756d --- /dev/null +++ b/src/features/cross-chain/calculation-manager/providers/stargate-v2-provider/constants/stargate-v2-token-address.ts @@ -0,0 +1,68 @@ +import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; + +import { StargateV2BridgeToken, stargateV2BridgeToken } from './stargate-v2-bridge-token'; +import { StargateV2SupportedBlockchains } from './stargate-v2-cross-chain-supported-blockchains'; + +type StargateV2TokenAddress = Record< + StargateV2SupportedBlockchains, + Partial> +>; + +export const stargateV2TokenAddress: StargateV2TokenAddress = { + [BLOCKCHAIN_NAME.ETHEREUM]: { + '0x0000000000000000000000000000000000000000': stargateV2BridgeToken.ETH, + '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48': stargateV2BridgeToken.USDC, + '0xdac17f958d2ee523a2206206994597c13d831ec7': stargateV2BridgeToken.USDT, + '0x9e32b13ce7f2e80a01932b42553652e053d6ed8e': stargateV2BridgeToken.METIS, + '0xd5f7838f5c461feff7fe49ea5ebaf7728bb0adfa': stargateV2BridgeToken.METH + }, + [BLOCKCHAIN_NAME.BINANCE_SMART_CHAIN]: { + '0x55d398326f99059fF775485246999027B3197955': stargateV2BridgeToken.USDT + }, + [BLOCKCHAIN_NAME.AVALANCHE]: { + '0xb97ef9ef8734c71904d8002f8b6bc66dd9c48a6e': stargateV2BridgeToken.USDC, + '0x9702230a8ea53601f5cd2dc00fdbc13d4df4a8c7': stargateV2BridgeToken.USDT + }, + [BLOCKCHAIN_NAME.POLYGON]: { + '0x3c499c542cef5e3811e1192ce70d8cc03d5c3359': stargateV2BridgeToken.USDC, + '0xc2132d05d31c914a87c6611c10748aeb04b58e8f': stargateV2BridgeToken.USDT + }, + [BLOCKCHAIN_NAME.ARBITRUM]: { + '0x0000000000000000000000000000000000000000': stargateV2BridgeToken.ETH, + '0xaf88d065e77c8cc2239327c5edb3a432268e5831': stargateV2BridgeToken.USDC, + '0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9': stargateV2BridgeToken.USDT + }, + [BLOCKCHAIN_NAME.OPTIMISM]: { + '0x0000000000000000000000000000000000000000': stargateV2BridgeToken.ETH, + '0x0b2c639c533813f4aa9d7837caf62653d097ff85': stargateV2BridgeToken.USDC, + '0x94b008aa00579c1307b0ef2c499ad98a8ce58e58': stargateV2BridgeToken.USDT + }, + [BLOCKCHAIN_NAME.METIS]: { + '0xbb06dca3ae6887fabf931640f67cab3e3a16f4dc': stargateV2BridgeToken.mUSD, + '0x420000000000000000000000000000000000000a': stargateV2BridgeToken.WETH, + '0x0000000000000000000000000000000000000000': stargateV2BridgeToken.METIS + }, + [BLOCKCHAIN_NAME.LINEA]: { + '0x0000000000000000000000000000000000000000': stargateV2BridgeToken.ETH + }, + [BLOCKCHAIN_NAME.MANTLE]: { + '0x09bc4e0d864854c6afb6eb9a9cdf58ac190d0df9': stargateV2BridgeToken.USDC, + '0x201eba5cc46d216ce6dc03f6a759e8e766e956ae': stargateV2BridgeToken.USDT, + '0xdeaddeaddeaddeaddeaddeaddeaddeaddead1111': stargateV2BridgeToken.WETH, + '0xcda86a272531e8640cd7f1a92c01839911b90bb0': stargateV2BridgeToken.METH + }, + [BLOCKCHAIN_NAME.BASE]: { + '0x0000000000000000000000000000000000000000': stargateV2BridgeToken.ETH, + '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913': stargateV2BridgeToken.USDC + }, + [BLOCKCHAIN_NAME.KAVA]: { + '0x919c1c267bc06a7039e03fcc2ef738525769109c': stargateV2BridgeToken.USDT + }, + [BLOCKCHAIN_NAME.SCROLL]: { + '0x0000000000000000000000000000000000000000': stargateV2BridgeToken.ETH, + '0x06efdbff2a14a7c8e15944d1f4a48f9f95f663a4': stargateV2BridgeToken.USDCe + }, + [BLOCKCHAIN_NAME.AURORA]: { + '0x368ebb46aca6b8d0787c96b2b20bd3cc3f2c45f7': stargateV2BridgeToken.USDC + } +}; diff --git a/src/features/cross-chain/calculation-manager/providers/stargate-v2-provider/modal/stargate-v2-quote-params-struct.ts b/src/features/cross-chain/calculation-manager/providers/stargate-v2-provider/modal/stargate-v2-quote-params-struct.ts new file mode 100644 index 0000000000..6c00ee0117 --- /dev/null +++ b/src/features/cross-chain/calculation-manager/providers/stargate-v2-provider/modal/stargate-v2-quote-params-struct.ts @@ -0,0 +1,19 @@ +export interface StargateV2QuoteParamsStruct { + dstEid: number; + to: string; + amountLD: string; + minAmountLD: string; + extraOptions: string; + composeMsg: string; + oftCmd: string; +} + +export interface StargateV2QuoteOFTResponse { + amountSentLD?: string[]; + amountReceivedLD: string[]; +} + +export interface StargateV2MessagingFee { + nativeFee: string; + lzTokenFee: string; +} diff --git a/src/features/cross-chain/calculation-manager/providers/stargate-v2-provider/stargate-v2-cross-chain-provider.ts b/src/features/cross-chain/calculation-manager/providers/stargate-v2-provider/stargate-v2-cross-chain-provider.ts new file mode 100644 index 0000000000..5284a9688d --- /dev/null +++ b/src/features/cross-chain/calculation-manager/providers/stargate-v2-provider/stargate-v2-cross-chain-provider.ts @@ -0,0 +1,340 @@ +import BigNumber from 'bignumber.js'; +import { ethers } from 'ethers'; +import { + MaxAmountError, + NotSupportedBlockchain, + NotSupportedTokensError, + RubicSdkError +} from 'src/common/errors'; +import { PriceToken, PriceTokenAmount } from 'src/common/tokens'; +import { nativeTokensList } from 'src/common/tokens/constants/native-tokens'; +import { parseError } from 'src/common/utils/errors'; +import { BlockchainName, EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure'; +import { Injector } from 'src/core/injector/injector'; +import { FAKE_WALLET_ADDRESS } from 'src/features/common/constants/fake-wallet-address'; +import { getFromWithoutFee } from 'src/features/common/utils/get-from-without-fee'; + +import { RequiredCrossChainOptions } from '../../models/cross-chain-options'; +import { CROSS_CHAIN_TRADE_TYPE } from '../../models/cross-chain-trade-type'; +import { CrossChainProvider } from '../common/cross-chain-provider'; +import { CalculationResult } from '../common/models/calculation-result'; +import { FeeInfo } from '../common/models/fee-info'; +import { RubicStep } from '../common/models/rubicStep'; +import { ProxyCrossChainEvmTrade } from '../common/proxy-cross-chain-evm-facade/proxy-cross-chain-evm-trade'; +import { stargateV2BlockchainSupportedPools } from './constants/stargate-v2-blockchain-supported-pools'; +import { StargateV2BridgeToken } from './constants/stargate-v2-bridge-token'; +import { stargateV2ChainIds } from './constants/stargate-v2-chain-id'; +import { stargateV2ContractAddress } from './constants/stargate-v2-contract-address'; +import { + StargateV2SupportedBlockchains, + stargateV2SupportedBlockchains +} from './constants/stargate-v2-cross-chain-supported-blockchains'; +import { + stargateV2PoolAbi, + stargateV2PoolBalanceAbi, + stargateV2SendQuoteAbi +} from './constants/stargate-v2-pool-abi'; +import { getTokenPoolByAddress } from './constants/stargate-v2-pool-id'; +import { stargateV2TokenAddress } from './constants/stargate-v2-token-address'; +import { + StargateV2MessagingFee, + StargateV2QuoteOFTResponse, + StargateV2QuoteParamsStruct +} from './modal/stargate-v2-quote-params-struct'; +import { StargateV2CrossChainTrade } from './stargate-v2-cross-chain-trade'; + +export class StargateV2CrossChainProvider extends CrossChainProvider { + public readonly type = CROSS_CHAIN_TRADE_TYPE.STARGATE_V2; + + public isSupportedBlockchain(fromBlockchain: BlockchainName): boolean { + return stargateV2SupportedBlockchains.some( + supportedBlockchain => supportedBlockchain === fromBlockchain + ); + } + + public async calculate( + from: PriceTokenAmount, + toToken: PriceToken, + options: RequiredCrossChainOptions + ): Promise { + if (!this.isSupportedBlockchain(from.blockchain)) { + throw new NotSupportedBlockchain(); + } + try { + const isSupportedPools = this.checkSupportedPools(from, toToken); + if (!isSupportedPools) { + return { + trade: null, + error: new NotSupportedTokensError(), + tradeType: this.type + }; + } + + const toBlockchain = toToken.blockchain as StargateV2SupportedBlockchains; + const useProxy = options?.useProxy?.[this.type] ?? true; + const fromTokenAddress = from.address.toLowerCase(); + + const dstChainId = stargateV2ChainIds[toBlockchain]; + + const feeInfo = await this.getFeeInfo( + from.blockchain, + options.providerAddress, + from, + useProxy + ); + const receiverAddress = + options?.receiverAddress || + this.getWalletAddress(from.blockchain) || + FAKE_WALLET_ADDRESS; + + const fromWithoutFee = getFromWithoutFee( + from, + feeInfo.rubicProxy?.platformFee?.percent + ); + + const maxAmountError = await this.checkMaxAmount( + from.blockchain, + fromTokenAddress, + fromWithoutFee.weiAmount + ); + + if (maxAmountError) { + return { + trade: null, + error: maxAmountError, + tradeType: this.type + }; + } + + const amountLD = fromWithoutFee.stringWeiAmount; + + const sendParams: StargateV2QuoteParamsStruct = { + dstEid: dstChainId, + to: ethers.utils.hexZeroPad(receiverAddress, 32), + amountLD: amountLD, + minAmountLD: amountLD, + extraOptions: '0x', + composeMsg: '0x', + oftCmd: '0x' + }; + + const { amountReceivedLD } = await this.getReceiveAmount( + sendParams, + from.blockchain, + fromTokenAddress + ); + const amountReceived = amountReceivedLD[1] as string; + const slippageAmount = new BigNumber(amountReceived).multipliedBy( + options.slippageTolerance + ); + const minReceivedAmount = new BigNumber(amountReceived).minus(slippageAmount); + sendParams.amountLD = amountReceived; + sendParams.minAmountLD = minReceivedAmount.toFixed(0); + const messagingFee = await this.getNativeFee( + sendParams, + from.blockchain, + fromTokenAddress + ); + const nativeToken = nativeTokensList[from.blockchain]; + + const cryptoFeeToken = await PriceTokenAmount.createFromToken({ + ...nativeToken, + weiAmount: new BigNumber(messagingFee.nativeFee) + }); + + const to = new PriceTokenAmount({ + ...toToken.asStruct, + tokenAmount: Web3Pure.fromWei(amountReceived, fromWithoutFee.decimals) + }); + + const routePath = await this.getRoutePath(from, to); + const gasData = + options.gasCalculation === 'enabled' + ? await StargateV2CrossChainTrade.getGasData( + from, + to, + feeInfo, + options.slippageTolerance, + options.providerAddress, + routePath, + sendParams, + messagingFee, + options.receiverAddress + ) + : null; + return { + trade: new StargateV2CrossChainTrade( + { + from, + to, + feeInfo: { + ...feeInfo, + provider: { + cryptoFee: { + amount: Web3Pure.fromWei( + messagingFee.nativeFee, + nativeToken.decimals + ), + token: cryptoFeeToken + } + } + }, + slippageTolerance: options.slippageTolerance, + gasData, + sendParams, + messagingFee, + priceImpact: from.calculatePriceImpactPercent(to), + toTokenAmountMin: Web3Pure.fromWei( + minReceivedAmount, + fromWithoutFee.decimals + ) + }, + options.providerAddress, + routePath + ), + tradeType: this.type + }; + } catch (err) { + return { + trade: null, + error: parseError(err), + tradeType: this.type + }; + } + } + + private async checkMaxAmount( + fromBlockchain: EvmBlockchainName, + tokenAddress: string, + amountToSend: BigNumber + ): Promise { + const tokenSymbol = stargateV2TokenAddress[ + fromBlockchain as StargateV2SupportedBlockchains + ][tokenAddress] as StargateV2BridgeToken; + const contractAddress = stargateV2ContractAddress[ + fromBlockchain as StargateV2SupportedBlockchains + ][tokenSymbol] as string; + + const maxAmount = await Injector.web3PublicService + .getWeb3Public(fromBlockchain) + .callContractMethod(contractAddress, stargateV2PoolBalanceAbi, 'poolBalance'); + const maxAmounSend = new BigNumber(maxAmount); + + if (amountToSend.gt(maxAmounSend)) { + return new MaxAmountError(maxAmounSend, tokenSymbol as string); + } + return null; + } + + protected async getFeeInfo( + fromBlockchain: Partial, + providerAddress: string, + percentFeeToken: PriceTokenAmount, + useProxy: boolean + ): Promise { + return ProxyCrossChainEvmTrade.getFeeInfo( + fromBlockchain, + providerAddress, + percentFeeToken, + useProxy + ); + } + + private checkSupportedPools( + from: PriceTokenAmount, + to: PriceToken + ): boolean { + const fromBlockchain = from.blockchain as StargateV2SupportedBlockchains; + const toBlockchain = to.blockchain as StargateV2SupportedBlockchains; + const srcTokenPool = getTokenPoolByAddress(fromBlockchain, from.address); + const dstTokenPool = getTokenPoolByAddress(toBlockchain, to.address); + + if (!srcTokenPool || !dstTokenPool) { + return false; + } + const srcSupportedPools = + stargateV2BlockchainSupportedPools[from.blockchain as StargateV2SupportedBlockchains]; + const dstSupportedPools = + stargateV2BlockchainSupportedPools[to.blockchain as StargateV2SupportedBlockchains]; + + return ( + srcSupportedPools.includes(srcTokenPool) && + dstSupportedPools.includes(dstTokenPool) && + srcTokenPool === dstTokenPool + ); + } + + private async getReceiveAmount( + sendParam: StargateV2QuoteParamsStruct, + fromBlockchain: EvmBlockchainName, + tokenAddress: string + ): Promise { + const tokenSymbol = stargateV2TokenAddress[ + fromBlockchain as StargateV2SupportedBlockchains + ][tokenAddress] as StargateV2BridgeToken; + + const contractAddress = stargateV2ContractAddress[ + fromBlockchain as StargateV2SupportedBlockchains + ][tokenSymbol] as string; + + try { + const { 2: amountReceivedLD } = await Injector.web3PublicService + .getWeb3Public(fromBlockchain) + .callContractMethod<{ 2: string[] }>( + contractAddress, + stargateV2PoolAbi, + 'quoteOFT', + [sendParam] + ); + + return { + amountReceivedLD + }; + } catch (err) { + throw new RubicSdkError(err?.message); + } + } + + protected async getRoutePath( + from: PriceTokenAmount, + to: PriceTokenAmount + ): Promise { + return [ + { + type: 'cross-chain', + provider: CROSS_CHAIN_TRADE_TYPE.STARGATE_V2, + path: [from, to] + } + ]; + } + + private async getNativeFee( + sendParam: StargateV2QuoteParamsStruct, + fromBlockchain: EvmBlockchainName, + tokenAddress: string + ): Promise { + const tokenSymbol = stargateV2TokenAddress[ + fromBlockchain as StargateV2SupportedBlockchains + ][tokenAddress] as StargateV2BridgeToken; + const contractAddress = stargateV2ContractAddress[ + fromBlockchain as StargateV2SupportedBlockchains + ][tokenSymbol] as string; + try { + const { 0: nativeFee, 1: lzTokenFee } = await Injector.web3PublicService + .getWeb3Public(fromBlockchain) + .callContractMethod<{ 0: string; 1: string }>( + contractAddress, + stargateV2SendQuoteAbi, + 'quoteSend', + [sendParam, false] + ); + return { + nativeFee, + lzTokenFee + }; + } catch (err) { + throw new RubicSdkError(err?.message); + } + } +} diff --git a/src/features/cross-chain/calculation-manager/providers/stargate-v2-provider/stargate-v2-cross-chain-trade.ts b/src/features/cross-chain/calculation-manager/providers/stargate-v2-provider/stargate-v2-cross-chain-trade.ts new file mode 100644 index 0000000000..d0b850fb3e --- /dev/null +++ b/src/features/cross-chain/calculation-manager/providers/stargate-v2-provider/stargate-v2-cross-chain-trade.ts @@ -0,0 +1,291 @@ +import BigNumber from 'bignumber.js'; +import { FailedToCheckForTransactionReceiptError, RubicSdkError } from 'src/common/errors'; +import { PriceTokenAmount } from 'src/common/tokens'; +import { BlockchainName, EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { EvmWeb3Private } from 'src/core/blockchain/web3-private-service/web3-private/evm-web3-private/evm-web3-private'; +import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure'; +import { EvmEncodeConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/models/evm-encode-config'; +import { Injector } from 'src/core/injector/injector'; +import { ContractParams } from 'src/features/common/models/contract-params'; +import { SwapTransactionOptions } from 'src/features/common/models/swap-transaction-options'; + +import { CROSS_CHAIN_TRADE_TYPE } from '../../models/cross-chain-trade-type'; +import { getCrossChainGasData } from '../../utils/get-cross-chain-gas-data'; +import { rubicProxyContractAddress } from '../common/constants/rubic-proxy-contract-address'; +import { evmCommonCrossChainAbi } from '../common/emv-cross-chain-trade/constants/evm-common-cross-chain-abi'; +import { gatewayRubicCrossChainAbi } from '../common/emv-cross-chain-trade/constants/gateway-rubic-cross-chain-abi'; +import { EvmCrossChainTrade } from '../common/emv-cross-chain-trade/evm-cross-chain-trade'; +import { GasData } from '../common/emv-cross-chain-trade/models/gas-data'; +import { BRIDGE_TYPE } from '../common/models/bridge-type'; +import { FeeInfo } from '../common/models/fee-info'; +import { GetContractParamsOptions } from '../common/models/get-contract-params-options'; +import { OnChainSubtype } from '../common/models/on-chain-subtype'; +import { RubicStep } from '../common/models/rubicStep'; +import { TradeInfo } from '../common/models/trade-info'; +import { ProxyCrossChainEvmTrade } from '../common/proxy-cross-chain-evm-facade/proxy-cross-chain-evm-trade'; +import { StargateV2BridgeToken } from './constants/stargate-v2-bridge-token'; +import { stargateV2ContractAddress } from './constants/stargate-v2-contract-address'; +import { StargateV2SupportedBlockchains } from './constants/stargate-v2-cross-chain-supported-blockchains'; +import { stargateV2SendTokenAbi } from './constants/stargate-v2-pool-abi'; +import { stargateV2TokenAddress } from './constants/stargate-v2-token-address'; +import { + StargateV2MessagingFee, + StargateV2QuoteParamsStruct +} from './modal/stargate-v2-quote-params-struct'; + +export class StargateV2CrossChainTrade extends EvmCrossChainTrade { + protected get methodName(): string { + return 'startBridgeTokensViaGenericCrossChain'; + } + + public static async getGasData( + from: PriceTokenAmount, + toToken: PriceTokenAmount, + feeInfo: FeeInfo, + slippageTolerance: number, + providerAddress: string, + routePath: RubicStep[], + sendParams: StargateV2QuoteParamsStruct, + messagingFee: StargateV2MessagingFee, + receiverAddress?: string + ): Promise { + try { + const trade = new StargateV2CrossChainTrade( + { + from, + to: toToken, + slippageTolerance, + gasData: null, + feeInfo, + sendParams, + messagingFee, + priceImpact: 0, + toTokenAmountMin: new BigNumber(0) + }, + providerAddress || EvmWeb3Pure.EMPTY_ADDRESS, + routePath + ); + + return getCrossChainGasData(trade, receiverAddress); + } catch (_err) { + return null; + } + } + + protected get web3Private(): EvmWeb3Private { + return Injector.web3PrivateService.getWeb3PrivateByBlockchain(this.from.blockchain); + } + + public readonly from: PriceTokenAmount; + + public readonly to: PriceTokenAmount; + + public readonly feeInfo: FeeInfo; + + public readonly type = CROSS_CHAIN_TRADE_TYPE.STARGATE_V2; + + public readonly gasData: GasData; + + public readonly toTokenAmountMin: BigNumber; + + public readonly isAggregator = false; + + public readonly slippageTolerance: number; + + public readonly priceImpact: number | null; + + public readonly onChainSubtype: OnChainSubtype = { from: undefined, to: undefined }; + + public readonly bridgeType = BRIDGE_TYPE.STARGATE_V2; + + public readonly messagingFee: StargateV2MessagingFee; + + private readonly fromTokenAddress: string; + + public get fromBlockchain(): StargateV2SupportedBlockchains { + return this.from.blockchain as StargateV2SupportedBlockchains; + } + + protected get fromContractAddress(): string { + const fromTokenSymbol = stargateV2TokenAddress[ + this.from.blockchain as StargateV2SupportedBlockchains + ][this.fromTokenAddress] as StargateV2BridgeToken; + return this.isProxyTrade + ? rubicProxyContractAddress[this.fromBlockchain].gateway + : (stargateV2ContractAddress?.[this.fromBlockchain]?.[fromTokenSymbol] as string); + } + + private readonly stargateV2SendParams: StargateV2QuoteParamsStruct; + + constructor( + crossChainTrade: { + from: PriceTokenAmount; + to: PriceTokenAmount; + slippageTolerance: number; + gasData: GasData | null; + feeInfo: FeeInfo; + sendParams: StargateV2QuoteParamsStruct; + messagingFee: StargateV2MessagingFee; + priceImpact: number | null; + toTokenAmountMin: BigNumber; + }, + providerAddress: string, + routePath: RubicStep[] + ) { + super(providerAddress, routePath); + this.from = crossChainTrade.from; + this.to = crossChainTrade.to; + this.feeInfo = crossChainTrade.feeInfo; + this.gasData = crossChainTrade.gasData; + this.slippageTolerance = crossChainTrade.slippageTolerance; + this.stargateV2SendParams = crossChainTrade.sendParams; + this.messagingFee = crossChainTrade.messagingFee; + this.priceImpact = crossChainTrade.priceImpact; + this.toTokenAmountMin = crossChainTrade.toTokenAmountMin; + this.fromTokenAddress = this.from.address.toLowerCase(); + } + + protected async getContractParams(options: GetContractParamsOptions): Promise { + const { + data, + value: providerValue, + to + } = await this.setTransactionConfig( + false, + options?.useCacheData || false, + options?.receiverAddress + ); + try { + const bridgeData = ProxyCrossChainEvmTrade.getBridgeData( + { ...options }, + { + walletAddress: this.walletAddress, + fromTokenAmount: this.from, + toTokenAmount: this.to, + toAddress: undefined, + srcChainTrade: null, + providerAddress: this.providerAddress, + type: `native:${this.type}`, + fromAddress: this.walletAddress + } + ); + const extraNativeFee = this.from.isNative + ? new BigNumber(providerValue).minus(this.from.stringWeiAmount).toFixed() + : new BigNumber(providerValue).toFixed(); + const providerData = await ProxyCrossChainEvmTrade.getGenericProviderData( + to, + data, + this.fromBlockchain as EvmBlockchainName, + to, + extraNativeFee + ); + const methodArguments = [bridgeData, providerData]; + const value = this.getSwapValue(providerValue); + + const transactionConfiguration = EvmWeb3Pure.encodeMethodCall( + rubicProxyContractAddress[this.from.blockchain].router, + evmCommonCrossChainAbi, + this.methodName, + methodArguments, + value + ); + const sendingToken = this.from.isNative ? [] : [this.from.address]; + const sendingAmount = this.from.isNative ? [] : [this.from.stringWeiAmount]; + + return { + contractAddress: rubicProxyContractAddress[this.from.blockchain].gateway, + contractAbi: gatewayRubicCrossChainAbi, + methodName: 'startViaRubic', + methodArguments: [sendingToken, sendingAmount, transactionConfiguration.data], + value + }; + } catch (err) { + console.log(err?.message); + throw new RubicSdkError(err?.message); + } + } + + protected async getTransactionConfigAndAmount( + receiverAddress?: string | undefined + ): Promise<{ config: EvmEncodeConfig; amount: string }> { + const fromBlockchain = this.from.blockchain as StargateV2SupportedBlockchains; + const fromTokenSymbol = stargateV2TokenAddress[fromBlockchain][ + this.fromTokenAddress + ] as StargateV2BridgeToken; + const sendParams = { + ...this.stargateV2SendParams, + to: receiverAddress || this.walletAddress + }; + const contractAddress = stargateV2ContractAddress?.[fromBlockchain]?.[fromTokenSymbol]; + if (!contractAddress) { + throw new RubicSdkError(); + } + const nativeFee = new BigNumber(this.messagingFee.nativeFee); + + const value = this.from.isNative + ? new BigNumber(this.from.stringWeiAmount).plus(nativeFee).toFixed() + : this.messagingFee.nativeFee; + const calldata = await EvmWeb3Pure.encodeMethodCall( + contractAddress, + stargateV2SendTokenAbi, + 'sendToken', + [sendParams, this.messagingFee, this.walletAddress], + value + ); + return { + config: calldata, + amount: this.to.stringWeiAmount + }; + } + + protected async swapDirect(options: SwapTransactionOptions = {}): Promise { + this.checkWalletConnected(); + await this.checkTradeErrors(); + await this.checkAllowanceAndApprove(options); + const { onConfirm, gasLimit, gasPriceOptions } = options; + let transactionHash: string; + const onTransactionHash = (hash: string) => { + if (onConfirm) { + onConfirm(hash); + } + transactionHash = hash; + }; + + try { + const { data, value, to } = await this.setTransactionConfig( + false, + options?.useCacheData || false, + options?.receiverAddress + ); + + await this.web3Private.trySendTransaction(to, { + data, + value, + onTransactionHash, + gas: gasLimit, + gasPriceOptions + }); + + return transactionHash!; + } catch (err) { + if (err instanceof FailedToCheckForTransactionReceiptError) { + return transactionHash!; + } + throw err; + } + } + + public getTradeAmountRatio(fromUsd: BigNumber): BigNumber { + return fromUsd.dividedBy(this.to.tokenAmount); + } + + public getTradeInfo(): TradeInfo { + return { + estimatedGas: this.estimatedGas, + feeInfo: this.feeInfo, + priceImpact: this.priceImpact, + slippage: this.slippageTolerance * 100, + routePath: this.routePath + }; + } +} diff --git a/src/features/cross-chain/status-manager/cross-chain-status-manager.ts b/src/features/cross-chain/status-manager/cross-chain-status-manager.ts index db0b7fd898..26624e8919 100644 --- a/src/features/cross-chain/status-manager/cross-chain-status-manager.ts +++ b/src/features/cross-chain/status-manager/cross-chain-status-manager.ts @@ -85,7 +85,6 @@ export class CrossChainStatusManager { [CROSS_CHAIN_TRADE_TYPE.XY]: this.getXyDstSwapStatus, [CROSS_CHAIN_TRADE_TYPE.CELER_BRIDGE]: this.getCelerBridgeDstSwapStatus, [CROSS_CHAIN_TRADE_TYPE.CHANGENOW]: this.getChangenowDstSwapStatus, - [CROSS_CHAIN_TRADE_TYPE.STARGATE]: this.getLayerZeroDstSwapStatus, [CROSS_CHAIN_TRADE_TYPE.ARBITRUM]: this.getArbitrumBridgeDstSwapStatus, [CROSS_CHAIN_TRADE_TYPE.SQUIDROUTER]: this.getSquidrouterDstSwapStatus, [CROSS_CHAIN_TRADE_TYPE.SCROLL_BRIDGE]: this.getScrollBridgeDstSwapStatus, @@ -97,7 +96,9 @@ export class CrossChainStatusManager { [CROSS_CHAIN_TRADE_TYPE.ARCHON_BRIDGE]: this.getLayerZeroDstSwapStatus, [CROSS_CHAIN_TRADE_TYPE.MESON]: this.getMesonDstSwapStatus, [CROSS_CHAIN_TRADE_TYPE.OWL_TO_BRIDGE]: this.getOwlToDstSwapStatus, - [CROSS_CHAIN_TRADE_TYPE.EDDY_BRIDGE]: this.getEddyBridgeDstSwapStatus + [CROSS_CHAIN_TRADE_TYPE.STARGATE_V2]: this.getLayerZeroDstSwapStatus, + [CROSS_CHAIN_TRADE_TYPE.EDDY_BRIDGE]: this.getEddyBridgeDstSwapStatus, + [CROSS_CHAIN_TRADE_TYPE.STARGATE]: this.getLayerZeroDstSwapStatus }; /** diff --git a/yarn.lock b/yarn.lock index 2f1bbfa117..1cd06d8de8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1671,12 +1671,10 @@ "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" -"@layerzerolabs/scan-client@^0.0.0-beta.6": - version "0.0.0-beta.6" - resolved "https://registry.yarnpkg.com/@layerzerolabs/scan-client/-/scan-client-0.0.0-beta.6.tgz#079275514e2a386677fc8129ced52a3c3f0a1b9a" - integrity sha512-TjhEfMcmN5Q8Gtog45lj6in+y8OzGPoLzUzSF45cJO703Lrlwf5tAA85TQnnRWPGBiChw70KG2S6nzgmMRQWJg== - dependencies: - axios "^0.27.2" +"@layerzerolabs/scan-client@0.0.8": + version "0.0.8" + resolved "https://registry.yarnpkg.com/@layerzerolabs/scan-client/-/scan-client-0.0.8.tgz#38316cde50c06556bb81b74f18d10a36b637eefb" + integrity sha512-V9vvt9GW0+AHoWXfOSNmZ9WUa28fVBmC93J/f9RLeDMEy5VR8srW2Du5pf1mMAg6ncEL0kQ1xkVCu+X6ARFBtQ== "@metamask/eth-sig-util@^4.0.1": version "4.0.1"