From 45dfa33680c1ab486c6929350d27a2a520f51811 Mon Sep 17 00:00:00 2001 From: chris Date: Mon, 4 Nov 2024 12:31:49 +1000 Subject: [PATCH 1/7] feat: Update cBridge native token parameters --- .../canonical-bridge-sdk/src/cbridge/index.ts | 20 ++++++++++++++++--- .../src/cbridge/types/index.ts | 3 +++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/packages/canonical-bridge-sdk/src/cbridge/index.ts b/packages/canonical-bridge-sdk/src/cbridge/index.ts index e0df24b0..81fe3c90 100644 --- a/packages/canonical-bridge-sdk/src/cbridge/index.ts +++ b/packages/canonical-bridge-sdk/src/cbridge/index.ts @@ -137,6 +137,7 @@ export class CBridge { fromChainId, address, isPegged, + isNativeToken, peggedConfig, args, }: ISendCBridgeToken): Promise { @@ -152,6 +153,7 @@ export class CBridge { }); const functionName = this.getTransferFunction({ isPegged, + isNativeToken, transferType, }); const cBridgeArgs = { @@ -222,6 +224,7 @@ export class CBridge { getTransferParams({ amount, isPegged, + isNativeToken = false, toChainId, tokenAddress, address, @@ -231,7 +234,9 @@ export class CBridge { nonce, }: IGetCBridgeTransferParamsInput) { return isPegged === false - ? [address, tokenAddress, amount, toChainId, nonce, maxSlippage] + ? isNativeToken + ? [address, amount, toChainId, nonce, maxSlippage] + : [address, tokenAddress, amount, toChainId, nonce, maxSlippage] : transferType === 'deposit' ? [tokenAddress, amount, toChainId, address as `0x${string}`, nonce] : transferType === 'withdraw' @@ -269,9 +274,15 @@ export class CBridge { * @param isPegged * @returns string */ - getTransferFunction({ isPegged, transferType }: IGetCBridgeTransferFunction) { + getTransferFunction({ + isPegged, + transferType, + isNativeToken = false, + }: IGetCBridgeTransferFunction) { return isPegged === false - ? 'send' + ? isNativeToken + ? 'sendNative' + : 'send' : transferType === 'deposit' ? 'deposit' : transferType === 'withdraw' @@ -317,6 +328,7 @@ export class CBridge { userAddress, maxSlippage, nonce, + isNativeToken = false, }: { isPegged: boolean; peggedConfig?: CBridgePeggedPairConfig; @@ -328,6 +340,7 @@ export class CBridge { userAddress: `0x${string}`; maxSlippage: number; nonce: number; + isNativeToken?: boolean; }) { const transferType = this.getTransferType({ peggedConfig, @@ -355,6 +368,7 @@ export class CBridge { transferType, peggedConfig, nonce, + isNativeToken, }); return { address: bridgeAddress as `0x${string}`, diff --git a/packages/canonical-bridge-sdk/src/cbridge/types/index.ts b/packages/canonical-bridge-sdk/src/cbridge/types/index.ts index ba06584a..2dab3141 100644 --- a/packages/canonical-bridge-sdk/src/cbridge/types/index.ts +++ b/packages/canonical-bridge-sdk/src/cbridge/types/index.ts @@ -196,6 +196,7 @@ export interface ISendCBridgeToken { address: `0x${string}`; peggedConfig?: CBridgePeggedPairConfig; isPegged: boolean; + isNativeToken?: boolean; args: any; } @@ -209,6 +210,7 @@ export interface IGetCBridgeTransferAddressInput { export interface IGetCBridgeTransferParamsInput { amount: bigint; isPegged: boolean; + isNativeToken?: boolean; toChainId: number; address: `0x${string}`; tokenAddress: `0x${string}`; @@ -226,5 +228,6 @@ export interface IGetCBridgeABI { export interface IGetCBridgeTransferFunction { isPegged: boolean; + isNativeToken?: boolean; transferType?: 'deposit' | 'withdraw'; } From 4e36f13e65bbbeebe651ad65d75d2210e545262d Mon Sep 17 00:00:00 2001 From: chris Date: Tue, 5 Nov 2024 18:53:23 +1000 Subject: [PATCH 2/7] feat: Add native token into token list --- .../adapters/cBridge/CBridgeAdapter.ts | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/cBridge/CBridgeAdapter.ts b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/cBridge/CBridgeAdapter.ts index c35a99e5..7377bd18 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/cBridge/CBridgeAdapter.ts +++ b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/cBridge/CBridgeAdapter.ts @@ -59,12 +59,13 @@ export class CBridgeAdapter extends BaseAdapter< } protected initTokens() { - const { chain_token } = this.config; + const { chain_token, chains } = this.config; const tokenMap = new Map(); const symbolMap = new Map>(); Object.entries(chain_token).forEach(([id, { token: chainTokens }]) => { const chainId = Number(id); + const nativeChain = chains.find((chain) => Number(chain.id) === Number(id)); const filteredTokens = chainTokens.filter((token) => { const isEnabledToken = !token.token.xfer_disabled; @@ -76,6 +77,28 @@ export class CBridgeAdapter extends BaseAdapter< return isEnabledToken && !isExcludedToken; }); + // Add native token info + if (nativeChain) { + filteredTokens.push({ + token: { + symbol: nativeChain?.gas_token_symbol ?? '', + address: '0x0000000000000000000000000000000000000000', + decimal: 18, + xfer_disabled: false, + }, + name: nativeChain?.gas_token_symbol, + icon: nativeChain?.icon, + inbound_lmt: '', + inbound_epoch_cap: '', + transfer_disabled: false, + liq_add_disabled: false, + liq_rm_disabled: false, + liq_agg_rm_src_disabled: false, + delay_threshold: '', + delay_period: 0, + }); + } + if (filteredTokens.length > 0 && this.chainMap.has(chainId)) { symbolMap.set(chainId, new Map()); From 3954b8c90406662ba36a45cc21028c4e9b0d11da Mon Sep 17 00:00:00 2001 From: chris Date: Wed, 6 Nov 2024 12:57:07 +1000 Subject: [PATCH 3/7] chore: Retrieve ETH fees by WETH token info --- .../pages/mainnet/index.tsx | 2 +- .../canonical-bridge-sdk/src/cbridge/index.ts | 26 ++++++++++++++----- .../adapters/cBridge/CBridgeAdapter.ts | 18 ++++++++++--- .../cBridge/hooks/useCBridgeSendMaxMin.ts | 6 ++++- .../cBridge/hooks/useCBridgeTransferParams.ts | 6 ++++- .../aggregator/adapters/cBridge/types.ts | 1 + .../components/Button/TransferButton.tsx | 2 ++ .../transfer/hooks/useLoadingBridgeFees.ts | 2 +- 8 files changed, 49 insertions(+), 14 deletions(-) diff --git a/apps/canonical-bridge-ui/pages/mainnet/index.tsx b/apps/canonical-bridge-ui/pages/mainnet/index.tsx index f9f48ec2..aa909928 100644 --- a/apps/canonical-bridge-ui/pages/mainnet/index.tsx +++ b/apps/canonical-bridge-ui/pages/mainnet/index.tsx @@ -31,7 +31,7 @@ export const bridgeConfig: ICanonicalBridgeConfig = { }, http: { refetchingInterval: 30 * 1000, // 30s - apiTimeOut: 60 * 1000, // 60s + apiTimeOut: 10 * 1000, // 60s deBridgeAccessToken: '', serverEndpoint: env.SERVER_ENDPOINT, }, diff --git a/packages/canonical-bridge-sdk/src/cbridge/index.ts b/packages/canonical-bridge-sdk/src/cbridge/index.ts index 81fe3c90..af24d813 100644 --- a/packages/canonical-bridge-sdk/src/cbridge/index.ts +++ b/packages/canonical-bridge-sdk/src/cbridge/index.ts @@ -156,13 +156,25 @@ export class CBridge { isNativeToken, transferType, }); - const cBridgeArgs = { - address: bridgeAddress, - abi: ABI, - functionName, - account: address, - args, - }; + let cBridgeArgs = null; + if (isNativeToken) { + cBridgeArgs = { + address: bridgeAddress, + abi: ABI, + functionName, + account: address, + args, + value: args[1], + }; + } else { + cBridgeArgs = { + address: bridgeAddress, + abi: ABI, + functionName, + account: address, + args, + }; + } const gas = await publicClient.estimateContractGas(cBridgeArgs as any); const gasPrice = await publicClient.getGasPrice(); const hash = await walletClient.writeContract({ diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/cBridge/CBridgeAdapter.ts b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/cBridge/CBridgeAdapter.ts index 7377bd18..e6c95da5 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/cBridge/CBridgeAdapter.ts +++ b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/cBridge/CBridgeAdapter.ts @@ -1,4 +1,5 @@ import { BridgeType } from '@bnb-chain/canonical-bridge-sdk'; +import { isAddress } from 'viem'; import { ICBridgeBurnPairConfig, @@ -77,15 +78,17 @@ export class CBridgeAdapter extends BaseAdapter< return isEnabledToken && !isExcludedToken; }); - // Add native token info + // Add native token info. if (nativeChain) { - filteredTokens.push({ + const weth_token = chainTokens.find((token) => token.token.symbol === 'WETH'); + const nativeTokenObj = { token: { symbol: nativeChain?.gas_token_symbol ?? '', address: '0x0000000000000000000000000000000000000000', decimal: 18, xfer_disabled: false, }, + weth_address: weth_token?.token.address, name: nativeChain?.gas_token_symbol, icon: nativeChain?.icon, inbound_lmt: '', @@ -96,7 +99,16 @@ export class CBridgeAdapter extends BaseAdapter< liq_agg_rm_src_disabled: false, delay_threshold: '', delay_period: 0, - }); + }; + // The address of WETH (weth_address) is required for retrieving native token min/ max send amount + // https://cbridge-docs.celer.network/developer/cbridge-limit-parameters#id-1.-minsend-maxsend + if ( + nativeChain?.gas_token_symbol === 'ETH' && + isAddress(weth_token?.token?.address ?? '') + ) { + nativeTokenObj.weth_address = weth_token?.token.address; + } + filteredTokens.push(nativeTokenObj); } if (filteredTokens.length > 0 && this.chainMap.has(chainId)) { diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/cBridge/hooks/useCBridgeSendMaxMin.ts b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/cBridge/hooks/useCBridgeSendMaxMin.ts index 1de1b9af..1acc9656 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/cBridge/hooks/useCBridgeSendMaxMin.ts +++ b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/cBridge/hooks/useCBridgeSendMaxMin.ts @@ -31,9 +31,13 @@ export const useCBridgeSendMaxMin = (isDisabled = false) => { ) { return; } + const tokenAddress = + selectedToken?.symbol === 'ETH' + ? selectedToken?.cBridge?.raw?.weth_address + : selectedToken?.address; const { min, max } = await bridgeSDK.cBridge.getSendRange({ bridgeAddress: bridgeAddress as `0x${string}`, - tokenAddress: selectedToken?.address as `0x${string}`, + tokenAddress: tokenAddress as `0x${string}`, client: publicClient, }); diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/cBridge/hooks/useCBridgeTransferParams.ts b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/cBridge/hooks/useCBridgeTransferParams.ts index 21d25c79..210b8389 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/cBridge/hooks/useCBridgeTransferParams.ts +++ b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/cBridge/hooks/useCBridgeTransferParams.ts @@ -4,6 +4,7 @@ import { useAccount } from 'wagmi'; import { useAppSelector } from '@/modules/store/StoreProvider'; import { useBridgeSDK } from '@/core/hooks/useBridgeSDK'; +import { isNativeToken } from '@/core/utils/address'; export const useCBridgeTransferParams = () => { const { address } = useAccount(); @@ -93,6 +94,7 @@ export const useCBridgeTransferParams = () => { maxSlippage: max_slippage, transferType: transferType ? transferType : undefined, peggedConfig: selectedToken.cBridge?.peggedConfig, + isNativeToken: isNativeToken(selectedToken.address), nonce, }); }, [ @@ -114,7 +116,8 @@ export const useCBridgeTransferParams = () => { (isPegged && !transferType) || !address || !bridgeAddress || - !bridgeSDK?.cBridge + !bridgeSDK?.cBridge || + !selectedToken ) { return null; } @@ -125,6 +128,7 @@ export const useCBridgeTransferParams = () => { }); const functionName = bridgeSDK.cBridge.getTransferFunction({ isPegged, + isNativeToken: isNativeToken(selectedToken?.address), transferType: transferType || undefined, }); return { diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/cBridge/types.ts b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/cBridge/types.ts index 666d35e3..3196305b 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/cBridge/types.ts +++ b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/cBridge/types.ts @@ -38,6 +38,7 @@ export interface ICBridgeToken { delay_period: number; method?: string; bridgeAddress?: string; //bridge address for transfer + weth_address?: string; } export interface ICBridgePeggedPairConfig { diff --git a/packages/canonical-bridge-widget/src/modules/transfer/components/Button/TransferButton.tsx b/packages/canonical-bridge-widget/src/modules/transfer/components/Button/TransferButton.tsx index bcc38a43..3ebfaea6 100644 --- a/packages/canonical-bridge-widget/src/modules/transfer/components/Button/TransferButton.tsx +++ b/packages/canonical-bridge-widget/src/modules/transfer/components/Button/TransferButton.tsx @@ -14,6 +14,7 @@ import { useCurrentWallet } from '@/modules/wallet/CurrentWalletProvider'; import { useTronTransferInfo } from '@/modules/transfer/hooks/tron/useTronTransferInfo'; import { utf8ToHex } from '@/core/utils/string'; import { useTronContract } from '@/modules/aggregator/adapters/meson/hooks/useTronContract'; +import { isNativeToken } from '@/core/utils/address'; export function TransferButton({ onOpenSubmittedModal, @@ -164,6 +165,7 @@ export function TransferButton({ bridgeAddress: transferActionInfo.bridgeAddress as string, fromChainId: fromChain?.id, isPegged: selectedToken.isPegged, + isNativeToken: isNativeToken(selectedToken.address), address, peggedConfig: selectedToken?.cBridge?.peggedConfig, args: cBridgeArgs.args, diff --git a/packages/canonical-bridge-widget/src/modules/transfer/hooks/useLoadingBridgeFees.ts b/packages/canonical-bridge-widget/src/modules/transfer/hooks/useLoadingBridgeFees.ts index da2e892c..b0575dce 100644 --- a/packages/canonical-bridge-widget/src/modules/transfer/hooks/useLoadingBridgeFees.ts +++ b/packages/canonical-bridge-widget/src/modules/transfer/hooks/useLoadingBridgeFees.ts @@ -90,7 +90,7 @@ export const useLoadingBridgeFees = () => { fromAccount: address || DEFAULT_ADDRESS, toChainId: toChain?.id, sendValue: amount, - fromTokenSymbol: selectedToken.symbol, + fromTokenSymbol: selectedToken.symbol === 'ETH' ? 'WETH' : selectedToken.symbol, publicClient, endPointId: { layerZeroV1: toToken?.layerZero?.raw?.endpointID, From 778fb755044f316080abf5e7dcb7276b36e73bb5 Mon Sep 17 00:00:00 2001 From: wenty22 Date: Wed, 13 Nov 2024 09:39:44 +0800 Subject: [PATCH 4/7] refactor: Refactor sdk --- .vscode/settings.json | 12 +- .../core/components/ThemeProvider/index.tsx | 3 + common/config/rush/pnpm-lock.yaml | 2 +- packages/canonical-bridge-sdk/CHANGELOG.md | 73 -- packages/canonical-bridge-sdk/package.json | 2 +- .../src/{core => }/abi/erc20Token.ts | 0 .../canonical-bridge-sdk/src/abi/exports.ts | 1 + .../src/adapters/base/exports.ts | 2 + .../src/adapters/base/index.ts | 334 ++++++ .../src/adapters/base/types.ts | 37 + .../cBridge}/abi/originalTokenVault.ts | 0 .../cBridge}/abi/originalTokenVaultV2.ts | 0 .../cBridge}/abi/peggedTokenBridge.ts | 0 .../cBridge}/abi/peggedTokenBridgeV2.ts | 0 .../cBridge}/abi/poolTransferBridge.ts | 0 .../src/adapters/cBridge/exports.ts | 8 + .../src/adapters/cBridge/index.ts | 736 ++++++++++++ .../index.ts => adapters/cBridge/types.ts} | 127 +- .../src/adapters/deBridge/exports.ts | 2 + .../src/adapters/deBridge/index.ts | 324 +++++ .../index.ts => adapters/deBridge/types.ts} | 82 +- .../layerZero/abi/cakeProxyOFT.ts | 0 .../src/adapters/layerZero/exports.ts | 4 + .../src/adapters/layerZero/index.ts | 278 +++++ .../index.ts => adapters/layerZero/types.ts} | 13 +- .../src/adapters/meson/exports.ts | 2 + .../src/adapters/meson/index.ts} | 156 ++- .../src/adapters/meson/types.ts | 45 + .../stargate/abi/stargatePool.ts | 0 .../src/adapters/stargate/exports.ts | 4 + .../src/adapters/stargate/index.ts | 320 +++++ .../index.ts => adapters/stargate/types.ts} | 22 +- .../src/aggregator/exports.ts | 2 + .../src/{core => aggregator}/index.ts | 317 +++-- .../src/aggregator/types.ts | 129 ++ .../canonical-bridge-sdk/src/cbridge/index.ts | 393 ------- .../src/cbridge/utils/createAdapter.ts | 361 ------ .../src/{core => }/constants/index.ts | 7 + .../src/core/types/index.ts | 84 -- .../src/core/utils/address.ts | 11 - .../src/core/utils/createBridgeAdapter.ts | 7 - .../src/debridge/index.ts | 184 --- .../src/debridge/utils/createAdapter.ts | 222 ---- packages/canonical-bridge-sdk/src/index.ts | 22 +- .../src/layerZero/index.ts | 141 --- .../src/layerZero/utils/createAdapter.ts | 213 ---- .../canonical-bridge-sdk/src/meson/index.ts | 101 -- .../canonical-bridge-sdk/src/meson/types.ts | 21 - .../src/meson/types/index.ts | 6 - .../src/shared/address.ts | 25 + .../src/{core/utils => shared}/assert.ts | 0 .../src/shared/exports.ts | 1 + .../src/{core/utils => shared}/number.ts | 0 .../src/stargate/index.ts | 193 --- .../src/stargate/utils/createAdapter.ts | 213 ---- .../src/CanonicalBridgeProvider.tsx | 3 +- .../src/core/hooks/useBridgeSDK.ts | 49 +- .../adapters/cBridge/CBridgeAdapter.ts | 337 ------ .../hooks/useCBridgeTransferWaitingTime.ts | 6 +- .../aggregator/adapters/cBridge/types.ts | 117 +- .../adapters/deBridge/DeBridgeAdapter.ts | 148 --- .../deBridge/hooks/useGetDeBridgeFees.ts | 5 +- .../aggregator/adapters/deBridge/types.ts | 21 - .../adapters/layerZero/LayerZeroAdapter.ts | 139 --- .../adapters/layerZero/abi/cakeProxyOFT.ts | 1040 ----------------- .../layerZero/hooks/useGetLayerZeroFees.ts | 2 +- .../aggregator/adapters/layerZero/types.ts | 21 - .../aggregator/adapters/meson/types.ts | 15 - .../adapters/stargate/StargateAdapter.ts | 139 --- .../stargate/hooks/useGetStarGateFees.ts | 2 +- .../stargate/hooks/useStargateTransfer.ts | 4 +- .../stargate/hooks/useStargateWaitTime.ts | 2 +- .../aggregator/adapters/stargate/types.ts | 21 - .../components/AggregatorProvider.tsx | 196 ++-- .../SelectModal/ChooseTokenModal.tsx | 5 +- .../SelectModal/DestinationNetworkModal.tsx | 5 +- .../SelectModal/SourceNetworkModal.tsx | 7 +- .../SelectModal/hooks/useTokenList.ts | 10 +- .../modules/aggregator/hooks/useAdapter.ts | 7 +- .../hooks/useDefaultSelectedInfo.ts | 6 +- .../modules/aggregator/hooks/useFromChains.ts | 14 +- .../modules/aggregator/hooks/useSelection.ts | 28 +- .../modules/aggregator/hooks/useToChains.ts | 17 +- .../aggregator/hooks/useTokenBalance.ts | 2 +- .../modules/aggregator/hooks/useTokenPrice.ts | 2 +- .../src/modules/aggregator/hooks/useTokens.ts | 25 +- .../modules/aggregator/shared/BaseAdapter.ts | 417 ------- .../aggregator/shared/aggregateChains.ts | 156 --- .../aggregator/shared/aggregateToToken.ts | 62 - .../aggregator/shared/aggregateTokens.ts | 79 -- .../aggregator/shared/getNativeCurrencies.ts | 13 - .../aggregator/shared/getTokenBalances.ts | 5 +- .../shared/isChainOrTokenCompatible.ts | 5 - .../modules/aggregator/shared/sortTokens.ts | 13 +- .../src/modules/aggregator/types.ts | 147 +-- .../components/Button/TransferButton.tsx | 12 +- .../SelectButton/TokenSelectButton.tsx | 2 +- .../components/SelectButton/index.tsx | 2 +- .../TransferOverview/RouteInfo/RouteTitle.tsx | 2 +- .../transfer/hooks/useLoadingBridgeFees.ts | 16 +- .../src/modules/transfer/reducer.ts | 3 +- .../modules/wallet/CurrentWalletProvider.tsx | 2 +- .../src/modules/wallet/WalletProvider.tsx | 2 +- 103 files changed, 3000 insertions(+), 5575 deletions(-) delete mode 100644 packages/canonical-bridge-sdk/CHANGELOG.md rename packages/canonical-bridge-sdk/src/{core => }/abi/erc20Token.ts (100%) create mode 100644 packages/canonical-bridge-sdk/src/abi/exports.ts create mode 100644 packages/canonical-bridge-sdk/src/adapters/base/exports.ts create mode 100644 packages/canonical-bridge-sdk/src/adapters/base/index.ts create mode 100644 packages/canonical-bridge-sdk/src/adapters/base/types.ts rename packages/canonical-bridge-sdk/src/{cbridge => adapters/cBridge}/abi/originalTokenVault.ts (100%) rename packages/canonical-bridge-sdk/src/{cbridge => adapters/cBridge}/abi/originalTokenVaultV2.ts (100%) rename packages/canonical-bridge-sdk/src/{cbridge => adapters/cBridge}/abi/peggedTokenBridge.ts (100%) rename packages/canonical-bridge-sdk/src/{cbridge => adapters/cBridge}/abi/peggedTokenBridgeV2.ts (100%) rename packages/canonical-bridge-sdk/src/{cbridge => adapters/cBridge}/abi/poolTransferBridge.ts (100%) create mode 100644 packages/canonical-bridge-sdk/src/adapters/cBridge/exports.ts create mode 100644 packages/canonical-bridge-sdk/src/adapters/cBridge/index.ts rename packages/canonical-bridge-sdk/src/{cbridge/types/index.ts => adapters/cBridge/types.ts} (68%) create mode 100644 packages/canonical-bridge-sdk/src/adapters/deBridge/exports.ts create mode 100644 packages/canonical-bridge-sdk/src/adapters/deBridge/index.ts rename packages/canonical-bridge-sdk/src/{debridge/types/index.ts => adapters/deBridge/types.ts} (51%) rename packages/canonical-bridge-sdk/src/{ => adapters}/layerZero/abi/cakeProxyOFT.ts (100%) create mode 100644 packages/canonical-bridge-sdk/src/adapters/layerZero/exports.ts create mode 100644 packages/canonical-bridge-sdk/src/adapters/layerZero/index.ts rename packages/canonical-bridge-sdk/src/{layerZero/types/index.ts => adapters/layerZero/types.ts} (70%) create mode 100644 packages/canonical-bridge-sdk/src/adapters/meson/exports.ts rename packages/{canonical-bridge-widget/src/modules/aggregator/adapters/meson/MesonAdapter.ts => canonical-bridge-sdk/src/adapters/meson/index.ts} (50%) create mode 100644 packages/canonical-bridge-sdk/src/adapters/meson/types.ts rename packages/canonical-bridge-sdk/src/{ => adapters}/stargate/abi/stargatePool.ts (100%) create mode 100644 packages/canonical-bridge-sdk/src/adapters/stargate/exports.ts create mode 100644 packages/canonical-bridge-sdk/src/adapters/stargate/index.ts rename packages/canonical-bridge-sdk/src/{stargate/types/index.ts => adapters/stargate/types.ts} (70%) create mode 100644 packages/canonical-bridge-sdk/src/aggregator/exports.ts rename packages/canonical-bridge-sdk/src/{core => aggregator}/index.ts (55%) create mode 100644 packages/canonical-bridge-sdk/src/aggregator/types.ts delete mode 100644 packages/canonical-bridge-sdk/src/cbridge/index.ts delete mode 100644 packages/canonical-bridge-sdk/src/cbridge/utils/createAdapter.ts rename packages/canonical-bridge-sdk/src/{core => }/constants/index.ts (55%) delete mode 100644 packages/canonical-bridge-sdk/src/core/types/index.ts delete mode 100644 packages/canonical-bridge-sdk/src/core/utils/address.ts delete mode 100644 packages/canonical-bridge-sdk/src/core/utils/createBridgeAdapter.ts delete mode 100644 packages/canonical-bridge-sdk/src/debridge/index.ts delete mode 100644 packages/canonical-bridge-sdk/src/debridge/utils/createAdapter.ts delete mode 100644 packages/canonical-bridge-sdk/src/layerZero/index.ts delete mode 100644 packages/canonical-bridge-sdk/src/layerZero/utils/createAdapter.ts delete mode 100644 packages/canonical-bridge-sdk/src/meson/index.ts delete mode 100644 packages/canonical-bridge-sdk/src/meson/types.ts delete mode 100644 packages/canonical-bridge-sdk/src/meson/types/index.ts create mode 100644 packages/canonical-bridge-sdk/src/shared/address.ts rename packages/canonical-bridge-sdk/src/{core/utils => shared}/assert.ts (100%) create mode 100644 packages/canonical-bridge-sdk/src/shared/exports.ts rename packages/canonical-bridge-sdk/src/{core/utils => shared}/number.ts (100%) delete mode 100644 packages/canonical-bridge-sdk/src/stargate/index.ts delete mode 100644 packages/canonical-bridge-sdk/src/stargate/utils/createAdapter.ts delete mode 100644 packages/canonical-bridge-widget/src/modules/aggregator/adapters/cBridge/CBridgeAdapter.ts delete mode 100644 packages/canonical-bridge-widget/src/modules/aggregator/adapters/deBridge/DeBridgeAdapter.ts delete mode 100644 packages/canonical-bridge-widget/src/modules/aggregator/adapters/layerZero/LayerZeroAdapter.ts delete mode 100644 packages/canonical-bridge-widget/src/modules/aggregator/adapters/layerZero/abi/cakeProxyOFT.ts delete mode 100644 packages/canonical-bridge-widget/src/modules/aggregator/adapters/stargate/StargateAdapter.ts delete mode 100644 packages/canonical-bridge-widget/src/modules/aggregator/shared/BaseAdapter.ts delete mode 100644 packages/canonical-bridge-widget/src/modules/aggregator/shared/aggregateChains.ts delete mode 100644 packages/canonical-bridge-widget/src/modules/aggregator/shared/aggregateToToken.ts delete mode 100644 packages/canonical-bridge-widget/src/modules/aggregator/shared/aggregateTokens.ts delete mode 100644 packages/canonical-bridge-widget/src/modules/aggregator/shared/getNativeCurrencies.ts delete mode 100644 packages/canonical-bridge-widget/src/modules/aggregator/shared/isChainOrTokenCompatible.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index 3d368fc1..f7bc85c4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,17 +3,17 @@ "*.json": "jsonc" }, "files.exclude": { - // "**/.git": true, + "**/.git": true, "**/.svn": true, "**/.hg": true, "**/CVS": true, "**/.DS_Store": true, - // "**/node_modules": true, - // "**/.next": true, + "**/node_modules": true, + "**/.next": true, "**/*.log": true, - // "**/dist": true, - // "**/.rush": true, - // "**/temp": true, + "**/dist": true, + "**/.rush": true, + "**/temp": true, "**/tsconfig.tsbuildinfo": true }, "[typescript]": { diff --git a/apps/canonical-bridge-ui/core/components/ThemeProvider/index.tsx b/apps/canonical-bridge-ui/core/components/ThemeProvider/index.tsx index 5e900bfb..deb245cb 100644 --- a/apps/canonical-bridge-ui/core/components/ThemeProvider/index.tsx +++ b/apps/canonical-bridge-ui/core/components/ThemeProvider/index.tsx @@ -24,6 +24,9 @@ export const ThemeProvider = ({ children }: ThemeProviderProps) => { global: ({ colorMode }: { colorMode: ColorMode }) => ({ body: { bg: theme.colors[colorMode].background[3], + '.transfer-widget': { + bg: 'white', + }, }, }), }, diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 36c8994c..52fc79bc 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -7516,7 +7516,7 @@ packages: resolution: {integrity: sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==} engines: {node: '>= 4.0'} os: [darwin] - deprecated: The v1 package contains DANGEROUS / INSECURE binaries. Upgrade to safe fsevents v2 + deprecated: Upgrade to fsevents v2 to mitigate potential security issues fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} diff --git a/packages/canonical-bridge-sdk/CHANGELOG.md b/packages/canonical-bridge-sdk/CHANGELOG.md deleted file mode 100644 index 673d2031..00000000 --- a/packages/canonical-bridge-sdk/CHANGELOG.md +++ /dev/null @@ -1,73 +0,0 @@ -# @bnb-chain/canonical-bridge-sdk - -## 0.3.3 - -### Patch Changes - -- 96adcfc: Update cicd - -## 0.3.2 - -### Patch Changes - -- c39d53e: Test cicd - -## 0.3.1 - -### Patch Changes - -- bdf228d: Test cicd - -## 0.3.0 - -### Minor Changes - -- f0ce0fd: Support tron - -## 0.2.0 - -### Minor Changes - -- 29d5a31: Support tron - -## 0.2.0-alpha.4 - -### Patch Changes - -- Only show supported networks in status popup - -## 0.2.0-alpha.3 - -### Patch Changes - -- Only show supported networks in status popup - -## 0.2.0-alpha.2 - -### Patch Changes - -- 8218b07: Add tips for switching network in tronLink - -## 0.2.0-alpha.1 - -### Patch Changes - -- Support switch network for tron & fix issues - -## 0.2.0-alpha.0 - -### Minor Changes - -- Support tron - -## 0.1.2 - -### Patch Changes - -- 982fc93: Clean up the code and add the README - -## 0.1.1 - -### Patch Changes - -- 75d930b: Clean up the code and add the README diff --git a/packages/canonical-bridge-sdk/package.json b/packages/canonical-bridge-sdk/package.json index 88805519..3f1c9370 100644 --- a/packages/canonical-bridge-sdk/package.json +++ b/packages/canonical-bridge-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@bnb-chain/canonical-bridge-sdk", - "version": "0.3.3", + "version": "0.3.2", "description": "canonical bridge sdk", "author": "bnb-chain", "private": false, diff --git a/packages/canonical-bridge-sdk/src/core/abi/erc20Token.ts b/packages/canonical-bridge-sdk/src/abi/erc20Token.ts similarity index 100% rename from packages/canonical-bridge-sdk/src/core/abi/erc20Token.ts rename to packages/canonical-bridge-sdk/src/abi/erc20Token.ts diff --git a/packages/canonical-bridge-sdk/src/abi/exports.ts b/packages/canonical-bridge-sdk/src/abi/exports.ts new file mode 100644 index 00000000..8be9656e --- /dev/null +++ b/packages/canonical-bridge-sdk/src/abi/exports.ts @@ -0,0 +1 @@ +export * from './erc20Token'; diff --git a/packages/canonical-bridge-sdk/src/adapters/base/exports.ts b/packages/canonical-bridge-sdk/src/adapters/base/exports.ts new file mode 100644 index 00000000..48bf6383 --- /dev/null +++ b/packages/canonical-bridge-sdk/src/adapters/base/exports.ts @@ -0,0 +1,2 @@ +export * from './index'; +export * from './types'; diff --git a/packages/canonical-bridge-sdk/src/adapters/base/index.ts b/packages/canonical-bridge-sdk/src/adapters/base/index.ts new file mode 100644 index 00000000..9d168ccf --- /dev/null +++ b/packages/canonical-bridge-sdk/src/adapters/base/index.ts @@ -0,0 +1,334 @@ +import { + IBaseAdapterOptions, + IBridgeTokenBaseInfo, + IInitialOptions, + ITransferTokenPair, +} from '@/adapters/base/types'; +import { + BridgeType, + ChainType, + IChainConfig, + IExternalChain, + INativeCurrency, +} from '@/aggregator/types'; +import { isSameAddress } from '@/shared/address'; + +export abstract class BaseAdapter { + public abstract bridgeType: BridgeType; + + protected config: G; + protected excludedChains: number[] = []; + protected excludedTokens: Record> = {}; + protected bridgedTokenGroups: string[][] = []; + + protected assetPrefix: string = ''; + protected includedChains: number[] = []; + protected nativeCurrencies: Record = {}; + protected brandChains: number[] = []; + protected externalChains: IExternalChain[] = []; + protected displayTokenSymbols: Record> = {}; + + protected chains: C[] = []; + protected chainMap = new Map(); + protected tokenMap = new Map(); + protected symbolMap = new Map>(); + protected transferMap = new Map< + number, + Map>> + >(); + + constructor(options: IBaseAdapterOptions) { + this.config = options.config ?? {}; + this.excludedChains = options.excludedChains ?? []; + this.excludedTokens = options.excludedTokens ?? {}; + this.bridgedTokenGroups = options.bridgedTokenGroups ?? []; + } + + public init(initialOptions?: IInitialOptions) { + this.initOptions(initialOptions); + this.initChains(); + this.initTokens(); + this.initTransferMap(); + this.filterTransferMap(); + } + + protected initOptions(initialOptions?: IInitialOptions) { + this.assetPrefix = initialOptions?.assetPrefix ?? ''; + this.includedChains = initialOptions?.includedChains ?? []; + this.nativeCurrencies = initialOptions?.nativeCurrencies ?? {}; + this.brandChains = initialOptions?.brandChains ?? []; + this.externalChains = initialOptions?.externalChains ?? []; + this.displayTokenSymbols = initialOptions?.displayTokenSymbols ?? {}; + } + + protected abstract initChains(): void; + protected abstract initTokens(): void; + protected abstract initTransferMap(): void; + + protected filterTransferMap() { + if (!this.brandChains.length) { + return; + } + + const filteredTransferMap = new Map< + number, + Map>> + >(); + + this.transferMap.forEach((toMap, fromChainId) => { + if (this.brandChains.includes(fromChainId)) { + filteredTransferMap.set(fromChainId, toMap); + } else { + toMap.forEach((tokenPairMap, toChainId) => { + if (this.brandChains.includes(toChainId)) { + if (!filteredTransferMap.has(fromChainId)) { + filteredTransferMap.set( + fromChainId, + new Map>>() + ); + } + filteredTransferMap.get(fromChainId)?.set(toChainId, tokenPairMap); + } + }); + } + }); + + this.transferMap = filteredTransferMap; + } + + public abstract getChainId(chain: C): number; + + public abstract getTokenInfo({ + chainId, + token, + }: { + chainId: number; + token: T; + }): IBridgeTokenBaseInfo; + + public getChainInfo({ + chainId, + chainConfig, + }: { + chainId: number; + chainConfig: IChainConfig; + }) { + const explorerUrl = chainConfig?.explorer.url?.replace(/\/$/, '') ?? ''; + const tmpUrlPattern = explorerUrl ? `${explorerUrl}/token/{0}` : ''; + const tokenUrlPattern = + chainConfig?.explorer?.tokenUrlPattern || tmpUrlPattern; + + const externalConfig = this.externalChains?.find( + (item) => item.chainId === chainId + ); + + const chainType: ChainType = externalConfig + ? 'link' + : chainConfig?.chainType ?? 'evm'; + const externalBridgeUrl = externalConfig?.bridgeUrl; + + return { + id: chainId, + name: chainConfig?.name ?? '', + icon: `${this.assetPrefix}/images/chains/${chainId}.png`, + explorerUrl, + rpcUrl: chainConfig?.rpcUrl ?? '', + tokenUrlPattern, + chainType, + externalBridgeUrl, + }; + } + + protected checkIsExcludedToken({ + excludedList, + tokenSymbol, + tokenAddress, + }: { + excludedList: string[]; + tokenSymbol: string; + tokenAddress: string; + }) { + return excludedList?.some( + (e) => + e.toUpperCase() === tokenSymbol.toUpperCase() || + e.toLowerCase() === tokenAddress.toLowerCase() + ); + } + + protected getTokenDisplaySymbolAndIcon({ + defaultSymbol, + chainId, + tokenAddress, + }: { + chainId: number; + defaultSymbol: string; + tokenAddress: string; + }) { + const symbolMap = this.displayTokenSymbols[chainId] ?? {}; + + const target = Object.entries(symbolMap).find(([address]) => + isSameAddress(address, tokenAddress) + ); + + const displaySymbol = target?.[1] ?? defaultSymbol; + const iconSymbol = displaySymbol.replace(/[+]$/, '_ICON')?.toUpperCase(); + const icon = `${this.assetPrefix}/images/tokens/${iconSymbol}.png`; + + return { + displaySymbol, + icon, + }; + } + + // 1. Native currency is ETH -> Native currency is ETH, all transfer to ETH + // 2. Native currency is ETH -> Native currency is NOT ETH, transfer to ETH first, if not, WETH + // 3. Native currency is NOT ETH -> Native currency is ETH, all transfer to ETH + protected getToToken({ + fromChainId, + toChainId, + fromTokenSymbol, + }: { + fromChainId: number; + toChainId: number; + fromTokenSymbol: string; + }) { + const fromNativeSymbol = + this.nativeCurrencies[fromChainId]?.symbol?.toUpperCase(); + const toNativeSymbol = + this.nativeCurrencies[toChainId]?.symbol?.toUpperCase(); + const tokenMap = this.symbolMap.get(toChainId); + + if (['ETH', 'WETH'].includes(fromTokenSymbol)) { + if (fromNativeSymbol === 'ETH') { + if (toNativeSymbol === 'ETH') { + return tokenMap?.get(fromTokenSymbol); + } else { + return tokenMap?.get('ETH') || tokenMap?.get('WETH'); + } + } else { + if (toNativeSymbol === 'ETH') { + return tokenMap?.get('ETH'); + } + } + } + + let toToken = tokenMap?.get(fromTokenSymbol); + if (!toToken) { + const bridgedGroup = this.bridgedTokenGroups.find((group) => + group.includes(fromTokenSymbol) + ); + const nextToken = bridgedGroup?.find( + (item) => item.toUpperCase() !== fromTokenSymbol + ); + if (nextToken) { + toToken = tokenMap?.get(nextToken?.toUpperCase()); + } + } + + return toToken; + } + + public getRealTokenSymbol({ + fromChainId, + toChainId, + tokenSymbol, + }: { + fromChainId: number; + toChainId: number; + tokenSymbol: string; + }) { + return tokenSymbol; + } + + public getChain({ chainId }: { chainId: number }) { + return this.chainMap.get(chainId); + } + + public getTokenPair({ + fromChainId, + toChainId, + tokenSymbol, + }: { + fromChainId: number; + toChainId: number; + tokenSymbol: string; + }) { + const realTokenSymbol = this.getRealTokenSymbol({ + fromChainId, + toChainId, + tokenSymbol, + }); + + const tokenPair = this.transferMap + .get(fromChainId) + ?.get(toChainId) + ?.get(realTokenSymbol); + return { + tokenPair, + }; + } + + public getFromChains() { + const fromChainIds = new Set(this.transferMap.keys()); + const fromChains = this.chains.filter((chain) => + fromChainIds.has(this.getChainId(chain)) + ); + return fromChains; + } + + public getToChains({ fromChainId }: { fromChainId: number }) { + const toChainIds = new Set(this.transferMap.get(fromChainId)?.keys()); + const toChains = this.chains.filter((chain) => + toChainIds.has(this.getChainId(chain)) + ); + return toChains; + } + + public getTokenPairs({ + fromChainId, + toChainId, + }: { + fromChainId: number; + toChainId: number; + }) { + const tokenPairs: ITransferTokenPair[] = []; + + const tokenPairsMap = this.transferMap.get(fromChainId)?.get(toChainId); + tokenPairsMap?.forEach((tokenPair) => { + tokenPairs.push(tokenPair); + }); + + return tokenPairs; + } + + public isToChainCompatible({ + fromChainId, + toChainId, + }: { + fromChainId: number; + toChainId: number; + }) { + return !!this.transferMap.get(fromChainId)?.get(toChainId); + } + + public isTokenCompatible({ + fromChainId, + toChainId, + tokenSymbol, + }: { + fromChainId: number; + toChainId: number; + tokenSymbol: string; + }) { + const realTokenSymbol = this.getRealTokenSymbol({ + fromChainId, + toChainId, + tokenSymbol, + }); + + return !!this.transferMap + .get(fromChainId) + ?.get(toChainId) + ?.get(realTokenSymbol); + } +} diff --git a/packages/canonical-bridge-sdk/src/adapters/base/types.ts b/packages/canonical-bridge-sdk/src/adapters/base/types.ts new file mode 100644 index 00000000..e3d800c9 --- /dev/null +++ b/packages/canonical-bridge-sdk/src/adapters/base/types.ts @@ -0,0 +1,37 @@ +import { INativeCurrency, IExternalChain } from '@/aggregator/types'; + +export interface ITransferTokenPair { + fromChainId: number; + toChainId: number; + fromTokenAddress: string; + toTokenAddress: string; + fromToken: T; + toToken: T; + isPegged?: boolean; + peggedConfig?: P; +} + +export interface IBaseAdapterOptions { + config: G; + excludedChains?: number[]; + excludedTokens?: Record>; + bridgedTokenGroups?: string[][]; +} + +export interface IInitialOptions { + assetPrefix?: string; + includedChains: number[]; + nativeCurrencies?: Record; + brandChains?: number[]; + externalChains?: IExternalChain[]; + displayTokenSymbols?: Record>; +} + +export interface IBridgeTokenBaseInfo { + name: string; + symbol: string; + address: string; + decimals: number; + displaySymbol: string; + icon: string; +} diff --git a/packages/canonical-bridge-sdk/src/cbridge/abi/originalTokenVault.ts b/packages/canonical-bridge-sdk/src/adapters/cBridge/abi/originalTokenVault.ts similarity index 100% rename from packages/canonical-bridge-sdk/src/cbridge/abi/originalTokenVault.ts rename to packages/canonical-bridge-sdk/src/adapters/cBridge/abi/originalTokenVault.ts diff --git a/packages/canonical-bridge-sdk/src/cbridge/abi/originalTokenVaultV2.ts b/packages/canonical-bridge-sdk/src/adapters/cBridge/abi/originalTokenVaultV2.ts similarity index 100% rename from packages/canonical-bridge-sdk/src/cbridge/abi/originalTokenVaultV2.ts rename to packages/canonical-bridge-sdk/src/adapters/cBridge/abi/originalTokenVaultV2.ts diff --git a/packages/canonical-bridge-sdk/src/cbridge/abi/peggedTokenBridge.ts b/packages/canonical-bridge-sdk/src/adapters/cBridge/abi/peggedTokenBridge.ts similarity index 100% rename from packages/canonical-bridge-sdk/src/cbridge/abi/peggedTokenBridge.ts rename to packages/canonical-bridge-sdk/src/adapters/cBridge/abi/peggedTokenBridge.ts diff --git a/packages/canonical-bridge-sdk/src/cbridge/abi/peggedTokenBridgeV2.ts b/packages/canonical-bridge-sdk/src/adapters/cBridge/abi/peggedTokenBridgeV2.ts similarity index 100% rename from packages/canonical-bridge-sdk/src/cbridge/abi/peggedTokenBridgeV2.ts rename to packages/canonical-bridge-sdk/src/adapters/cBridge/abi/peggedTokenBridgeV2.ts diff --git a/packages/canonical-bridge-sdk/src/cbridge/abi/poolTransferBridge.ts b/packages/canonical-bridge-sdk/src/adapters/cBridge/abi/poolTransferBridge.ts similarity index 100% rename from packages/canonical-bridge-sdk/src/cbridge/abi/poolTransferBridge.ts rename to packages/canonical-bridge-sdk/src/adapters/cBridge/abi/poolTransferBridge.ts diff --git a/packages/canonical-bridge-sdk/src/adapters/cBridge/exports.ts b/packages/canonical-bridge-sdk/src/adapters/cBridge/exports.ts new file mode 100644 index 00000000..9839c0cd --- /dev/null +++ b/packages/canonical-bridge-sdk/src/adapters/cBridge/exports.ts @@ -0,0 +1,8 @@ +export * from './index'; +export * from './types'; + +export * from './abi/originalTokenVault'; +export * from './abi/originalTokenVaultV2'; +export * from './abi/peggedTokenBridge'; +export * from './abi/peggedTokenBridgeV2'; +export * from './abi/poolTransferBridge'; diff --git a/packages/canonical-bridge-sdk/src/adapters/cBridge/index.ts b/packages/canonical-bridge-sdk/src/adapters/cBridge/index.ts new file mode 100644 index 00000000..79a8f187 --- /dev/null +++ b/packages/canonical-bridge-sdk/src/adapters/cBridge/index.ts @@ -0,0 +1,736 @@ +import { getContract, Hash, isAddress } from 'viem'; +import { BaseAdapter } from '@/adapters/base'; +import { IInitialOptions, ITransferTokenPair } from '@/adapters/base/types'; +import { + ICBridgeAdapterOptions, + ICBridgeBurnPairConfig, + ICBridgeChain, + ICBridgeEstimateAmountRequest, + ICBridgeEstimateAmountResponse, + ICBridgePeggedPairConfig, + ICBridgeSendRangeInput, + ICBridgeToken, + ICBridgeTransferConfig, + ICBridgeTransferEstimatedTime, + IGetCBridgeABI, + IGetCBridgeTransferAddressInput, + IGetCBridgeTransferFunction, + IGetCBridgeTransferParamsInput, + ISendCBridgeToken, +} from '@/adapters/cBridge/types'; +import { BridgeType } from '@/aggregator/types'; +import axios, { AxiosInstance } from 'axios'; +import { CLIENT_TIME_OUT, env } from '@/constants'; +import { + POOL_TRANSFER_BRIDGE, + ORIGINAL_TOKEN_VAULT, + ORIGINAL_TOKEN_VAULT_V2, + PEGGED_TOKEN_BRIDGE, + PEGGED_TOKEN_BRIDGE_V2, +} from '@/adapters/cBridge/exports'; + +export class CBridgeAdapter extends BaseAdapter< + ICBridgeTransferConfig, + ICBridgeChain, + ICBridgeToken +> { + private client: AxiosInstance; + public bridgeType: BridgeType = 'cBridge'; + + private peggedPairConfigs: ICBridgePeggedPairConfig[] = []; + private burnPairConfigs: ICBridgeBurnPairConfig[] = []; + + constructor(options: ICBridgeAdapterOptions) { + const { + timeout = CLIENT_TIME_OUT, + endpoint = env.CBRIDGE_ENDPOINT, + ...baseOptions + } = options; + + super(baseOptions); + + this.client = axios.create({ + timeout, + baseURL: endpoint, + }); + } + + // https://cbridge-docs.celer.network/developer/api-reference/gateway-estimateamt + async getEstimatedAmount(params: ICBridgeEstimateAmountRequest) { + return ( + await this.client.get(`v2/estimateAmt`, { + params, + }) + ).data; + } + + /** + * Get estimated waiting time for cross-chain transfer + * + * @param number srcChainId source chain ID + * @param number dstChainId destination chain ID + */ + async getEstimatedWaitingTime({ + srcChainId, + dstChainId, + }: { + srcChainId: number; + dstChainId: number; + }) { + const params = { + src_chain_id: srcChainId, + dst_chain_id: dstChainId, + }; + return ( + await this.client.get( + `v2/getLatest7DayTransferLatencyForQuery`, + { + params, + } + ) + ).data; + } + + /** + * Get minimum and maximum token transfer + * Only get minimum and maximum send amount + * @param {Address} bridgeAddress - Bridge address + * @param {Address} tokenAddress - Token address + * @param {PublicClient} client - Public client + * @returns {Object} min and max amount + */ + async getSendRange({ + bridgeAddress, + tokenAddress, + client, + }: ICBridgeSendRangeInput): Promise<{ min: bigint; max: bigint }> { + const contract = getContract({ + address: bridgeAddress, + abi: POOL_TRANSFER_BRIDGE, + client: client, + }); + try { + const minAmount = await contract.read.minSend([tokenAddress]); + const maxAmount = await contract.read.maxSend([tokenAddress]); + return { + min: minAmount, + max: maxAmount, + }; + } catch (error: any) { + throw new Error( + `Failed to get cBridge minimum and maximum transfer amount: ${error}` + ); + } + } + + /** + * Send token through CBridge + * @param {WalletClient} walletClient Wallet client + * @param {PublicClient} publicClient Wallet client + * @param {Address} bridgeAddress Bridge address + * @param {Object[]} bridgeABI Bridge ABI + * @param {String} functionName Function name + * @param {Address} address wallet/account address + * @param {Object[]} args Function arguments + * @returns + */ + async sendToken({ + walletClient, + publicClient, + bridgeAddress, + fromChainId, + address, + isPegged, + isNativeToken, + peggedConfig, + args, + }: ISendCBridgeToken): Promise { + try { + const transferType = this.getTransferType({ + peggedConfig, + fromChainId, + }); + const ABI = this.getABI({ + isPegged, + transferType, + peggedConfig, + }); + const functionName = this.getTransferFunction({ + isPegged, + isNativeToken, + transferType, + }); + let cBridgeArgs = null; + if (isNativeToken) { + cBridgeArgs = { + address: bridgeAddress, + abi: ABI, + functionName, + account: address, + args, + value: args[1], + }; + } else { + cBridgeArgs = { + address: bridgeAddress, + abi: ABI, + functionName, + account: address, + args, + }; + } + const gas = await publicClient.estimateContractGas(cBridgeArgs as any); + const gasPrice = await publicClient.getGasPrice(); + const hash = await walletClient.writeContract({ + ...(cBridgeArgs as any), + gas, + gasPrice, + }); + return hash; + } catch (error) { + throw new Error(`Failed to send CBridge token: ${error}`); + } + } + + /** + * Get cBridge contract address from cross chain transfer + * + * @param fromChainId Chain ID of the source chain + * @param isPegged Pool-based transfer(xLiquidity) - false + * Canonical Mapping Transfer(xAsset) - true + * @param peggedConfig Pegged pair configuration + * @param chainConfig Chain configuration + */ + getTransferAddress({ + fromChainId, + isPegged, + peggedConfig, + chainConfig, + }: IGetCBridgeTransferAddressInput) { + if (isPegged) { + if (peggedConfig?.org_chain_id === fromChainId) { + // cBridge deposit + return peggedConfig.pegged_deposit_contract_addr; + } else if (peggedConfig?.pegged_chain_id === fromChainId) { + // cBridge burn + return peggedConfig.pegged_burn_contract_addr; + } + } else { + if (chainConfig?.contract_addr) { + return chainConfig?.contract_addr; + } + } + throw new Error('No cBridge bridge address found'); + } + + /** + * Get cBridge transfer parameters + * + * @param amount Send amount + * @param isPegged Pool-based transfer(xLiquidity) - false + * Canonical Mapping Transfer(xAsset) - true + * @param toChainId Chain ID of the destination chain + * @param tokenAddress Address of ERC20 token + * @param address User address + * @param maxSlippage Maximum slippage + * @param transferType Transfer type - deposit | withdraw + * @param peggedConfig Pegged pair configuration + * @param nonce Nonce current timestamp + */ + getTransferParams({ + amount, + isPegged, + isNativeToken = false, + toChainId, + tokenAddress, + address, + maxSlippage, + transferType, + peggedConfig, + nonce, + }: IGetCBridgeTransferParamsInput) { + return isPegged === false + ? isNativeToken + ? [address, amount, toChainId, nonce, maxSlippage] + : [address, tokenAddress, amount, toChainId, nonce, maxSlippage] + : transferType === 'deposit' + ? [tokenAddress, amount, toChainId, address as `0x${string}`, nonce] + : transferType === 'withdraw' + ? peggedConfig?.bridge_version === 0 + ? [tokenAddress, amount, address as `0x${string}`, nonce] + : [tokenAddress, amount, toChainId, address as `0x${string}`, nonce] + : null; + } + + /** + * Get cross chain transfer ABI + * + * @param isPegged Pool-based transfer(xLiquidity) - false + * Canonical Mapping Transfer(xAsset) - true + * @param transferType Transfer type - deposit | withdraw + * @param peggedConfig Pegged pair configuration + */ + getABI({ isPegged, transferType, peggedConfig }: IGetCBridgeABI) { + return isPegged === false || !peggedConfig + ? POOL_TRANSFER_BRIDGE + : transferType === 'deposit' + ? peggedConfig?.vault_version === 0 + ? ORIGINAL_TOKEN_VAULT + : ORIGINAL_TOKEN_VAULT_V2 + : transferType === 'withdraw' + ? peggedConfig?.bridge_version === 0 + ? PEGGED_TOKEN_BRIDGE + : PEGGED_TOKEN_BRIDGE_V2 + : (undefined as any); + } + + /** + * Get cross chain transfer function name + * + * @param isPegged + * @returns string + */ + getTransferFunction({ + isPegged, + transferType, + isNativeToken = false, + }: IGetCBridgeTransferFunction) { + return isPegged === false + ? isNativeToken + ? 'sendNative' + : 'send' + : transferType === 'deposit' + ? 'deposit' + : transferType === 'withdraw' + ? 'burn' + : ''; + } + + /** + * Get transfer type + */ + getTransferType({ + peggedConfig, + fromChainId, + }: { + fromChainId: number; + peggedConfig?: ICBridgePeggedPairConfig; + }) { + if (peggedConfig?.org_chain_id === fromChainId) { + return 'deposit'; + } + if (peggedConfig?.pegged_chain_id === fromChainId) { + return 'withdraw'; + } + return undefined; + } + + /** + * Generate cBridge transfer arguments + */ + getArguments({ + isPegged, + peggedConfig, + chainConfig, + amount, + fromChainId, + toChainId, + tokenAddress, + userAddress, + maxSlippage, + nonce, + isNativeToken = false, + }: { + isPegged: boolean; + peggedConfig?: ICBridgePeggedPairConfig; + chainConfig?: ICBridgeChain; + amount: bigint; + fromChainId: number; + toChainId: number; + tokenAddress: `0x${string}`; + userAddress: `0x${string}`; + maxSlippage: number; + nonce: number; + isNativeToken?: boolean; + }) { + const transferType = this.getTransferType({ + peggedConfig, + fromChainId, + }); + const functionName = this.getTransferFunction({ isPegged }); + const bridgeABI = this.getABI({ + isPegged, + transferType: transferType, + peggedConfig, + }); + const bridgeAddress = this.getTransferAddress({ + fromChainId: toChainId, + isPegged, + peggedConfig, + chainConfig, + }); + const args = this.getTransferParams({ + amount, + isPegged, + toChainId, + tokenAddress, + address: userAddress, + maxSlippage, + transferType, + peggedConfig, + nonce, + isNativeToken, + }); + return { + address: bridgeAddress as `0x${string}`, + abi: bridgeABI, + functionName: functionName, + account: userAddress as `0x${string}`, + args: args, + }; + } + + public init(initialOptions?: IInitialOptions) { + this.initOptions(initialOptions); + + this.initChains(); + this.initTokens(); + + this.initPeggedPairConfigs(); + this.initBurnPairConfigs(); + + this.initTransferMap(); + this.filterTransferMap(); + } + + protected initChains() { + const { chains, chain_token, pegged_pair_configs } = this.config; + + const filteredChains = chains.filter((chain) => { + const hasChainConfig = this.includedChains.includes(chain.id); + const isExcludedChain = this.excludedChains.includes(chain.id); + const hasEnabledToken = chain_token[chain.id]?.token?.some( + (e) => !e.token.xfer_disabled + ); + const hasPeggedToken = pegged_pair_configs.some( + (e) => + (e.org_chain_id === chain.id || e.pegged_chain_id === chain.id) && + !e.org_token.token.xfer_disabled && + !e.pegged_token.token.xfer_disabled + ); + return ( + hasChainConfig && + !isExcludedChain && + (hasEnabledToken || hasPeggedToken) + ); + }); + + const chainMap = new Map(); + filteredChains.forEach((chain) => { + chainMap.set(chain.id, chain); + }); + + this.chains = filteredChains; + this.chainMap = chainMap; + } + + protected initTokens() { + const { chain_token, chains } = this.config; + + const tokenMap = new Map(); + const symbolMap = new Map>(); + Object.entries(chain_token).forEach(([id, { token: chainTokens }]) => { + const chainId = Number(id); + const nativeChain = chains.find( + (chain) => Number(chain.id) === Number(id) + ); + + const filteredTokens = chainTokens.filter((token) => { + const isEnabledToken = !token.token.xfer_disabled; + const isExcludedToken = this.checkIsExcludedToken({ + excludedList: this.excludedTokens?.[chainId], + tokenSymbol: token.token.symbol?.toUpperCase(), + tokenAddress: token.token.address, + }); + return isEnabledToken && !isExcludedToken; + }); + + // Add native token info. + if (nativeChain) { + const weth_token = chainTokens.find( + (token) => token.token.symbol === 'WETH' + ); + const nativeTokenObj = { + token: { + symbol: nativeChain?.gas_token_symbol ?? '', + address: '0x0000000000000000000000000000000000000000', + decimal: 18, + xfer_disabled: false, + }, + weth_address: weth_token?.token.address, + name: nativeChain?.gas_token_symbol, + icon: nativeChain?.icon, + inbound_lmt: '', + inbound_epoch_cap: '', + transfer_disabled: false, + liq_add_disabled: false, + liq_rm_disabled: false, + liq_agg_rm_src_disabled: false, + delay_threshold: '', + delay_period: 0, + }; + // The address of WETH (weth_address) is required for retrieving native token min/ max send amount + // https://cbridge-docs.celer.network/developer/cbridge-limit-parameters#id-1.-minsend-maxsend + if ( + nativeChain?.gas_token_symbol === 'ETH' && + isAddress(weth_token?.token?.address ?? '') + ) { + nativeTokenObj.weth_address = weth_token?.token.address; + } + filteredTokens.push(nativeTokenObj); + } + + if (filteredTokens.length > 0 && this.chainMap.has(chainId)) { + symbolMap.set(chainId, new Map()); + + filteredTokens.forEach((token) => { + symbolMap.get(chainId)?.set(token.token.symbol?.toUpperCase(), token); + }); + + tokenMap.set(chainId, filteredTokens); + } + }); + + this.tokenMap = tokenMap; + this.symbolMap = symbolMap; + } + + protected initTransferMap() { + const transferMap = new Map< + number, + Map>> + >(); + + this.chains.forEach((fromChain) => { + this.chains.forEach((toChain) => { + if (fromChain.id !== toChain.id) { + const fromTokens = this.tokenMap.get(fromChain.id) ?? []; + + const transferableTokenMap = new Map< + string, + ITransferTokenPair + >(); + fromTokens.forEach((fromToken) => { + const toToken = this.getToToken({ + fromChainId: fromChain.id, + toChainId: toChain.id, + fromTokenSymbol: fromToken.token.symbol?.toUpperCase(), + }); + if (toToken) { + const tokenPair: ITransferTokenPair = { + fromChainId: fromChain.id, + toChainId: toChain.id, + fromTokenAddress: fromToken.token.address, + toTokenAddress: toToken.token.address, + fromToken, + toToken, + }; + transferableTokenMap.set( + fromToken.token.symbol?.toUpperCase(), + tokenPair + ); + } + }); + + if (transferableTokenMap.size > 0) { + if (!transferMap.has(fromChain.id)) { + transferMap.set( + fromChain.id, + new Map< + number, + Map> + >() + ); + } + transferMap + .get(fromChain.id) + ?.set(toChain.id, transferableTokenMap); + } + } + }); + }); + + const addPeggedTokenPair = ( + fromChainId: number, + fromToken: ICBridgeToken, + toChainId: number, + toToken: ICBridgeToken, + item: ICBridgePeggedPairConfig + ) => { + if ( + !transferMap + .get(fromChainId) + ?.get(toChainId) + ?.get(fromToken.token.symbol?.toUpperCase()) + ) { + if (!transferMap.has(fromChainId)) { + transferMap.set( + fromChainId, + new Map>>() + ); + } + + const peggedTokenPair: ITransferTokenPair = { + fromChainId, + toChainId, + fromTokenAddress: fromToken.token.address, + toTokenAddress: toToken.token.address, + fromToken, + toToken, + isPegged: true, + peggedConfig: item, + }; + + if (transferMap.get(fromChainId)?.get(toChainId)) { + transferMap + .get(fromChainId) + ?.get(toChainId) + ?.set(fromToken.token.symbol?.toUpperCase(), peggedTokenPair); + } else { + const transferableTokenMap = new Map< + string, + ITransferTokenPair + >(); + transferableTokenMap.set( + fromToken.token.symbol?.toUpperCase(), + peggedTokenPair + ); + transferMap.get(fromChainId)?.set(toChainId, transferableTokenMap); + } + } + }; + + this.peggedPairConfigs.forEach((item) => { + const fromChainId = item.org_chain_id; + const fromToken = item.org_token; + + const toChainId = item.pegged_chain_id; + const toToken = item.pegged_token; + + addPeggedTokenPair(fromChainId, fromToken, toChainId, toToken, item); + addPeggedTokenPair(toChainId, toToken, fromChainId, fromToken, item); + }); + + this.transferMap = transferMap; + } + + private initPeggedPairConfigs() { + const peggedPairConfigs = this.config.pegged_pair_configs; + + const isAvailablePair = (chainId: number, token: ICBridgeToken) => { + const hasChain = this.chainMap.has(chainId); + const isEnabledToken = !token.token.xfer_disabled; + + const isExcludedToken = this.checkIsExcludedToken({ + excludedList: this.excludedTokens?.[chainId], + tokenSymbol: token.token.symbol, + tokenAddress: token.token.address, + }); + + return hasChain && isEnabledToken && !isExcludedToken; + }; + + const filteredPeggedPairConfigs = peggedPairConfigs.filter( + (item) => + isAvailablePair(item.org_chain_id, item.org_token) && + isAvailablePair(item.pegged_chain_id, item.pegged_token) + ); + + this.peggedPairConfigs = filteredPeggedPairConfigs; + } + + private initBurnPairConfigs() { + const burnPairConfigs: ICBridgeBurnPairConfig[] = []; + + for (let i = 0; i < this.peggedPairConfigs.length; i++) { + for (let j = i + 1; j < this.peggedPairConfigs.length; j++) { + const A = this.peggedPairConfigs[i]; + const B = this.peggedPairConfigs[j]; + if ( + A.org_chain_id === B.org_chain_id && + A.org_token.token.symbol === B.org_token.token.symbol + ) { + /// Only upgraded PegBridge can support multi burn to other pegged chain + if (A.bridge_version === 2 && B.bridge_version === 2) { + burnPairConfigs.push({ + burn_config_as_org: { + chain_id: A.pegged_chain_id, + token: A.pegged_token, + burn_contract_addr: A.pegged_burn_contract_addr, + canonical_token_contract_addr: A.canonical_token_contract_addr, + burn_contract_version: A.bridge_version, + }, + burn_config_as_dst: { + chain_id: B.pegged_chain_id, + token: B.pegged_token, + burn_contract_addr: B.pegged_burn_contract_addr, + canonical_token_contract_addr: B.canonical_token_contract_addr, + burn_contract_version: B.bridge_version, + }, + }); + burnPairConfigs.push({ + burn_config_as_org: { + chain_id: B.pegged_chain_id, + token: B.pegged_token, + burn_contract_addr: B.pegged_burn_contract_addr, + canonical_token_contract_addr: B.canonical_token_contract_addr, + burn_contract_version: B.bridge_version, + }, + burn_config_as_dst: { + chain_id: A.pegged_chain_id, + token: A.pegged_token, + burn_contract_addr: A.pegged_burn_contract_addr, + canonical_token_contract_addr: A.canonical_token_contract_addr, + burn_contract_version: A.bridge_version, + }, + }); + } + } + } + } + + this.burnPairConfigs = burnPairConfigs; + } + + public getChainId(chain: ICBridgeChain) { + return chain.id; + } + + public getTokenInfo({ + chainId, + token, + }: { + chainId: number; + token: ICBridgeToken; + }) { + return { + name: token.name, + symbol: token.token.symbol, + address: token.token.address, + decimals: token.token.decimal, + ...this.getTokenDisplaySymbolAndIcon({ + chainId, + tokenAddress: token.token.address, + defaultSymbol: token.token.symbol, + }), + }; + } + + public getPeggedPairConfigs() { + return this.burnPairConfigs; + } + + public getBurnPairConfigs() { + return this.peggedPairConfigs; + } +} diff --git a/packages/canonical-bridge-sdk/src/cbridge/types/index.ts b/packages/canonical-bridge-sdk/src/adapters/cBridge/types.ts similarity index 68% rename from packages/canonical-bridge-sdk/src/cbridge/types/index.ts rename to packages/canonical-bridge-sdk/src/adapters/cBridge/types.ts index 2dab3141..ed47b046 100644 --- a/packages/canonical-bridge-sdk/src/cbridge/types/index.ts +++ b/packages/canonical-bridge-sdk/src/adapters/cBridge/types.ts @@ -1,6 +1,13 @@ +import { IBaseAdapterOptions } from '@/adapters/base/types'; import { type PublicClient, type WalletClient } from 'viem'; -export interface CBridgeChain { +export interface ICBridgeAdapterOptions + extends IBaseAdapterOptions { + timeout?: number; + endpoint?: string; +} + +export interface ICBridgeChain { id: number; name: string; icon: string; @@ -18,7 +25,7 @@ export interface CBridgeChain { disabled: boolean; } -export interface CBridgeToken { +export interface ICBridgeToken { token: { symbol: string; address: string; @@ -38,13 +45,14 @@ export interface CBridgeToken { delay_period: number; method?: string; bridgeAddress?: string; //bridge address for transfer + weth_address?: string; } -export interface CBridgePeggedPairConfig { +export interface ICBridgePeggedPairConfig { org_chain_id: number; - org_token: CBridgeToken; + org_token: ICBridgeToken; pegged_chain_id: number; - pegged_token: CBridgeToken; + pegged_token: ICBridgeToken; pegged_deposit_contract_addr: string; pegged_burn_contract_addr: string; canonical_token_contract_addr: string; @@ -53,15 +61,34 @@ export interface CBridgePeggedPairConfig { migration_peg_burn_contract_addr: string; } -export interface CBridgeTransferConfigs { - chains: CBridgeChain[]; +export interface ICBridgeBurnConfig { + chain_id: number; + token: ICBridgeToken; + burn_contract_addr: string; + canonical_token_contract_addr: string; + burn_contract_version: number; +} + +/// burn_config_as_org.bridge_version === 2 +/// burn_config_as_dst.bridge_version is not required +/// If the bridge_version of burnConfig1 and burnConfig2 are 2, +/// There should be two MultiBurnPairConfigs +/// 1: burnConfig1 ----> burnConfig2 +/// 2: burnConfig2 ----> burnConfig1 +export interface ICBridgeBurnPairConfig { + burn_config_as_org: ICBridgeBurnConfig; /// Could be used only as from chain + burn_config_as_dst: ICBridgeBurnConfig; /// Could be used only as to chain +} + +export interface ICBridgeTransferConfig { + chains: ICBridgeChain[]; chain_token: { [k: number]: { - token: CBridgeToken[]; + token: ICBridgeToken[]; }; }; farming_reward_contract_addr: string; - pegged_pair_configs: CBridgePeggedPairConfig[]; + pegged_pair_configs: ICBridgePeggedPairConfig[]; blocked_bridge_direct: { symbol: string; src_chain_id: string; @@ -74,29 +101,16 @@ export interface CBridgeTransferConfigs { }[]; } -export interface CBridgeTransferStatusResponse { - err: object; - status: number; - wd_onchain: null; - sorted_sigs: string[]; - signers: string[]; - powers: string[]; - refund_reason: number; - block_delay: number; - src_block_tx_link: string; - dst_block_tx_link: string; -} - -export interface CBridgeTransferInfo { +export interface ICBridgeTransferInfo { chain: any; token: any; amount: string; } -export interface CBridgeTransferHistory { +export interface ICBridgeTransferHistory { transfer_id: string; - src_send_info: CBridgeTransferInfo; - dst_received_info: CBridgeTransferInfo; + src_send_info: ICBridgeTransferInfo; + dst_received_info: ICBridgeTransferInfo; ts: number; src_block_tx_link: string; dst_block_tx_link: string; @@ -104,19 +118,19 @@ export interface CBridgeTransferHistory { refund_reason: string; } -export interface CBridgeTransferHistoryResponse { +export interface ICBridgeTransferHistoryResponse { err: object; - history: CBridgeTransferHistory[]; + history: ICBridgeTransferHistory[]; next_page_token: string; current_size: string; } -export interface CBridgeTransferEstimatedTime { +export interface ICBridgeTransferEstimatedTime { err: object; median_transfer_latency_in_second: number; } -export type CBridgeTransactionResponse = { +export interface ICBridgeTransactionResponse { data: null | { gasFee: bigint; gasPrice: bigint; @@ -126,9 +140,9 @@ export type CBridgeTransactionResponse = { isLoading: boolean; isError: boolean; error: null | unknown; -}; +} -export type CBridgeEstimateAmountRequest = { +export interface ICBridgeEstimateAmountRequest { src_chain_id: number; dst_chain_id: number; token_symbol: string; @@ -136,9 +150,9 @@ export type CBridgeEstimateAmountRequest = { user_addr?: string; slippage_tolerance: number; is_pegged?: boolean; -}; +} -export interface CBridgeEstimateAmountResponse { +export interface ICBridgeEstimateAmountResponse { err: object; eq_value_token_amt: string; bridge_rate: number; @@ -153,35 +167,7 @@ export interface CBridgeEstimateAmountResponse { op_fee_rebate_end_time: string; } -export interface CBridgeBurnConfig { - chain_id: number; - token: CBridgeToken; - burn_contract_addr: string; - canonical_token_contract_addr: string; - burn_contract_version: number; -} - -/// burn_config_as_org.bridge_version === 2 -/// burn_config_as_dst.bridge_version is not required -/// If the bridge_version of burnConfig1 and burnConfig2 are 2, -/// There should be two MultiBurnPairConfigs -/// 1: burnConfig1 ----> burnConfig2 -/// 2: burnConfig2 ----> burnConfig1 -export interface CBridgeBurnPairConfig { - burn_config_as_org: CBridgeBurnConfig; /// Could be used only as from chain - burn_config_as_dst: CBridgeBurnConfig; /// Could be used only as to chain -} - -export interface CBridgeGetSupportedFuncParams { - fromChainId?: number; - toChainId?: number; - fromTokenSymbol?: string; - peggedPairConfigs: CBridgePeggedPairConfig[]; - burnPairConfigs: CBridgeBurnPairConfig[]; - data: CBridgeTransferConfigs; -} - -export interface CBridgeSendRangeInput { +export interface ICBridgeSendRangeInput { bridgeAddress: `0x${string}`; tokenAddress: `0x${string}`; isPegged?: boolean; @@ -194,7 +180,7 @@ export interface ISendCBridgeToken { bridgeAddress: string; fromChainId: number; address: `0x${string}`; - peggedConfig?: CBridgePeggedPairConfig; + peggedConfig?: ICBridgePeggedPairConfig; isPegged: boolean; isNativeToken?: boolean; args: any; @@ -203,8 +189,8 @@ export interface ISendCBridgeToken { export interface IGetCBridgeTransferAddressInput { fromChainId: number; isPegged: boolean; - peggedConfig?: CBridgePeggedPairConfig; - chainConfig?: CBridgeChain; + peggedConfig?: ICBridgePeggedPairConfig; + chainConfig?: ICBridgeChain; } export interface IGetCBridgeTransferParamsInput { @@ -216,14 +202,14 @@ export interface IGetCBridgeTransferParamsInput { tokenAddress: `0x${string}`; maxSlippage: number; transferType?: 'deposit' | 'withdraw'; - peggedConfig?: CBridgePeggedPairConfig; + peggedConfig?: ICBridgePeggedPairConfig; nonce: number; } export interface IGetCBridgeABI { isPegged: boolean; transferType?: 'deposit' | 'withdraw'; - peggedConfig?: CBridgePeggedPairConfig; + peggedConfig?: ICBridgePeggedPairConfig; } export interface IGetCBridgeTransferFunction { @@ -231,3 +217,8 @@ export interface IGetCBridgeTransferFunction { isNativeToken?: boolean; transferType?: 'deposit' | 'withdraw'; } + +export interface ICBridgeMaxMinSendAmt { + max: string; + min: string; +} diff --git a/packages/canonical-bridge-sdk/src/adapters/deBridge/exports.ts b/packages/canonical-bridge-sdk/src/adapters/deBridge/exports.ts new file mode 100644 index 00000000..48bf6383 --- /dev/null +++ b/packages/canonical-bridge-sdk/src/adapters/deBridge/exports.ts @@ -0,0 +1,2 @@ +export * from './index'; +export * from './types'; diff --git a/packages/canonical-bridge-sdk/src/adapters/deBridge/index.ts b/packages/canonical-bridge-sdk/src/adapters/deBridge/index.ts new file mode 100644 index 00000000..6f8dd309 --- /dev/null +++ b/packages/canonical-bridge-sdk/src/adapters/deBridge/index.ts @@ -0,0 +1,324 @@ +import { BaseAdapter } from '@/adapters/base'; +import { ITransferTokenPair } from '@/adapters/base/types'; +import { + IDeBridgeTransferConfig, + IDeBridgeChain, + IDeBridgeToken, + IDeBridgeAdapterOptions, + ISendDebridgeTokenInput, + IDeBridgeEstimatedFeesInput, + IDeBridgeCreateQuoteResponse, +} from '@/adapters/deBridge/types'; +import { isSameAddress } from '@/shared/address'; +import { BridgeType } from '@/aggregator/types'; +import axios, { AxiosInstance } from 'axios'; +import { CLIENT_TIME_OUT, env } from '@/constants'; +import { Hash } from 'viem'; + +export class DeBridgeAdapter extends BaseAdapter< + IDeBridgeTransferConfig, + IDeBridgeChain, + IDeBridgeToken +> { + private client: AxiosInstance; + private statsClient: AxiosInstance; + public bridgeType: BridgeType = 'deBridge'; + + constructor(options: IDeBridgeAdapterOptions) { + const { + timeout = CLIENT_TIME_OUT, + endpoint = env.DEBRIDGE_ENDPOINT, + statsEndpoint = env.DEBRIDGE_STATS_ENDPOINT, + ...baseOptions + } = options; + + super(baseOptions); + + this.client = axios.create({ + timeout, + baseURL: endpoint, + }); + + this.statsClient = axios.create({ + timeout, + baseURL: statsEndpoint, + }); + } + + // https://deswap.debridge.finance/v1.0/#/DLN/DlnOrderControllerV10_createOrder + async createTxQuote(urlParams: any) { + return ( + await this.client.get( + `/dln/order/create-tx?${urlParams.toString()}` + ) + ).data; + } + + /** + * Get estimated fees from transaction quote API + * @param {number} fromChainId - Chain ID of the source chain + * @param {Address} fromTokenAddress - Address of ERC20 token on the source chain + * @param {BigInt} amount - Send amount + * @param {number} toChainId - Chain ID of the destination chain + * @param {Address} toTokenAddress - Address of ERC20 token on the destination chain + * @param {Address} userAddress - user/account address + */ + async getEstimatedFees({ + fromChainId, + fromTokenAddress, + amount, + toChainId, + toTokenAddress, + userAddress, + toUserAddress, + affiliateFeePercent = 0, + accesstoken = '', + prependOperatingExpenses = false, + }: IDeBridgeEstimatedFeesInput): Promise { + try { + const deBridgeParams = { + srcChainId: fromChainId, + srcChainTokenIn: fromTokenAddress, + srcChainTokenInAmount: amount, + dstChainId: toChainId, + dstChainTokenOut: toTokenAddress, + prependOperatingExpenses, + affiliateFeePercent, + dstChainTokenOutRecipient: toUserAddress || userAddress, + dstChainOrderAuthorityAddress: toUserAddress || userAddress, + srcChainOrderAuthorityAddress: userAddress, + } as any; + + if (accesstoken) { + deBridgeParams.accesstoken = accesstoken; + } + const urlParams = new URLSearchParams(deBridgeParams as any); + const deBridgeQuote = await this.createTxQuote(urlParams); + return deBridgeQuote; + } catch (error: any) { + if (error.response?.data?.errorMessage) { + throw new Error(error.response.data.errorId); + } else { + throw new Error(`${error.message || error}`); + } + } + } + + // https://deswap.debridge.finance/v1.0/#/DLN/DlnOrderControllerV10_getOrder + async getOrder({ id }: { id: string }) { + return (await this.client.get(`/dln/order/${id}`)).data; + } + + // https://stats-api.dln.trade/swagger/index.html + /** + * Get list of orders by filters + * @param address Account address + * @param pageId Page number + * @param pageSize Records per page + * @param fromChainIds Source chain IDs + * @param toChainIds Destination chain IDs + */ + async getStatsHistory({ + address, + pageId = 0, + pageSize = 20, + fromChainIds = [], + toChainIds = [], + }: { + address: string; + pageId: number; + pageSize: number; + fromChainIds: number[]; + toChainIds: number[]; + }) { + return ( + await this.statsClient!.post('/Orders/filteredList', { + filter: address, + skip: pageId, // page number + take: pageSize, // data per page + giveChainIds: fromChainIds, + takeChainIds: toChainIds, + }) + ).data; + } + + /** + * Send token via DeBridge + * @param {WalletClient} walletClient Wallet client + * @param {Address} bridgeAddress Bridge address + * @param {String} data Transaction data + * @param {BigInt} amount Send amount + * @param {Address} address wallet/account address + * @returns {Hash} transaction hash + */ + async sendToken({ + walletClient, + bridgeAddress, + data, + amount, + address, + }: ISendDebridgeTokenInput): Promise { + try { + const hash = await walletClient.sendTransaction({ + to: bridgeAddress as `0x${string}`, + data: data, + value: amount, + account: address, + chain: walletClient.chain, + }); + return hash; + } catch (error) { + throw new Error(`Failed to send DeBridge token: ${error}`); + } + } + + protected initChains() { + const { chains, tokens } = this.config; + + const filteredChains = chains.filter((chain) => { + const hasChainConfig = this.includedChains.includes(chain.chainId); + const isExcludedChain = this.excludedChains.includes(chain.chainId); + const hasToken = tokens[chain.chainId]?.length > 0; + return hasChainConfig && !isExcludedChain && hasToken; + }); + + const chainMap = new Map(); + filteredChains.forEach((chain) => { + chainMap.set(chain.chainId, chain); + }); + + this.chains = filteredChains; + this.chainMap = chainMap; + } + + protected initTokens() { + const { tokens } = this.config; + + const tokenMap = new Map(); + const symbolMap = new Map>(); + Object.entries(tokens).forEach(([id, chainTokens]) => { + const chainId = Number(id); + + const filteredTokens = chainTokens.filter((token) => { + const isExcludedToken = this.checkIsExcludedToken({ + excludedList: this.excludedTokens?.[chainId], + tokenSymbol: token.symbol?.toUpperCase(), + tokenAddress: token.address, + }); + return !isExcludedToken; + }); + + if (filteredTokens.length > 0 && this.chainMap.has(chainId)) { + symbolMap.set(chainId, new Map()); + + filteredTokens.forEach((token) => { + symbolMap.get(chainId)?.set(token.symbol?.toUpperCase(), token); + }); + + tokenMap.set(chainId, filteredTokens); + } + }); + + this.tokenMap = tokenMap; + this.symbolMap = symbolMap; + } + + protected initTransferMap() { + const transferMap = new Map< + number, + Map>> + >(); + + this.chains.forEach((fromChain) => { + this.chains.forEach((toChain) => { + if (fromChain.chainId !== toChain.chainId) { + const fromTokens = this.tokenMap.get(fromChain.chainId) ?? []; + + const transferableTokenMap = new Map< + string, + ITransferTokenPair + >(); + fromTokens.forEach((fromToken) => { + const toToken = this.getToToken({ + fromChainId: fromChain.chainId, + toChainId: toChain.chainId, + fromTokenSymbol: fromToken.symbol?.toUpperCase(), + }); + + if (toToken) { + const tokenPair: ITransferTokenPair = { + fromChainId: fromChain.chainId, + toChainId: toChain.chainId, + fromTokenAddress: fromToken.address, + toTokenAddress: toToken.address, + fromToken, + toToken, + }; + transferableTokenMap.set( + fromToken.symbol?.toUpperCase(), + tokenPair + ); + } + }); + + if (transferableTokenMap.size > 0) { + if (!transferMap.has(fromChain.chainId)) { + transferMap.set( + fromChain.chainId, + new Map< + number, + Map> + >() + ); + } + transferMap + .get(fromChain.chainId) + ?.set(toChain.chainId, transferableTokenMap); + } + } + }); + }); + + this.transferMap = transferMap; + } + + public getChainId(chain: IDeBridgeChain) { + return chain.chainId; + } + + public getTokenInfo({ + chainId, + token, + }: { + chainId: number; + token: IDeBridgeToken; + }) { + return { + name: token.name, + symbol: token.symbol, + address: token.address, + decimals: token.decimals, + ...this.getTokenDisplaySymbolAndIcon({ + chainId, + tokenAddress: token.address, + defaultSymbol: token.symbol, + }), + }; + } + + public getTokenByAddress({ + chainId, + address, + }: { + chainId: number; + address: string; + }) { + if (chainId && address) { + const tokens = this.tokenMap.get(Number(chainId)); + const target = tokens?.find((item) => + isSameAddress(item.address, address) + ); + return target; + } + } +} diff --git a/packages/canonical-bridge-sdk/src/debridge/types/index.ts b/packages/canonical-bridge-sdk/src/adapters/deBridge/types.ts similarity index 51% rename from packages/canonical-bridge-sdk/src/debridge/types/index.ts rename to packages/canonical-bridge-sdk/src/adapters/deBridge/types.ts index c85b9c32..fab2a8c6 100644 --- a/packages/canonical-bridge-sdk/src/debridge/types/index.ts +++ b/packages/canonical-bridge-sdk/src/adapters/deBridge/types.ts @@ -1,11 +1,19 @@ -import { type WalletClient } from 'viem'; +import { IBaseAdapterOptions } from '@/adapters/base/types'; +import { WalletClient } from 'viem'; -export interface DeBridgeChain { +export interface IDeBridgeAdapterOptions + extends IBaseAdapterOptions { + timeout?: number; + endpoint?: string; + statsEndpoint?: string; +} + +export interface IDeBridgeChain { chainId: number; chainName: string; } -export interface DeBridgeToken { +export interface IDeBridgeToken { address: string; symbol: string; decimals: number; @@ -16,13 +24,71 @@ export interface DeBridgeToken { domainVersion?: string; } -export interface DeBridgeTransferConfigs { - chains: DeBridgeChain[]; - tokens: Record; +export interface IDeBridgeTransferConfig { + chains: IDeBridgeChain[]; + tokens: Record; +} + +// https://deswap.debridge.finance/v1.0/#/DLN +export type IQuoteResponse = { + estimation: { + srcChainTokenIn: { + address: `0x${string}`; + name: string; + symbol: string; + chainId: number; + decimals: number; + amount: string; + approximateOperatingExpense: string; + mutatedWithOperatingExpense: boolean; + }; + srcChainTokenOut: { + address: `0x${string}`; + name: string; + symbol: string; + decimals: number; + amount: string; + chainId: number; + maxRefundAmount: string; + }; + dstChainTokenOut: { + address: `0x${string}`; + name: string; + symbol: string; + decimals: number; + amount: string; + recommendedAmount: string; + withoutAdditionalTakerRewardsAmount: string; + maxTheoreticalAmount: string; + chainId: number; + }; + costsDetails: any[]; + recommendedSlippage: number; + }; + tx: { + allowanceTarget: `0x${string}`; + allowanceValue: string; + data: `0x${string}`; + to: `0x${string}`; // Bridge address + value: string; + }; + prependedOperatingExpenseCost: string; + order: { + approximateFulfillmentDelay: number; + }; + fixFee: string; + userPoints: number; + integratorPoints: number; + orderId: string; +}; + +export interface ITokenQueryParams { + chainId: number; + address: string; } // https://deswap.debridge.finance/v1.0/#/DLN -export type DeBridgeCreateQuoteResponse = { +export type IDeBridgeCreateQuoteResponse = { estimation: { srcChainTokenIn: { address: `0x${string}`; @@ -95,7 +161,7 @@ export interface ISendDebridgeTokenInput { address: `0x${string}`; } -export interface TokenQueryParams { +export interface ITokenQueryParams { chainId: number; address: string; } diff --git a/packages/canonical-bridge-sdk/src/layerZero/abi/cakeProxyOFT.ts b/packages/canonical-bridge-sdk/src/adapters/layerZero/abi/cakeProxyOFT.ts similarity index 100% rename from packages/canonical-bridge-sdk/src/layerZero/abi/cakeProxyOFT.ts rename to packages/canonical-bridge-sdk/src/adapters/layerZero/abi/cakeProxyOFT.ts diff --git a/packages/canonical-bridge-sdk/src/adapters/layerZero/exports.ts b/packages/canonical-bridge-sdk/src/adapters/layerZero/exports.ts new file mode 100644 index 00000000..08fd6495 --- /dev/null +++ b/packages/canonical-bridge-sdk/src/adapters/layerZero/exports.ts @@ -0,0 +1,4 @@ +export * from './index'; +export * from './types'; + +export * from './abi/cakeProxyOFT'; diff --git a/packages/canonical-bridge-sdk/src/adapters/layerZero/index.ts b/packages/canonical-bridge-sdk/src/adapters/layerZero/index.ts new file mode 100644 index 00000000..bf1c3fec --- /dev/null +++ b/packages/canonical-bridge-sdk/src/adapters/layerZero/index.ts @@ -0,0 +1,278 @@ +import { BaseAdapter } from '@/adapters/base'; +import { ITransferTokenPair } from '@/adapters/base/types'; +import { CAKE_PROXY_OFT_ABI } from '@/adapters/layerZero/exports'; +import { + ILayerZeroTransferConfig, + ILayerZeroChain, + ILayerZeroToken, + ISendCakeTokenInput, + IGetEstimateFeeInput, +} from '@/adapters/layerZero/types'; +import { BridgeType } from '@/aggregator/types'; +import { formatNumber } from '@/shared/number'; +import { encodePacked, formatUnits, Hash, pad, parseUnits } from 'viem'; + +export class LayerZeroAdapter extends BaseAdapter< + ILayerZeroTransferConfig, + ILayerZeroChain, + ILayerZeroToken +> { + public bridgeType: BridgeType = 'layerZero'; + + /** + * Send token through layerZero V1 OFT + * https://docs.layerzero.network/v1/developers/evm/evm-guides/advanced/relayer-adapter-parameters + * + * @param userAddress User address + * @param bridgeAddress Bridge address + * @param amount Send amount + * @param dstEndpoint Destination endpoint + * @param publicClient Public client + * @param walletClient Wallet client + * @param gasAmount Gas amount + * @param version Relayer adapter parameters version + */ + async sendToken({ + userAddress, + bridgeAddress, + amount, + dstEndpoint, + publicClient, + walletClient, + gasAmount = 200000n, + version = 1, + }: ISendCakeTokenInput): Promise { + try { + const address32Bytes = pad(userAddress, { size: 32 }); + const adapterParams = encodePacked( + ['uint16', 'uint256'], + [version, gasAmount] + ); + const fees = await publicClient.readContract({ + address: bridgeAddress, + abi: CAKE_PROXY_OFT_ABI, + functionName: 'estimateSendFee', + args: [ + dstEndpoint, + address32Bytes, + amount, + false, + adapterParams as `0x${string}`, + ], + }); + const callParams = [ + userAddress, + '0x0000000000000000000000000000000000000000', // zroPaymentAddress + adapterParams, + ]; + const nativeFee = fees[0]; + const minAmount = parseUnits( + String(formatNumber(Number(formatUnits(amount, 18)), 8)), + 18 + ); + const cakeArgs = { + address: bridgeAddress, + abi: CAKE_PROXY_OFT_ABI, + functionName: 'sendFrom', + args: [ + userAddress, + dstEndpoint, + address32Bytes, + amount, + minAmount, + callParams, + ], + value: nativeFee, + account: userAddress, + }; + const gas = await publicClient.estimateContractGas(cakeArgs as any); + const gasPrice = await publicClient.getGasPrice(); + const hash = await walletClient.writeContract({ + ...(cakeArgs as any), + gas, + gasPrice, + }); + return hash; + } catch (error: any) { + throw new Error(`Failed to send CAKE token ${error}`); + } + } + + /** + * Get estimate fee + * @param bridgeAddress Bridge address + * @param amount Send amount + * @param dstEndpoint Destination endpoint + * @param userAddress User address + * @param publicClient Public client + * @param gasAmount Gas amount + * @param version Relayer adapter parameters version + * @returns Estimate fee + */ + async getEstimateFee({ + bridgeAddress, + amount, + dstEndpoint, + userAddress, + publicClient, + gasAmount = 200000n, + version = 1, + }: IGetEstimateFeeInput) { + try { + const address32Bytes = pad(userAddress, { size: 32 }); + const adapterParams = encodePacked( + ['uint16', 'uint256'], + [version, gasAmount] + ); + const fees = await publicClient.readContract({ + address: bridgeAddress, + abi: CAKE_PROXY_OFT_ABI, + functionName: 'estimateSendFee', + args: [ + dstEndpoint, + address32Bytes, + amount, + false, + adapterParams as `0x${string}`, + ], + }); + return fees; + } catch (error: any) { + throw new Error(`Failed to get estimate fee ${error}`); + } + } + + protected initChains() { + const { chains, tokens } = this.config; + + const filteredChains = chains.filter((chain) => { + const hasChainConfig = this.includedChains.includes(chain.chainId); + const isExcludedChain = this.excludedChains.includes(chain.chainId); + const hasToken = tokens[chain.chainId]?.length > 0; + return hasChainConfig && !isExcludedChain && hasToken; + }); + + const chainMap = new Map(); + filteredChains.forEach((chain) => { + chainMap.set(chain.chainId, chain); + }); + + this.chains = filteredChains; + this.chainMap = chainMap; + } + + protected initTokens() { + const { tokens } = this.config; + + const tokenMap = new Map(); + const symbolMap = new Map>(); + Object.entries(tokens).forEach(([id, chainTokens]) => { + const chainId = Number(id); + + const filteredTokens = chainTokens.filter((token) => { + const isExcludedToken = this.checkIsExcludedToken({ + excludedList: this.excludedTokens?.[chainId], + tokenSymbol: token.symbol?.toUpperCase(), + tokenAddress: token.address, + }); + return !isExcludedToken; + }); + + if (filteredTokens.length > 0 && this.chainMap.has(chainId)) { + symbolMap.set(chainId, new Map()); + + filteredTokens.forEach((token) => { + symbolMap.get(chainId)?.set(token.symbol, token); + }); + + tokenMap.set(chainId, filteredTokens); + } + }); + + this.tokenMap = tokenMap; + this.symbolMap = symbolMap; + } + + protected initTransferMap() { + const transferMap = new Map< + number, + Map>> + >(); + + this.chains.forEach((fromChain) => { + this.chains.forEach((toChain) => { + if ( + fromChain.chainId !== toChain.chainId && + fromChain.network === toChain.network + ) { + const fromTokens = this.tokenMap.get(fromChain.chainId) ?? []; + + const transferableTokenMap = new Map< + string, + ITransferTokenPair + >(); + fromTokens.forEach((fromToken) => { + const toToken = this.getToToken({ + fromChainId: fromChain.chainId, + toChainId: toChain.chainId, + fromTokenSymbol: fromToken.symbol?.toUpperCase(), + }); + + if (toToken) { + const tokenPair: ITransferTokenPair = { + fromChainId: fromChain.chainId, + toChainId: toChain.chainId, + fromToken, + toToken, + fromTokenAddress: fromToken.address, + toTokenAddress: toToken.address, + }; + transferableTokenMap.set(fromToken.symbol, tokenPair); + } + }); + + if (transferableTokenMap.size > 0) { + if (!transferMap.has(fromChain.chainId)) { + transferMap.set( + fromChain.chainId, + new Map< + number, + Map> + >() + ); + } + transferMap + .get(fromChain.chainId) + ?.set(toChain.chainId, transferableTokenMap); + } + } + }); + }); + + this.transferMap = transferMap; + } + + public getChainId(chain: ILayerZeroChain) { + return chain.chainId; + } + + public getTokenInfo({ + chainId, + token, + }: { + chainId: number; + token: ILayerZeroToken; + }) { + return { + name: (token as any).name, // TODO + symbol: token.symbol, + address: token.address, + decimals: token.decimals, + ...this.getTokenDisplaySymbolAndIcon({ + chainId, + tokenAddress: token.address, + defaultSymbol: token.symbol, + }), + }; + } +} diff --git a/packages/canonical-bridge-sdk/src/layerZero/types/index.ts b/packages/canonical-bridge-sdk/src/adapters/layerZero/types.ts similarity index 70% rename from packages/canonical-bridge-sdk/src/layerZero/types/index.ts rename to packages/canonical-bridge-sdk/src/adapters/layerZero/types.ts index de081be1..2cbed673 100644 --- a/packages/canonical-bridge-sdk/src/layerZero/types/index.ts +++ b/packages/canonical-bridge-sdk/src/adapters/layerZero/types.ts @@ -1,7 +1,5 @@ -import { BaseBridgeConfig } from '@/core'; import { type PublicClient, type WalletClient } from 'viem'; -export type LayerZeroConfig = Omit; export interface ISendCakeTokenInput { userAddress: `0x${string}`; bridgeAddress: `0x${string}`; @@ -23,22 +21,23 @@ export interface IGetEstimateFeeInput { publicClient: PublicClient; } -export interface LayerZeroToken { +export interface ILayerZeroToken { address: string; bridgeAddress: string; decimals: number; symbol: string; + name: string; endpointID: number; version: number; // LayerZero version } -export interface LayerZeroChain { +export interface ILayerZeroChain { chainId: number; chainName: string; network?: string; } -export interface LayerZeroTransferConfigs { - chains: LayerZeroChain[]; - tokens: Record; +export interface ILayerZeroTransferConfig { + chains: ILayerZeroChain[]; + tokens: Record; } diff --git a/packages/canonical-bridge-sdk/src/adapters/meson/exports.ts b/packages/canonical-bridge-sdk/src/adapters/meson/exports.ts new file mode 100644 index 00000000..48bf6383 --- /dev/null +++ b/packages/canonical-bridge-sdk/src/adapters/meson/exports.ts @@ -0,0 +1,2 @@ +export * from './index'; +export * from './types'; diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/meson/MesonAdapter.ts b/packages/canonical-bridge-sdk/src/adapters/meson/index.ts similarity index 50% rename from packages/canonical-bridge-widget/src/modules/aggregator/adapters/meson/MesonAdapter.ts rename to packages/canonical-bridge-sdk/src/adapters/meson/index.ts index 7d710baa..ebc0913f 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/meson/MesonAdapter.ts +++ b/packages/canonical-bridge-sdk/src/adapters/meson/index.ts @@ -1,24 +1,134 @@ -import { BridgeType } from '@bnb-chain/canonical-bridge-sdk'; +import { BaseAdapter } from '@/adapters/base'; +import { ITransferTokenPair } from '@/adapters/base/types'; +import { + IGetMesonEstimateFeeInput, + IMesonAdapterOptions, + IMesonChain, + IMesonEncodeSwapInput, + IMesonSendTokenInput, + IMesonToken, +} from '@/adapters/meson/types'; +import { isNativeToken } from '@/shared/address'; +import { BridgeType } from '@/aggregator/types'; +import axios, { AxiosInstance } from 'axios'; +import { CLIENT_TIME_OUT, env } from '@/constants'; + +export class MesonAdapter extends BaseAdapter< + IMesonChain[], + IMesonChain, + IMesonToken +> { + private client: AxiosInstance; + public bridgeType: BridgeType = 'meson'; -import { BaseAdapter, ITransferTokenPair } from '@/modules/aggregator/shared/BaseAdapter'; -import { IMesonChain, IMesonToken } from '@/modules/aggregator/adapters/meson/types'; -import { isNativeToken } from '@/core/utils/address'; + constructor(options: IMesonAdapterOptions) { + const { + timeout = CLIENT_TIME_OUT, + endpoint = env.MESON_ENDPOINT, + ...baseOptions + } = options; -// const SUPPORTED_CHAIN_IDS = [56, 97, 3448148188, 728126428]; -// const SUPPORTED_TOKENS = ['USDT', 'USDC']; + super(baseOptions); -export class MesonAdapter extends BaseAdapter { - public bridgeType: BridgeType = 'meson'; + this.client = axios.create({ + timeout, + baseURL: endpoint, + }); + } + + async getEstimatedFees({ + fromToken, + toToken, + amount, + fromAddr, + }: IGetMesonEstimateFeeInput) { + try { + const { data: priceResponse } = await this.client.post('/price', { + from: fromToken, + to: toToken, + amount: amount, + fromAddress: fromAddr, + fromContract: false, + }); + return priceResponse; + } catch (error: any) { + // eslint-disable-next-line no-console + if (error?.response.data) { + console.log('Meson fee error', error?.response.data); + return error?.response?.data; + } else { + throw new Error(`Failed to get Meson fees ${error}`); + } + } + } + + async sendToken({ + fromAddress, + recipient, + signature, + encodedData, + }: IMesonSendTokenInput) { + try { + const { data: swapResponse } = await this.client.post( + `/swap/${encodedData}`, + { + fromAddress: fromAddress, + recipient: recipient, + signature: signature, + } + ); + return swapResponse; + } catch (error: any) { + // eslint-disable-next-line no-console + if (error?.response.data) { + console.log('Meson send token error', error?.response.data); + return error?.response?.data; + } else { + throw new Error(`Failed to send transaction ${error}`); + } + } + } + + async getUnsignedMessage({ + fromToken, + toToken, + amount, + fromAddress, + recipient, + }: IMesonEncodeSwapInput) { + try { + const { data: swapResponse } = await this.client.post('/swap', { + from: fromToken, + to: toToken, + amount: amount, + fromAddress: fromAddress, + recipient: recipient, + dataToContract: '', + }); + return swapResponse; + } catch (error: any) { + // eslint-disable-next-line no-console + if (error?.response.data) { + console.log('Meson get unsigned message error', error?.response.data); + return error?.response?.data; + } else { + throw new Error(`Failed to get unsigned message ${error}`); + } + } + } protected initChains() { const chains = this.config; const filteredChains = chains.filter((chain) => { - const hasChainConfig = this.includedChains.includes(Number(chain.chainId)); - const isExcludedChain = this.excludedChains.includes(Number(chain.chainId)); + const hasChainConfig = this.includedChains.includes( + Number(chain.chainId) + ); + const isExcludedChain = this.excludedChains.includes( + Number(chain.chainId) + ); const hasToken = chain.tokens?.length > 0; - // const isSupported = SUPPORTED_CHAIN_IDS.includes(Number(chain.chainId)); // TODO return hasChainConfig && !isExcludedChain && hasToken; }); @@ -75,7 +185,10 @@ export class MesonAdapter extends BaseAdapter { if (fromChain?.chainId !== toChain?.chainId) { const fromTokens = this.tokenMap.get(Number(fromChain.chainId)) ?? []; - const transferableTokenMap = new Map>(); + const transferableTokenMap = new Map< + string, + ITransferTokenPair + >(); fromTokens.forEach((fromToken) => { const toToken = this.getToToken({ fromChainId: Number(fromChain.chainId), @@ -100,7 +213,7 @@ export class MesonAdapter extends BaseAdapter>>(), + new Map>>() ); } transferMap @@ -118,13 +231,13 @@ export class MesonAdapter extends BaseAdapter { + timeout?: number; + endpoint?: string; +} + +export interface IMesonToken { + id: string; + addr: string; + decimals: number; + min: string; + max: string; +} + +export interface IMesonChain { + id: string; + name: string; + chainId: string; + address: string; // bridge address + tokens: IMesonToken[]; +} + +export interface IGetMesonEstimateFeeInput { + fromToken: string; + toToken: string; + fromAddr: string; + amount: string; +} + +export interface IMesonEncodeSwapInput { + fromToken: string; + toToken: string; + amount: string; + fromAddress: string; + recipient: string; +} + +export interface IMesonSendTokenInput { + fromAddress: string; + recipient: string; + signature: string; + encodedData: string; +} diff --git a/packages/canonical-bridge-sdk/src/stargate/abi/stargatePool.ts b/packages/canonical-bridge-sdk/src/adapters/stargate/abi/stargatePool.ts similarity index 100% rename from packages/canonical-bridge-sdk/src/stargate/abi/stargatePool.ts rename to packages/canonical-bridge-sdk/src/adapters/stargate/abi/stargatePool.ts diff --git a/packages/canonical-bridge-sdk/src/adapters/stargate/exports.ts b/packages/canonical-bridge-sdk/src/adapters/stargate/exports.ts new file mode 100644 index 00000000..dfb47563 --- /dev/null +++ b/packages/canonical-bridge-sdk/src/adapters/stargate/exports.ts @@ -0,0 +1,4 @@ +export * from './index'; +export * from './types'; + +export * from './abi/stargatePool'; diff --git a/packages/canonical-bridge-sdk/src/adapters/stargate/index.ts b/packages/canonical-bridge-sdk/src/adapters/stargate/index.ts new file mode 100644 index 00000000..b81f06aa --- /dev/null +++ b/packages/canonical-bridge-sdk/src/adapters/stargate/index.ts @@ -0,0 +1,320 @@ +import { BaseAdapter } from '@/adapters/base'; +import { ITransferTokenPair } from '@/adapters/base/types'; +import { STARGATE_POOL } from '@/adapters/stargate/exports'; +import { + IStargateTransferConfig, + IStargateChain, + IStargateToken, + IStargateAdapterOptions, + ISendTokenInput, + IStargateOFTQuote, + IStargateQuoteOFT, + IStarGateBusDriveSettings, +} from '@/adapters/stargate/types'; +import { BridgeType } from '@/aggregator/types'; +import { CLIENT_TIME_OUT } from '@/constants'; +import axios, { AxiosInstance } from 'axios'; +import { Hash, pad } from 'viem'; + +export class StargateAdapter extends BaseAdapter< + IStargateTransferConfig, + IStargateChain, + IStargateToken +> { + private client: AxiosInstance; + public bridgeType: BridgeType = 'stargate'; + + constructor(options: IStargateAdapterOptions) { + const { timeout = CLIENT_TIME_OUT, endpoint, ...baseOptions } = options; + + super(baseOptions); + + this.client = axios.create({ + timeout, + baseURL: endpoint, + }); + } + + // https://mainnet.stargate-api.com/v1/swagger + async getBusQueueTime({ + fromEndpointId, + toEndpointId, + }: { + fromEndpointId: string; + toEndpointId: string; + }) { + return ( + await this.client.get( + `${fromEndpointId}/${toEndpointId}` + ) + ).data; + } + + // https://stargateprotocol.gitbook.io/stargate/v/v2-developer-docs/integrate-with-stargate/estimating-fees#quoteoft + async getQuoteOFT({ + publicClient, + bridgeAddress, + endPointId, + receiver, + amount, + }: IStargateQuoteOFT) { + const sendParams = { + dstEid: endPointId, + to: pad(receiver, { size: 32 }) as `0x${string}`, + amountLD: amount, + minAmountLD: amount, + extraOptions: '0x' as `0x${string}`, + composeMsg: '0x' as `0x${string}`, + oftCmd: '0x01' as `0x${string}`, // '0x01' for bus, '' for taxi + }; + + try { + const quoteOFTResponse = await publicClient?.readContract({ + address: bridgeAddress, + abi: STARGATE_POOL, + functionName: 'quoteOFT', + args: [sendParams] as any, + }); + return quoteOFTResponse; + } catch (error: any) { + throw new Error(`Failed to get quote OFT: ${error}`); + } + } + + // https://stargateprotocol.gitbook.io/stargate/v/v2-developer-docs/integrate-with-stargate/estimating-fees#quotesend + async getQuoteSend({ + publicClient, + bridgeAddress, + endPointId, + receiver, + amount, + minAmount, + }: IStargateOFTQuote) { + const sendParams = { + dstEid: endPointId, + to: pad(receiver, { size: 32 }) as `0x${string}`, + amountLD: amount, + minAmountLD: minAmount, + extraOptions: '0x' as `0x${string}`, + composeMsg: '0x' as `0x${string}`, + oftCmd: '0x01' as `0x${string}`, // '0x01' for bus, '' for taxi + }; + try { + const quoteSendResponse = await publicClient?.readContract({ + address: bridgeAddress, + abi: STARGATE_POOL, + functionName: 'quoteSend', + args: [sendParams, false] as any, // false for not paying lzToken + }); + return quoteSendResponse; + } catch (error: any) { + throw new Error(`Failed to get quote send: ${error}`); + } + } + + /** + * Send token through Stargate bridge + * @param {WalletClient} walletClient Wallet client + * @param {PublicClient} publicClient Public client + * @param {Address} bridgeAddress Bridge address + * @param {Address} tokenAddress ERC-20 token address + * @param {Number} endPointId Stargate end point ID + * @param {Address} receiver Receiver address + * @param {BigInt} amount Send amount + * @returns {Hash} transaction hash + */ + async sendToken({ + walletClient, + publicClient, + bridgeAddress, + tokenAddress, + endPointId, + receiver, + amount, + }: ISendTokenInput): Promise { + const sendParams = { + dstEid: endPointId, + to: pad(receiver, { size: 32 }) as `0x${string}`, + amountLD: amount, + minAmountLD: amount, + extraOptions: '0x' as `0x${string}`, + composeMsg: '0x' as `0x${string}`, + oftCmd: '0x01' as `0x${string}`, // '0x01' for bus, '' for taxi + }; + try { + const quoteOFTResponse = await this.getQuoteOFT({ + publicClient, + bridgeAddress, + endPointId, + receiver, + amount, + }); + if (quoteOFTResponse?.[2].amountReceivedLD) { + sendParams.minAmountLD = BigInt(quoteOFTResponse[2].amountReceivedLD); + } + const quoteSendResponse = await this.getQuoteSend({ + publicClient, + bridgeAddress, + endPointId, + receiver, + amount, + minAmount: sendParams.minAmountLD, + }); + let nativeFee = quoteSendResponse.nativeFee; + if (tokenAddress === '0x0000000000000000000000000000000000000000') { + nativeFee += sendParams.amountLD; + } + const sendTokenArgs = { + address: bridgeAddress, + abi: STARGATE_POOL, + functionName: 'sendToken', + args: [sendParams, quoteSendResponse, receiver], + value: nativeFee, + account: receiver, + }; + const hash = await walletClient?.writeContract({ + ...(sendTokenArgs as any), + }); + return hash; + } catch (error: any) { + throw new Error(`Failed to send token: ${error}`); + } + } + + protected initChains() { + const { chains, tokens } = this.config; + + const filteredChains = chains.filter((chain) => { + const hasChainConfig = this.includedChains.includes(chain.chainId); + const isExcludedChain = this.excludedChains.includes(chain.chainId); + const hasToken = tokens[chain.chainId]?.length > 0; + return hasChainConfig && !isExcludedChain && hasToken; + }); + + const chainMap = new Map(); + filteredChains.forEach((chain) => { + chainMap.set(chain.chainId, chain); + }); + + this.chains = filteredChains; + this.chainMap = chainMap; + } + + protected initTokens() { + const { tokens } = this.config; + + const tokenMap = new Map(); + const symbolMap = new Map>(); + Object.entries(tokens).forEach(([id, chainTokens]) => { + const chainId = Number(id); + + const filteredTokens = chainTokens.filter((token) => { + const isExcludedToken = this.checkIsExcludedToken({ + excludedList: this.excludedTokens?.[chainId], + tokenSymbol: token.symbol?.toUpperCase(), + tokenAddress: token.address, + }); + return !isExcludedToken; + }); + + if (filteredTokens.length > 0 && this.chainMap.has(chainId)) { + symbolMap.set(chainId, new Map()); + + filteredTokens.forEach((token) => { + symbolMap.get(chainId)?.set(token.symbol?.toUpperCase(), token); + }); + + tokenMap.set(chainId, filteredTokens); + } + }); + + this.tokenMap = tokenMap; + this.symbolMap = symbolMap; + } + + protected initTransferMap() { + const transferMap = new Map< + number, + Map>> + >(); + + this.chains.forEach((fromChain) => { + this.chains.forEach((toChain) => { + if ( + fromChain.chainId !== toChain.chainId && + fromChain.network === toChain.network + ) { + const fromTokens = this.tokenMap.get(fromChain.chainId) ?? []; + + const transferableTokenMap = new Map< + string, + ITransferTokenPair + >(); + fromTokens.forEach((fromToken) => { + const toToken = this.getToToken({ + fromChainId: fromChain.chainId, + toChainId: toChain.chainId, + fromTokenSymbol: fromToken.symbol?.toUpperCase(), + }); + + if (toToken) { + const tokenPair: ITransferTokenPair = { + fromChainId: fromChain.chainId, + toChainId: toChain.chainId, + fromToken, + toToken, + fromTokenAddress: fromToken.address, + toTokenAddress: toToken.address, + }; + transferableTokenMap.set( + fromToken.symbol?.toUpperCase(), + tokenPair + ); + } + }); + + if (transferableTokenMap.size > 0) { + if (!transferMap.has(fromChain.chainId)) { + transferMap.set( + fromChain.chainId, + new Map< + number, + Map> + >() + ); + } + transferMap + .get(fromChain.chainId) + ?.set(toChain.chainId, transferableTokenMap); + } + } + }); + }); + + this.transferMap = transferMap; + } + + public getChainId(chain: IStargateChain) { + return chain.chainId; + } + + public getTokenInfo({ + chainId, + token, + }: { + chainId: number; + token: IStargateToken; + }) { + return { + name: token.name, + symbol: token.symbol, + address: token.address, + decimals: token.decimals, + ...this.getTokenDisplaySymbolAndIcon({ + chainId, + tokenAddress: token.address, + defaultSymbol: token.symbol, + }), + }; + } +} diff --git a/packages/canonical-bridge-sdk/src/stargate/types/index.ts b/packages/canonical-bridge-sdk/src/adapters/stargate/types.ts similarity index 70% rename from packages/canonical-bridge-sdk/src/stargate/types/index.ts rename to packages/canonical-bridge-sdk/src/adapters/stargate/types.ts index adb5249b..0477e593 100644 --- a/packages/canonical-bridge-sdk/src/stargate/types/index.ts +++ b/packages/canonical-bridge-sdk/src/adapters/stargate/types.ts @@ -1,26 +1,34 @@ -import { type PublicClient, type WalletClient } from 'viem'; +import { IBaseAdapterOptions } from '@/adapters/base/types'; +import { PublicClient, WalletClient } from 'viem'; -export interface StarGateToken { +export interface IStargateAdapterOptions + extends IBaseAdapterOptions { + timeout?: number; + endpoint?: string; +} + +export interface IStargateToken { address: string; bridgeAddress: string; decimals: number; type?: string; symbol: string; endpointID: number; + name: string; } -export interface StarGateTransferConfigs { - chains: StarGateChain[]; - tokens: Record; +export interface IStargateTransferConfig { + chains: IStargateChain[]; + tokens: Record; } -export interface StarGateChain { +export interface IStargateChain { chainId: number; chainName: string; network?: string; } -export interface IStarGateParams { +export interface IStargateParams { dstEid: number; to: `0x${string}`; amountLD: bigint; diff --git a/packages/canonical-bridge-sdk/src/aggregator/exports.ts b/packages/canonical-bridge-sdk/src/aggregator/exports.ts new file mode 100644 index 00000000..48bf6383 --- /dev/null +++ b/packages/canonical-bridge-sdk/src/aggregator/exports.ts @@ -0,0 +1,2 @@ +export * from './index'; +export * from './types'; diff --git a/packages/canonical-bridge-sdk/src/core/index.ts b/packages/canonical-bridge-sdk/src/aggregator/index.ts similarity index 55% rename from packages/canonical-bridge-sdk/src/core/index.ts rename to packages/canonical-bridge-sdk/src/aggregator/index.ts index c196e87b..14dc52dd 100644 --- a/packages/canonical-bridge-sdk/src/core/index.ts +++ b/packages/canonical-bridge-sdk/src/aggregator/index.ts @@ -1,96 +1,128 @@ -import { LayerZero } from '@/layerZero'; -import { CBridge, CBridgePeggedPairConfig } from '@/cbridge'; -import { ERC20_TOKEN } from '@/core/abi/erc20Token'; +import { ERC20_TOKEN } from '@/abi/erc20Token'; +import { ITransferTokenPair } from '@/adapters/base/types'; +import { CBridgeAdapter } from '@/adapters/cBridge'; +import { ICBridgePeggedPairConfig } from '@/adapters/cBridge/types'; +import { DeBridgeAdapter } from '@/adapters/deBridge'; +import { IDeBridgeEstimatedFeesInput } from '@/adapters/deBridge/types'; +import { LayerZeroAdapter } from '@/adapters/layerZero'; +import { MesonAdapter } from '@/adapters/meson'; +import { IGetMesonEstimateFeeInput } from '@/adapters/meson/types'; +import { StargateAdapter } from '@/adapters/stargate'; import { - BaseBridgeConfig, - BridgeAddress, - BridgeEndpointId, + IBridgeChain, BridgeType, + IChainConfig, + IExternalChain, + INativeCurrency, + IBridgeToken, IApproveTokenInput, IGetAllowanceInput, IGetTokenBalanceInput, -} from '@/core/types'; -import { - DeBridge, - DeBridgeConfig, - IDeBridgeEstimatedFeesInput, -} from '@/debridge'; -import { Stargate } from '@/stargate'; -import { Hash, type PublicClient, type WalletClient } from 'viem'; -import { Meson } from '@/meson'; -import { IGetMesonEstimateFeeInput } from '@/meson/types'; - -export * from './types'; - -export interface CanonicalBridgeSDKOptions { - bridgeConfigs: T[]; + IBridgeEndpointId, + IBridgeAddress, +} from '@/aggregator/types'; +import { Hash, PublicClient, WalletClient } from 'viem'; + +export interface CanonicalBridgeSDKOptions { + chains: IChainConfig[]; + assetPrefix?: string; + brandChains?: number[]; + externalChains?: IExternalChain[]; + displayTokenSymbols?: Record>; + chainOrder?: number[]; + tokenOrder?: string[]; + adapters: Array< + | CBridgeAdapter + | DeBridgeAdapter + | LayerZeroAdapter + | StargateAdapter + | MesonAdapter + >; } export class CanonicalBridgeSDK { - cBridge!: CBridge; - deBridge!: DeBridge; - stargate!: Stargate; - layerZero!: LayerZero; - meson!: Meson; - - private options: CanonicalBridgeSDKOptions; - - constructor( - options: CanonicalBridgeSDKOptions - ) { - const cBridgeConfig = options.bridgeConfigs.find( - (item) => item.bridgeType === 'cBridge' - ); - - const deBridgeConfig = options.bridgeConfigs.find( - (item) => item.bridgeType === 'deBridge' - ); - - const stargateConfig = options.bridgeConfigs.find( - (item) => item.bridgeType === 'stargate' - ); - - const layerZeroConfig = options.bridgeConfigs.find( - (item) => item.bridgeType === 'layerZero' - ); - - const mesonConfig = options.bridgeConfigs.find( - (item) => item.bridgeType === 'meson' - ); - - if (cBridgeConfig) { - this.cBridge = new CBridge(cBridgeConfig); - } - if (deBridgeConfig) { - this.deBridge = new DeBridge(deBridgeConfig as DeBridgeConfig); - } - if (stargateConfig) { - this.stargate = new Stargate(stargateConfig); - } - if (layerZeroConfig) { - this.layerZero = new LayerZero(); - } - if (mesonConfig) { - this.meson = new Meson(mesonConfig); - } + public cBridge?: CBridgeAdapter; + public deBridge?: DeBridgeAdapter; + public layerZero?: LayerZeroAdapter; + public stargate?: StargateAdapter; + public meson?: MesonAdapter; + + private supportedBridges: BridgeType[] = [ + 'cBridge', + 'deBridge', + 'layerZero', + 'meson', + 'stargate', + ]; - this.options = options; + private options: CanonicalBridgeSDKOptions = {} as CanonicalBridgeSDKOptions; + + constructor(options?: CanonicalBridgeSDKOptions) { + this.setSDKOptions(options); + } + + public setSDKOptions(options?: CanonicalBridgeSDKOptions) { + const { chains, ...restOptions } = options ?? {}; + + const finalChains = + options?.chains.map((item) => ({ + ...item, + chainType: item.chainType ? item.chainType : 'evm', + })) ?? []; + + this.options = { + chains: finalChains, + assetPrefix: '', + brandChains: [], + externalChains: [], + displayTokenSymbols: {}, + adapters: [], + chainOrder: [], + tokenOrder: [], + ...restOptions, + }; + + this.options.adapters.forEach((adapter) => { + adapter.init({ + nativeCurrencies: this.getNativeCurrencies(), + includedChains: this.options.chains.map((item) => item.id), + brandChains: this.options.brandChains, + externalChains: this.options.externalChains, + displayTokenSymbols: this.options.displayTokenSymbols, + assetPrefix: this.options.assetPrefix, + }); + }); + + this.supportedBridges.forEach((bridgeType) => { + Object.defineProperty(this, bridgeType, { + get: () => { + const adapter = this.options.adapters.find( + (e) => e.bridgeType === bridgeType + ); + if (adapter) return adapter; + + throw Error( + `${bridgeType} adapter is not found, you should initialize an adapter before using` + ); + }, + }); + }); } - /** - * Get sdk options - * @returns {CanonicalBridgeSDKOptions} sdk options - */ public getSDKOptions() { return this.options; } - /** - * Get supported bridges - * @returns {BridgeType[]} A string list of supported bridges - */ - public getSupportedBridges() { - return this.options.bridgeConfigs.map((item) => item.bridgeType); + public getNativeCurrencies() { + const nativeCurrencies: Record = {}; + + this.options.chains.forEach((chain) => { + if (chain.id && chain.nativeCurrency) { + nativeCurrencies[chain.id] = chain.nativeCurrency; + } + }); + + return nativeCurrencies; } /** @@ -202,8 +234,8 @@ export class CanonicalBridgeSDK { sendValue: bigint; fromTokenSymbol: string; publicClient?: PublicClient; - endPointId?: BridgeEndpointId; - bridgeAddress?: BridgeAddress; + endPointId?: IBridgeEndpointId; + bridgeAddress?: IBridgeAddress; isPegged?: boolean; slippage?: number; mesonOpts?: IGetMesonEstimateFeeInput; @@ -309,9 +341,9 @@ export class CanonicalBridgeSDK { walletClient: WalletClient; publicClient: PublicClient; slippage?: number; - peggedConfig?: CBridgePeggedPairConfig; + peggedConfig?: ICBridgePeggedPairConfig; deBridgeData?: `0x${string}`; - bridgeEndPointId?: BridgeEndpointId; + bridgeEndPointId?: IBridgeEndpointId; debridgeOpts?: { data?: `0x${string}`; }; @@ -406,4 +438,125 @@ export class CanonicalBridgeSDK { throw new Error('Invalid bridge inputs'); } } + + public getFromChains() { + const chainMap = new Map(); + + this.options.adapters.forEach((adapter) => { + const fromChains = adapter.getFromChains(); + + fromChains.forEach((item: any) => { + const chainId = adapter.getChainId(item); + const chainConfig = this.options.chains.find((e) => e.id === chainId); + + if (chainConfig) { + let bridgeChain = chainMap.get(chainId); + if (!bridgeChain) { + bridgeChain = { + isCompatible: true, + ...adapter.getChainInfo({ + chainId, + chainConfig, + }), + }; + chainMap.set(chainId, bridgeChain); + } + } + }); + }); + + return Array.from(chainMap.values()); + } + + public getToChains({ fromChainId }: { fromChainId: number }) { + const chainMap = new Map(); + + this.options.adapters.forEach((adapter) => { + const toChains = adapter.getToChains({ + fromChainId, + }); + + toChains.forEach((item: any) => { + const chainId = adapter.getChainId(item); + const chainConfig = this.options.chains.find((e) => e.id === chainId); + + if (chainConfig) { + const isCompatible = adapter.isToChainCompatible({ + fromChainId, + toChainId: chainId, + }); + + let bridgeChain = chainMap.get(chainId); + if (!bridgeChain) { + bridgeChain = { + isCompatible, + ...adapter.getChainInfo({ + chainId, + chainConfig, + }), + }; + } else { + bridgeChain = { + ...bridgeChain, + isCompatible: bridgeChain.isCompatible || isCompatible, + }; + } + + chainMap.set(chainId, bridgeChain); + } + }); + }); + + return Array.from(chainMap.values()); + } + + public getTokens({ + fromChainId, + toChainId, + }: { + fromChainId: number; + toChainId: number; + }) { + const tokenMap = new Map(); + + this.options.adapters.forEach((adapter) => { + const tokenPairs = adapter.getTokenPairs({ + fromChainId, + toChainId, + }); + + tokenPairs.forEach((item: ITransferTokenPair) => { + const { fromToken, fromChainId, toChainId } = item; + + const baseInfo = adapter.getTokenInfo({ + chainId: fromChainId, + token: fromToken, + }); + + const isCompatible = adapter.isTokenCompatible({ + fromChainId, + toChainId, + tokenSymbol: baseInfo.displaySymbol, + }); + + let bridgeToken = tokenMap.get(baseInfo.displaySymbol.toUpperCase()); + + if (!bridgeToken) { + bridgeToken = { + ...baseInfo, + isPegged: !!item.isPegged, + isCompatible, + }; + } else { + bridgeToken = { + ...bridgeToken, + isCompatible: bridgeToken.isCompatible || isCompatible, + }; + } + tokenMap.set(baseInfo.displaySymbol.toUpperCase(), bridgeToken); + }); + }); + + return Array.from(tokenMap.values()); + } } diff --git a/packages/canonical-bridge-sdk/src/aggregator/types.ts b/packages/canonical-bridge-sdk/src/aggregator/types.ts new file mode 100644 index 00000000..1bc094e0 --- /dev/null +++ b/packages/canonical-bridge-sdk/src/aggregator/types.ts @@ -0,0 +1,129 @@ +import { IBridgeTokenBaseInfo } from '@/adapters/base/types'; +import { + ICBridgeChain, + ICBridgePeggedPairConfig, + ICBridgeToken, +} from '@/adapters/cBridge/types'; +import { IDeBridgeChain, IDeBridgeToken } from '@/adapters/deBridge/types'; +import { ILayerZeroChain, ILayerZeroToken } from '@/adapters/layerZero/types'; +import { IMesonChain, IMesonToken } from '@/adapters/meson/types'; +import { IStargateChain, IStargateToken } from '@/adapters/stargate/types'; +import { PublicClient, WalletClient } from 'viem'; + +export type ChainType = 'link' | 'evm' | 'tron' | 'solana'; + +export type BridgeType = + | 'cBridge' + | 'deBridge' + | 'stargate' + | 'layerZero' + | 'meson'; + +export interface IBridgeChain { + id: number; + name: string; + icon?: string; + explorerUrl: string; + rpcUrl: string; + tokenUrlPattern: string; + chainType: ChainType; + externalBridgeUrl?: string; + isCompatible: boolean; + cBridge?: { + raw?: ICBridgeChain; + }; + deBridge?: { + raw?: IDeBridgeChain; + }; + stargate?: { + raw?: IStargateChain; + }; + layerZero?: { + raw?: ILayerZeroChain; + }; + meson?: { + raw?: IMesonChain; + }; +} + +export interface IBridgeToken extends IBridgeTokenBaseInfo { + isPegged: boolean; + isCompatible: boolean; + cBridge?: IBridgeTokenBaseInfo & { + peggedConfig?: ICBridgePeggedPairConfig; + raw?: ICBridgeToken; + }; + deBridge?: IBridgeTokenBaseInfo & { + raw?: IDeBridgeToken; + }; + stargate?: IBridgeTokenBaseInfo & { + raw?: IStargateToken; + }; + layerZero?: IBridgeTokenBaseInfo & { + raw?: ILayerZeroToken; + }; + meson?: IBridgeTokenBaseInfo & { + raw?: IMesonToken; + }; +} + +export interface INativeCurrency { + name: string; + symbol: string; + decimals: number; +} + +export interface IExternalChain { + chainId: number; + bridgeUrl: string; + tokens: Record; +} + +export interface IChainConfig { + id: number; + name: string; + nativeCurrency: { + name: string; + symbol: string; + decimals: number; + }; + rpcUrl: string; + explorer: { + name: string; + url: string; + tokenUrlPattern?: string; + }; + contracts?: any; + chainType?: ChainType; +} + +export interface IApproveTokenInput { + walletClient: WalletClient; + tokenAddress: `0x${string}`; + amount: bigint; + address: `0x${string}`; + spender: `0x${string}`; +} + +export interface IGetAllowanceInput { + publicClient: PublicClient; + tokenAddress: `0x${string}`; + owner: `0x${string}`; + spender: `0x${string}`; +} + +export interface IGetTokenBalanceInput { + publicClient: PublicClient; + tokenAddress: `0x${string}`; + owner: `0x${string}`; +} + +export interface IBridgeEndpointId { + layerZeroV1?: number; + layerZeroV2?: number; +} + +export interface IBridgeAddress { + stargate?: `0x${string}`; + layerZero?: `0x${string}`; +} diff --git a/packages/canonical-bridge-sdk/src/cbridge/index.ts b/packages/canonical-bridge-sdk/src/cbridge/index.ts deleted file mode 100644 index af24d813..00000000 --- a/packages/canonical-bridge-sdk/src/cbridge/index.ts +++ /dev/null @@ -1,393 +0,0 @@ -import { - BaseBridgeConfig, - BaseBridgeConfigOptions, - CreateAdapterParameters, -} from '@/core/types'; -import { CLIENT_TIME_OUT } from '@/core/constants'; -import axios, { AxiosInstance } from 'axios'; -import { - CBridgeChain, - CBridgeEstimateAmountRequest, - CBridgeEstimateAmountResponse, - CBridgePeggedPairConfig, - CBridgeSendRangeInput, - CBridgeTransferConfigs, - CBridgeTransferEstimatedTime, - IGetCBridgeABI, - IGetCBridgeTransferAddressInput, - IGetCBridgeTransferFunction, - IGetCBridgeTransferParamsInput, - ISendCBridgeToken, -} from '@/cbridge/types'; -import { getContract, Hash } from 'viem'; -import { POOL_TRANSFER_BRIDGE } from '@/cbridge/abi/poolTransferBridge'; -import { ORIGINAL_TOKEN_VAULT } from '@/cbridge/abi/originalTokenVault'; -import { ORIGINAL_TOKEN_VAULT_V2 } from '@/cbridge/abi/originalTokenVaultV2'; -import { PEGGED_TOKEN_BRIDGE } from '@/cbridge/abi/peggedTokenBridge'; -import { PEGGED_TOKEN_BRIDGE_V2 } from '@/cbridge/abi/peggedTokenBridgeV2'; -import { createAdapter } from '@/cbridge/utils/createAdapter'; - -export * from './types'; - -export function cBridgeConfig( - options: BaseBridgeConfigOptions -): BaseBridgeConfig { - return { - bridgeType: 'cBridge', - timeout: CLIENT_TIME_OUT, - ...options, - }; -} - -export class CBridge { - private client?: AxiosInstance; - - constructor(config: BaseBridgeConfig) { - const { timeout, endpoint } = config; - - this.client = axios.create({ - timeout, - baseURL: endpoint, - }); - } - - // https://cbridge-docs.celer.network/developer/api-reference/gateway-estimateamt - async getEstimatedAmount(params: CBridgeEstimateAmountRequest) { - return ( - await this.client!.get(`v2/estimateAmt`, { - params, - }) - ).data; - } - - /** - * Get estimated waiting time for cross-chain transfer - * - * @param number srcChainId source chain ID - * @param number dstChainId destination chain ID - */ - async getEstimatedWaitingTime({ - srcChainId, - dstChainId, - }: { - srcChainId: number; - dstChainId: number; - }) { - const params = { - src_chain_id: srcChainId, - dst_chain_id: dstChainId, - }; - return ( - await this.client!.get( - `v2/getLatest7DayTransferLatencyForQuery`, - { - params, - } - ) - ).data; - } - - /** - * Get minimum and maximum token transfer - * Only get minimum and maximum send amount - * @param {Address} bridgeAddress - Bridge address - * @param {Address} tokenAddress - Token address - * @param {PublicClient} client - Public client - * @returns {Object} min and max amount - */ - async getSendRange({ - bridgeAddress, - tokenAddress, - client, - }: CBridgeSendRangeInput): Promise<{ min: bigint; max: bigint }> { - const contract = getContract({ - address: bridgeAddress, - abi: POOL_TRANSFER_BRIDGE, - client: client, - }); - try { - const minAmount = await contract.read.minSend([tokenAddress]); - const maxAmount = await contract.read.maxSend([tokenAddress]); - return { - min: minAmount, - max: maxAmount, - }; - } catch (error: any) { - throw new Error( - `Failed to get cBridge minimum and maximum transfer amount: ${error}` - ); - } - } - - /** - * Send token through CBridge - * @param {WalletClient} walletClient Wallet client - * @param {PublicClient} publicClient Wallet client - * @param {Address} bridgeAddress Bridge address - * @param {Object[]} bridgeABI Bridge ABI - * @param {String} functionName Function name - * @param {Address} address wallet/account address - * @param {Object[]} args Function arguments - * @returns - */ - async sendToken({ - walletClient, - publicClient, - bridgeAddress, - fromChainId, - address, - isPegged, - isNativeToken, - peggedConfig, - args, - }: ISendCBridgeToken): Promise { - try { - const transferType = this.getTransferType({ - peggedConfig, - fromChainId, - }); - const ABI = this.getABI({ - isPegged, - transferType, - peggedConfig, - }); - const functionName = this.getTransferFunction({ - isPegged, - isNativeToken, - transferType, - }); - let cBridgeArgs = null; - if (isNativeToken) { - cBridgeArgs = { - address: bridgeAddress, - abi: ABI, - functionName, - account: address, - args, - value: args[1], - }; - } else { - cBridgeArgs = { - address: bridgeAddress, - abi: ABI, - functionName, - account: address, - args, - }; - } - const gas = await publicClient.estimateContractGas(cBridgeArgs as any); - const gasPrice = await publicClient.getGasPrice(); - const hash = await walletClient.writeContract({ - ...(cBridgeArgs as any), - gas, - gasPrice, - }); - return hash; - } catch (error) { - throw new Error(`Failed to send CBridge token: ${error}`); - } - } - - /** - * Get cBridge contract address from cross chain transfer - * - * @param fromChainId Chain ID of the source chain - * @param isPegged Pool-based transfer(xLiquidity) - false - * Canonical Mapping Transfer(xAsset) - true - * @param peggedConfig Pegged pair configuration - * @param chainConfig Chain configuration - */ - getTransferAddress({ - fromChainId, - isPegged, - peggedConfig, - chainConfig, - }: IGetCBridgeTransferAddressInput) { - if (isPegged) { - if (peggedConfig?.org_chain_id === fromChainId) { - // cBridge deposit - return peggedConfig.pegged_deposit_contract_addr; - } else if (peggedConfig?.pegged_chain_id === fromChainId) { - // cBridge burn - return peggedConfig.pegged_burn_contract_addr; - } - } else { - if (chainConfig?.contract_addr) { - return chainConfig?.contract_addr; - } - } - throw new Error('No cBridge bridge address found'); - } - - /** - * Get cBridge transfer parameters - * - * @param amount Send amount - * @param isPegged Pool-based transfer(xLiquidity) - false - * Canonical Mapping Transfer(xAsset) - true - * @param toChainId Chain ID of the destination chain - * @param tokenAddress Address of ERC20 token - * @param address User address - * @param maxSlippage Maximum slippage - * @param transferType Transfer type - deposit | withdraw - * @param peggedConfig Pegged pair configuration - * @param nonce Nonce current timestamp - */ - getTransferParams({ - amount, - isPegged, - isNativeToken = false, - toChainId, - tokenAddress, - address, - maxSlippage, - transferType, - peggedConfig, - nonce, - }: IGetCBridgeTransferParamsInput) { - return isPegged === false - ? isNativeToken - ? [address, amount, toChainId, nonce, maxSlippage] - : [address, tokenAddress, amount, toChainId, nonce, maxSlippage] - : transferType === 'deposit' - ? [tokenAddress, amount, toChainId, address as `0x${string}`, nonce] - : transferType === 'withdraw' - ? peggedConfig?.bridge_version === 0 - ? [tokenAddress, amount, address as `0x${string}`, nonce] - : [tokenAddress, amount, toChainId, address as `0x${string}`, nonce] - : null; - } - - /** - * Get cross chain transfer ABI - * - * @param isPegged Pool-based transfer(xLiquidity) - false - * Canonical Mapping Transfer(xAsset) - true - * @param transferType Transfer type - deposit | withdraw - * @param peggedConfig Pegged pair configuration - */ - getABI({ isPegged, transferType, peggedConfig }: IGetCBridgeABI) { - return isPegged === false || !peggedConfig - ? POOL_TRANSFER_BRIDGE - : transferType === 'deposit' - ? peggedConfig?.vault_version === 0 - ? ORIGINAL_TOKEN_VAULT - : ORIGINAL_TOKEN_VAULT_V2 - : transferType === 'withdraw' - ? peggedConfig?.bridge_version === 0 - ? PEGGED_TOKEN_BRIDGE - : PEGGED_TOKEN_BRIDGE_V2 - : (undefined as any); - } - - /** - * Get cross chain transfer function name - * - * @param isPegged - * @returns string - */ - getTransferFunction({ - isPegged, - transferType, - isNativeToken = false, - }: IGetCBridgeTransferFunction) { - return isPegged === false - ? isNativeToken - ? 'sendNative' - : 'send' - : transferType === 'deposit' - ? 'deposit' - : transferType === 'withdraw' - ? 'burn' - : ''; - } - - /** @see createAdapter for implementation details */ - createAdapter(params: CreateAdapterParameters) { - return createAdapter(params); - } - - /** - * Get transfer type - */ - getTransferType({ - peggedConfig, - fromChainId, - }: { - fromChainId: number; - peggedConfig?: CBridgePeggedPairConfig; - }) { - if (peggedConfig?.org_chain_id === fromChainId) { - return 'deposit'; - } - if (peggedConfig?.pegged_chain_id === fromChainId) { - return 'withdraw'; - } - return undefined; - } - - /** - * Generate cBridge transfer arguments - */ - getArguments({ - isPegged, - peggedConfig, - chainConfig, - amount, - fromChainId, - toChainId, - tokenAddress, - userAddress, - maxSlippage, - nonce, - isNativeToken = false, - }: { - isPegged: boolean; - peggedConfig?: CBridgePeggedPairConfig; - chainConfig?: CBridgeChain; - amount: bigint; - fromChainId: number; - toChainId: number; - tokenAddress: `0x${string}`; - userAddress: `0x${string}`; - maxSlippage: number; - nonce: number; - isNativeToken?: boolean; - }) { - const transferType = this.getTransferType({ - peggedConfig, - fromChainId, - }); - const functionName = this.getTransferFunction({ isPegged }); - const bridgeABI = this.getABI({ - isPegged, - transferType: transferType, - peggedConfig, - }); - const bridgeAddress = this.getTransferAddress({ - fromChainId: toChainId, - isPegged, - peggedConfig, - chainConfig, - }); - const args = this.getTransferParams({ - amount, - isPegged, - toChainId, - tokenAddress, - address: userAddress, - maxSlippage, - transferType, - peggedConfig, - nonce, - isNativeToken, - }); - return { - address: bridgeAddress as `0x${string}`, - abi: bridgeABI, - functionName: functionName, - account: userAddress as `0x${string}`, - args: args, - }; - } -} diff --git a/packages/canonical-bridge-sdk/src/cbridge/utils/createAdapter.ts b/packages/canonical-bridge-sdk/src/cbridge/utils/createAdapter.ts deleted file mode 100644 index 3a0e89d2..00000000 --- a/packages/canonical-bridge-sdk/src/cbridge/utils/createAdapter.ts +++ /dev/null @@ -1,361 +0,0 @@ -import { - CBridgeBurnPairConfig, - CBridgeChain, - CBridgePeggedPairConfig, - CBridgeToken, - CBridgeTransferConfigs, -} from '@/cbridge/types'; -import { - CreateAdapterParameters, - NativeCurrency, - TransferTokenPair, -} from '@/core/types'; -import { createBridgeAdapter } from '@/core/utils/createBridgeAdapter'; - -/** - * Create a bridge adapter based on provided configurations - * - * @param {CBridgeTransferConfigs} configs cBridge transfer configs - * @param {number[]} [excludedChains] Optional chain IDs that should be excluded - * @param {Record} [excludedTokens] Optional tokens that should be excluded - * @param {Record} [nativeCurrencies] Optional nativeCurrencies, the information to exclude native tokens - * @param {string[][]} [bridgedTokenGroups] Optional bridgedTokenGroups, tokens within a group can be swapped with each other - * - * @returns An adapter object contains normalized configuration of the bridge - */ -export function createAdapter({ - configs, - excludedChains = [], - excludedTokens = {}, - nativeCurrencies = {}, - bridgedTokenGroups = [], -}: CreateAdapterParameters) { - const { chains, chainMap } = getChainConfigs({ - configs, - excludedChains, - }); - - const { chainTokensMap, chainSymbolTokenMap } = getTokenConfigs({ - configs, - chainMap, - nativeCurrencies, - excludedTokens, - }); - - const peggedPairConfigs = getPeggedPairConfigs({ - peggedPairConfigs: configs.pegged_pair_configs, - chainMap, - nativeCurrencies, - excludedTokens, - }); - - const burnPairConfigs = getBurnPairConfigs(peggedPairConfigs); - - const transferMap = getTransferMap({ - chains, - peggedPairConfigs, - chainTokensMap, - chainSymbolTokenMap, - bridgedTokenGroups, - }); - - const supportedChains = chains.filter((chain) => transferMap.has(chain.id)); - - return { - ...createBridgeAdapter({ - bridgeType: 'cBridge', - supportedChains, - transferMap, - getChainId(chain: CBridgeChain) { - return chain.id; - }, - getTokenInfo(token: CBridgeToken) { - return { - name: token.name, - address: token.token.address, - decimal: token.token.decimal, - symbol: token.token.symbol, - }; - }, - }), - getPeggedPairConfigs() { - return peggedPairConfigs; - }, - getBurnPairConfigs() { - return burnPairConfigs; - }, - }; -} - -/** - * Get available chains - */ -function getChainConfigs(params: { - configs: CBridgeTransferConfigs; - excludedChains: number[]; -}) { - const { configs, excludedChains } = params; - const { chains, chain_token, pegged_pair_configs } = configs; - - const filteredChains = chains.filter((chain) => { - const isExcludedChain = excludedChains.includes(chain.id); - const hasEnabledToken = chain_token[chain.id]?.token?.some( - (e) => !e.token.xfer_disabled - ); - const hasPeggedToken = pegged_pair_configs.some( - (e) => - (e.org_chain_id === chain.id || e.pegged_chain_id === chain.id) && - !e.org_token.token.xfer_disabled && - !e.pegged_token.token.xfer_disabled - ); - return !isExcludedChain && (hasEnabledToken || hasPeggedToken); - }); - - const chainMap = new Map(); - filteredChains.forEach((chain) => { - chainMap.set(chain.id, chain); - }); - - return { - chains: filteredChains, - chainMap, - }; -} - -/** - * Get available tokens - */ -function getTokenConfigs(params: { - configs: CBridgeTransferConfigs; - chainMap: Map; - excludedTokens: Record; - nativeCurrencies: Record; -}) { - const { configs, chainMap, nativeCurrencies, excludedTokens } = params; - const { chain_token } = configs; - - const chainTokensMap = new Map(); - const chainSymbolTokenMap = new Map>(); - Object.entries(chain_token).forEach(([id, { token: tokens }]) => { - const chainId = Number(id); - - const filteredTokens = tokens.filter((token) => { - const isEnabledToken = !token.token.xfer_disabled; - const isNativeToken = - nativeCurrencies[chainId]?.symbol === token.token.symbol; - const isExcludedToken = excludedTokens?.[chainId]?.includes( - token.token.symbol - ); - return isEnabledToken && !isNativeToken && !isExcludedToken; - }); - - if (filteredTokens.length > 0 && chainMap.has(chainId)) { - chainSymbolTokenMap.set(chainId, new Map()); - - filteredTokens.forEach((token) => { - chainSymbolTokenMap.get(chainId)?.set(token.token.symbol, token); - }); - - chainTokensMap.set(chainId, filteredTokens); - } - }); - - return { - chainTokensMap, - chainSymbolTokenMap, - }; -} - -/** - * Get available pegged pair configs - */ -function getPeggedPairConfigs(params: { - peggedPairConfigs: CBridgePeggedPairConfig[]; - chainMap: Map; - excludedTokens: Record; - nativeCurrencies: Record; -}) { - const { peggedPairConfigs, chainMap, excludedTokens, nativeCurrencies } = - params; - - const isAvailablePair = (chainId: number, token: CBridgeToken) => { - const hasChain = chainMap.has(chainId); - const isEnabledToken = !token.token.xfer_disabled; - const isNativeToken = - nativeCurrencies[chainId]?.symbol === token.token.symbol; - const isExcludedToken = excludedTokens[chainId]?.includes( - token.token.symbol - ); - return hasChain && isEnabledToken && !isNativeToken && !isExcludedToken; - }; - - const filteredPeggedPairConfigs = peggedPairConfigs.filter( - (item) => - isAvailablePair(item.org_chain_id, item.org_token) && - isAvailablePair(item.pegged_chain_id, item.pegged_token) - ); - - return filteredPeggedPairConfigs; -} - -/** - * Get available burn pair configs - */ -function getBurnPairConfigs(peggedPairConfigs: CBridgePeggedPairConfig[]) { - const burnPairConfigs: CBridgeBurnPairConfig[] = []; - - for (let i = 0; i < peggedPairConfigs.length; i++) { - for (let j = i + 1; j < peggedPairConfigs.length; j++) { - const A = peggedPairConfigs[i]; - const B = peggedPairConfigs[j]; - if ( - A.org_chain_id === B.org_chain_id && - A.org_token.token.symbol === B.org_token.token.symbol - ) { - /// Only upgraded PegBridge can support multi burn to other pegged chain - if (A.bridge_version === 2 && B.bridge_version === 2) { - burnPairConfigs.push({ - burn_config_as_org: { - chain_id: A.pegged_chain_id, - token: A.pegged_token, - burn_contract_addr: A.pegged_burn_contract_addr, - canonical_token_contract_addr: A.canonical_token_contract_addr, - burn_contract_version: A.bridge_version, - }, - burn_config_as_dst: { - chain_id: B.pegged_chain_id, - token: B.pegged_token, - burn_contract_addr: B.pegged_burn_contract_addr, - canonical_token_contract_addr: B.canonical_token_contract_addr, - burn_contract_version: B.bridge_version, - }, - }); - burnPairConfigs.push({ - burn_config_as_org: { - chain_id: B.pegged_chain_id, - token: B.pegged_token, - burn_contract_addr: B.pegged_burn_contract_addr, - canonical_token_contract_addr: B.canonical_token_contract_addr, - burn_contract_version: B.bridge_version, - }, - burn_config_as_dst: { - chain_id: A.pegged_chain_id, - token: A.pegged_token, - burn_contract_addr: A.pegged_burn_contract_addr, - canonical_token_contract_addr: A.canonical_token_contract_addr, - burn_contract_version: A.bridge_version, - }, - }); - } - } - } - } - - return burnPairConfigs; -} - -/** - * Exhaust all transferable cases and return a transfer map containing all possible transaction paths - */ -function getTransferMap(params: { - chains: CBridgeChain[]; - peggedPairConfigs: CBridgePeggedPairConfig[]; - chainTokensMap: Map; - chainSymbolTokenMap: Map>; - bridgedTokenGroups: string[][]; -}) { - const { chains, peggedPairConfigs, chainTokensMap, chainSymbolTokenMap } = - params; - - // [fromChainId][toChainId][tokenSymbol]{fromToken, toToken, isPegged, peggedConfig} - const transferMap = new Map< - number, - Map> - >(); - - chains.forEach((fromChain) => { - chains.forEach((toChain) => { - if (fromChain.id !== toChain.id) { - const fromTokens = chainTokensMap.get(fromChain.id) ?? []; - - const transferableTokenMap = new Map(); - fromTokens.forEach((fromToken) => { - const toToken = chainSymbolTokenMap - .get(toChain.id) - ?.get(fromToken.token.symbol); - if (toToken) { - const tokenPair = { - fromTokenAddress: fromToken.token.address, - toTokenAddress: toToken.token.address, - fromToken, - toToken, - }; - transferableTokenMap.set(fromToken.token.symbol, tokenPair); - } - }); - - if (transferableTokenMap.size > 0) { - if (!transferMap.has(fromChain.id)) { - transferMap.set( - fromChain.id, - new Map>() - ); - } - transferMap.get(fromChain.id)?.set(toChain.id, transferableTokenMap); - } - } - }); - }); - - const addPeggedTokenPair = ( - fromChainId: number, - fromToken: CBridgeToken, - toChainId: number, - toToken: CBridgeToken, - item: CBridgePeggedPairConfig - ) => { - if ( - !transferMap.get(fromChainId)?.get(toChainId)?.get(fromToken.token.symbol) - ) { - if (!transferMap.has(fromChainId)) { - transferMap.set( - fromChainId, - new Map>() - ); - } - - const peggedTokenPair: TransferTokenPair = { - fromTokenAddress: fromToken.token.address, - toTokenAddress: toToken.token.address, - fromToken, - toToken, - isPegged: true, - peggedConfig: item, - }; - - if (transferMap.get(fromChainId)?.get(toChainId)) { - transferMap - .get(fromChainId) - ?.get(toChainId) - ?.set(fromToken.token.symbol, peggedTokenPair); - } else { - const transferableTokenMap = new Map(); - transferableTokenMap.set(fromToken.token.symbol, peggedTokenPair); - transferMap.get(fromChainId)?.set(toChainId, transferableTokenMap); - } - } - }; - - peggedPairConfigs.forEach((item) => { - const fromChainId = item.org_chain_id; - const fromToken = item.org_token; - - const toChainId = item.pegged_chain_id; - const toToken = item.pegged_token; - - addPeggedTokenPair(fromChainId, fromToken, toChainId, toToken, item); - addPeggedTokenPair(toChainId, toToken, fromChainId, fromToken, item); - }); - - return transferMap; -} diff --git a/packages/canonical-bridge-sdk/src/core/constants/index.ts b/packages/canonical-bridge-sdk/src/constants/index.ts similarity index 55% rename from packages/canonical-bridge-sdk/src/core/constants/index.ts rename to packages/canonical-bridge-sdk/src/constants/index.ts index ad959ee2..c86735da 100644 --- a/packages/canonical-bridge-sdk/src/core/constants/index.ts +++ b/packages/canonical-bridge-sdk/src/constants/index.ts @@ -1,3 +1,10 @@ +export const env = { + CBRIDGE_ENDPOINT: 'https://cbridge-prod2.celer.app', + DEBRIDGE_ENDPOINT: 'https://deswap.debridge.finance/v1.0', + DEBRIDGE_STATS_ENDPOINT: 'https://stats-api.dln.trade/api', + MESON_ENDPOINT: 'https://relayer.meson.fi/api/v1', +}; + export const CLIENT_TIME_OUT = 60 * 1000; export const ESTIMATE_AMOUNT_DATA_RELOAD = 30000; diff --git a/packages/canonical-bridge-sdk/src/core/types/index.ts b/packages/canonical-bridge-sdk/src/core/types/index.ts deleted file mode 100644 index 5d69f40d..00000000 --- a/packages/canonical-bridge-sdk/src/core/types/index.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { type PublicClient, type WalletClient } from 'viem'; - -export type BridgeType = - | 'cBridge' - | 'deBridge' - | 'stargate' - | 'layerZero' - | 'meson'; - -export interface BaseBridgeConfigOptions { - timeout?: number; - endpoint: string; -} - -export interface BaseBridgeConfig extends BaseBridgeConfigOptions { - bridgeType: BridgeType; -} - -export interface IApproveTokenInput { - walletClient: WalletClient; - tokenAddress: `0x${string}`; - amount: bigint; - address: `0x${string}`; - spender: `0x${string}`; -} - -export interface IGetAllowanceInput { - publicClient: PublicClient; - tokenAddress: `0x${string}`; - owner: `0x${string}`; - spender: `0x${string}`; -} - -export interface IGetTokenBalanceInput { - publicClient: PublicClient; - tokenAddress: `0x${string}`; - owner: `0x${string}`; -} - -export interface NativeCurrency { - name: string; - symbol: string; - decimals: number; -} - -export interface TransferTokenPair { - fromTokenAddress: string; - toTokenAddress: string; - fromToken: any; - toToken: any; - isPegged?: boolean; - peggedConfig?: any; -} - -export interface CreateAdapterParameters { - configs: T; - excludedChains?: number[]; - excludedTokens?: Record; - nativeCurrencies?: Record; - bridgedTokenGroups?: string[][]; -} - -export interface BridgeAdapter { - bridgeType: BridgeType; - supportedChains: T[]; - transferMap: Map>>; - getChainId: (chain: T) => number; - getTokenInfo: (token: P) => { - name: string; - address: string; - decimal: number; - symbol: string; - }; -} - -export interface BridgeEndpointId { - layerZeroV1?: number; - layerZeroV2?: number; -} - -export interface BridgeAddress { - stargate?: `0x${string}`; - layerZero?: `0x${string}`; -} diff --git a/packages/canonical-bridge-sdk/src/core/utils/address.ts b/packages/canonical-bridge-sdk/src/core/utils/address.ts deleted file mode 100644 index f69f6324..00000000 --- a/packages/canonical-bridge-sdk/src/core/utils/address.ts +++ /dev/null @@ -1,11 +0,0 @@ -export function isSameAddress(A?: string, B?: string) { - if (isEvmAddress(A) && isEvmAddress(B)) { - return A?.toLowerCase() === B?.toLowerCase(); - } - - return false; -} - -export function isEvmAddress(address?: string) { - return !!address && /^0x[a-f0-9]{40}$/i.test(address); -} diff --git a/packages/canonical-bridge-sdk/src/core/utils/createBridgeAdapter.ts b/packages/canonical-bridge-sdk/src/core/utils/createBridgeAdapter.ts deleted file mode 100644 index 2b9514c8..00000000 --- a/packages/canonical-bridge-sdk/src/core/utils/createBridgeAdapter.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { BridgeAdapter } from '@/core/types'; - -export function createBridgeAdapter( - params: BridgeAdapter -) { - return params; -} diff --git a/packages/canonical-bridge-sdk/src/debridge/index.ts b/packages/canonical-bridge-sdk/src/debridge/index.ts deleted file mode 100644 index b643bd58..00000000 --- a/packages/canonical-bridge-sdk/src/debridge/index.ts +++ /dev/null @@ -1,184 +0,0 @@ -import { createAdapter } from '@/debridge/utils/createAdapter'; -import { CLIENT_TIME_OUT } from '@/core/constants'; -import { - BaseBridgeConfigOptions, - BaseBridgeConfig, - CreateAdapterParameters, -} from '@/core/types'; -import { - DeBridgeCreateQuoteResponse, - DeBridgeTransferConfigs, - IDeBridgeEstimatedFeesInput, - ISendDebridgeTokenInput, -} from '@/debridge/types'; -import axios, { AxiosInstance } from 'axios'; -import { Hash } from 'viem'; - -export * from './types'; - -export interface DeBridgeConfigOptions extends BaseBridgeConfigOptions { - statsEndpoint: string; -} - -export interface DeBridgeConfig extends BaseBridgeConfig { - statsEndpoint: string; -} - -export function deBridgeConfig(options: DeBridgeConfigOptions): DeBridgeConfig { - return { - bridgeType: 'deBridge', - timeout: CLIENT_TIME_OUT, - ...options, - }; -} - -export class DeBridge { - private client?: AxiosInstance; - private statsClient?: AxiosInstance; - - constructor(config: DeBridgeConfigOptions) { - const { timeout, endpoint, statsEndpoint } = config; - - this.client = axios.create({ - timeout, - baseURL: endpoint, - }); - - this.statsClient = axios.create({ - timeout, - baseURL: statsEndpoint, - }); - } - - // https://deswap.debridge.finance/v1.0/#/DLN/DlnOrderControllerV10_createOrder - async createTxQuote(urlParams: any) { - return ( - await this.client!.get( - `/dln/order/create-tx?${urlParams.toString()}` - ) - ).data; - } - - /** - * Get estimated fees from transaction quote API - * @param {number} fromChainId - Chain ID of the source chain - * @param {Address} fromTokenAddress - Address of ERC20 token on the source chain - * @param {BigInt} amount - Send amount - * @param {number} toChainId - Chain ID of the destination chain - * @param {Address} toTokenAddress - Address of ERC20 token on the destination chain - * @param {Address} userAddress - user/account address - */ - async getEstimatedFees({ - fromChainId, - fromTokenAddress, - amount, - toChainId, - toTokenAddress, - userAddress, - toUserAddress, - affiliateFeePercent = 0, - accesstoken = '', - prependOperatingExpenses = false, - }: IDeBridgeEstimatedFeesInput): Promise { - try { - const deBridgeParams = { - srcChainId: fromChainId, - srcChainTokenIn: fromTokenAddress, - srcChainTokenInAmount: amount, - dstChainId: toChainId, - dstChainTokenOut: toTokenAddress, - prependOperatingExpenses, - affiliateFeePercent, - dstChainTokenOutRecipient: toUserAddress || userAddress, - dstChainOrderAuthorityAddress: toUserAddress || userAddress, - srcChainOrderAuthorityAddress: userAddress, - } as any; - - if (accesstoken) { - deBridgeParams.accesstoken = accesstoken; - } - const urlParams = new URLSearchParams(deBridgeParams as any); - const deBridgeQuote = await this.createTxQuote(urlParams); - return deBridgeQuote; - } catch (error: any) { - if (error.response?.data?.errorMessage) { - throw new Error(error.response.data.errorId); - } else { - throw new Error(`${error.message || error}`); - } - } - } - - // https://deswap.debridge.finance/v1.0/#/DLN/DlnOrderControllerV10_getOrder - async getOrder({ id }: { id: string }) { - return (await this.client!.get(`/dln/order/${id}`)).data; - } - - // https://stats-api.dln.trade/swagger/index.html - /** - * Get list of orders by filters - * @param address Account address - * @param pageId Page number - * @param pageSize Records per page - * @param fromChainIds Source chain IDs - * @param toChainIds Destination chain IDs - */ - async getStatsHistory({ - address, - pageId = 0, - pageSize = 20, - fromChainIds = [], - toChainIds = [], - }: { - address: string; - pageId: number; - pageSize: number; - fromChainIds: number[]; - toChainIds: number[]; - }) { - return ( - await this.statsClient!.post('/Orders/filteredList', { - filter: address, - skip: pageId, // page number - take: pageSize, // data per page - giveChainIds: fromChainIds, - takeChainIds: toChainIds, - }) - ).data; - } - - /** - * Send token via DeBridge - * @param {WalletClient} walletClient Wallet client - * @param {Address} bridgeAddress Bridge address - * @param {String} data Transaction data - * @param {BigInt} amount Send amount - * @param {Address} address wallet/account address - * @returns {Hash} transaction hash - */ - async sendToken({ - walletClient, - bridgeAddress, - data, - amount, - address, - }: ISendDebridgeTokenInput): Promise { - try { - const hash = await walletClient.sendTransaction({ - to: bridgeAddress as `0x${string}`, - data: data, - value: amount, - account: address, - chain: walletClient.chain, - }); - return hash; - } catch (error) { - throw new Error(`Failed to send DeBridge token: ${error}`); - } - } - - /** @see createAdapter for implementation details */ - createAdapter(params: CreateAdapterParameters) { - return createAdapter(params); - } -} diff --git a/packages/canonical-bridge-sdk/src/debridge/utils/createAdapter.ts b/packages/canonical-bridge-sdk/src/debridge/utils/createAdapter.ts deleted file mode 100644 index a37bd593..00000000 --- a/packages/canonical-bridge-sdk/src/debridge/utils/createAdapter.ts +++ /dev/null @@ -1,222 +0,0 @@ -import { - CreateAdapterParameters, - NativeCurrency, - TransferTokenPair, -} from '@/core/types'; -import { createBridgeAdapter } from '@/core/utils/createBridgeAdapter'; -import { - DeBridgeChain, - DeBridgeToken, - DeBridgeTransferConfigs, - TokenQueryParams, -} from '@/debridge/types'; -import { isSameAddress } from '@/core/utils/address'; - -/** - * Create a bridge adapter based on provided configurations - * - * @param {DeBridgeTransferConfigs} configs deBridge transfer configs - * @param {number[]} [excludedChains] Optional chain IDs that should be excluded - * @param {Record} [excludedTokens] Optional tokens that should be excluded - * @param {Record} [nativeCurrencies] Optional nativeCurrencies, the information to exclude native tokens - * @param {string[][]} [bridgedTokenGroups] Optional bridgedTokenGroups, tokens within a group can be swapped with each other - * - * @returns An adapter object contains normalized configuration of the bridge - */ -export function createAdapter({ - configs, - excludedChains = [], - excludedTokens = {}, - nativeCurrencies = {}, - bridgedTokenGroups = [], -}: CreateAdapterParameters) { - const { chains, chainMap } = getChainConfigs({ - configs, - excludedChains, - }); - const { chainTokensMap, chainSymbolTokenMap } = getTokenConfigs({ - configs, - chainMap, - excludedTokens, - nativeCurrencies, - }); - const transferMap = getTransferMap({ - chains, - chainTokensMap, - chainSymbolTokenMap, - bridgedTokenGroups, - }); - const supportedChains = chains.filter((chain) => - transferMap.has(chain.chainId) - ); - - return { - ...createBridgeAdapter({ - bridgeType: 'deBridge', - supportedChains, - transferMap, - getChainId(chain: DeBridgeChain) { - return chain.chainId; - }, - getTokenInfo(token: DeBridgeToken) { - return { - name: token.name, - address: token.address, - decimal: token.decimals, - symbol: token.symbol, - }; - }, - }), - getTokenByAddress(params: TokenQueryParams) { - const { chainId, address } = params; - if (chainId && address) { - const tokens = chainTokensMap.get(Number(chainId)); - const target = tokens?.find((item) => - isSameAddress(item.address, address) - ); - return target; - } - }, - }; -} - -/** - * Get available chains - */ -function getChainConfigs(params: { - configs: DeBridgeTransferConfigs; - excludedChains: number[]; -}) { - const { configs, excludedChains } = params; - const { chains, tokens } = configs; - - const filteredChains = chains.filter((chain) => { - const isExcludedChain = excludedChains.includes(chain.chainId); - const hasToken = tokens[chain.chainId]?.length > 0; - return !isExcludedChain && hasToken; - }); - - const chainMap = new Map(); - filteredChains.forEach((chain) => { - chainMap.set(chain.chainId, chain); - }); - - return { - chains: filteredChains, - chainMap, - }; -} - -/** - * Get available tokens - */ -function getTokenConfigs(params: { - configs: DeBridgeTransferConfigs; - chainMap: Map; - excludedTokens: Record; - nativeCurrencies: Record; -}) { - const { configs, chainMap, excludedTokens, nativeCurrencies } = params; - const { tokens } = configs; - - const chainTokensMap = new Map(); - const chainSymbolTokenMap = new Map>(); - Object.entries(tokens).forEach(([id, chainTokens]) => { - const chainId = Number(id); - - const filteredTokens = chainTokens.filter((token) => { - const isNativeToken = nativeCurrencies[chainId]?.symbol === token.symbol; - const isExcludedToken = excludedTokens[chainId]?.includes(token.symbol); - return !isNativeToken && !isExcludedToken; - }); - - if (filteredTokens.length > 0 && chainMap.has(chainId)) { - chainSymbolTokenMap.set(chainId, new Map()); - - filteredTokens.forEach((token) => { - chainSymbolTokenMap.get(chainId)?.set(token.symbol, token); - }); - - chainTokensMap.set(chainId, filteredTokens); - } - }); - - return { - chainTokensMap, - chainSymbolTokenMap, - }; -} - -/** - * Exhaust all transferable cases and return a transfer map containing all possible transaction paths - */ -function getTransferMap(params: { - chains: DeBridgeChain[]; - chainTokensMap: Map; - chainSymbolTokenMap: Map>; - bridgedTokenGroups: string[][]; -}) { - const { chains, chainTokensMap, chainSymbolTokenMap, bridgedTokenGroups } = - params; - - // [fromChainId][toChainId][tokenSymbol]{fromToken, toToken} - const transferMap = new Map< - number, - Map> - >(); - - const getToToken = (toChainId: number, tokenSymbol: string) => { - let toToken = chainSymbolTokenMap.get(toChainId)?.get(tokenSymbol); - if (!toToken) { - const bridgedGroup = bridgedTokenGroups.find((group) => - group.includes(tokenSymbol) - ); - const otherTokens = bridgedGroup?.filter((item) => item !== tokenSymbol); - - otherTokens?.forEach((symbol) => { - if (!toToken) { - toToken = chainSymbolTokenMap.get(toChainId)?.get(symbol); - } - }); - } - - return toToken; - }; - - chains.forEach((fromChain) => { - chains.forEach((toChain) => { - if (fromChain.chainId !== toChain.chainId) { - const fromTokens = chainTokensMap.get(fromChain.chainId) ?? []; - - const transferableTokenMap = new Map(); - fromTokens.forEach((fromToken) => { - const toToken = getToToken(toChain.chainId, fromToken.symbol); - - if (toToken) { - const tokenPair: TransferTokenPair = { - fromToken, - toToken, - fromTokenAddress: fromToken.address, - toTokenAddress: toToken.address, - }; - transferableTokenMap.set(fromToken.symbol, tokenPair); - } - }); - - if (transferableTokenMap.size > 0) { - if (!transferMap.has(fromChain.chainId)) { - transferMap.set( - fromChain.chainId, - new Map>() - ); - } - transferMap - .get(fromChain.chainId) - ?.set(toChain.chainId, transferableTokenMap); - } - } - }); - }); - - return transferMap; -} diff --git a/packages/canonical-bridge-sdk/src/index.ts b/packages/canonical-bridge-sdk/src/index.ts index fcd6fd30..c09e69e7 100644 --- a/packages/canonical-bridge-sdk/src/index.ts +++ b/packages/canonical-bridge-sdk/src/index.ts @@ -1,6 +1,16 @@ -export * from './cbridge'; -export * from './core'; -export * from './debridge'; -export * from './layerZero'; -export * from './stargate'; -export * from './meson'; +// adapters +export * from '@/adapters/base/exports'; +export * from '@/adapters/cBridge/exports'; +export * from '@/adapters/deBridge/exports'; +export * from '@/adapters/layerZero/exports'; +export * from '@/adapters/stargate/exports'; +export * from '@/adapters/meson/exports'; + +// aggregator +export * from '@/aggregator/exports'; + +// shared +export * from '@/shared/exports'; + +// abi +export * from '@/abi/exports'; diff --git a/packages/canonical-bridge-sdk/src/layerZero/index.ts b/packages/canonical-bridge-sdk/src/layerZero/index.ts deleted file mode 100644 index a441c588..00000000 --- a/packages/canonical-bridge-sdk/src/layerZero/index.ts +++ /dev/null @@ -1,141 +0,0 @@ -import { CreateAdapterParameters } from '@/core'; -import { formatNumber } from '@/core/utils/number'; -import { CAKE_PROXY_OFT_ABI } from '@/layerZero/abi/cakeProxyOFT'; -import { - IGetEstimateFeeInput, - ISendCakeTokenInput, - LayerZeroTransferConfigs, -} from '@/layerZero/types'; -import { createAdapter } from '@/layerZero/utils/createAdapter'; -import { encodePacked, formatUnits, Hash, pad, parseUnits } from 'viem'; - -export * from './types'; -export class LayerZero { - /** - * Send token through layerZero V1 OFT - * https://docs.layerzero.network/v1/developers/evm/evm-guides/advanced/relayer-adapter-parameters - * - * @param userAddress User address - * @param bridgeAddress Bridge address - * @param amount Send amount - * @param dstEndpoint Destination endpoint - * @param publicClient Public client - * @param walletClient Wallet client - * @param gasAmount Gas amount - * @param version Relayer adapter parameters version - */ - async sendToken({ - userAddress, - bridgeAddress, - amount, - dstEndpoint, - publicClient, - walletClient, - gasAmount = 200000n, - version = 1, - }: ISendCakeTokenInput): Promise { - try { - const address32Bytes = pad(userAddress, { size: 32 }); - const adapterParams = encodePacked( - ['uint16', 'uint256'], - [version, gasAmount] - ); - const fees = await publicClient.readContract({ - address: bridgeAddress, - abi: CAKE_PROXY_OFT_ABI, - functionName: 'estimateSendFee', - args: [ - dstEndpoint, - address32Bytes, - amount, - false, - adapterParams as `0x${string}`, - ], - }); - const callParams = [ - userAddress, - '0x0000000000000000000000000000000000000000', // zroPaymentAddress - adapterParams, - ]; - const nativeFee = fees[0]; - const minAmount = parseUnits( - String(formatNumber(Number(formatUnits(amount, 18)), 8)), - 18 - ); - const cakeArgs = { - address: bridgeAddress, - abi: CAKE_PROXY_OFT_ABI, - functionName: 'sendFrom', - args: [ - userAddress, - dstEndpoint, - address32Bytes, - amount, - minAmount, - callParams, - ], - value: nativeFee, - account: userAddress, - }; - const gas = await publicClient.estimateContractGas(cakeArgs as any); - const gasPrice = await publicClient.getGasPrice(); - const hash = await walletClient.writeContract({ - ...(cakeArgs as any), - gas, - gasPrice, - }); - return hash; - } catch (error: any) { - throw new Error(`Failed to send CAKE token ${error}`); - } - } - - /** - * Get estimate fee - * @param bridgeAddress Bridge address - * @param amount Send amount - * @param dstEndpoint Destination endpoint - * @param userAddress User address - * @param publicClient Public client - * @param gasAmount Gas amount - * @param version Relayer adapter parameters version - * @returns Estimate fee - */ - async getEstimateFee({ - bridgeAddress, - amount, - dstEndpoint, - userAddress, - publicClient, - gasAmount = 200000n, - version = 1, - }: IGetEstimateFeeInput) { - try { - const address32Bytes = pad(userAddress, { size: 32 }); - const adapterParams = encodePacked( - ['uint16', 'uint256'], - [version, gasAmount] - ); - const fees = await publicClient.readContract({ - address: bridgeAddress, - abi: CAKE_PROXY_OFT_ABI, - functionName: 'estimateSendFee', - args: [ - dstEndpoint, - address32Bytes, - amount, - false, - adapterParams as `0x${string}`, - ], - }); - return fees; - } catch (error: any) { - throw new Error(`Failed to get estimate fee ${error}`); - } - } - - /** @see createAdapter for implementation details */ - createAdapter(params: CreateAdapterParameters) { - return createAdapter(params); - } -} diff --git a/packages/canonical-bridge-sdk/src/layerZero/utils/createAdapter.ts b/packages/canonical-bridge-sdk/src/layerZero/utils/createAdapter.ts deleted file mode 100644 index a642ee12..00000000 --- a/packages/canonical-bridge-sdk/src/layerZero/utils/createAdapter.ts +++ /dev/null @@ -1,213 +0,0 @@ -import { - CreateAdapterParameters, - NativeCurrency, - TransferTokenPair, -} from '@/core/types'; -import { createBridgeAdapter } from '@/core/utils/createBridgeAdapter'; -import { - LayerZeroChain, - LayerZeroToken, - LayerZeroTransferConfigs, -} from '@/layerZero/types'; - -/** - * Create a bridge adapter based on provided configurations - * - * @param {LayerZeroTransferConfigs} [configs] LayerZero transfer configs - * @param {number[]} [excludedChains] Optional chain IDs that should be excluded - * @param {Record} [excludedTokens] Optional tokens that should be excluded - * @param {Record} [nativeCurrencies] Optional nativeCurrencies, the information to exclude native tokens - * @param {string[][]} [bridgedTokenGroups] Optional bridgedTokenGroups, tokens within a group can be swapped with each other - * - * @returns An adapter object contains normalized configuration of the bridge - */ -export function createAdapter({ - configs, - excludedChains = [], - excludedTokens = {}, - nativeCurrencies = {}, - bridgedTokenGroups = [], -}: CreateAdapterParameters) { - const { chains, chainMap } = getChainConfigs({ configs, excludedChains }); - - const { chainTokensMap, chainSymbolTokenMap } = getTokenConfigs({ - configs, - chainMap, - nativeCurrencies, - excludedTokens, - }); - - const transferMap = getTransferMap({ - chains, - chainTokensMap, - chainSymbolTokenMap, - bridgedTokenGroups, - }); - - const supportedChains = chains.filter((chain) => - transferMap.has(chain.chainId) - ); - - return { - ...createBridgeAdapter({ - bridgeType: 'layerZero', - supportedChains, - transferMap, - getChainId(chain: LayerZeroChain) { - return chain.chainId; - }, - getTokenInfo(token: LayerZeroToken) { - return { - name: '', - address: token.address, - decimal: token.decimals, - symbol: token.symbol, - }; - }, - }), - }; -} - -/** - * Get available chains - */ -function getChainConfigs(params: { - configs: LayerZeroTransferConfigs; - excludedChains: number[]; -}) { - const { configs, excludedChains } = params; - const { chains, tokens } = configs; - - const filteredChains = chains.filter((chain) => { - const isExcludedChain = excludedChains.includes(chain.chainId); - const hasToken = tokens[chain.chainId]?.length > 0; - return !isExcludedChain && hasToken; - }); - - const chainMap = new Map(); - filteredChains.forEach((chain) => { - chainMap.set(chain.chainId, chain); - }); - - return { - chains: filteredChains, - chainMap, - }; -} - -/** - * Get available tokens - */ -function getTokenConfigs(params: { - configs: LayerZeroTransferConfigs; - chainMap: Map; - excludedTokens: Record; - nativeCurrencies: Record; -}) { - const { configs, chainMap, excludedTokens, nativeCurrencies } = params; - const { tokens } = configs; - - const chainTokensMap = new Map(); - const chainSymbolTokenMap = new Map>(); - Object.entries(tokens).forEach(([id, tokens]) => { - const chainId = Number(id); - - const filteredTokens = tokens.filter((token) => { - const isNativeToken = nativeCurrencies[chainId]?.symbol === token.symbol; - const isExcludedToken = excludedTokens[chainId]?.includes(token.symbol); - return !isNativeToken && !isExcludedToken; - }); - - if (filteredTokens.length > 0 && chainMap.has(chainId)) { - chainSymbolTokenMap.set(chainId, new Map()); - - filteredTokens.forEach((token) => { - chainSymbolTokenMap.get(chainId)?.set(token.symbol, token); - }); - - chainTokensMap.set(chainId, filteredTokens); - } - }); - - return { - chainTokensMap, - chainSymbolTokenMap, - }; -} - -/** - * Exhaust all transferable cases and return a transfer map containing all possible transaction paths - */ -function getTransferMap(params: { - chains: LayerZeroChain[]; - chainTokensMap: Map; - chainSymbolTokenMap: Map>; - bridgedTokenGroups: string[][]; -}) { - const { chains, chainTokensMap, chainSymbolTokenMap, bridgedTokenGroups } = - params; - - // [fromChainId][toChainId][tokenSymbol]{fromToken, toToken} - const transferMap = new Map< - number, - Map> - >(); - - const getToToken = (toChainId: number, tokenSymbol: string) => { - let toToken = chainSymbolTokenMap.get(toChainId)?.get(tokenSymbol); - if (!toToken) { - const bridgedGroup = bridgedTokenGroups.find((group) => - group.includes(tokenSymbol) - ); - const otherTokens = bridgedGroup?.filter((item) => item !== tokenSymbol); - - otherTokens?.forEach((symbol) => { - if (!toToken) { - toToken = chainSymbolTokenMap.get(toChainId)?.get(symbol); - } - }); - } - - return toToken; - }; - - chains.forEach((fromChain) => { - chains.forEach((toChain) => { - if ( - fromChain.chainId !== toChain.chainId && - fromChain.network === toChain.network - ) { - const fromTokens = chainTokensMap.get(fromChain.chainId) ?? []; - - const transferableTokenMap = new Map(); - fromTokens.forEach((fromToken) => { - const toToken = getToToken(toChain.chainId, fromToken.symbol); - - if (toToken) { - const tokenPair: TransferTokenPair = { - fromToken, - toToken, - fromTokenAddress: fromToken.address, - toTokenAddress: toToken.address, - }; - transferableTokenMap.set(fromToken.symbol, tokenPair); - } - }); - - if (transferableTokenMap.size > 0) { - if (!transferMap.has(fromChain.chainId)) { - transferMap.set( - fromChain.chainId, - new Map>() - ); - } - transferMap - .get(fromChain.chainId) - ?.set(toChain.chainId, transferableTokenMap); - } - } - }); - }); - - return transferMap; -} diff --git a/packages/canonical-bridge-sdk/src/meson/index.ts b/packages/canonical-bridge-sdk/src/meson/index.ts deleted file mode 100644 index 01b7385f..00000000 --- a/packages/canonical-bridge-sdk/src/meson/index.ts +++ /dev/null @@ -1,101 +0,0 @@ -import axios, { AxiosInstance } from 'axios'; -import { - IGetMesonEstimateFeeInput, - IMesonEncodeSwapInput, - IMesonSendTokenInput, -} from '@/meson/types'; -import { BaseBridgeConfig } from '@/core'; - -export class Meson { - private client?: AxiosInstance; - - constructor(config: BaseBridgeConfig) { - const { timeout, endpoint } = config; - - this.client = axios.create({ - timeout, - baseURL: endpoint, - }); - } - - async getEstimatedFees({ - fromToken, - toToken, - amount, - fromAddr, - }: IGetMesonEstimateFeeInput) { - try { - const { data: priceResponse } = await this.client!.post('/price', { - from: fromToken, - to: toToken, - amount: amount, - fromAddress: fromAddr, - fromContract: false, - }); - return priceResponse; - } catch (error: any) { - // eslint-disable-next-line no-console - if (error?.response.data) { - console.log('Meson fee error', error?.response.data); - return error?.response?.data; - } else { - throw new Error(`Failed to get Meson fees ${error}`); - } - } - } - - async sendToken({ - fromAddress, - recipient, - signature, - encodedData, - }: IMesonSendTokenInput) { - try { - const { data: swapResponse } = await this.client!.post( - `/swap/${encodedData}`, - { - fromAddress: fromAddress, - recipient: recipient, - signature: signature, - } - ); - return swapResponse; - } catch (error: any) { - // eslint-disable-next-line no-console - if (error?.response.data) { - console.log('Meson send token error', error?.response.data); - return error?.response?.data; - } else { - throw new Error(`Failed to send transaction ${error}`); - } - } - } - - async getUnsignedMessage({ - fromToken, - toToken, - amount, - fromAddress, - recipient, - }: IMesonEncodeSwapInput) { - try { - const { data: swapResponse } = await this.client!.post('/swap', { - from: fromToken, - to: toToken, - amount: amount, - fromAddress: fromAddress, - recipient: recipient, - dataToContract: '', - }); - return swapResponse; - } catch (error: any) { - // eslint-disable-next-line no-console - if (error?.response.data) { - console.log('Meson get unsigned message error', error?.response.data); - return error?.response?.data; - } else { - throw new Error(`Failed to get unsigned message ${error}`); - } - } - } -} diff --git a/packages/canonical-bridge-sdk/src/meson/types.ts b/packages/canonical-bridge-sdk/src/meson/types.ts deleted file mode 100644 index 790ff7c7..00000000 --- a/packages/canonical-bridge-sdk/src/meson/types.ts +++ /dev/null @@ -1,21 +0,0 @@ -export interface IGetMesonEstimateFeeInput { - fromToken: string; - toToken: string; - fromAddr: string; - amount: string; -} - -export interface IMesonEncodeSwapInput { - fromToken: string; - toToken: string; - amount: string; - fromAddress: string; - recipient: string; -} - -export interface IMesonSendTokenInput { - fromAddress: string; - recipient: string; - signature: string; - encodedData: string; -} diff --git a/packages/canonical-bridge-sdk/src/meson/types/index.ts b/packages/canonical-bridge-sdk/src/meson/types/index.ts deleted file mode 100644 index 8be3f09e..00000000 --- a/packages/canonical-bridge-sdk/src/meson/types/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface IGetMesonEstimateFeeInput { - fromToken: string; - toToken: string; - fromAddr: string; - amount: string; -} diff --git a/packages/canonical-bridge-sdk/src/shared/address.ts b/packages/canonical-bridge-sdk/src/shared/address.ts new file mode 100644 index 00000000..570e7b0a --- /dev/null +++ b/packages/canonical-bridge-sdk/src/shared/address.ts @@ -0,0 +1,25 @@ +export function isSameAddress(A?: string, B?: string) { + if (!A || !B) return false; + + if (isEvmAddress(A) && isEvmAddress(B)) { + return A.toLowerCase() === B.toLowerCase(); + } + + return A === B; +} + +export function isEvmAddress(address?: string) { + return !!address && /^0x[a-f0-9]{40}$/i.test(address); +} + +export function isNativeToken(tokenAddress: string) { + return tokenAddress === '0x0000000000000000000000000000000000000000'; +} + +export function isSolanaAddress(address?: string) { + return !!address && /^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(address); +} + +export function isTronAddress(address?: string) { + return !!address && /^T[a-zA-Z0-9]{33}$/.test(address); +} diff --git a/packages/canonical-bridge-sdk/src/core/utils/assert.ts b/packages/canonical-bridge-sdk/src/shared/assert.ts similarity index 100% rename from packages/canonical-bridge-sdk/src/core/utils/assert.ts rename to packages/canonical-bridge-sdk/src/shared/assert.ts diff --git a/packages/canonical-bridge-sdk/src/shared/exports.ts b/packages/canonical-bridge-sdk/src/shared/exports.ts new file mode 100644 index 00000000..cef16803 --- /dev/null +++ b/packages/canonical-bridge-sdk/src/shared/exports.ts @@ -0,0 +1 @@ +export * from './address'; diff --git a/packages/canonical-bridge-sdk/src/core/utils/number.ts b/packages/canonical-bridge-sdk/src/shared/number.ts similarity index 100% rename from packages/canonical-bridge-sdk/src/core/utils/number.ts rename to packages/canonical-bridge-sdk/src/shared/number.ts diff --git a/packages/canonical-bridge-sdk/src/stargate/index.ts b/packages/canonical-bridge-sdk/src/stargate/index.ts deleted file mode 100644 index 8c31d68e..00000000 --- a/packages/canonical-bridge-sdk/src/stargate/index.ts +++ /dev/null @@ -1,193 +0,0 @@ -import { CLIENT_TIME_OUT } from '@/core/constants'; -import { - BaseBridgeConfig, - BaseBridgeConfigOptions, - CreateAdapterParameters, -} from '@/core/types'; -import { STARGATE_POOL } from '@/stargate/abi/stargatePool'; -import { - ISendTokenInput, - IStarGateBusDriveSettings, - IStargateOFTQuote, - IStargateQuoteOFT, - StarGateTransferConfigs, -} from '@/stargate/types'; -import { createAdapter } from '@/stargate/utils/createAdapter'; -import axios, { AxiosInstance } from 'axios'; -import { Hash, pad } from 'viem'; - -export * from './types'; - -export function stargateConfig( - options: BaseBridgeConfigOptions -): BaseBridgeConfig { - return { - bridgeType: 'stargate', - timeout: CLIENT_TIME_OUT, - ...options, - }; -} - -export class Stargate { - private client?: AxiosInstance; - - constructor(config: BaseBridgeConfig) { - const { timeout, endpoint } = config; - - this.client = axios.create({ - timeout, - baseURL: endpoint, - }); - } - - // https://mainnet.stargate-api.com/v1/swagger - async getBusQueueTime({ - fromEndpointId, - toEndpointId, - }: { - fromEndpointId: string; - toEndpointId: string; - }) { - return ( - await this.client!.get( - `${fromEndpointId}/${toEndpointId}` - ) - ).data; - } - - // https://stargateprotocol.gitbook.io/stargate/v/v2-developer-docs/integrate-with-stargate/estimating-fees#quoteoft - async getQuoteOFT({ - publicClient, - bridgeAddress, - endPointId, - receiver, - amount, - }: IStargateQuoteOFT) { - const sendParams = { - dstEid: endPointId, - to: pad(receiver, { size: 32 }) as `0x${string}`, - amountLD: amount, - minAmountLD: amount, - extraOptions: '0x' as `0x${string}`, - composeMsg: '0x' as `0x${string}`, - oftCmd: '0x01' as `0x${string}`, // '0x01' for bus, '' for taxi - }; - - try { - const quoteOFTResponse = await publicClient?.readContract({ - address: bridgeAddress, - abi: STARGATE_POOL, - functionName: 'quoteOFT', - args: [sendParams] as any, - }); - return quoteOFTResponse; - } catch (error: any) { - throw new Error(`Failed to get quote OFT: ${error}`); - } - } - - // https://stargateprotocol.gitbook.io/stargate/v/v2-developer-docs/integrate-with-stargate/estimating-fees#quotesend - async getQuoteSend({ - publicClient, - bridgeAddress, - endPointId, - receiver, - amount, - minAmount, - }: IStargateOFTQuote) { - const sendParams = { - dstEid: endPointId, - to: pad(receiver, { size: 32 }) as `0x${string}`, - amountLD: amount, - minAmountLD: minAmount, - extraOptions: '0x' as `0x${string}`, - composeMsg: '0x' as `0x${string}`, - oftCmd: '0x01' as `0x${string}`, // '0x01' for bus, '' for taxi - }; - try { - const quoteSendResponse = await publicClient?.readContract({ - address: bridgeAddress, - abi: STARGATE_POOL, - functionName: 'quoteSend', - args: [sendParams, false] as any, // false for not paying lzToken - }); - return quoteSendResponse; - } catch (error: any) { - throw new Error(`Failed to get quote send: ${error}`); - } - } - - /** - * Send token through Stargate bridge - * @param {WalletClient} walletClient Wallet client - * @param {PublicClient} publicClient Public client - * @param {Address} bridgeAddress Bridge address - * @param {Address} tokenAddress ERC-20 token address - * @param {Number} endPointId Stargate end point ID - * @param {Address} receiver Receiver address - * @param {BigInt} amount Send amount - * @returns {Hash} transaction hash - */ - async sendToken({ - walletClient, - publicClient, - bridgeAddress, - tokenAddress, - endPointId, - receiver, - amount, - }: ISendTokenInput): Promise { - const sendParams = { - dstEid: endPointId, - to: pad(receiver, { size: 32 }) as `0x${string}`, - amountLD: amount, - minAmountLD: amount, - extraOptions: '0x' as `0x${string}`, - composeMsg: '0x' as `0x${string}`, - oftCmd: '0x01' as `0x${string}`, // '0x01' for bus, '' for taxi - }; - try { - const quoteOFTResponse = await this.getQuoteOFT({ - publicClient, - bridgeAddress, - endPointId, - receiver, - amount, - }); - if (quoteOFTResponse?.[2].amountReceivedLD) { - sendParams.minAmountLD = BigInt(quoteOFTResponse[2].amountReceivedLD); - } - const quoteSendResponse = await this.getQuoteSend({ - publicClient, - bridgeAddress, - endPointId, - receiver, - amount, - minAmount: sendParams.minAmountLD, - }); - let nativeFee = quoteSendResponse.nativeFee; - if (tokenAddress === '0x0000000000000000000000000000000000000000') { - nativeFee += sendParams.amountLD; - } - const sendTokenArgs = { - address: bridgeAddress, - abi: STARGATE_POOL, - functionName: 'sendToken', - args: [sendParams, quoteSendResponse, receiver], - value: nativeFee, - account: receiver, - }; - const hash = await walletClient?.writeContract({ - ...(sendTokenArgs as any), - }); - return hash; - } catch (error: any) { - throw new Error(`Failed to send token: ${error}`); - } - } - - /** @see createAdapter for implementation details */ - createAdapter(params: CreateAdapterParameters) { - return createAdapter(params); - } -} diff --git a/packages/canonical-bridge-sdk/src/stargate/utils/createAdapter.ts b/packages/canonical-bridge-sdk/src/stargate/utils/createAdapter.ts deleted file mode 100644 index 588bb0d9..00000000 --- a/packages/canonical-bridge-sdk/src/stargate/utils/createAdapter.ts +++ /dev/null @@ -1,213 +0,0 @@ -import { - CreateAdapterParameters, - NativeCurrency, - TransferTokenPair, -} from '@/core/types'; -import { createBridgeAdapter } from '@/core/utils/createBridgeAdapter'; -import { - StarGateChain, - StarGateToken, - StarGateTransferConfigs, -} from '@/stargate/types'; - -/** - * Create a bridge adapter based on provided configurations - * - * @param {StarGateTransferConfigs} configs stargate transfer configs - * @param {number[]} [excludedChains] Optional chain IDs that should be excluded - * @param {Record} [excludedTokens] Optional tokens that should be excluded - * @param {Record} [nativeCurrencies] Optional nativeCurrencies, the information to exclude native tokens - * @param {string[][]} [bridgedTokenGroups] Optional bridgedTokenGroups, tokens within a group can be swapped with each other - * - * @returns An adapter object contains normalized configuration of the bridge - */ -export function createAdapter({ - configs, - excludedChains = [], - excludedTokens = {}, - nativeCurrencies = {}, - bridgedTokenGroups = [], -}: CreateAdapterParameters) { - const { chains, chainMap } = getChainConfigs({ configs, excludedChains }); - - const { chainTokensMap, chainSymbolTokenMap } = getTokenConfigs({ - configs, - chainMap, - nativeCurrencies, - excludedTokens, - }); - - const transferMap = getTransferMap({ - chains, - chainTokensMap, - chainSymbolTokenMap, - bridgedTokenGroups, - }); - - const supportedChains = chains.filter((chain) => - transferMap.has(chain.chainId) - ); - - return { - ...createBridgeAdapter({ - bridgeType: 'stargate', - supportedChains, - transferMap, - getChainId(chain: StarGateChain) { - return chain.chainId; - }, - getTokenInfo(token: StarGateToken) { - return { - name: '', - address: token.address, - decimal: token.decimals, - symbol: token.symbol, - }; - }, - }), - }; -} - -/** - * Get available chains - */ -function getChainConfigs(params: { - configs: StarGateTransferConfigs; - excludedChains: number[]; -}) { - const { configs, excludedChains } = params; - const { chains, tokens } = configs; - - const filteredChains = chains.filter((chain) => { - const isExcludedChain = excludedChains.includes(chain.chainId); - const hasToken = tokens[chain.chainId]?.length > 0; - return !isExcludedChain && hasToken; - }); - - const chainMap = new Map(); - filteredChains.forEach((chain) => { - chainMap.set(chain.chainId, chain); - }); - - return { - chains: filteredChains, - chainMap, - }; -} - -/** - * Get available tokens - */ -function getTokenConfigs(params: { - configs: StarGateTransferConfigs; - chainMap: Map; - excludedTokens: Record; - nativeCurrencies: Record; -}) { - const { configs, chainMap, excludedTokens, nativeCurrencies } = params; - const { tokens } = configs; - - const chainTokensMap = new Map(); - const chainSymbolTokenMap = new Map>(); - Object.entries(tokens).forEach(([id, tokens]) => { - const chainId = Number(id); - - const filteredTokens = tokens.filter((token) => { - const isNativeToken = nativeCurrencies[chainId]?.symbol === token.symbol; - const isExcludedToken = excludedTokens[chainId]?.includes(token.symbol); - return !isNativeToken && !isExcludedToken; - }); - - if (filteredTokens.length > 0 && chainMap.has(chainId)) { - chainSymbolTokenMap.set(chainId, new Map()); - - filteredTokens.forEach((token) => { - chainSymbolTokenMap.get(chainId)?.set(token.symbol, token); - }); - - chainTokensMap.set(chainId, filteredTokens); - } - }); - - return { - chainTokensMap, - chainSymbolTokenMap, - }; -} - -/** - * Exhaust all transferable cases and return a transfer map containing all possible transaction paths - */ -function getTransferMap(params: { - chains: StarGateChain[]; - chainTokensMap: Map; - chainSymbolTokenMap: Map>; - bridgedTokenGroups: string[][]; -}) { - const { chains, chainTokensMap, chainSymbolTokenMap, bridgedTokenGroups } = - params; - - // [fromChainId][toChainId][tokenSymbol]{fromToken, toToken} - const transferMap = new Map< - number, - Map> - >(); - - const getToToken = (toChainId: number, tokenSymbol: string) => { - let toToken = chainSymbolTokenMap.get(toChainId)?.get(tokenSymbol); - if (!toToken) { - const bridgedGroup = bridgedTokenGroups.find((group) => - group.includes(tokenSymbol) - ); - const otherTokens = bridgedGroup?.filter((item) => item !== tokenSymbol); - - otherTokens?.forEach((symbol) => { - if (!toToken) { - toToken = chainSymbolTokenMap.get(toChainId)?.get(symbol); - } - }); - } - - return toToken; - }; - - chains.forEach((fromChain) => { - chains.forEach((toChain) => { - if ( - fromChain.chainId !== toChain.chainId && - fromChain.network === toChain.network - ) { - const fromTokens = chainTokensMap.get(fromChain.chainId) ?? []; - - const transferableTokenMap = new Map(); - fromTokens.forEach((fromToken) => { - const toToken = getToToken(toChain.chainId, fromToken.symbol); - - if (toToken) { - const tokenPair: TransferTokenPair = { - fromToken, - toToken, - fromTokenAddress: fromToken.address, - toTokenAddress: toToken.address, - }; - transferableTokenMap.set(fromToken.symbol, tokenPair); - } - }); - - if (transferableTokenMap.size > 0) { - if (!transferMap.has(fromChain.chainId)) { - transferMap.set( - fromChain.chainId, - new Map>() - ); - } - transferMap - .get(fromChain.chainId) - ?.set(toChain.chainId, transferableTokenMap); - } - } - }); - }); - - return transferMap; -} diff --git a/packages/canonical-bridge-widget/src/CanonicalBridgeProvider.tsx b/packages/canonical-bridge-widget/src/CanonicalBridgeProvider.tsx index 42e5243d..770f3671 100644 --- a/packages/canonical-bridge-widget/src/CanonicalBridgeProvider.tsx +++ b/packages/canonical-bridge-widget/src/CanonicalBridgeProvider.tsx @@ -1,8 +1,9 @@ import '@node-real/walletkit/styles.css'; import React, { useContext, useMemo } from 'react'; import { ColorMode, IntlProvider } from '@bnb-chain/space'; +import { IChainConfig } from '@bnb-chain/canonical-bridge-sdk'; -import { IChainConfig, ITransferConfig } from '@/modules/aggregator/types'; +import { ITransferConfig } from '@/modules/aggregator/types'; import { StoreProvider } from '@/modules/store/StoreProvider'; import { WalletProvider } from '@/modules/wallet/WalletProvider'; import { ThemeProvider, ThemeProviderProps } from '@/core/theme/ThemeProvider'; diff --git a/packages/canonical-bridge-widget/src/core/hooks/useBridgeSDK.ts b/packages/canonical-bridge-widget/src/core/hooks/useBridgeSDK.ts index dccb9a12..a8272b6f 100644 --- a/packages/canonical-bridge-widget/src/core/hooks/useBridgeSDK.ts +++ b/packages/canonical-bridge-widget/src/core/hooks/useBridgeSDK.ts @@ -1,46 +1,5 @@ -import { CanonicalBridgeSDK } from '@bnb-chain/canonical-bridge-sdk'; -import { useMemo } from 'react'; +import { useAggregator } from '@/modules/aggregator/components/AggregatorProvider'; -import { env } from '@/core/configs/env'; -import { STARGATE_QUEUE_URL } from '@/core/constants'; -import { useBridgeConfig } from '@/index'; - -/** - * Initialize SDK Instance - * @returns SDK Instance - */ -export const useBridgeSDK = () => { - const bridgeConfig = useBridgeConfig(); - - const bridgeSDK = useMemo(() => { - const timeout = bridgeConfig.http.apiTimeOut; - - return new CanonicalBridgeSDK({ - bridgeConfigs: [ - { - bridgeType: 'cBridge', - endpoint: env.CBRIDGE_ENDPOINT, - timeout, - }, - { - bridgeType: 'deBridge', - endpoint: env.DEBRIDGE_ENDPOINT, - statsEndpoint: env.DEBRIDGE_STATS_ENDPOINT, - timeout, - }, - { - bridgeType: 'stargate', - endpoint: STARGATE_QUEUE_URL, - timeout, - }, - { - bridgeType: 'layerZero', - endpoint: '', - }, - { bridgeType: 'meson', endpoint: bridgeConfig.http.mesonEndpoint! }, - ], - }); - }, [bridgeConfig.http.apiTimeOut, bridgeConfig.http.mesonEndpoint]); - - return bridgeSDK; -}; +export function useBridgeSDK() { + return useAggregator().bridgeSDK; +} diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/cBridge/CBridgeAdapter.ts b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/cBridge/CBridgeAdapter.ts deleted file mode 100644 index e6c95da5..00000000 --- a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/cBridge/CBridgeAdapter.ts +++ /dev/null @@ -1,337 +0,0 @@ -import { BridgeType } from '@bnb-chain/canonical-bridge-sdk'; -import { isAddress } from 'viem'; - -import { - ICBridgeBurnPairConfig, - ICBridgeChain, - ICBridgePeggedPairConfig, - ICBridgeToken, - ICBridgeTransferConfig, -} from '@/modules/aggregator/adapters/cBridge/types'; -import { BaseAdapter, ITransferTokenPair } from '@/modules/aggregator/shared/BaseAdapter'; - -export class CBridgeAdapter extends BaseAdapter< - ICBridgeTransferConfig, - ICBridgeChain, - ICBridgeToken -> { - public bridgeType: BridgeType = 'cBridge'; - - private peggedPairConfigs: ICBridgePeggedPairConfig[] = []; - private burnPairConfigs: ICBridgeBurnPairConfig[] = []; - - protected init() { - this.initChains(); - this.initTokens(); - - this.initPeggedPairConfigs(); - this.initBurnPairConfigs(); - - this.initTransferMap(); - this.filterTransferMap(); - - this.initFromChains(); - this.initToChains(); - } - - protected initChains() { - const { chains, chain_token, pegged_pair_configs } = this.config; - - const filteredChains = chains.filter((chain) => { - const hasChainConfig = this.includedChains.includes(chain.id); - const isExcludedChain = this.excludedChains.includes(chain.id); - const hasEnabledToken = chain_token[chain.id]?.token?.some((e) => !e.token.xfer_disabled); - const hasPeggedToken = pegged_pair_configs.some( - (e) => - (e.org_chain_id === chain.id || e.pegged_chain_id === chain.id) && - !e.org_token.token.xfer_disabled && - !e.pegged_token.token.xfer_disabled, - ); - return hasChainConfig && !isExcludedChain && (hasEnabledToken || hasPeggedToken); - }); - - const chainMap = new Map(); - filteredChains.forEach((chain) => { - chainMap.set(chain.id, chain); - }); - - this.chains = filteredChains; - this.chainMap = chainMap; - } - - protected initTokens() { - const { chain_token, chains } = this.config; - - const tokenMap = new Map(); - const symbolMap = new Map>(); - Object.entries(chain_token).forEach(([id, { token: chainTokens }]) => { - const chainId = Number(id); - const nativeChain = chains.find((chain) => Number(chain.id) === Number(id)); - - const filteredTokens = chainTokens.filter((token) => { - const isEnabledToken = !token.token.xfer_disabled; - const isExcludedToken = this.checkIsExcludedToken({ - excludedList: this.excludedTokens?.[chainId], - tokenSymbol: token.token.symbol?.toUpperCase(), - tokenAddress: token.token.address, - }); - return isEnabledToken && !isExcludedToken; - }); - - // Add native token info. - if (nativeChain) { - const weth_token = chainTokens.find((token) => token.token.symbol === 'WETH'); - const nativeTokenObj = { - token: { - symbol: nativeChain?.gas_token_symbol ?? '', - address: '0x0000000000000000000000000000000000000000', - decimal: 18, - xfer_disabled: false, - }, - weth_address: weth_token?.token.address, - name: nativeChain?.gas_token_symbol, - icon: nativeChain?.icon, - inbound_lmt: '', - inbound_epoch_cap: '', - transfer_disabled: false, - liq_add_disabled: false, - liq_rm_disabled: false, - liq_agg_rm_src_disabled: false, - delay_threshold: '', - delay_period: 0, - }; - // The address of WETH (weth_address) is required for retrieving native token min/ max send amount - // https://cbridge-docs.celer.network/developer/cbridge-limit-parameters#id-1.-minsend-maxsend - if ( - nativeChain?.gas_token_symbol === 'ETH' && - isAddress(weth_token?.token?.address ?? '') - ) { - nativeTokenObj.weth_address = weth_token?.token.address; - } - filteredTokens.push(nativeTokenObj); - } - - if (filteredTokens.length > 0 && this.chainMap.has(chainId)) { - symbolMap.set(chainId, new Map()); - - filteredTokens.forEach((token) => { - symbolMap.get(chainId)?.set(token.token.symbol?.toUpperCase(), token); - }); - - tokenMap.set(chainId, filteredTokens); - } - }); - - this.tokenMap = tokenMap; - this.symbolMap = symbolMap; - } - - protected initTransferMap() { - const transferMap = new Map< - number, - Map>> - >(); - - this.chains.forEach((fromChain) => { - this.chains.forEach((toChain) => { - if (fromChain.id !== toChain.id) { - const fromTokens = this.tokenMap.get(fromChain.id) ?? []; - - const transferableTokenMap = new Map>(); - fromTokens.forEach((fromToken) => { - const toToken = this.getToToken({ - fromChainId: fromChain.id, - toChainId: toChain.id, - fromTokenSymbol: fromToken.token.symbol?.toUpperCase(), - }); - if (toToken) { - const tokenPair: ITransferTokenPair = { - fromChainId: fromChain.id, - toChainId: toChain.id, - fromTokenAddress: fromToken.token.address, - toTokenAddress: toToken.token.address, - fromToken, - toToken, - }; - transferableTokenMap.set(fromToken.token.symbol?.toUpperCase(), tokenPair); - } - }); - - if (transferableTokenMap.size > 0) { - if (!transferMap.has(fromChain.id)) { - transferMap.set( - fromChain.id, - new Map>>(), - ); - } - transferMap.get(fromChain.id)?.set(toChain.id, transferableTokenMap); - } - } - }); - }); - - const addPeggedTokenPair = ( - fromChainId: number, - fromToken: ICBridgeToken, - toChainId: number, - toToken: ICBridgeToken, - item: ICBridgePeggedPairConfig, - ) => { - if ( - !transferMap.get(fromChainId)?.get(toChainId)?.get(fromToken.token.symbol?.toUpperCase()) - ) { - if (!transferMap.has(fromChainId)) { - transferMap.set( - fromChainId, - new Map>>(), - ); - } - - const peggedTokenPair: ITransferTokenPair = { - fromChainId, - toChainId, - fromTokenAddress: fromToken.token.address, - toTokenAddress: toToken.token.address, - fromToken, - toToken, - isPegged: true, - peggedConfig: item, - }; - - if (transferMap.get(fromChainId)?.get(toChainId)) { - transferMap - .get(fromChainId) - ?.get(toChainId) - ?.set(fromToken.token.symbol?.toUpperCase(), peggedTokenPair); - } else { - const transferableTokenMap = new Map>(); - transferableTokenMap.set(fromToken.token.symbol?.toUpperCase(), peggedTokenPair); - transferMap.get(fromChainId)?.set(toChainId, transferableTokenMap); - } - } - }; - - this.peggedPairConfigs.forEach((item) => { - const fromChainId = item.org_chain_id; - const fromToken = item.org_token; - - const toChainId = item.pegged_chain_id; - const toToken = item.pegged_token; - - addPeggedTokenPair(fromChainId, fromToken, toChainId, toToken, item); - addPeggedTokenPair(toChainId, toToken, fromChainId, fromToken, item); - }); - - this.transferMap = transferMap; - } - - private initPeggedPairConfigs() { - const peggedPairConfigs = this.config.pegged_pair_configs; - - const isAvailablePair = (chainId: number, token: ICBridgeToken) => { - const hasChain = this.chainMap.has(chainId); - const isEnabledToken = !token.token.xfer_disabled; - - const isExcludedToken = this.checkIsExcludedToken({ - excludedList: this.excludedTokens?.[chainId], - tokenSymbol: token.token.symbol, - tokenAddress: token.token.address, - }); - - return hasChain && isEnabledToken && !isExcludedToken; - }; - - const filteredPeggedPairConfigs = peggedPairConfigs.filter( - (item) => - isAvailablePair(item.org_chain_id, item.org_token) && - isAvailablePair(item.pegged_chain_id, item.pegged_token), - ); - - this.peggedPairConfigs = filteredPeggedPairConfigs; - } - - private initBurnPairConfigs() { - const burnPairConfigs: ICBridgeBurnPairConfig[] = []; - - for (let i = 0; i < this.peggedPairConfigs.length; i++) { - for (let j = i + 1; j < this.peggedPairConfigs.length; j++) { - const A = this.peggedPairConfigs[i]; - const B = this.peggedPairConfigs[j]; - if ( - A.org_chain_id === B.org_chain_id && - A.org_token.token.symbol === B.org_token.token.symbol - ) { - /// Only upgraded PegBridge can support multi burn to other pegged chain - if (A.bridge_version === 2 && B.bridge_version === 2) { - burnPairConfigs.push({ - burn_config_as_org: { - chain_id: A.pegged_chain_id, - token: A.pegged_token, - burn_contract_addr: A.pegged_burn_contract_addr, - canonical_token_contract_addr: A.canonical_token_contract_addr, - burn_contract_version: A.bridge_version, - }, - burn_config_as_dst: { - chain_id: B.pegged_chain_id, - token: B.pegged_token, - burn_contract_addr: B.pegged_burn_contract_addr, - canonical_token_contract_addr: B.canonical_token_contract_addr, - burn_contract_version: B.bridge_version, - }, - }); - burnPairConfigs.push({ - burn_config_as_org: { - chain_id: B.pegged_chain_id, - token: B.pegged_token, - burn_contract_addr: B.pegged_burn_contract_addr, - canonical_token_contract_addr: B.canonical_token_contract_addr, - burn_contract_version: B.bridge_version, - }, - burn_config_as_dst: { - chain_id: A.pegged_chain_id, - token: A.pegged_token, - burn_contract_addr: A.pegged_burn_contract_addr, - canonical_token_contract_addr: A.canonical_token_contract_addr, - burn_contract_version: A.bridge_version, - }, - }); - } - } - } - } - - this.burnPairConfigs = burnPairConfigs; - } - - public getChainId(chain: ICBridgeChain) { - return chain.id; - } - - protected getChainIdAsObject(chainId: number) { - return { - id: chainId, - }; - } - - public getTokenInfo({ chainId, token }: { chainId: number; token: ICBridgeToken }) { - return { - name: token.name, - symbol: token.token.symbol, - address: token.token.address, - decimals: token.token.decimal, - ...this.getTokenDisplaySymbolAndIcon({ - chainId, - tokenAddress: token.token.address, - defaultSymbol: token.token.symbol, - }), - }; - } - - public getPeggedPairConfigs() { - return this.burnPairConfigs; - } - - public getBurnPairConfigs() { - return this.peggedPairConfigs; - } -} diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/cBridge/hooks/useCBridgeTransferWaitingTime.ts b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/cBridge/hooks/useCBridgeTransferWaitingTime.ts index 0db9f2d1..f4f0e574 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/cBridge/hooks/useCBridgeTransferWaitingTime.ts +++ b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/cBridge/hooks/useCBridgeTransferWaitingTime.ts @@ -1,5 +1,5 @@ import { useQuery } from '@tanstack/react-query'; -import { CBridgeTransferEstimatedTime } from '@bnb-chain/canonical-bridge-sdk'; +import { ICBridgeTransferEstimatedTime } from '@bnb-chain/canonical-bridge-sdk'; import { useBridgeSDK } from '@/core/hooks/useBridgeSDK'; @@ -13,10 +13,10 @@ export const useCBridgeTransferWaitingTime = ({ isEnabled?: boolean; }) => { const bridgeSDK = useBridgeSDK(); - return useQuery({ + return useQuery({ queryKey: ['cbridge-transfer-waiting-time', srcChainId, dstChainId], queryFn: async () => { - return await bridgeSDK.cBridge.getEstimatedWaitingTime({ + return await bridgeSDK.cBridge!.getEstimatedWaitingTime({ srcChainId: srcChainId!, dstChainId: dstChainId!, }); diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/cBridge/types.ts b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/cBridge/types.ts index 3196305b..1d75c6d0 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/cBridge/types.ts +++ b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/cBridge/types.ts @@ -1,112 +1,6 @@ +import { ICBridgeChain, ICBridgePeggedPairConfig } from '@bnb-chain/canonical-bridge-sdk'; import { type PublicClient, type WalletClient } from 'viem'; -export interface ICBridgeChain { - id: number; - name: string; - icon: string; - block_delay: number; - gas_token_symbol: string; - explore_url: string; - contract_addr: string; - drop_gas_amt: string; - drop_gas_cost_amt: string; - drop_gas_balance_alert: string; - suggested_gas_cost: string; - flat_usd_fee: number; - farming_reward_contract_addr: string; - transfer_agent_contract_addr: string; - disabled: boolean; -} - -export interface ICBridgeToken { - token: { - symbol: string; - address: string; - decimal: number; - xfer_disabled: boolean; - display_symbol?: string; /// FOR ETH <=====> WETH - }; - name: string; - icon: string; - inbound_lmt: string; - inbound_epoch_cap: string; - transfer_disabled: boolean; - liq_add_disabled: boolean; - liq_rm_disabled: boolean; - liq_agg_rm_src_disabled: boolean; - delay_threshold: string; - delay_period: number; - method?: string; - bridgeAddress?: string; //bridge address for transfer - weth_address?: string; -} - -export interface ICBridgePeggedPairConfig { - org_chain_id: number; - org_token: ICBridgeToken; - pegged_chain_id: number; - pegged_token: ICBridgeToken; - pegged_deposit_contract_addr: string; - pegged_burn_contract_addr: string; - canonical_token_contract_addr: string; - vault_version: number; - bridge_version: number; - migration_peg_burn_contract_addr: string; -} - -export interface ICBridgeBurnConfig { - chain_id: number; - token: ICBridgeToken; - burn_contract_addr: string; - canonical_token_contract_addr: string; - burn_contract_version: number; -} - -/// burn_config_as_org.bridge_version === 2 -/// burn_config_as_dst.bridge_version is not required -/// If the bridge_version of burnConfig1 and burnConfig2 are 2, -/// There should be two MultiBurnPairConfigs -/// 1: burnConfig1 ----> burnConfig2 -/// 2: burnConfig2 ----> burnConfig1 -export interface ICBridgeBurnPairConfig { - burn_config_as_org: ICBridgeBurnConfig; /// Could be used only as from chain - burn_config_as_dst: ICBridgeBurnConfig; /// Could be used only as to chain -} - -export interface ICBridgeTransferConfig { - chains: ICBridgeChain[]; - chain_token: { - [k: number]: { - token: ICBridgeToken[]; - }; - }; - farming_reward_contract_addr: string; - pegged_pair_configs: ICBridgePeggedPairConfig[]; - blocked_bridge_direct: { - symbol: string; - src_chain_id: string; - dst_chain_id: string; - }[]; - redirect_to_aggregators_config: { - symbol: string; - src_chain_id: string; - dst_chain_id: string; - }[]; -} - -export interface ICBridgeTransferStatusResponse { - err: object; - status: number; - wd_onchain: null; - sorted_sigs: string[]; - signers: string[]; - powers: string[]; - refund_reason: number; - block_delay: number; - src_block_tx_link: string; - dst_block_tx_link: string; -} - export interface ICBridgeTransferInfo { chain: any; token: any; @@ -173,15 +67,6 @@ export interface ICBridgeEstimateAmountResponse { op_fee_rebate_end_time: string; } -export interface ICBridgeGetSupportedFuncParams { - fromChainId?: number; - toChainId?: number; - fromTokenSymbol?: string; - peggedPairConfigs: ICBridgePeggedPairConfig[]; - burnPairConfigs: ICBridgeBurnPairConfig[]; - data: ICBridgeTransferConfig; -} - export interface ICBridgeSendRangeInput { bridgeAddress: `0x${string}`; tokenAddress: `0x${string}`; diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/deBridge/DeBridgeAdapter.ts b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/deBridge/DeBridgeAdapter.ts deleted file mode 100644 index 9aed0cbe..00000000 --- a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/deBridge/DeBridgeAdapter.ts +++ /dev/null @@ -1,148 +0,0 @@ -import { BridgeType } from '@bnb-chain/canonical-bridge-sdk'; - -import { - IDeBridgeTransferConfig, - IDeBridgeChain, - IDeBridgeToken, -} from '@/modules/aggregator/adapters/deBridge/types'; -import { BaseAdapter, ITransferTokenPair } from '@/modules/aggregator/shared/BaseAdapter'; -import { isSameAddress } from '@/core/utils/address'; - -export class DeBridgeAdapter extends BaseAdapter< - IDeBridgeTransferConfig, - IDeBridgeChain, - IDeBridgeToken -> { - public bridgeType: BridgeType = 'deBridge'; - - protected initChains() { - const { chains, tokens } = this.config; - - const filteredChains = chains.filter((chain) => { - const hasChainConfig = this.includedChains.includes(chain.chainId); - const isExcludedChain = this.excludedChains.includes(chain.chainId); - const hasToken = tokens[chain.chainId]?.length > 0; - return hasChainConfig && !isExcludedChain && hasToken; - }); - - const chainMap = new Map(); - filteredChains.forEach((chain) => { - chainMap.set(chain.chainId, chain); - }); - - this.chains = filteredChains; - this.chainMap = chainMap; - } - - protected initTokens() { - const { tokens } = this.config; - - const tokenMap = new Map(); - const symbolMap = new Map>(); - Object.entries(tokens).forEach(([id, chainTokens]) => { - const chainId = Number(id); - - const filteredTokens = chainTokens.filter((token) => { - const isExcludedToken = this.checkIsExcludedToken({ - excludedList: this.excludedTokens?.[chainId], - tokenSymbol: token.symbol?.toUpperCase(), - tokenAddress: token.address, - }); - return !isExcludedToken; - }); - - if (filteredTokens.length > 0 && this.chainMap.has(chainId)) { - symbolMap.set(chainId, new Map()); - - filteredTokens.forEach((token) => { - symbolMap.get(chainId)?.set(token.symbol?.toUpperCase(), token); - }); - - tokenMap.set(chainId, filteredTokens); - } - }); - - this.tokenMap = tokenMap; - this.symbolMap = symbolMap; - } - - protected initTransferMap() { - const transferMap = new Map< - number, - Map>> - >(); - - this.chains.forEach((fromChain) => { - this.chains.forEach((toChain) => { - if (fromChain.chainId !== toChain.chainId) { - const fromTokens = this.tokenMap.get(fromChain.chainId) ?? []; - - const transferableTokenMap = new Map>(); - fromTokens.forEach((fromToken) => { - const toToken = this.getToToken({ - fromChainId: fromChain.chainId, - toChainId: toChain.chainId, - fromTokenSymbol: fromToken.symbol?.toUpperCase(), - }); - - if (toToken) { - const tokenPair: ITransferTokenPair = { - fromChainId: fromChain.chainId, - toChainId: toChain.chainId, - fromTokenAddress: fromToken.address, - toTokenAddress: toToken.address, - fromToken, - toToken, - }; - transferableTokenMap.set(fromToken.symbol?.toUpperCase(), tokenPair); - } - }); - - if (transferableTokenMap.size > 0) { - if (!transferMap.has(fromChain.chainId)) { - transferMap.set( - fromChain.chainId, - new Map>>(), - ); - } - transferMap.get(fromChain.chainId)?.set(toChain.chainId, transferableTokenMap); - } - } - }); - }); - - this.transferMap = transferMap; - } - - public getChainId(chain: IDeBridgeChain) { - return chain.chainId; - } - - protected getChainIdAsObject(chainId: number) { - return { - chainId, - }; - } - - public getTokenInfo({ chainId, token }: { chainId: number; token: IDeBridgeToken }) { - return { - name: token.name, - symbol: token.symbol, - address: token.address, - decimals: token.decimals, - ...this.getTokenDisplaySymbolAndIcon({ - chainId, - tokenAddress: token.address, - defaultSymbol: token.symbol, - }), - }; - } - - public getTokenByAddress({ chainId, address }: { chainId: number; address: string }) { - if (chainId && address) { - const tokens = this.tokenMap.get(Number(chainId)); - const target = tokens?.find((item) => isSameAddress(item.address, address)); - return target; - } - } -} diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/deBridge/hooks/useGetDeBridgeFees.ts b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/deBridge/hooks/useGetDeBridgeFees.ts index 4b4783f2..0b6280b0 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/deBridge/hooks/useGetDeBridgeFees.ts +++ b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/deBridge/hooks/useGetDeBridgeFees.ts @@ -1,12 +1,11 @@ import { useCallback } from 'react'; import { formatUnits, parseUnits } from 'viem'; -import { DeBridgeCreateQuoteResponse } from '@bnb-chain/canonical-bridge-sdk'; +import { DeBridgeAdapter, IDeBridgeCreateQuoteResponse } from '@bnb-chain/canonical-bridge-sdk'; import { useAccount, useBalance, usePublicClient } from 'wagmi'; import { useIntl } from '@bnb-chain/space'; import { formatNumber } from '@/core/utils/number'; import { useAppDispatch, useAppSelector } from '@/modules/store/StoreProvider'; -import { DeBridgeAdapter } from '@/modules/aggregator/adapters/deBridge/DeBridgeAdapter'; import { formatFeeAmount } from '@/core/utils/string'; import { useAdapter } from '@/modules/aggregator/hooks/useAdapter'; import { setRouteError, setRouteFees } from '@/modules/transfer/action'; @@ -52,7 +51,7 @@ export const useGetDeBridgeFees = () => { const publicClient = usePublicClient({ chainId: fromChain?.id }) as any; const deBridgeFeeSorting = useCallback( - async (fees: DeBridgeCreateQuoteResponse) => { + async (fees: IDeBridgeCreateQuoteResponse) => { const nativeToken = nativeCurrency?.symbol; const nativeDecimals = nativeCurrency?.decimals ?? 18; diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/deBridge/types.ts b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/deBridge/types.ts index d36e4fad..dde03ecc 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/deBridge/types.ts +++ b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/deBridge/types.ts @@ -1,24 +1,3 @@ -export interface IDeBridgeChain { - chainId: number; - chainName: string; -} - -export interface IDeBridgeToken { - address: string; - symbol: string; - decimals: number; - name: string; - logoURI: string | null; - eip2612?: boolean; - tags: string[]; - domainVersion?: string; -} - -export interface IDeBridgeTransferConfig { - chains: IDeBridgeChain[]; - tokens: Record; -} - // https://deswap.debridge.finance/v1.0/#/DLN export type IQuoteResponse = { estimation: { diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/layerZero/LayerZeroAdapter.ts b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/layerZero/LayerZeroAdapter.ts deleted file mode 100644 index 301f2bb4..00000000 --- a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/layerZero/LayerZeroAdapter.ts +++ /dev/null @@ -1,139 +0,0 @@ -import { BridgeType } from '@bnb-chain/canonical-bridge-sdk'; - -import { - ILayerZeroTransferConfig, - ILayerZeroChain, - ILayerZeroToken, -} from '@/modules/aggregator/adapters/layerZero/types'; -import { BaseAdapter, ITransferTokenPair } from '@/modules/aggregator/shared/BaseAdapter'; - -export class LayerZeroAdapter extends BaseAdapter< - ILayerZeroTransferConfig, - ILayerZeroChain, - ILayerZeroToken -> { - public bridgeType: BridgeType = 'layerZero'; - - protected initChains() { - const { chains, tokens } = this.config; - - const filteredChains = chains.filter((chain) => { - const hasChainConfig = this.includedChains.includes(chain.chainId); - const isExcludedChain = this.excludedChains.includes(chain.chainId); - const hasToken = tokens[chain.chainId]?.length > 0; - return hasChainConfig && !isExcludedChain && hasToken; - }); - - const chainMap = new Map(); - filteredChains.forEach((chain) => { - chainMap.set(chain.chainId, chain); - }); - - this.chains = filteredChains; - this.chainMap = chainMap; - } - - protected initTokens() { - const { tokens } = this.config; - - const tokenMap = new Map(); - const symbolMap = new Map>(); - Object.entries(tokens).forEach(([id, chainTokens]) => { - const chainId = Number(id); - - const filteredTokens = chainTokens.filter((token) => { - const isExcludedToken = this.checkIsExcludedToken({ - excludedList: this.excludedTokens?.[chainId], - tokenSymbol: token.symbol?.toUpperCase(), - tokenAddress: token.address, - }); - return !isExcludedToken; - }); - - if (filteredTokens.length > 0 && this.chainMap.has(chainId)) { - symbolMap.set(chainId, new Map()); - - filteredTokens.forEach((token) => { - symbolMap.get(chainId)?.set(token.symbol, token); - }); - - tokenMap.set(chainId, filteredTokens); - } - }); - - this.tokenMap = tokenMap; - this.symbolMap = symbolMap; - } - - protected initTransferMap() { - const transferMap = new Map< - number, - Map>> - >(); - - this.chains.forEach((fromChain) => { - this.chains.forEach((toChain) => { - if (fromChain.chainId !== toChain.chainId && fromChain.network === toChain.network) { - const fromTokens = this.tokenMap.get(fromChain.chainId) ?? []; - - const transferableTokenMap = new Map>(); - fromTokens.forEach((fromToken) => { - const toToken = this.getToToken({ - fromChainId: fromChain.chainId, - toChainId: toChain.chainId, - fromTokenSymbol: fromToken.symbol?.toUpperCase(), - }); - - if (toToken) { - const tokenPair: ITransferTokenPair = { - fromChainId: fromChain.chainId, - toChainId: toChain.chainId, - fromToken, - toToken, - fromTokenAddress: fromToken.address, - toTokenAddress: toToken.address, - }; - transferableTokenMap.set(fromToken.symbol, tokenPair); - } - }); - - if (transferableTokenMap.size > 0) { - if (!transferMap.has(fromChain.chainId)) { - transferMap.set( - fromChain.chainId, - new Map>>(), - ); - } - transferMap.get(fromChain.chainId)?.set(toChain.chainId, transferableTokenMap); - } - } - }); - }); - - this.transferMap = transferMap; - } - - public getChainId(chain: ILayerZeroChain) { - return chain.chainId; - } - - protected getChainIdAsObject(chainId: number) { - return { - chainId, - }; - } - - public getTokenInfo({ chainId, token }: { chainId: number; token: ILayerZeroToken }) { - return { - name: (token as any).name, // TODO - symbol: token.symbol, - address: token.address, - decimals: token.decimals, - ...this.getTokenDisplaySymbolAndIcon({ - chainId, - tokenAddress: token.address, - defaultSymbol: token.symbol, - }), - }; - } -} diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/layerZero/abi/cakeProxyOFT.ts b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/layerZero/abi/cakeProxyOFT.ts deleted file mode 100644 index 10a75c5a..00000000 --- a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/layerZero/abi/cakeProxyOFT.ts +++ /dev/null @@ -1,1040 +0,0 @@ -export const CAKE_PROXY_OFT_ABI = [ - { - inputs: [ - { internalType: 'address', name: '_token', type: 'address' }, - { internalType: 'uint8', name: '_sharedDecimals', type: 'uint8' }, - { internalType: 'address', name: '_lzEndpoint', type: 'address' }, - ], - stateMutability: 'nonpayable', - type: 'constructor', - }, - { - inputs: [ - { internalType: 'uint256', name: 'cap', type: 'uint256' }, - { internalType: 'uint256', name: 'amount', type: 'uint256' }, - ], - name: 'ExceedInboundCap', - type: 'error', - }, - { - inputs: [ - { internalType: 'uint256', name: 'cap', type: 'uint256' }, - { internalType: 'uint256', name: 'amount', type: 'uint256' }, - ], - name: 'ExceedOutboundCap', - type: 'error', - }, - { inputs: [], name: 'NotOperator', type: 'error' }, - { - anonymous: false, - inputs: [ - { - indexed: true, - internalType: 'uint16', - name: '_srcChainId', - type: 'uint16', - }, - { - indexed: false, - internalType: 'bytes', - name: '_srcAddress', - type: 'bytes', - }, - { - indexed: false, - internalType: 'uint64', - name: '_nonce', - type: 'uint64', - }, - { - indexed: false, - internalType: 'bytes32', - name: '_hash', - type: 'bytes32', - }, - ], - name: 'CallOFTReceivedSuccess', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { - indexed: false, - internalType: 'uint16', - name: 'srcChainId', - type: 'uint16', - }, - { - indexed: false, - internalType: 'bytes', - name: 'srcAddress', - type: 'bytes', - }, - { indexed: false, internalType: 'uint64', name: 'nonce', type: 'uint64' }, - ], - name: 'DropFailedMessage', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { indexed: true, internalType: 'address', name: 'to', type: 'address' }, - { - indexed: false, - internalType: 'uint256', - name: 'amount', - type: 'uint256', - }, - ], - name: 'FallbackWithdraw', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { - indexed: false, - internalType: 'uint16', - name: '_srcChainId', - type: 'uint16', - }, - { - indexed: false, - internalType: 'bytes', - name: '_srcAddress', - type: 'bytes', - }, - { - indexed: false, - internalType: 'uint64', - name: '_nonce', - type: 'uint64', - }, - { - indexed: false, - internalType: 'bytes', - name: '_payload', - type: 'bytes', - }, - { indexed: false, internalType: 'bytes', name: '_reason', type: 'bytes' }, - ], - name: 'MessageFailed', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { - indexed: false, - internalType: 'address', - name: '_address', - type: 'address', - }, - ], - name: 'NonContractAddress', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - internalType: 'address', - name: 'previousOwner', - type: 'address', - }, - { - indexed: true, - internalType: 'address', - name: 'newOwner', - type: 'address', - }, - ], - name: 'OwnershipTransferred', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { - indexed: false, - internalType: 'address', - name: 'account', - type: 'address', - }, - ], - name: 'Paused', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - internalType: 'uint16', - name: '_srcChainId', - type: 'uint16', - }, - { indexed: true, internalType: 'address', name: '_to', type: 'address' }, - { - indexed: false, - internalType: 'uint256', - name: '_amount', - type: 'uint256', - }, - ], - name: 'ReceiveFromChain', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { - indexed: false, - internalType: 'uint16', - name: '_srcChainId', - type: 'uint16', - }, - { - indexed: false, - internalType: 'bytes', - name: '_srcAddress', - type: 'bytes', - }, - { - indexed: false, - internalType: 'uint64', - name: '_nonce', - type: 'uint64', - }, - { - indexed: false, - internalType: 'bytes32', - name: '_payloadHash', - type: 'bytes32', - }, - ], - name: 'RetryMessageSuccess', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - internalType: 'uint16', - name: '_dstChainId', - type: 'uint16', - }, - { - indexed: true, - internalType: 'address', - name: '_from', - type: 'address', - }, - { - indexed: true, - internalType: 'bytes32', - name: '_toAddress', - type: 'bytes32', - }, - { - indexed: false, - internalType: 'uint256', - name: '_amount', - type: 'uint256', - }, - ], - name: 'SendToChain', - type: 'event', - }, - { - anonymous: false, - inputs: [{ indexed: false, internalType: 'uint16', name: 'feeBp', type: 'uint16' }], - name: 'SetDefaultFeeBp', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { - indexed: false, - internalType: 'uint16', - name: 'dstchainId', - type: 'uint16', - }, - { indexed: false, internalType: 'bool', name: 'enabled', type: 'bool' }, - { indexed: false, internalType: 'uint16', name: 'feeBp', type: 'uint16' }, - ], - name: 'SetFeeBp', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { - indexed: false, - internalType: 'address', - name: 'feeOwner', - type: 'address', - }, - ], - name: 'SetFeeOwner', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - internalType: 'uint16', - name: 'chainId', - type: 'uint16', - }, - { indexed: false, internalType: 'uint256', name: 'cap', type: 'uint256' }, - ], - name: 'SetInboundCap', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { - indexed: false, - internalType: 'uint16', - name: '_dstChainId', - type: 'uint16', - }, - { indexed: false, internalType: 'uint16', name: '_type', type: 'uint16' }, - { - indexed: false, - internalType: 'uint256', - name: '_minDstGas', - type: 'uint256', - }, - ], - name: 'SetMinDstGas', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { - indexed: false, - internalType: 'address', - name: 'newOperator', - type: 'address', - }, - ], - name: 'SetOperator', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - internalType: 'uint16', - name: 'chainId', - type: 'uint16', - }, - { indexed: false, internalType: 'uint256', name: 'cap', type: 'uint256' }, - ], - name: 'SetOutboundCap', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { - indexed: false, - internalType: 'address', - name: 'precrime', - type: 'address', - }, - ], - name: 'SetPrecrime', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { - indexed: false, - internalType: 'uint16', - name: '_remoteChainId', - type: 'uint16', - }, - { indexed: false, internalType: 'bytes', name: '_path', type: 'bytes' }, - ], - name: 'SetTrustedRemote', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { - indexed: false, - internalType: 'uint16', - name: '_remoteChainId', - type: 'uint16', - }, - { - indexed: false, - internalType: 'bytes', - name: '_remoteAddress', - type: 'bytes', - }, - ], - name: 'SetTrustedRemoteAddress', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { - indexed: false, - internalType: 'bool', - name: '_useCustomAdapterParams', - type: 'bool', - }, - ], - name: 'SetUseCustomAdapterParams', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { indexed: true, internalType: 'address', name: 'addr', type: 'address' }, - { - indexed: false, - internalType: 'bool', - name: 'isWhitelist', - type: 'bool', - }, - ], - name: 'SetWhitelist', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { - indexed: false, - internalType: 'address', - name: 'account', - type: 'address', - }, - ], - name: 'Unpaused', - type: 'event', - }, - { - inputs: [], - name: 'BP_DENOMINATOR', - outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'NO_EXTRA_GAS', - outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'PT_SEND', - outputs: [{ internalType: 'uint8', name: '', type: 'uint8' }], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'PT_SEND_AND_CALL', - outputs: [{ internalType: 'uint8', name: '', type: 'uint8' }], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { internalType: 'uint16', name: '', type: 'uint16' }, - { internalType: 'bytes', name: '', type: 'bytes' }, - { internalType: 'uint64', name: '', type: 'uint64' }, - ], - name: 'amountsForCall', - outputs: [ - { internalType: 'uint256', name: 'amount', type: 'uint256' }, - { internalType: 'bool', name: 'credited', type: 'bool' }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { internalType: 'uint16', name: '_srcChainId', type: 'uint16' }, - { internalType: 'bytes', name: '_srcAddress', type: 'bytes' }, - { internalType: 'uint64', name: '_nonce', type: 'uint64' }, - { internalType: 'bytes32', name: '_from', type: 'bytes32' }, - { internalType: 'address', name: '_to', type: 'address' }, - { internalType: 'uint256', name: '_amount', type: 'uint256' }, - { internalType: 'bytes', name: '_payload', type: 'bytes' }, - { internalType: 'uint256', name: '_gasForCall', type: 'uint256' }, - ], - name: 'callOnOFTReceived', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [{ internalType: 'uint16', name: '', type: 'uint16' }], - name: 'chainIdToFeeBps', - outputs: [ - { internalType: 'uint16', name: 'feeBP', type: 'uint16' }, - { internalType: 'bool', name: 'enabled', type: 'bool' }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [{ internalType: 'uint16', name: '', type: 'uint16' }], - name: 'chainIdToInboundCap', - outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [{ internalType: 'uint16', name: '', type: 'uint16' }], - name: 'chainIdToLastReceivedTimestamp', - outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [{ internalType: 'uint16', name: '', type: 'uint16' }], - name: 'chainIdToLastSentTimestamp', - outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [{ internalType: 'uint16', name: '', type: 'uint16' }], - name: 'chainIdToOutboundCap', - outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [{ internalType: 'uint16', name: '', type: 'uint16' }], - name: 'chainIdToReceivedTokenAmount', - outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [{ internalType: 'uint16', name: '', type: 'uint16' }], - name: 'chainIdToSentTokenAmount', - outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'circulatingSupply', - outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'defaultFeeBp', - outputs: [{ internalType: 'uint16', name: '', type: 'uint16' }], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { internalType: 'uint16', name: 'srcChainId', type: 'uint16' }, - { internalType: 'bytes', name: 'srcAddress', type: 'bytes' }, - { internalType: 'uint64', name: 'nonce', type: 'uint64' }, - ], - name: 'dropFailedMessage', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { internalType: 'uint16', name: '_dstChainId', type: 'uint16' }, - { internalType: 'bytes32', name: '_toAddress', type: 'bytes32' }, - { internalType: 'uint256', name: '_amount', type: 'uint256' }, - { internalType: 'bytes', name: '_payload', type: 'bytes' }, - { internalType: 'uint64', name: '_dstGasForCall', type: 'uint64' }, - { internalType: 'bool', name: '_useZro', type: 'bool' }, - { internalType: 'bytes', name: '_adapterParams', type: 'bytes' }, - ], - name: 'estimateSendAndCallFee', - outputs: [ - { internalType: 'uint256', name: 'nativeFee', type: 'uint256' }, - { internalType: 'uint256', name: 'zroFee', type: 'uint256' }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { internalType: 'uint16', name: '_dstChainId', type: 'uint16' }, - { internalType: 'bytes32', name: '_toAddress', type: 'bytes32' }, - { internalType: 'uint256', name: '_amount', type: 'uint256' }, - { internalType: 'bool', name: '_useZro', type: 'bool' }, - { internalType: 'bytes', name: '_adapterParams', type: 'bytes' }, - ], - name: 'estimateSendFee', - outputs: [ - { internalType: 'uint256', name: 'nativeFee', type: 'uint256' }, - { internalType: 'uint256', name: 'zroFee', type: 'uint256' }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { internalType: 'uint16', name: '', type: 'uint16' }, - { internalType: 'bytes', name: '', type: 'bytes' }, - { internalType: 'uint64', name: '', type: 'uint64' }, - ], - name: 'failedMessages', - outputs: [{ internalType: 'bytes32', name: '', type: 'bytes32' }], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { internalType: 'address', name: 'to', type: 'address' }, - { internalType: 'uint256', name: 'amount', type: 'uint256' }, - ], - name: 'fallbackWithdraw', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [], - name: 'feeOwner', - outputs: [{ internalType: 'address', name: '', type: 'address' }], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { internalType: 'uint16', name: '_srcChainId', type: 'uint16' }, - { internalType: 'bytes', name: '_srcAddress', type: 'bytes' }, - ], - name: 'forceResumeReceive', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { internalType: 'uint16', name: '_version', type: 'uint16' }, - { internalType: 'uint16', name: '_chainId', type: 'uint16' }, - { internalType: 'address', name: '', type: 'address' }, - { internalType: 'uint256', name: '_configType', type: 'uint256' }, - ], - name: 'getConfig', - outputs: [{ internalType: 'bytes', name: '', type: 'bytes' }], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [{ internalType: 'uint16', name: '_remoteChainId', type: 'uint16' }], - name: 'getTrustedRemoteAddress', - outputs: [{ internalType: 'bytes', name: '', type: 'bytes' }], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { internalType: 'uint16', name: '_srcChainId', type: 'uint16' }, - { internalType: 'bytes', name: '_srcAddress', type: 'bytes' }, - ], - name: 'isTrustedRemote', - outputs: [{ internalType: 'bool', name: '', type: 'bool' }], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'lzEndpoint', - outputs: [ - { - internalType: 'contract ILayerZeroEndpoint', - name: '', - type: 'address', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { internalType: 'uint16', name: '_srcChainId', type: 'uint16' }, - { internalType: 'bytes', name: '_srcAddress', type: 'bytes' }, - { internalType: 'uint64', name: '_nonce', type: 'uint64' }, - { internalType: 'bytes', name: '_payload', type: 'bytes' }, - ], - name: 'lzReceive', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { internalType: 'uint16', name: '', type: 'uint16' }, - { internalType: 'uint16', name: '', type: 'uint16' }, - ], - name: 'minDstGasLookup', - outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { internalType: 'uint16', name: '_srcChainId', type: 'uint16' }, - { internalType: 'bytes', name: '_srcAddress', type: 'bytes' }, - { internalType: 'uint64', name: '_nonce', type: 'uint64' }, - { internalType: 'bytes', name: '_payload', type: 'bytes' }, - ], - name: 'nonblockingLzReceive', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [], - name: 'operator', - outputs: [{ internalType: 'address', name: '', type: 'address' }], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'outboundAmount', - outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'owner', - outputs: [{ internalType: 'address', name: '', type: 'address' }], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'pause', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [], - name: 'paused', - outputs: [{ internalType: 'bool', name: '', type: 'bool' }], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'precrime', - outputs: [{ internalType: 'address', name: '', type: 'address' }], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { internalType: 'uint16', name: '_dstChainId', type: 'uint16' }, - { internalType: 'uint256', name: '_amount', type: 'uint256' }, - ], - name: 'quoteOFTFee', - outputs: [{ internalType: 'uint256', name: 'fee', type: 'uint256' }], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'renounceOwnership', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { internalType: 'uint16', name: '_srcChainId', type: 'uint16' }, - { internalType: 'bytes', name: '_srcAddress', type: 'bytes' }, - { internalType: 'uint64', name: '_nonce', type: 'uint64' }, - { internalType: 'bytes', name: '_payload', type: 'bytes' }, - ], - name: 'retryMessage', - outputs: [], - stateMutability: 'payable', - type: 'function', - }, - { - inputs: [ - { internalType: 'address', name: '_from', type: 'address' }, - { internalType: 'uint16', name: '_dstChainId', type: 'uint16' }, - { internalType: 'bytes32', name: '_toAddress', type: 'bytes32' }, - { internalType: 'uint256', name: '_amount', type: 'uint256' }, - { internalType: 'uint256', name: '_minAmount', type: 'uint256' }, - { internalType: 'bytes', name: '_payload', type: 'bytes' }, - { internalType: 'uint64', name: '_dstGasForCall', type: 'uint64' }, - { - components: [ - { - internalType: 'address payable', - name: 'refundAddress', - type: 'address', - }, - { - internalType: 'address', - name: 'zroPaymentAddress', - type: 'address', - }, - { internalType: 'bytes', name: 'adapterParams', type: 'bytes' }, - ], - internalType: 'struct ICommonOFT.LzCallParams', - name: '_callParams', - type: 'tuple', - }, - ], - name: 'sendAndCall', - outputs: [], - stateMutability: 'payable', - type: 'function', - }, - { - inputs: [ - { internalType: 'address', name: '_from', type: 'address' }, - { internalType: 'uint16', name: '_dstChainId', type: 'uint16' }, - { internalType: 'bytes32', name: '_toAddress', type: 'bytes32' }, - { internalType: 'uint256', name: '_amount', type: 'uint256' }, - { internalType: 'uint256', name: '_minAmount', type: 'uint256' }, - { - components: [ - { - internalType: 'address payable', - name: 'refundAddress', - type: 'address', - }, - { - internalType: 'address', - name: 'zroPaymentAddress', - type: 'address', - }, - { internalType: 'bytes', name: 'adapterParams', type: 'bytes' }, - ], - internalType: 'struct ICommonOFT.LzCallParams', - name: '_callParams', - type: 'tuple', - }, - ], - name: 'sendFrom', - outputs: [], - stateMutability: 'payable', - type: 'function', - }, - { - inputs: [ - { internalType: 'uint16', name: '_version', type: 'uint16' }, - { internalType: 'uint16', name: '_chainId', type: 'uint16' }, - { internalType: 'uint256', name: '_configType', type: 'uint256' }, - { internalType: 'bytes', name: '_config', type: 'bytes' }, - ], - name: 'setConfig', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [{ internalType: 'uint16', name: '_feeBp', type: 'uint16' }], - name: 'setDefaultFeeBp', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { internalType: 'uint16', name: '_dstChainId', type: 'uint16' }, - { internalType: 'bool', name: '_enabled', type: 'bool' }, - { internalType: 'uint16', name: '_feeBp', type: 'uint16' }, - ], - name: 'setFeeBp', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [{ internalType: 'address', name: '_feeOwner', type: 'address' }], - name: 'setFeeOwner', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { internalType: 'uint16', name: 'chainId', type: 'uint16' }, - { internalType: 'uint256', name: 'cap', type: 'uint256' }, - ], - name: 'setInboundCap', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { internalType: 'uint16', name: '_dstChainId', type: 'uint16' }, - { internalType: 'uint16', name: '_packetType', type: 'uint16' }, - { internalType: 'uint256', name: '_minGas', type: 'uint256' }, - ], - name: 'setMinDstGas', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [{ internalType: 'address', name: 'newOperator', type: 'address' }], - name: 'setOperator', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { internalType: 'uint16', name: 'chainId', type: 'uint16' }, - { internalType: 'uint256', name: 'cap', type: 'uint256' }, - ], - name: 'setOutboundCap', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [{ internalType: 'address', name: '_precrime', type: 'address' }], - name: 'setPrecrime', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [{ internalType: 'uint16', name: '_version', type: 'uint16' }], - name: 'setReceiveVersion', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [{ internalType: 'uint16', name: '_version', type: 'uint16' }], - name: 'setSendVersion', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { internalType: 'uint16', name: '_srcChainId', type: 'uint16' }, - { internalType: 'bytes', name: '_path', type: 'bytes' }, - ], - name: 'setTrustedRemote', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { internalType: 'uint16', name: '_remoteChainId', type: 'uint16' }, - { internalType: 'bytes', name: '_remoteAddress', type: 'bytes' }, - ], - name: 'setTrustedRemoteAddress', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [{ internalType: 'bool', name: '_useCustomAdapterParams', type: 'bool' }], - name: 'setUseCustomAdapterParams', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { internalType: 'address', name: 'addr', type: 'address' }, - { internalType: 'bool', name: 'isWhitelist', type: 'bool' }, - ], - name: 'setWhitelist', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [], - name: 'sharedDecimals', - outputs: [{ internalType: 'uint8', name: '', type: 'uint8' }], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [{ internalType: 'bytes4', name: 'interfaceId', type: 'bytes4' }], - name: 'supportsInterface', - outputs: [{ internalType: 'bool', name: '', type: 'bool' }], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'token', - outputs: [{ internalType: 'address', name: '', type: 'address' }], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [{ internalType: 'address', name: 'newOwner', type: 'address' }], - name: 'transferOwnership', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [{ internalType: 'uint16', name: '', type: 'uint16' }], - name: 'trustedRemoteLookup', - outputs: [{ internalType: 'bytes', name: '', type: 'bytes' }], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'unpause', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [], - name: 'useCustomAdapterParams', - outputs: [{ internalType: 'bool', name: '', type: 'bool' }], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [{ internalType: 'address', name: '', type: 'address' }], - name: 'whitelist', - outputs: [{ internalType: 'bool', name: '', type: 'bool' }], - stateMutability: 'view', - type: 'function', - }, -] as const; diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/layerZero/hooks/useGetLayerZeroFees.ts b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/layerZero/hooks/useGetLayerZeroFees.ts index d149e52e..467e9953 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/layerZero/hooks/useGetLayerZeroFees.ts +++ b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/layerZero/hooks/useGetLayerZeroFees.ts @@ -2,12 +2,12 @@ import { useCallback } from 'react'; import { useAccount, useBalance, usePublicClient } from 'wagmi'; import { encodePacked, formatUnits, pad, parseUnits } from 'viem'; import { useIntl } from '@bnb-chain/space'; +import { CAKE_PROXY_OFT_ABI } from '@bnb-chain/canonical-bridge-sdk'; import { useAppDispatch, useAppSelector } from '@/modules/store/StoreProvider'; import { useToTokenInfo } from '@/modules/transfer/hooks/useToTokenInfo'; import { DEFAULT_ADDRESS } from '@/core/constants'; import { useGetTokenBalance } from '@/core/contract/hooks/useGetTokenBalance'; -import { CAKE_PROXY_OFT_ABI } from '@/modules/aggregator/adapters/layerZero/abi/cakeProxyOFT'; import { setRouteError, setRouteFees } from '@/modules/transfer/action'; import { useGetNativeToken } from '@/modules/transfer/hooks/useGetNativeToken'; import { formatNumber } from '@/core/utils/number'; diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/layerZero/types.ts b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/layerZero/types.ts index 2cbed673..b2216f1a 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/layerZero/types.ts +++ b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/layerZero/types.ts @@ -20,24 +20,3 @@ export interface IGetEstimateFeeInput { version?: number; publicClient: PublicClient; } - -export interface ILayerZeroToken { - address: string; - bridgeAddress: string; - decimals: number; - symbol: string; - name: string; - endpointID: number; - version: number; // LayerZero version -} - -export interface ILayerZeroChain { - chainId: number; - chainName: string; - network?: string; -} - -export interface ILayerZeroTransferConfig { - chains: ILayerZeroChain[]; - tokens: Record; -} diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/meson/types.ts b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/meson/types.ts index eaa25eb3..e69de29b 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/meson/types.ts +++ b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/meson/types.ts @@ -1,15 +0,0 @@ -export interface IMesonToken { - id: string; - addr: string; - decimals: number; - min: string; - max: string; -} - -export interface IMesonChain { - id: string; - name: string; - chainId: string; - address: string; // bridge address - tokens: IMesonToken[]; -} diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/stargate/StargateAdapter.ts b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/stargate/StargateAdapter.ts deleted file mode 100644 index 3e26baf4..00000000 --- a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/stargate/StargateAdapter.ts +++ /dev/null @@ -1,139 +0,0 @@ -import { BridgeType } from '@bnb-chain/canonical-bridge-sdk'; - -import { - IStargateTransferConfig, - IStargateChain, - IStargateToken, -} from '@/modules/aggregator/adapters/stargate/types'; -import { BaseAdapter, ITransferTokenPair } from '@/modules/aggregator/shared/BaseAdapter'; - -export class StargateAdapter extends BaseAdapter< - IStargateTransferConfig, - IStargateChain, - IStargateToken -> { - public bridgeType: BridgeType = 'stargate'; - - protected initChains() { - const { chains, tokens } = this.config; - - const filteredChains = chains.filter((chain) => { - const hasChainConfig = this.includedChains.includes(chain.chainId); - const isExcludedChain = this.excludedChains.includes(chain.chainId); - const hasToken = tokens[chain.chainId]?.length > 0; - return hasChainConfig && !isExcludedChain && hasToken; - }); - - const chainMap = new Map(); - filteredChains.forEach((chain) => { - chainMap.set(chain.chainId, chain); - }); - - this.chains = filteredChains; - this.chainMap = chainMap; - } - - protected initTokens() { - const { tokens } = this.config; - - const tokenMap = new Map(); - const symbolMap = new Map>(); - Object.entries(tokens).forEach(([id, chainTokens]) => { - const chainId = Number(id); - - const filteredTokens = chainTokens.filter((token) => { - const isExcludedToken = this.checkIsExcludedToken({ - excludedList: this.excludedTokens?.[chainId], - tokenSymbol: token.symbol?.toUpperCase(), - tokenAddress: token.address, - }); - return !isExcludedToken; - }); - - if (filteredTokens.length > 0 && this.chainMap.has(chainId)) { - symbolMap.set(chainId, new Map()); - - filteredTokens.forEach((token) => { - symbolMap.get(chainId)?.set(token.symbol?.toUpperCase(), token); - }); - - tokenMap.set(chainId, filteredTokens); - } - }); - - this.tokenMap = tokenMap; - this.symbolMap = symbolMap; - } - - protected initTransferMap() { - const transferMap = new Map< - number, - Map>> - >(); - - this.chains.forEach((fromChain) => { - this.chains.forEach((toChain) => { - if (fromChain.chainId !== toChain.chainId && fromChain.network === toChain.network) { - const fromTokens = this.tokenMap.get(fromChain.chainId) ?? []; - - const transferableTokenMap = new Map>(); - fromTokens.forEach((fromToken) => { - const toToken = this.getToToken({ - fromChainId: fromChain.chainId, - toChainId: toChain.chainId, - fromTokenSymbol: fromToken.symbol?.toUpperCase(), - }); - - if (toToken) { - const tokenPair: ITransferTokenPair = { - fromChainId: fromChain.chainId, - toChainId: toChain.chainId, - fromToken, - toToken, - fromTokenAddress: fromToken.address, - toTokenAddress: toToken.address, - }; - transferableTokenMap.set(fromToken.symbol?.toUpperCase(), tokenPair); - } - }); - - if (transferableTokenMap.size > 0) { - if (!transferMap.has(fromChain.chainId)) { - transferMap.set( - fromChain.chainId, - new Map>>(), - ); - } - transferMap.get(fromChain.chainId)?.set(toChain.chainId, transferableTokenMap); - } - } - }); - }); - - this.transferMap = transferMap; - } - - public getChainId(chain: IStargateChain) { - return chain.chainId; - } - - protected getChainIdAsObject(chainId: number) { - return { - chainId, - }; - } - - public getTokenInfo({ chainId, token }: { chainId: number; token: IStargateToken }) { - return { - name: token.name, - symbol: token.symbol, - address: token.address, - decimals: token.decimals, - ...this.getTokenDisplaySymbolAndIcon({ - chainId, - tokenAddress: token.address, - defaultSymbol: token.symbol, - }), - }; - } -} diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/stargate/hooks/useGetStarGateFees.ts b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/stargate/hooks/useGetStarGateFees.ts index f8d5ad9f..675cf851 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/stargate/hooks/useGetStarGateFees.ts +++ b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/stargate/hooks/useGetStarGateFees.ts @@ -106,7 +106,7 @@ export const useGetStargateFees = () => { } } // native fee - const quoteSendResponse = await bridgeSDK.stargate.getQuoteSend({ + const quoteSendResponse = await bridgeSDK.stargate?.getQuoteSend({ publicClient: publicClient, bridgeAddress, endPointId: args.dstEid, diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/stargate/hooks/useStargateTransfer.ts b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/stargate/hooks/useStargateTransfer.ts index 3a28b6a7..eb783dd5 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/stargate/hooks/useStargateTransfer.ts +++ b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/stargate/hooks/useStargateTransfer.ts @@ -46,7 +46,7 @@ export const useStargateTransfer = () => { try { const bridgeAddress = selectedToken.stargate?.raw?.bridgeAddress as `0x${string}`; - const quoteOFTResponse = await bridgeSDK.stargate.getQuoteOFT({ + const quoteOFTResponse = await bridgeSDK.stargate?.getQuoteOFT({ publicClient: publicClient, bridgeAddress, endPointId: args.dstEid, @@ -93,7 +93,7 @@ export const useStargateTransfer = () => { if (amountReceivedLD) { sendParams.minAmountLD = BigInt(amountReceivedLD); } - const hash = await bridgeSDK.stargate.sendToken({ + const hash = await bridgeSDK.stargate?.sendToken({ walletClient: walletClient as any, publicClient: publicClient, bridgeAddress, diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/stargate/hooks/useStargateWaitTime.ts b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/stargate/hooks/useStargateWaitTime.ts index 4049f205..e9fb6027 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/stargate/hooks/useStargateWaitTime.ts +++ b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/stargate/hooks/useStargateWaitTime.ts @@ -17,7 +17,7 @@ export const useStargateWaitTime = () => { return useQuery({ queryKey: ['stargate-bus-wait-time', fromEndpointId, toEndpointId], queryFn: async () => { - return await bridgeSDK.stargate.getBusQueueTime({ + return await bridgeSDK.stargate!.getBusQueueTime({ fromEndpointId: String(fromEndpointId), toEndpointId: String(toEndpointId), }); diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/stargate/types.ts b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/stargate/types.ts index 95ca11c2..de4e54d4 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/stargate/types.ts +++ b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/stargate/types.ts @@ -1,24 +1,3 @@ -export interface IStargateToken { - address: string; - bridgeAddress: string; - decimals: number; - type?: string; - symbol: string; - endpointID: number; - name: string; -} - -export interface IStargateTransferConfig { - chains: IStargateChain[]; - tokens: Record; -} - -export interface IStargateChain { - chainId: number; - chainName: string; - network?: string; -} - export interface IStargateParams { dstEid: number; to: `0x${string}`; diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/components/AggregatorProvider.tsx b/packages/canonical-bridge-widget/src/modules/aggregator/components/AggregatorProvider.tsx index 80e6641d..04620f19 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/components/AggregatorProvider.tsx +++ b/packages/canonical-bridge-widget/src/modules/aggregator/components/AggregatorProvider.tsx @@ -1,58 +1,28 @@ import React, { useContext, useMemo } from 'react'; -import { BridgeType } from '@bnb-chain/canonical-bridge-sdk'; - import { - AdapterConstructorType, - AdapterType, - IBridgeChain, - ITransferConfig, - IBridgeToken, + BridgeType, + CBridgeAdapter, + DeBridgeAdapter, + LayerZeroAdapter, + MesonAdapter, + StargateAdapter, IChainConfig, INativeCurrency, -} from '@/modules/aggregator/types'; -import { getNativeCurrencies } from '@/modules/aggregator/shared/getNativeCurrencies'; -import { CBridgeAdapter } from '@/modules/aggregator/adapters/cBridge/CBridgeAdapter'; -import { DeBridgeAdapter } from '@/modules/aggregator/adapters/deBridge/DeBridgeAdapter'; -import { LayerZeroAdapter } from '@/modules/aggregator/adapters/layerZero/LayerZeroAdapter'; -import { StargateAdapter } from '@/modules/aggregator/adapters/stargate/StargateAdapter'; -import { MesonAdapter } from '@/modules/aggregator/adapters/meson/MesonAdapter'; -import { IBaseAdapterOptions } from '@/modules/aggregator/shared/BaseAdapter'; -import { - aggregateChains, - IGetFromChainsParams, - IGetToChainsParams, -} from '@/modules/aggregator/shared/aggregateChains'; -import { aggregateTokens, IGetTokensParams } from '@/modules/aggregator/shared/aggregateTokens'; -import { aggregateToToken, IGetToTokenParams } from '@/modules/aggregator/shared/aggregateToToken'; +} from '@bnb-chain/canonical-bridge-sdk'; +import { CanonicalBridgeSDK } from '@bnb-chain/canonical-bridge-sdk'; + +import { ITransferConfig } from '@/modules/aggregator/types'; import { useBridgeConfig } from '@/index'; export interface AggregatorContextProps { isReady: boolean; transferConfig: ITransferConfig; - defaultSelectedInfo: ITransferConfig['defaultSelectedInfo']; chainConfigs: IChainConfig[]; nativeCurrencies: Record; - adapters: AdapterType[]; - getFromChains: (params: IGetFromChainsParams) => IBridgeChain[]; - getToChains: (params: IGetToChainsParams) => IBridgeChain[]; - getTokens: (params: IGetTokensParams) => IBridgeToken[]; - getToToken: (params: IGetToTokenParams) => IBridgeToken | undefined; + bridgeSDK: CanonicalBridgeSDK; } -const DEFAULT_CONTEXT: AggregatorContextProps = { - isReady: false, - transferConfig: {} as ITransferConfig, - defaultSelectedInfo: {} as ITransferConfig['defaultSelectedInfo'], - chainConfigs: [], - nativeCurrencies: {}, - adapters: [], - getFromChains: () => [], - getToChains: () => [], - getTokens: () => [], - getToToken: () => undefined, -}; - -export const AggregatorContext = React.createContext(DEFAULT_CONTEXT); +export const AggregatorContext = React.createContext({} as AggregatorContextProps); export interface AggregatorProviderProps { transferConfig?: ITransferConfig; @@ -64,110 +34,92 @@ export function AggregatorProvider(props: AggregatorProviderProps) { const { transferConfig, chains, children } = props; const bridgeConfig = useBridgeConfig(); - const chainConfigs = useMemo(() => { - return chains.map((item) => ({ - ...item, - chainType: item.chainType ? item.chainType : 'evm', - })); - }, [chains]); const value = useMemo(() => { - if (!transferConfig) { - return { - ...DEFAULT_CONTEXT, - chainConfigs, - }; - } + type GetAdapterParams = { + config: any; + bridgedTokenGroups?: string[][]; + excludedChains?: number[]; + excludedTokens?: Record; + }; const bridges: Array<{ bridgeType: BridgeType; - Adapter: AdapterConstructorType; + getAdapter: (params: GetAdapterParams) => any; }> = [ { bridgeType: 'cBridge', - Adapter: CBridgeAdapter, + getAdapter(params: GetAdapterParams) { + return new CBridgeAdapter({ + ...params, + }); + }, }, { bridgeType: 'deBridge', - Adapter: DeBridgeAdapter, + getAdapter(params: GetAdapterParams) { + return new DeBridgeAdapter({ + ...params, + }); + }, }, { bridgeType: 'stargate', - Adapter: StargateAdapter, + getAdapter(params: GetAdapterParams) { + return new StargateAdapter({ + ...params, + }); + }, }, { bridgeType: 'layerZero', - Adapter: LayerZeroAdapter, + getAdapter(params: GetAdapterParams) { + return new LayerZeroAdapter({ + ...params, + }); + }, + }, + { + bridgeType: 'meson', + getAdapter(params: GetAdapterParams) { + return new MesonAdapter({ + ...params, + }); + }, }, - { bridgeType: 'meson', Adapter: MesonAdapter }, ]; - const nativeCurrencies = getNativeCurrencies(chainConfigs); - const includedChains = chainConfigs.map((item) => item.id); - const assetPrefix = bridgeConfig.assetPrefix; - const adapters = bridges - .filter((item) => transferConfig[item.bridgeType]) - .map(({ bridgeType, Adapter }) => { - const adapterConfig = transferConfig[bridgeType]!; - - return new Adapter({ - config: adapterConfig.config, - excludedChains: adapterConfig.exclude?.chains, - excludedTokens: adapterConfig.exclude?.tokens, - bridgedTokenGroups: adapterConfig.bridgedTokenGroups, - includedChains, - nativeCurrencies, - brandChains: transferConfig.brandChains, - externalChains: transferConfig.externalChains, - displayTokenSymbols: transferConfig.displayTokenSymbols, - assetPrefix, - } as IBaseAdapterOptions); - }); - - return { - isReady: true, - transferConfig, - - defaultSelectedInfo: transferConfig.defaultSelectedInfo, - nativeCurrencies, + .map((e) => { + const adapterConfig = transferConfig?.[e.bridgeType]; + if (adapterConfig) { + return e.getAdapter({ + config: adapterConfig.config, + bridgedTokenGroups: adapterConfig.bridgedTokenGroups, + excludedChains: adapterConfig?.exclude?.chains, + excludedTokens: adapterConfig?.exclude?.tokens, + }); + } + }) + .filter(Boolean); + + const bridgeSDK = new CanonicalBridgeSDK({ + chains, + assetPrefix: bridgeConfig.assetPrefix, + brandChains: transferConfig?.brandChains, + externalChains: transferConfig?.externalChains, + displayTokenSymbols: transferConfig?.displayTokenSymbols, adapters, - chainConfigs, + }); - getFromChains: (params: IGetFromChainsParams) => { - return aggregateChains({ - direction: 'from', - transferConfig, - chainConfigs, - assetPrefix, - adapters, - params, - }); - }, - getToChains: (params: IGetToChainsParams) => { - return aggregateChains({ - direction: 'to', - transferConfig, - chainConfigs, - assetPrefix, - adapters, - params, - }); - }, - getTokens: (params: IGetTokensParams) => { - return aggregateTokens({ - adapters, - params, - }); - }, - getToToken: (params: IGetToTokenParams) => { - return aggregateToToken({ - adapters, - params, - }); - }, + return { + isReady: !!transferConfig, + transferConfig: transferConfig ?? ({} as ITransferConfig), + bridgeSDK, + chainConfigs: bridgeSDK.getSDKOptions().chains, + nativeCurrencies: bridgeSDK.getNativeCurrencies(), }; - }, [transferConfig, chainConfigs, bridgeConfig.assetPrefix]); + }, [chains, bridgeConfig.assetPrefix, transferConfig]); return {children}; } diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/components/SelectModal/ChooseTokenModal.tsx b/packages/canonical-bridge-widget/src/modules/aggregator/components/SelectModal/ChooseTokenModal.tsx index 1f7a9dfd..2408ba5b 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/components/SelectModal/ChooseTokenModal.tsx +++ b/packages/canonical-bridge-widget/src/modules/aggregator/components/SelectModal/ChooseTokenModal.tsx @@ -3,7 +3,6 @@ import { Flex, Text, useColorMode, useIntl, useTheme } from '@bnb-chain/space'; import { useAppSelector } from '@/modules/store/StoreProvider'; import { useTokens } from '@/modules/aggregator/hooks/useTokens'; import { VirtualList } from '@/core/components/VirtualList'; -import { isChainOrTokenCompatible } from '@/modules/aggregator/shared/isChainOrTokenCompatible'; import { useSelection } from '@/modules/aggregator/hooks/useSelection'; import { formatAppAddress, isNativeToken, isSameAddress } from '@/core/utils/address'; import { ExLinkIcon } from '@/core/components/icons/ExLinkIcon'; @@ -79,9 +78,9 @@ export function ChooseTokenModal(props: ChooseTokenModalProps) { {(item) => { - const isDisabled = !isChainOrTokenCompatible(item); + const isDisabled = !item.isCompatible; const isActive = - isSameAddress(item.address, selectedToken?.address) && isChainOrTokenCompatible(item); + isSameAddress(item.address, selectedToken?.address) && item.isCompatible; const isNative = isNativeToken(item.address); return ( diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/components/SelectModal/DestinationNetworkModal.tsx b/packages/canonical-bridge-widget/src/modules/aggregator/components/SelectModal/DestinationNetworkModal.tsx index abb2afa5..3f1a3d73 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/components/SelectModal/DestinationNetworkModal.tsx +++ b/packages/canonical-bridge-widget/src/modules/aggregator/components/SelectModal/DestinationNetworkModal.tsx @@ -1,7 +1,6 @@ import { Flex, useColorMode, useIntl, useTheme, Text } from '@bnb-chain/space'; import { VirtualList } from '@/core/components/VirtualList'; -import { isChainOrTokenCompatible } from '@/modules/aggregator/shared/isChainOrTokenCompatible'; import { useAppSelector } from '@/modules/store/StoreProvider'; import { useToChains } from '@/modules/aggregator/hooks/useToChains'; import { useSelection } from '@/modules/aggregator/hooks/useSelection'; @@ -23,7 +22,6 @@ export function DestinationNetworkModal(props: DestinationNetworkModalProps) { const fromChain = useAppSelector((state) => state.transfer.fromChain); const toChain = useAppSelector((state) => state.transfer.toChain); - const selectedToken = useAppSelector((state) => state.transfer.selectedToken); const { selectToChain } = useSelection(); const theme = useTheme(); @@ -31,7 +29,6 @@ export function DestinationNetworkModal(props: DestinationNetworkModalProps) { const toChains = useToChains({ fromChainId: fromChain?.id, - token: selectedToken, }); const { isNoResult, result, onSearch } = useSearch({ @@ -55,7 +52,7 @@ export function DestinationNetworkModal(props: DestinationNetworkModalProps) { key={item.id} iconUrl={item.icon} isActive={toChain?.id === item.id} - isDisabled={!isChainOrTokenCompatible(item)} + isDisabled={!item.isCompatible} incompatibleTooltip={formatMessage({ id: 'select-modal.destination.incompatible.tooltip', })} diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/components/SelectModal/SourceNetworkModal.tsx b/packages/canonical-bridge-widget/src/modules/aggregator/components/SelectModal/SourceNetworkModal.tsx index 401f3ed8..79e5cc64 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/components/SelectModal/SourceNetworkModal.tsx +++ b/packages/canonical-bridge-widget/src/modules/aggregator/components/SelectModal/SourceNetworkModal.tsx @@ -23,17 +23,12 @@ export function SourceNetworkModal(props: SourceNetworkModalProps) { const dispatch = useAppDispatch(); const fromChain = useAppSelector((state) => state.transfer.fromChain); - const toChain = useAppSelector((state) => state.transfer.toChain); - const selectedToken = useAppSelector((state) => state.transfer.selectedToken); const { selectFromChain } = useSelection(); const theme = useTheme(); const { colorMode } = useColorMode(); - const fromChains = useFromChains({ - toChainId: toChain?.id, - token: selectedToken, - }); + const fromChains = useFromChains(); const { isNoResult, result, onSearch } = useSearch({ filter: (item, keyword) => item.name.toLowerCase().includes(keyword), diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/components/SelectModal/hooks/useTokenList.ts b/packages/canonical-bridge-widget/src/modules/aggregator/components/SelectModal/hooks/useTokenList.ts index 8e27d4b7..1409185f 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/components/SelectModal/hooks/useTokenList.ts +++ b/packages/canonical-bridge-widget/src/modules/aggregator/components/SelectModal/hooks/useTokenList.ts @@ -1,13 +1,12 @@ import { useMemo } from 'react'; +import { IBridgeToken } from '@bnb-chain/canonical-bridge-sdk'; -import { IBridgeToken, IBridgeTokenWithBalance } from '@/modules/aggregator/types'; +import { IBridgeTokenWithBalance } from '@/modules/aggregator/types'; import { useTokenPrice } from '@/modules/aggregator/hooks/useTokenPrice'; import { useAppSelector } from '@/modules/store/StoreProvider'; -import { isSameAddress } from '@/core/utils/address'; import { useTokenBalance } from '@/modules/aggregator/hooks/useTokenBalance'; import { sortTokens } from '@/modules/aggregator/shared/sortTokens'; import { useAggregator } from '@/modules/aggregator/components/AggregatorProvider'; -import { isChainOrTokenCompatible } from '@/modules/aggregator/shared/isChainOrTokenCompatible'; export function useTokenList(tokens: IBridgeToken[] = []) { const selectedToken = useAppSelector((state) => state.transfer.selectedToken); @@ -38,10 +37,7 @@ export function useTokenList(tokens: IBridgeToken[] = []) { const sortedTokens = sortTokens({ tokens: tmpTokens, orders: transferConfig.order?.tokens, - }).sort((a) => { - return isSameAddress(a.address, selectedToken?.address) && isChainOrTokenCompatible(a) - ? -1 - : 0; + selectedTokenAddress: selectedToken?.address, }); return sortedTokens; diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useAdapter.ts b/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useAdapter.ts index e132e860..55f849e4 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useAdapter.ts +++ b/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useAdapter.ts @@ -1,14 +1,15 @@ import { BridgeType } from '@bnb-chain/canonical-bridge-sdk'; import { useMemo } from 'react'; -import { useAggregator } from '@/modules/aggregator/components/AggregatorProvider'; +import { useBridgeSDK } from '@/core/hooks/useBridgeSDK'; export function useAdapter(bridgeType: BridgeType) { - const { adapters } = useAggregator(); + const bridgeSDK = useBridgeSDK(); const adapter = useMemo(() => { + const adapters = bridgeSDK.getSDKOptions().adapters; return adapters.find((adapter) => adapter.bridgeType === bridgeType); - }, [adapters, bridgeType]); + }, [bridgeSDK, bridgeType]); return adapter as T; } diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useDefaultSelectedInfo.ts b/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useDefaultSelectedInfo.ts index c61e0942..9188690d 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useDefaultSelectedInfo.ts +++ b/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useDefaultSelectedInfo.ts @@ -7,7 +7,7 @@ import { useAggregator } from '@/modules/aggregator/components/AggregatorProvide import { useCurrentWallet } from '@/modules/wallet/CurrentWalletProvider.tsx'; export function useDefaultSelectedInfo() { - const { isReady, defaultSelectedInfo } = useAggregator(); + const { isReady, transferConfig } = useAggregator(); const { selectDefault } = useSelection(); const dispatch = useAppDispatch(); const { chainId } = useCurrentWallet(); @@ -15,8 +15,8 @@ export function useDefaultSelectedInfo() { useEffect(() => { if (isReady) { - selectDefault(defaultSelectedInfo); - dispatch(setSendValue(sendValue || defaultSelectedInfo.amount)); + selectDefault(transferConfig.defaultSelectedInfo); + dispatch(setSendValue(sendValue || transferConfig.defaultSelectedInfo.amount)); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [isReady, chainId]); diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useFromChains.ts b/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useFromChains.ts index c2f15b59..db09a405 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useFromChains.ts +++ b/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useFromChains.ts @@ -1,17 +1,13 @@ import { useMemo } from 'react'; -import { useSavedValue } from '@/core/hooks/useSavedValue'; -import { IGetFromChainsParams } from '@/modules/aggregator/shared/aggregateChains'; -import { useAggregator } from '@/modules/aggregator/components/AggregatorProvider'; +import { useBridgeSDK } from '@/core/hooks/useBridgeSDK'; -export function useFromChains(props: IGetFromChainsParams = {}) { - const { getFromChains } = useAggregator(); - - const params = useSavedValue(props); +export function useFromChains() { + const bridgeSDK = useBridgeSDK(); const fromChains = useMemo(() => { - return getFromChains(params); - }, [getFromChains, params]); + return bridgeSDK.getFromChains(); + }, [bridgeSDK]); return fromChains; } diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useSelection.ts b/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useSelection.ts index 327053f4..46278454 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useSelection.ts +++ b/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useSelection.ts @@ -1,15 +1,10 @@ import { useChains } from 'wagmi'; import { useCallback } from 'react'; import { useConnection } from '@solana/wallet-adapter-react'; +import { IBridgeChain, IBridgeToken } from '@bnb-chain/canonical-bridge-sdk'; import { useAggregator } from '@/modules/aggregator/components/AggregatorProvider'; -import { isChainOrTokenCompatible } from '@/modules/aggregator/shared/isChainOrTokenCompatible'; -import { - ChainType, - IBridgeChain, - IBridgeToken, - IBridgeTokenWithBalance, -} from '@/modules/aggregator/types'; +import { IBridgeTokenWithBalance } from '@/modules/aggregator/types'; import { useAppDispatch, useAppSelector } from '@/modules/store/StoreProvider'; import { setFromChain, setSelectedToken, setToChain, setToToken } from '@/modules/transfer/action'; import { useTokenPrice } from '@/modules/aggregator/hooks/useTokenPrice'; @@ -17,10 +12,12 @@ import { getTokenBalances } from '@/modules/aggregator/shared/getTokenBalances'; import { sortTokens } from '@/modules/aggregator/shared/sortTokens'; import { useTronWeb } from '@/core/hooks/useTronWeb'; import { useCurrentWallet } from '@/modules/wallet/CurrentWalletProvider'; +import { useBridgeSDK } from '@/core/hooks/useBridgeSDK'; export function useSelection() { const { chainId } = useCurrentWallet(); - const { getFromChains, getToChains, getTokens, getToToken, adapters } = useAggregator(); + + const bridgeSDK = useBridgeSDK(); const fromChain = useAppSelector((state) => state.transfer.fromChain); const toChain = useAppSelector((state) => state.transfer.toChain); @@ -43,6 +40,7 @@ export function useSelection() { toChainId: toChainId!, token: token!, }); + dispatch(setToToken(newToToken)); }; @@ -55,10 +53,12 @@ export function useSelection() { tmpToChain?: IBridgeChain; tmpToken?: IBridgeToken; }) => { - const newToken = getTokens({ - fromChainId: tmpFromChain?.id, - toChainId: tmpToChain?.id, - }).find((t) => t.displaySymbol?.toUpperCase() === tmpToken?.displaySymbol?.toUpperCase()); + const newToken = bridgeSDK + .getTokens({ + fromChainId: tmpFromChain?.id, + toChainId: tmpToChain?.id, + }) + .find((t) => t.displaySymbol?.toUpperCase() === tmpToken?.displaySymbol?.toUpperCase()); const newFromChain = getFromChains({ toChainId: tmpToChain?.id, @@ -123,13 +123,15 @@ export function useSelection() { toChainId: number; tokenSymbol: string; }) { + const { adapters } = bridgeSDK.getSDKOptions(); const bridgeTypes = adapters.map((item) => item.bridgeType); + const token = Object.fromEntries( bridgeTypes.map((item) => [item, { symbol: tokenSymbol }]), ) as any as IBridgeToken; if (chainId) { - const fromChains = getFromChains({}); + const fromChains = bridgeSDK.getFromChains(); const chain = fromChains.find((chain) => chain.id === chainId); if (chain) { selectFromChain(chain); diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useToChains.ts b/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useToChains.ts index ce10f4d2..8fbcb235 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useToChains.ts +++ b/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useToChains.ts @@ -1,17 +1,16 @@ import { useMemo } from 'react'; -import { useSavedValue } from '@/core/hooks/useSavedValue'; -import { useAggregator } from '@/modules/aggregator/components/AggregatorProvider'; -import { IGetToChainsParams } from '@/modules/aggregator/shared/aggregateChains'; +import { useBridgeSDK } from '@/core/hooks/useBridgeSDK'; -export function useToChains(props: IGetToChainsParams) { - const { getToChains } = useAggregator(); - - const params = useSavedValue(props); +export function useToChains({ fromChainId }: { fromChainId?: number }) { + const bridgeSDK = useBridgeSDK(); const toChains = useMemo(() => { - return getToChains(params); - }, [getToChains, params]); + if (!fromChainId) return []; + return bridgeSDK.getToChains({ + fromChainId, + }); + }, [bridgeSDK, fromChainId]); return toChains; } diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useTokenBalance.ts b/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useTokenBalance.ts index fb240384..6cbc93b3 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useTokenBalance.ts +++ b/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useTokenBalance.ts @@ -1,6 +1,6 @@ import { useCallback } from 'react'; +import { IBridgeToken } from '@bnb-chain/canonical-bridge-sdk'; -import { IBridgeToken } from '@/modules/aggregator/types'; import { useAppSelector } from '@/modules/store/StoreProvider'; export function useTokenBalance() { diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useTokenPrice.ts b/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useTokenPrice.ts index 3486a7ef..5e0e12cb 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useTokenPrice.ts +++ b/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useTokenPrice.ts @@ -1,6 +1,6 @@ import { useCallback } from 'react'; +import { IBridgeToken } from '@bnb-chain/canonical-bridge-sdk'; -import { IBridgeToken } from '@/modules/aggregator/types'; import { useAppSelector } from '@/modules/store/StoreProvider'; export function useTokenPrice() { diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useTokens.ts b/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useTokens.ts index 1fc4ab94..1c6645a3 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useTokens.ts +++ b/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useTokens.ts @@ -1,17 +1,24 @@ import { useMemo } from 'react'; -import { useSavedValue } from '@/core/hooks/useSavedValue'; -import { IGetTokensParams } from '@/modules/aggregator/shared/aggregateTokens'; -import { useAggregator } from '@/modules/aggregator/components/AggregatorProvider'; +import { useBridgeSDK } from '@/core/hooks/useBridgeSDK'; -export function useTokens(props: IGetTokensParams) { - const { getTokens } = useAggregator(); - - const params = useSavedValue(props); +export function useTokens({ + fromChainId, + toChainId, +}: { + fromChainId?: number; + toChainId?: number; +}) { + const bridgeSDK = useBridgeSDK(); const tokens = useMemo(() => { - return getTokens(params); - }, [params, getTokens]); + if (!fromChainId || !toChainId) return []; + + return bridgeSDK.getTokens({ + fromChainId, + toChainId, + }); + }, [bridgeSDK, fromChainId, toChainId]); return tokens; } diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/shared/BaseAdapter.ts b/packages/canonical-bridge-widget/src/modules/aggregator/shared/BaseAdapter.ts deleted file mode 100644 index 4dd25425..00000000 --- a/packages/canonical-bridge-widget/src/modules/aggregator/shared/BaseAdapter.ts +++ /dev/null @@ -1,417 +0,0 @@ -import { Address } from 'viem'; -import { BridgeType } from '@bnb-chain/canonical-bridge-sdk'; - -import { IBridgeTokenBaseInfo, IExternalChain, INativeCurrency } from '@/modules/aggregator/types'; -import { isSameAddress } from '@/core/utils/address'; - -export interface ITransferTokenPair { - fromChainId: number; - toChainId: number; - fromTokenAddress: string; - toTokenAddress: string; - fromToken: T; - toToken: T; - isPegged?: boolean; - peggedConfig?: P; -} - -export interface IBaseAdapterOptions { - config: G; - includedChains: number[]; - excludedChains?: number[]; - excludedTokens?: Record>; - nativeCurrencies?: Record; - bridgedTokenGroups?: string[][]; - brandChains?: number[]; - externalChains?: IExternalChain[]; - displayTokenSymbols?: Record>; - assetPrefix?: string; -} - -export abstract class BaseAdapter { - public abstract bridgeType: BridgeType; - - protected readonly config: G; - - protected readonly includedChains: number[] = []; - protected readonly excludedChains: number[] = []; - protected readonly excludedTokens: Record> = {}; - protected readonly nativeCurrencies: Record = {}; - protected readonly bridgedTokenGroups: string[][] = []; - protected readonly brandChains: number[] = []; - protected readonly externalChains: IExternalChain[] = []; - protected readonly displayTokenSymbols: Record> = {}; - protected readonly assetPrefix: string; - - protected chains: C[] = []; - protected chainMap = new Map(); - - protected tokenMap = new Map(); - protected symbolMap = new Map>(); - - protected transferMap = new Map>>>(); - - protected fromChainIds = new Set(); - protected fromChains: C[] = []; - protected toChainIds = new Set(); - protected toChains: C[] = []; - - constructor(options: IBaseAdapterOptions) { - this.config = options.config ?? {}; - - this.includedChains = options.includedChains ?? []; - this.excludedChains = options.excludedChains ?? []; - this.excludedTokens = options.excludedTokens ?? {}; - this.nativeCurrencies = options.nativeCurrencies ?? {}; - this.bridgedTokenGroups = options.bridgedTokenGroups ?? []; - this.brandChains = options.brandChains ?? []; - this.externalChains = options.externalChains ?? []; - this.displayTokenSymbols = options.displayTokenSymbols ?? {}; - this.assetPrefix = options.assetPrefix ?? ''; - - this.init(); - } - - protected init() { - this.initChains(); - this.initTokens(); - - this.initTransferMap(); - this.filterTransferMap(); - - this.initFromChains(); - this.initToChains(); - } - - protected abstract initChains(): void; - protected abstract initTokens(): void; - protected abstract initTransferMap(): void; - protected abstract getChainIdAsObject(chainId: number): unknown; - - public abstract getChainId(chain: C): number; - public abstract getTokenInfo({ - chainId, - token, - }: { - chainId: number; - token: T; - }): IBridgeTokenBaseInfo; - - protected initFromChains() { - this.fromChainIds = new Set(this.transferMap.keys()); - this.fromChains = this.chains.filter((chain) => this.fromChainIds.has(this.getChainId(chain))); - } - - protected initToChains() { - this.toChainIds = new Set(); - this.transferMap.forEach((toMap) => { - toMap.forEach((_, toChainId) => { - this.toChainIds.add(toChainId); - }); - }); - this.toChains = this.chains.filter((chain) => this.toChainIds.has(this.getChainId(chain))); - } - - protected filterTransferMap() { - if (!this.brandChains.length) { - return; - } - - const filteredTransferMap = new Map>>>(); - - this.transferMap.forEach((toMap, fromChainId) => { - if (this.brandChains.includes(fromChainId)) { - filteredTransferMap.set(fromChainId, toMap); - } else { - toMap.forEach((tokenPairMap, toChainId) => { - if (this.brandChains.includes(toChainId)) { - if (!filteredTransferMap.has(fromChainId)) { - filteredTransferMap.set( - fromChainId, - new Map>>(), - ); - } - filteredTransferMap.get(fromChainId)?.set(toChainId, tokenPairMap); - } - }); - } - }); - - this.transferMap = filteredTransferMap; - } - - protected filterToChains({ - fromChainId, - matchedChainIds, - compatibleChainIds, - chains, - tokenSymbol, - }: { - fromChainId?: number; - matchedChainIds: Set; - compatibleChainIds: Set; - chains: C[]; - tokenSymbol?: string; - }) { - let finalMatchedChainIds = new Set(matchedChainIds); - let finalCompatibleChainIds = new Set(compatibleChainIds); - let finalChains = [...chains]; - - if (this.brandChains.length && fromChainId) { - if (this.brandChains.includes(fromChainId)) { - finalChains = chains.filter((chain) => this.getChainId(chain) !== fromChainId); - } else { - finalChains = chains.filter((chain) => this.brandChains.includes(this.getChainId(chain))); - } - } - - if (this.externalChains.length && tokenSymbol && fromChainId) { - this.externalChains.forEach((item) => { - const targetIndex = finalChains.findIndex( - (chain) => this.getChainId(chain) === item.chainId, - ); - - if (item.tokens[fromChainId]?.includes(tokenSymbol)) { - finalMatchedChainIds = new Set([...finalMatchedChainIds, item.chainId]); - finalCompatibleChainIds = new Set([...finalCompatibleChainIds, item.chainId]); - - if (targetIndex === -1) { - const chain = this.getChainIdAsObject(item.chainId) as C; - finalChains.push(chain); - } - } else { - finalMatchedChainIds.delete(item.chainId); - finalCompatibleChainIds.delete(item.chainId); - - if (targetIndex > -1) { - finalChains.splice(targetIndex, 1); - } - } - }); - } - - return { - matchedChainIds: finalMatchedChainIds, - compatibleChainIds: finalCompatibleChainIds, - chains: finalChains, - }; - } - - protected checkIsExcludedToken({ - excludedList, - tokenSymbol, - tokenAddress, - }: { - excludedList: string[]; - tokenSymbol: string; - tokenAddress: string; - }) { - return excludedList?.some( - (e) => - e.toUpperCase() === tokenSymbol.toUpperCase() || - e.toLowerCase() === tokenAddress.toLowerCase(), - ); - } - - protected getTokenDisplaySymbolAndIcon({ - defaultSymbol, - chainId, - tokenAddress, - }: { - chainId: number; - defaultSymbol: string; - tokenAddress: string; - }) { - const symbolMap = this.displayTokenSymbols[chainId] ?? {}; - - const target = Object.entries(symbolMap).find(([address]) => - isSameAddress(address, tokenAddress), - ); - - const displaySymbol = target?.[1] ?? defaultSymbol; - const icon = this.formatTokenIcon(displaySymbol); - - return { - displaySymbol, - icon, - }; - } - - protected formatTokenIcon(tokenSymbol: string) { - const iconSymbol = tokenSymbol.replace(/[+]$/, '_ICON')?.toUpperCase(); - return `${this.assetPrefix}/images/tokens/${iconSymbol}.png`; - } - - // 1. Native currency is ETH -> Native currency is ETH, all transfer to ETH - // 2. Native currency is ETH -> Native currency is NOT ETH, transfer to ETH first, if not, WETH - // 3. Native currency is NOT ETH -> Native currency is ETH, all transfer to ETH - protected getToToken({ - fromChainId, - toChainId, - fromTokenSymbol, - }: { - fromChainId: number; - toChainId: number; - fromTokenSymbol: string; - }) { - const fromNativeSymbol = this.nativeCurrencies[fromChainId]?.symbol?.toUpperCase(); - const toNativeSymbol = this.nativeCurrencies[toChainId]?.symbol?.toUpperCase(); - const tokenMap = this.symbolMap.get(toChainId); - - if (['ETH', 'WETH'].includes(fromTokenSymbol)) { - if (fromNativeSymbol === 'ETH') { - if (toNativeSymbol === 'ETH') { - return tokenMap?.get(fromTokenSymbol); - } else { - return tokenMap?.get('ETH') || tokenMap?.get('WETH'); - } - } else { - if (toNativeSymbol === 'ETH') { - return tokenMap?.get('ETH'); - } - } - } - - let toToken = tokenMap?.get(fromTokenSymbol); - if (!toToken) { - const bridgedGroup = this.bridgedTokenGroups.find((group) => group.includes(fromTokenSymbol)); - const nextToken = bridgedGroup?.find((item) => item.toUpperCase() !== fromTokenSymbol); - if (nextToken) { - toToken = tokenMap?.get(nextToken?.toUpperCase()); - } - } - - return toToken; - } - - public getFromChains({ toChainId, tokenSymbol }: { toChainId?: number; tokenSymbol?: string }) { - let matchedChainIds = new Set(); - - if (!toChainId && !tokenSymbol) { - matchedChainIds = this.fromChainIds; - } - - if (toChainId && !tokenSymbol) { - this.transferMap.forEach((toMap, fromChainId) => { - if (toMap.get(toChainId)) { - matchedChainIds.add(fromChainId); - } - }); - } - - if (!toChainId && tokenSymbol) { - this.transferMap.forEach((toMap, fromChainId) => { - toMap.forEach((tokenPairMap) => { - if (tokenPairMap.get(tokenSymbol)) { - matchedChainIds.add(fromChainId); - } - }); - }); - } - - if (toChainId && tokenSymbol) { - this.transferMap.forEach((toMap, fromChainId) => { - if (toMap.get(toChainId)?.get(tokenSymbol)) { - matchedChainIds.add(fromChainId); - } - }); - } - - return { - matchedChainIds, - compatibleChainIds: this.fromChainIds, - chains: this.fromChains, - }; - } - - public getToChains({ fromChainId, tokenSymbol }: { fromChainId?: number; tokenSymbol?: string }) { - let matchedChainIds = new Set(); - - if (!fromChainId && !tokenSymbol) { - matchedChainIds = this.toChainIds; - } - - if (fromChainId && !tokenSymbol) { - const toMap = this.transferMap.get(fromChainId); - matchedChainIds = new Set(toMap?.keys()); - } - - if (!fromChainId && tokenSymbol) { - this.transferMap.forEach((toMap) => { - toMap.forEach((tokenPairMap, toChainId) => { - if (tokenPairMap.get(tokenSymbol)) { - matchedChainIds.add(toChainId); - } - }); - }); - } - - if (fromChainId && tokenSymbol) { - const toMap = this.transferMap.get(fromChainId); - toMap?.forEach((tokenPairMap, toChainId) => { - if (tokenPairMap.get(tokenSymbol)) { - matchedChainIds.add(toChainId); - } - }); - } - - let compatibleChainIds = this.toChainIds; - let toChains = this.fromChains; - - if (fromChainId) { - const toMap = this.transferMap.get(fromChainId); - compatibleChainIds = new Set(toMap?.keys()); - - const toChainIds = new Set(toMap?.keys()); - toChains = this.chains.filter((c) => toChainIds.has(this.getChainId(c))); - } - - return this.filterToChains({ - fromChainId, - matchedChainIds, - compatibleChainIds, - chains: toChains, - tokenSymbol, - }); - } - - public getTokens({ fromChainId, toChainId }: { fromChainId: number; toChainId: number }) { - const allTokenPairMap = new Map>(); - - const toMap = this.transferMap.get(fromChainId); - toMap?.forEach((tokenPairMap) => { - tokenPairMap.forEach((tokenPair, tokenSymbol) => { - allTokenPairMap.set(tokenSymbol, tokenPair); - }); - }); - - // update info - const tokenPairMap = this.transferMap.get(fromChainId)?.get(toChainId); - tokenPairMap?.forEach((tokenPair, tokenSymbol) => { - allTokenPairMap.set(tokenSymbol, tokenPair); - }); - - const matchedTokens = new Set(tokenPairMap ? tokenPairMap.keys() : []); - - return { - matchedTokens, - compatibleTokens: matchedTokens, - tokenPairs: [...allTokenPairMap.values()], - }; - } - - public getTokenPair({ - fromChainId, - toChainId, - tokenSymbol, - }: { - fromChainId: number; - toChainId: number; - tokenSymbol: string; - }) { - const tokenPair = this.transferMap.get(fromChainId)?.get(toChainId)?.get(tokenSymbol); - return { - tokenPair, - }; - } -} diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/shared/aggregateChains.ts b/packages/canonical-bridge-widget/src/modules/aggregator/shared/aggregateChains.ts deleted file mode 100644 index 17b3f0e8..00000000 --- a/packages/canonical-bridge-widget/src/modules/aggregator/shared/aggregateChains.ts +++ /dev/null @@ -1,156 +0,0 @@ -import { - AdapterType, - ChainType, - IBridgeChain, - ITransferConfig, - IBridgeToken, - IChainConfig, -} from '@/modules/aggregator/types'; -import { isChainOrTokenCompatible } from '@/modules/aggregator/shared/isChainOrTokenCompatible'; - -export interface IGetFromChainsParams { - toChainId?: number; - token?: IBridgeToken; -} - -export interface IGetToChainsParams { - fromChainId?: number; - token?: IBridgeToken; -} - -export interface IAggregateChainsParams { - direction: 'from' | 'to'; - transferConfig: ITransferConfig; - chainConfigs: IChainConfig[]; - adapters: AdapterType[]; - params: IGetFromChainsParams | IGetToChainsParams; - assetPrefix?: string; -} - -export function aggregateChains({ - direction, - adapters, - params, - transferConfig, - chainConfigs, - assetPrefix, -}: IAggregateChainsParams) { - const chainMap = new Map(); - - const chainOrder = transferConfig.order?.chains ?? []; - - adapters.forEach((adapter) => { - const { bridgeType } = adapter; - - const tmpSymbol = params.token?.[bridgeType]?.symbol?.toUpperCase(); - const isNotSelf = params.token && !tmpSymbol; - const tokenSymbol = isNotSelf ? '???????' : tmpSymbol; // TODO - - const { chains, matchedChainIds, compatibleChainIds } = - direction === 'from' - ? adapter.getFromChains({ - toChainId: (params as IGetFromChainsParams).toChainId, - tokenSymbol, - }) - : adapter.getToChains({ - fromChainId: (params as IGetToChainsParams).fromChainId, - tokenSymbol, - }); - - chains.forEach((item: any) => { - const chainId = adapter.getChainId(item); - - let bridgeChain = chainMap.get(chainId); - const isMatched = matchedChainIds.has(chainId); - const isCompatible = compatibleChainIds.has(chainId); - - if (!bridgeChain) { - bridgeChain = { - ...getChainInfo({ - assetPrefix, - chainId, - transferConfig, - chainConfigs, - }), - }; - } - - bridgeChain = { - ...bridgeChain, - [bridgeType]: { - isCompatible, - isMatched, - raw: isMatched ? item : undefined, - }, - }; - - chainMap.set(chainId, bridgeChain); - }); - }); - - const chains = [...chainMap.values()]; - - chains.sort((a, b) => { - if (direction === 'to') { - const isA = isChainOrTokenCompatible(a); - const isB = isChainOrTokenCompatible(b); - - if (isA && !isB) { - return -1; - } - if (!isA && isB) { - return 1; - } - } - - const indexA = chainOrder.indexOf(a.id); - const indexB = chainOrder.indexOf(b.id); - - if (indexA > -1 && indexB === -1) { - return -1; - } - if (indexA === -1 && indexB > -1) { - return 1; - } - if (indexA > -1 && indexB > -1) { - return indexA - indexB; - } - - return a.name < b.name ? -1 : 1; - }); - - return chains; -} - -function getChainInfo({ - assetPrefix, - chainId, - transferConfig, - chainConfigs, -}: { - assetPrefix?: string; - chainId: number; - transferConfig: ITransferConfig; - chainConfigs: IChainConfig[]; -}) { - const chainConfig = chainConfigs.find((item) => item.id === chainId); - - const explorerUrl = chainConfig?.explorer.url?.replace(/\/$/, '') ?? ''; - const tmpUrlPattern = explorerUrl ? `${explorerUrl}/token/{0}` : ''; - const tokenUrlPattern = chainConfig?.explorer?.tokenUrlPattern || tmpUrlPattern; - - const externalConfig = transferConfig.externalChains?.find((item) => item.chainId === chainId); - const chainType: ChainType = externalConfig ? 'link' : chainConfig?.chainType ?? 'evm'; - const externalBridgeUrl = externalConfig?.bridgeUrl; - - return { - id: chainId, - name: chainConfig?.name ?? '', - icon: `${assetPrefix}/images/chains/${chainId}.png`, - explorerUrl, - rpcUrl: chainConfig?.rpcUrl ?? '', - tokenUrlPattern, - chainType, - externalBridgeUrl, - }; -} diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/shared/aggregateToToken.ts b/packages/canonical-bridge-widget/src/modules/aggregator/shared/aggregateToToken.ts deleted file mode 100644 index 3e8e315c..00000000 --- a/packages/canonical-bridge-widget/src/modules/aggregator/shared/aggregateToToken.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { AdapterType, IBridgeToken } from '@/modules/aggregator/types'; - -export interface IGetToTokenParams { - fromChainId: number; - toChainId: number; - token: IBridgeToken; -} - -export interface IAggregateToTokenParams { - adapters: AdapterType[]; - params: IGetToTokenParams; -} - -export function aggregateToToken({ adapters, params }: IAggregateToTokenParams) { - let bridgeToken: IBridgeToken | undefined; - - adapters.forEach((adapter) => { - const { bridgeType } = adapter; - - const { tokenPair } = adapter.getTokenPair({ - fromChainId: params.fromChainId, - toChainId: params.toChainId, - tokenSymbol: params.token?.[bridgeType]?.symbol?.toUpperCase() as string, - }) as any; - - const toToken = tokenPair?.toToken; - - if (toToken) { - const baseInfo = adapter.getTokenInfo({ - chainId: tokenPair.toChainId, - token: toToken, - }); - - if (!bridgeToken) { - bridgeToken = { - ...baseInfo, - isPegged: !!tokenPair?.isPegged, - }; - } - - const common = { - ...baseInfo, - isCompatible: true, - isMatched: true, - raw: toToken, - }; - - if (bridgeType === 'cBridge') { - bridgeToken[bridgeType] = { - ...common, - peggedConfig: tokenPair.peggedConfig, - }; - } else { - bridgeToken[bridgeType] = { - ...common, - }; - } - } - }); - - return bridgeToken; -} diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/shared/aggregateTokens.ts b/packages/canonical-bridge-widget/src/modules/aggregator/shared/aggregateTokens.ts deleted file mode 100644 index 3d125ea6..00000000 --- a/packages/canonical-bridge-widget/src/modules/aggregator/shared/aggregateTokens.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { CBridgePeggedPairConfig } from '@bnb-chain/canonical-bridge-sdk'; - -import { ITransferTokenPair } from '@/modules/aggregator/shared/BaseAdapter'; -import { AdapterType, IBridgeToken } from '@/modules/aggregator/types'; - -export interface IGetTokensParams { - fromChainId?: number; - toChainId?: number; -} - -export interface IAggregateTokensParams { - adapters: AdapterType[]; - params: IGetTokensParams; -} - -export function aggregateTokens({ adapters, params }: IAggregateTokensParams) { - const tokenMap = new Map(); - - adapters.forEach((adapter) => { - const { bridgeType } = adapter; - - const { matchedTokens, compatibleTokens, tokenPairs } = adapter.getTokens({ - fromChainId: params.fromChainId!, - toChainId: params.toChainId!, - }); - - tokenPairs.forEach((item: ITransferTokenPair) => { - const { fromToken, fromChainId, peggedConfig } = item; - - const baseInfo = adapter.getTokenInfo({ - chainId: fromChainId, - token: fromToken, - }); - let bridgeToken = tokenMap.get(baseInfo.displaySymbol.toUpperCase()); - - const isMatched = matchedTokens.has(baseInfo.symbol.toUpperCase()); - const isCompatible = compatibleTokens.has(baseInfo.symbol.toUpperCase()); - - if (!bridgeToken) { - bridgeToken = { - ...baseInfo, - isPegged: !!item.isPegged, - }; - } - - // update base info - if (isMatched) { - bridgeToken = { - ...bridgeToken, - address: baseInfo.address, - decimals: baseInfo.decimals, - }; - } - - const common = { - ...baseInfo, - isCompatible, - isMatched, - raw: isMatched ? fromToken : undefined, - }; - - if (bridgeType === 'cBridge') { - bridgeToken[bridgeType] = { - ...common, - peggedConfig: !!item.isPegged ? (peggedConfig as CBridgePeggedPairConfig) : undefined, - }; - } else { - bridgeToken[bridgeType] = { - ...common, - }; - } - - tokenMap.set(baseInfo.displaySymbol.toUpperCase(), bridgeToken); - }); - }); - - const tokens = [...tokenMap.values()]; - return tokens; -} diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/shared/getNativeCurrencies.ts b/packages/canonical-bridge-widget/src/modules/aggregator/shared/getNativeCurrencies.ts deleted file mode 100644 index 00db5b73..00000000 --- a/packages/canonical-bridge-widget/src/modules/aggregator/shared/getNativeCurrencies.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { IChainConfig, INativeCurrency } from '@/modules/aggregator/types'; - -export function getNativeCurrencies(chainConfigs: IChainConfig[]) { - const nativeCurrencies: Record = {}; - - chainConfigs.forEach((chain) => { - if (chain.id && chain.nativeCurrency) { - nativeCurrencies[chain.id] = chain.nativeCurrency; - } - }); - - return nativeCurrencies; -} diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/shared/getTokenBalances.ts b/packages/canonical-bridge-widget/src/modules/aggregator/shared/getTokenBalances.ts index b98833ef..aae55c74 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/shared/getTokenBalances.ts +++ b/packages/canonical-bridge-widget/src/modules/aggregator/shared/getTokenBalances.ts @@ -4,10 +4,9 @@ import axios from 'axios'; import * as SPLToken from '@solana/spl-token'; import { Connection, PublicKey } from '@solana/web3.js'; import { TOKEN_PROGRAM_ID } from '@solana/spl-token'; +import { ChainType, IBridgeToken } from '@bnb-chain/canonical-bridge-sdk'; -import { ChainType, IBridgeToken } from '@/modules/aggregator/types'; import { ERC20_TOKEN } from '@/core/contract/abi'; -import { isChainOrTokenCompatible } from '@/modules/aggregator/shared/isChainOrTokenCompatible'; import { isSameAddress } from '@/core/utils/address'; export async function getTokenBalances({ @@ -29,7 +28,7 @@ export async function getTokenBalances({ }) { if (walletType !== chainType) return {}; - const compatibleTokens = tokens?.filter((item) => isChainOrTokenCompatible(item)); + const compatibleTokens = tokens?.filter((item) => item.isCompatible); if (chainType === 'solana') { return await getSolanaTokenBalances({ diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/shared/isChainOrTokenCompatible.ts b/packages/canonical-bridge-widget/src/modules/aggregator/shared/isChainOrTokenCompatible.ts deleted file mode 100644 index b05ac927..00000000 --- a/packages/canonical-bridge-widget/src/modules/aggregator/shared/isChainOrTokenCompatible.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { IBridgeChain, IBridgeToken } from '../types'; - -export function isChainOrTokenCompatible(data?: IBridgeToken | IBridgeChain) { - return data && Object.values(data).some((item) => typeof item === 'object' && item.isCompatible); -} diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/shared/sortTokens.ts b/packages/canonical-bridge-widget/src/modules/aggregator/shared/sortTokens.ts index 460514b9..bed5f977 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/shared/sortTokens.ts +++ b/packages/canonical-bridge-widget/src/modules/aggregator/shared/sortTokens.ts @@ -1,21 +1,28 @@ -import { isChainOrTokenCompatible } from '@/modules/aggregator/shared/isChainOrTokenCompatible'; +import { isSameAddress } from '@bnb-chain/canonical-bridge-sdk'; + import { IBridgeTokenWithBalance } from '@/modules/aggregator/types'; export function sortTokens({ tokens = [], orders = [], + selectedTokenAddress, }: { tokens?: IBridgeTokenWithBalance[]; orders?: string[]; + selectedTokenAddress?: string; }) { const tokenOrders = orders.map((item) => item.toUpperCase()); const sortedTokens = [...tokens].sort((a, b) => { + if (isSameAddress(a.address, selectedTokenAddress) && a.isCompatible) { + return -1; + } + const aSymbol = a.displaySymbol.toUpperCase(); const bSymbol = b.displaySymbol.toUpperCase(); - const isA = isChainOrTokenCompatible(a); - const isB = isChainOrTokenCompatible(b); + const isA = a.isCompatible; + const isB = b.isCompatible; const indexA = tokenOrders.indexOf(aSymbol); const indexB = tokenOrders.indexOf(bSymbol); diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/types.ts b/packages/canonical-bridge-widget/src/modules/aggregator/types.ts index a49742c1..cac84720 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/types.ts +++ b/packages/canonical-bridge-widget/src/modules/aggregator/types.ts @@ -1,123 +1,18 @@ -import { CBridgeAdapter } from '@/modules/aggregator/adapters/cBridge/CBridgeAdapter'; import { - ICBridgeChain, - ICBridgePeggedPairConfig, - ICBridgeToken, + IBridgeToken, ICBridgeTransferConfig, -} from '@/modules/aggregator/adapters/cBridge/types'; -import { DeBridgeAdapter } from '@/modules/aggregator/adapters/deBridge/DeBridgeAdapter'; -import { - IDeBridgeChain, - IDeBridgeToken, IDeBridgeTransferConfig, -} from '@/modules/aggregator/adapters/deBridge/types'; -import { LayerZeroAdapter } from '@/modules/aggregator/adapters/layerZero/LayerZeroAdapter'; -import { - ILayerZeroChain, - ILayerZeroToken, + IExternalChain, ILayerZeroTransferConfig, -} from '@/modules/aggregator/adapters/layerZero/types'; -import { StargateAdapter } from '@/modules/aggregator/adapters/stargate/StargateAdapter'; -import { - IStargateChain, - IStargateToken, + IMesonChain, IStargateTransferConfig, -} from '@/modules/aggregator/adapters/stargate/types'; -import { IMesonChain, IMesonToken } from '@/modules/aggregator/adapters/meson/types'; -import { MesonAdapter } from '@/modules/aggregator/adapters/meson/MesonAdapter'; - -export interface IBridgeChain { - id: number; - name: string; - icon?: string; - explorerUrl: string; - rpcUrl: string; - tokenUrlPattern: string; - chainType: ChainType; - externalBridgeUrl?: string; - cBridge?: { - isCompatible: boolean; - isMatched: boolean; - raw?: ICBridgeChain; - }; - deBridge?: { - isCompatible: boolean; - isMatched: boolean; - raw?: IDeBridgeChain; - }; - stargate?: { - isCompatible: boolean; - isMatched: boolean; - raw?: IStargateChain; - }; - layerZero?: { - isCompatible: boolean; - isMatched: boolean; - raw?: ILayerZeroChain; - }; - meson?: { - isCompatible: boolean; - isMatched: boolean; - raw?: IMesonChain; - }; -} - -export interface IBridgeTokenBaseInfo { - name: string; - symbol: string; - address: string; - decimals: number; - displaySymbol: string; - icon: string; -} - -export interface IBridgeToken extends IBridgeTokenBaseInfo { - isPegged: boolean; - cBridge?: IBridgeTokenBaseInfo & { - isCompatible: boolean; - isMatched: boolean; - peggedConfig?: ICBridgePeggedPairConfig; - raw?: ICBridgeToken; - }; - deBridge?: IBridgeTokenBaseInfo & { - isCompatible: boolean; - isMatched: boolean; - raw?: IDeBridgeToken; - }; - stargate?: IBridgeTokenBaseInfo & { - isCompatible: boolean; - isMatched: boolean; - raw?: IStargateToken; - }; - layerZero?: IBridgeTokenBaseInfo & { - isCompatible: boolean; - isMatched: boolean; - raw?: ILayerZeroToken; - }; - meson?: IBridgeTokenBaseInfo & { - isCompatible: boolean; - isMatched: boolean; - raw?: IMesonToken; - }; -} +} from '@bnb-chain/canonical-bridge-sdk'; export interface IBridgeTokenWithBalance extends IBridgeToken { balance?: number; value?: number; } -export interface INativeCurrency { - name: string; - symbol: string; - decimals: number; -} - -export interface IExternalChain { - chainId: number; - bridgeUrl: string; - tokens: Record; -} - export interface ITransferConfig { defaultSelectedInfo: { fromChainId: number; @@ -173,37 +68,3 @@ export interface ITransferConfig { bridgedTokenGroups?: string[][]; }; } - -export interface IChainConfig { - id: number; - name: string; - nativeCurrency: { - name: string; - symbol: string; - decimals: number; - }; - rpcUrl: string; - explorer: { - name: string; - url: string; - tokenUrlPattern?: string; - }; - contracts?: any; - chainType?: ChainType; -} - -export type ChainType = 'link' | 'evm' | 'tron' | 'solana'; - -export type AdapterConstructorType = - | typeof CBridgeAdapter - | typeof DeBridgeAdapter - | typeof StargateAdapter - | typeof LayerZeroAdapter - | typeof MesonAdapter; - -export type AdapterType = - | CBridgeAdapter - | DeBridgeAdapter - | StargateAdapter - | LayerZeroAdapter - | MesonAdapter; diff --git a/packages/canonical-bridge-widget/src/modules/transfer/components/Button/TransferButton.tsx b/packages/canonical-bridge-widget/src/modules/transfer/components/Button/TransferButton.tsx index bde923d6..1ea08ae3 100644 --- a/packages/canonical-bridge-widget/src/modules/transfer/components/Button/TransferButton.tsx +++ b/packages/canonical-bridge-widget/src/modules/transfer/components/Button/TransferButton.tsx @@ -168,7 +168,7 @@ export function TransferButton({ if (transferActionInfo.bridgeType === 'cBridge' && cBridgeArgs && fromChain && address) { try { - const cBridgeHash = await bridgeSDK.cBridge.sendToken({ + const cBridgeHash = await bridgeSDK.cBridge?.sendToken({ // eslint-disable-next-line @typescript-eslint/no-explicit-any walletClient: walletClient as any, publicClient, @@ -211,7 +211,7 @@ export function TransferButton({ let deBridgeHash: string | undefined; if (fromChain?.chainType === 'evm' && transferActionInfo.value && address) { - deBridgeHash = await bridgeSDK.deBridge.sendToken({ + deBridgeHash = await bridgeSDK.deBridge?.sendToken({ // eslint-disable-next-line @typescript-eslint/no-explicit-any walletClient: walletClient as any, bridgeAddress: transferActionInfo.bridgeAddress as string, @@ -255,7 +255,7 @@ export function TransferButton({ handleFailure(e); } } else if (transferActionInfo.bridgeType === 'stargate' && address) { - const stargateHash = await bridgeSDK.stargate.sendToken({ + const stargateHash = await bridgeSDK.stargate?.sendToken({ // eslint-disable-next-line @typescript-eslint/no-explicit-any walletClient: walletClient as any, publicClient, @@ -282,7 +282,7 @@ export function TransferButton({ onOpenSubmittedModal(); } } else if (transferActionInfo.bridgeType === 'layerZero' && address) { - const layerZeroHash = await bridgeSDK.layerZero.sendToken({ + const layerZeroHash = await bridgeSDK.layerZero?.sendToken({ bridgeAddress: transferActionInfo.bridgeAddress as `0x${string}`, dstEndpoint: toToken?.layerZero?.raw?.endpointID as number, userAddress: address, @@ -326,7 +326,7 @@ export function TransferButton({ } // get unsigned message - const unsignedMessage = await bridgeSDK.meson.getUnsignedMessage({ + const unsignedMessage = await bridgeSDK.meson?.getUnsignedMessage({ fromToken: `${fromChain?.meson?.raw?.id}:${selectedToken?.meson?.raw?.id}`, toToken: `${toChain?.meson?.raw?.id}:${toToken?.meson?.raw?.id}`, amount: sendValue, @@ -359,7 +359,7 @@ export function TransferButton({ signature = String(await signTransaction(msg as any)); } - const swapId = await bridgeSDK.meson.sendToken({ + const swapId = await bridgeSDK.meson?.sendToken({ fromAddress: fromAddress, recipient: toAddress, signature: signature, diff --git a/packages/canonical-bridge-widget/src/modules/transfer/components/SelectButton/TokenSelectButton.tsx b/packages/canonical-bridge-widget/src/modules/transfer/components/SelectButton/TokenSelectButton.tsx index 4d0a8505..0bbdf9e4 100644 --- a/packages/canonical-bridge-widget/src/modules/transfer/components/SelectButton/TokenSelectButton.tsx +++ b/packages/canonical-bridge-widget/src/modules/transfer/components/SelectButton/TokenSelectButton.tsx @@ -1,9 +1,9 @@ import { ButtonProps, Flex, Text, useColorMode, Button, useTheme, Box } from '@bnb-chain/space'; import { CaretDownIcon } from '@bnb-chain/icons'; import { useMemo } from 'react'; +import { IBridgeToken } from '@bnb-chain/canonical-bridge-sdk'; import { IconImage } from '@/core/components/IconImage'; -import { IBridgeToken } from '@/modules/aggregator/types'; import { TokenInfoTooltip } from '@/modules/transfer/components/TransferOverview/RouteInfo/TokenInfoTooltip'; import { formatTokenUrl } from '@/core/utils/string'; import { useAppSelector } from '@/modules/store/StoreProvider'; diff --git a/packages/canonical-bridge-widget/src/modules/transfer/components/SelectButton/index.tsx b/packages/canonical-bridge-widget/src/modules/transfer/components/SelectButton/index.tsx index 3c781247..671452df 100644 --- a/packages/canonical-bridge-widget/src/modules/transfer/components/SelectButton/index.tsx +++ b/packages/canonical-bridge-widget/src/modules/transfer/components/SelectButton/index.tsx @@ -1,8 +1,8 @@ import { ButtonProps, Flex, Text, useColorMode, Button, useTheme } from '@bnb-chain/space'; import { CaretDownIcon } from '@bnb-chain/icons'; +import { IBridgeChain } from '@bnb-chain/canonical-bridge-sdk'; import { IconImage } from '@/core/components/IconImage'; -import { IBridgeChain } from '@/modules/aggregator/types'; export interface SelectButtonProps extends Omit { isActive: boolean; diff --git a/packages/canonical-bridge-widget/src/modules/transfer/components/TransferOverview/RouteInfo/RouteTitle.tsx b/packages/canonical-bridge-widget/src/modules/transfer/components/TransferOverview/RouteInfo/RouteTitle.tsx index d8feca21..82fe7bc3 100644 --- a/packages/canonical-bridge-widget/src/modules/transfer/components/TransferOverview/RouteInfo/RouteTitle.tsx +++ b/packages/canonical-bridge-widget/src/modules/transfer/components/TransferOverview/RouteInfo/RouteTitle.tsx @@ -1,10 +1,10 @@ import { Flex, useColorMode, useTheme } from '@bnb-chain/space'; +import { IBridgeTokenBaseInfo } from '@bnb-chain/canonical-bridge-sdk'; import { TokenInfoTooltip } from '@/modules/transfer/components/TransferOverview/RouteInfo/TokenInfoTooltip'; import { IconImage } from '@/core/components/IconImage'; import { useAppSelector } from '@/modules/store/StoreProvider'; import { formatTokenUrl } from '@/core/utils/string'; -import { IBridgeTokenBaseInfo } from '@/modules/aggregator/types'; interface RouteTitleProps { receiveAmt?: string; diff --git a/packages/canonical-bridge-widget/src/modules/transfer/hooks/useLoadingBridgeFees.ts b/packages/canonical-bridge-widget/src/modules/transfer/hooks/useLoadingBridgeFees.ts index 40cc6613..ea9b24bf 100644 --- a/packages/canonical-bridge-widget/src/modules/transfer/hooks/useLoadingBridgeFees.ts +++ b/packages/canonical-bridge-widget/src/modules/transfer/hooks/useLoadingBridgeFees.ts @@ -1,7 +1,7 @@ import { useCallback, useRef } from 'react'; import { formatUnits, parseUnits } from 'viem'; import { useAccount, useBalance, usePublicClient } from 'wagmi'; -import { BridgeType, DeBridgeCreateQuoteResponse } from '@bnb-chain/canonical-bridge-sdk'; +import { BridgeType, IDeBridgeCreateQuoteResponse } from '@bnb-chain/canonical-bridge-sdk'; import { useTronWallet } from '@node-real/walletkit/tron'; import { useIntl } from '@bnb-chain/space'; @@ -118,10 +118,10 @@ export const useLoadingBridgeFees = () => { const bridgeTypeList: BridgeType[] = []; const valueArr = []; - const availableBridgeTypes = bridgeSDK.getSupportedBridges(); - availableBridgeTypes.forEach((bridge) => { - if (selectedToken[bridge]?.isMatched) { - bridgeTypeList.push(bridge); + const adapters = bridgeSDK.getSDKOptions().adapters; + adapters.forEach((adapter) => { + if (selectedToken[adapter.bridgeType]) { + bridgeTypeList.push(adapter.bridgeType); } }); try { @@ -258,17 +258,17 @@ export const useLoadingBridgeFees = () => { // deBridge if (debridgeEst.status === 'fulfilled' && debridgeEst?.value) { const feeSortingRes = await deBridgeFeeSorting.current( - debridgeEst.value as DeBridgeCreateQuoteResponse, + debridgeEst.value as IDeBridgeCreateQuoteResponse, ); if (!feeSortingRes?.isFailedToGetGas) { dispatch( - setEstimatedAmount({ deBridge: debridgeEst.value as DeBridgeCreateQuoteResponse }), + setEstimatedAmount({ deBridge: debridgeEst.value as IDeBridgeCreateQuoteResponse }), ); valueArr.push({ type: 'deBridge', value: formatUnits( BigInt( - (debridgeEst.value as DeBridgeCreateQuoteResponse)?.estimation.dstChainTokenOut + (debridgeEst.value as IDeBridgeCreateQuoteResponse)?.estimation.dstChainTokenOut .amount, ), getToDecimals()['deBridge'], diff --git a/packages/canonical-bridge-widget/src/modules/transfer/reducer.ts b/packages/canonical-bridge-widget/src/modules/transfer/reducer.ts index 057b6844..a8384ea6 100644 --- a/packages/canonical-bridge-widget/src/modules/transfer/reducer.ts +++ b/packages/canonical-bridge-widget/src/modules/transfer/reducer.ts @@ -1,6 +1,7 @@ +import { IBridgeChain, IBridgeToken } from '@bnb-chain/canonical-bridge-sdk'; + import * as actions from '@/modules/transfer/action'; import { createReducer } from '@/modules/store/createReducer'; -import { IBridgeChain, IBridgeToken } from '@/modules/aggregator/types'; import { IBridgeError, IEstimatedAmount, diff --git a/packages/canonical-bridge-widget/src/modules/wallet/CurrentWalletProvider.tsx b/packages/canonical-bridge-widget/src/modules/wallet/CurrentWalletProvider.tsx index 07ac1398..9aae7174 100644 --- a/packages/canonical-bridge-widget/src/modules/wallet/CurrentWalletProvider.tsx +++ b/packages/canonical-bridge-widget/src/modules/wallet/CurrentWalletProvider.tsx @@ -3,8 +3,8 @@ import { TronWallet, useTronWallet } from '@node-real/walletkit/tron'; import React, { useCallback, useContext, useEffect, useState } from 'react'; import { useAccount, useDisconnect } from 'wagmi'; import { SolanaWallet, useSolanaWallet } from '@node-real/walletkit/solana'; +import { ChainType, IChainConfig } from '@bnb-chain/canonical-bridge-sdk'; -import { ChainType, IChainConfig } from '@/modules/aggregator'; import { useAggregator } from '@/modules/aggregator/components/AggregatorProvider'; import { FormattedBalance, useEvmBalance } from '@/modules/wallet/hooks/useEvmBalance'; import { useEvmSwitchChain } from '@/modules/wallet/hooks/useEvmSwitchChain'; diff --git a/packages/canonical-bridge-widget/src/modules/wallet/WalletProvider.tsx b/packages/canonical-bridge-widget/src/modules/wallet/WalletProvider.tsx index 4aaaa061..e8977c73 100644 --- a/packages/canonical-bridge-widget/src/modules/wallet/WalletProvider.tsx +++ b/packages/canonical-bridge-widget/src/modules/wallet/WalletProvider.tsx @@ -23,8 +23,8 @@ import { } from '@node-real/walletkit/solana'; import React from 'react'; import { useDisclosure, useIntl } from '@bnb-chain/space'; +import { IChainConfig } from '@bnb-chain/canonical-bridge-sdk'; -import { IChainConfig } from '@/modules/aggregator/types'; import { useBridgeConfig } from '@/CanonicalBridgeProvider'; import { useAggregator } from '@/modules/aggregator/components/AggregatorProvider'; import { CurrentWalletProvider } from '@/modules/wallet/CurrentWalletProvider'; From 030d8c38e046039048196c2c0173737dc42ec2b8 Mon Sep 17 00:00:00 2001 From: wenty22 Date: Wed, 13 Nov 2024 09:40:14 +0800 Subject: [PATCH 5/7] refactor: Refactor sdk --- .../src/aggregator/index.ts | 16 ++- .../stargate/hooks/useStargateWaitTime.ts | 2 +- .../SelectModal/DestinationNetworkModal.tsx | 5 +- .../SelectModal/SourceNetworkModal.tsx | 5 +- .../SelectModal/hooks/useChainList.ts | 59 +++++++++ .../SelectModal/hooks/useTokenList.ts | 5 +- .../modules/aggregator/hooks/useSelection.ts | 114 ++++++++---------- .../modules/aggregator/shared/sortTokens.ts | 8 -- 8 files changed, 131 insertions(+), 83 deletions(-) create mode 100644 packages/canonical-bridge-widget/src/modules/aggregator/components/SelectModal/hooks/useChainList.ts diff --git a/packages/canonical-bridge-sdk/src/aggregator/index.ts b/packages/canonical-bridge-sdk/src/aggregator/index.ts index 14dc52dd..e268d9d3 100644 --- a/packages/canonical-bridge-sdk/src/aggregator/index.ts +++ b/packages/canonical-bridge-sdk/src/aggregator/index.ts @@ -99,11 +99,13 @@ export class CanonicalBridgeSDK { const adapter = this.options.adapters.find( (e) => e.bridgeType === bridgeType ); - if (adapter) return adapter; - - throw Error( - `${bridgeType} adapter is not found, you should initialize an adapter before using` - ); + if (adapter) { + return adapter; + } else { + console.warn( + `${bridgeType} adapter is not found, you should initialize an adapter before using` + ); + } }, }); }); @@ -559,4 +561,8 @@ export class CanonicalBridgeSDK { return Array.from(tokenMap.values()); } + + public getChainDetail() {} + + public getTokenDetail() {} } diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/stargate/hooks/useStargateWaitTime.ts b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/stargate/hooks/useStargateWaitTime.ts index e9fb6027..baae8a76 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/stargate/hooks/useStargateWaitTime.ts +++ b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/stargate/hooks/useStargateWaitTime.ts @@ -22,7 +22,7 @@ export const useStargateWaitTime = () => { toEndpointId: String(toEndpointId), }); }, - enabled: !!fromEndpointId && !!toEndpointId && isMainnet, + enabled: !!fromEndpointId && !!toEndpointId && isMainnet && !!bridgeSDK.stargate, staleTime: 1000 * 30, }); }; diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/components/SelectModal/DestinationNetworkModal.tsx b/packages/canonical-bridge-widget/src/modules/aggregator/components/SelectModal/DestinationNetworkModal.tsx index 3f1a3d73..e7794bb5 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/components/SelectModal/DestinationNetworkModal.tsx +++ b/packages/canonical-bridge-widget/src/modules/aggregator/components/SelectModal/DestinationNetworkModal.tsx @@ -10,6 +10,7 @@ import { reportEvent } from '@/core/utils/gtm'; import { useSearch } from '@/modules/aggregator/components/SelectModal/hooks/useSearch'; import { BaseModal } from '@/modules/aggregator/components/SelectModal/components/BaseModal'; import { ListItem } from '@/modules/aggregator/components/SelectModal/components/ListItem'; +import { useChainList } from '@/modules/aggregator/components/SelectModal/hooks/useChainList'; interface DestinationNetworkModalProps { isOpen: boolean; @@ -33,9 +34,9 @@ export function DestinationNetworkModal(props: DestinationNetworkModalProps) { const { isNoResult, result, onSearch } = useSearch({ filter: (item, keyword) => item.name.toLowerCase().includes(keyword), - sorter: (a) => (toChain?.id === a.id ? -1 : 0), data: toChains, }); + const { data } = useChainList('to', result); return ( - + {(item) => ( item.name.toLowerCase().includes(keyword), - sorter: (a) => (fromChain?.id === a.id ? -1 : 0), data: fromChains, }); + const { data } = useChainList('from', result); return ( - + {(item) => ( state.transfer.fromChain); + const toChain = useAppSelector((state) => state.transfer.toChain); + + const sortedChains = useMemo(() => { + const chainOrder = transferConfig.order?.chains ?? []; + + const sortedChains = chains + .sort((a, b) => { + if (direction === 'to') { + const isA = a.isCompatible; + const isB = b.isCompatible; + + if (isA && !isB) { + return -1; + } + if (!isA && isB) { + return 1; + } + } + + const indexA = chainOrder.indexOf(a.id); + const indexB = chainOrder.indexOf(b.id); + + if (indexA > -1 && indexB === -1) { + return -1; + } + if (indexA === -1 && indexB > -1) { + return 1; + } + if (indexA > -1 && indexB > -1) { + return indexA - indexB; + } + + return a.name < b.name ? -1 : 1; + }) + .sort((a) => { + if (direction === 'from' && a.id === fromChain?.id) { + return -1; + } + if (direction === 'to' && a.id === toChain?.id) { + return -1; + } + return 0; + }); + + return sortedChains; + }, [transferConfig.order?.chains, chains, direction, fromChain?.id, toChain?.id]); + + return { data: sortedChains }; +} diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/components/SelectModal/hooks/useTokenList.ts b/packages/canonical-bridge-widget/src/modules/aggregator/components/SelectModal/hooks/useTokenList.ts index 1409185f..4498dcdc 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/components/SelectModal/hooks/useTokenList.ts +++ b/packages/canonical-bridge-widget/src/modules/aggregator/components/SelectModal/hooks/useTokenList.ts @@ -1,5 +1,5 @@ import { useMemo } from 'react'; -import { IBridgeToken } from '@bnb-chain/canonical-bridge-sdk'; +import { IBridgeToken, isSameAddress } from '@bnb-chain/canonical-bridge-sdk'; import { IBridgeTokenWithBalance } from '@/modules/aggregator/types'; import { useTokenPrice } from '@/modules/aggregator/hooks/useTokenPrice'; @@ -37,7 +37,8 @@ export function useTokenList(tokens: IBridgeToken[] = []) { const sortedTokens = sortTokens({ tokens: tmpTokens, orders: transferConfig.order?.tokens, - selectedTokenAddress: selectedToken?.address, + }).sort((a) => { + return isSameAddress(a.address, selectedToken?.address) && a.isCompatible ? -1 : 0; }); return sortedTokens; diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useSelection.ts b/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useSelection.ts index 46278454..5eb8b549 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useSelection.ts +++ b/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useSelection.ts @@ -1,7 +1,7 @@ import { useChains } from 'wagmi'; import { useCallback } from 'react'; import { useConnection } from '@solana/wallet-adapter-react'; -import { IBridgeChain, IBridgeToken } from '@bnb-chain/canonical-bridge-sdk'; +import { ChainType, IBridgeChain, IBridgeToken } from '@bnb-chain/canonical-bridge-sdk'; import { useAggregator } from '@/modules/aggregator/components/AggregatorProvider'; import { IBridgeTokenWithBalance } from '@/modules/aggregator/types'; @@ -35,13 +35,12 @@ export function useSelection() { toChainId?: number; token?: IBridgeToken; }) => { - const newToToken = getToToken({ - fromChainId: fromChainId!, - toChainId: toChainId!, - token: token!, - }); - - dispatch(setToToken(newToToken)); + // const newToToken = getToToken({ + // fromChainId: fromChainId!, + // toChainId: toChainId!, + // token: token!, + // }); + // dispatch(setToToken(newToToken)); }; const updateSelectedInfo = ({ @@ -53,58 +52,58 @@ export function useSelection() { tmpToChain?: IBridgeChain; tmpToken?: IBridgeToken; }) => { - const newToken = bridgeSDK - .getTokens({ - fromChainId: tmpFromChain?.id, - toChainId: tmpToChain?.id, - }) - .find((t) => t.displaySymbol?.toUpperCase() === tmpToken?.displaySymbol?.toUpperCase()); - - const newFromChain = getFromChains({ - toChainId: tmpToChain?.id, - token: newToken, - }).find((c) => c.id === tmpFromChain?.id); - - const newToChain = getToChains({ - fromChainId: tmpFromChain?.id, - token: newToken, - }).find((c) => c.id === tmpToChain?.id); - - dispatch(setFromChain(newFromChain)); - dispatch(setToChain(newToChain)); - dispatch(setSelectedToken(newToken)); - - updateToToken({ - fromChainId: newFromChain?.id, - toChainId: newToChain?.id, - token: newToken, - }); + // const newToken = bridgeSDK + // .getTokens({ + // fromChainId: tmpFromChain?.id, + // toChainId: tmpToChain?.id, + // }) + // .find((t) => t.displaySymbol?.toUpperCase() === tmpToken?.displaySymbol?.toUpperCase()); + // const newFromChain = bridgeSDK + // .getFromChains({ + // toChainId: tmpToChain?.id, + // token: newToken, + // }) + // .find((c) => c.id === tmpFromChain?.id); + // const newToChain = bridgeSDK + // .getToChains({ + // fromChainId: tmpFromChain?.id, + // token: newToken, + // }) + // .find((c) => c.id === tmpToChain?.id); + // dispatch(setFromChain(newFromChain)); + // dispatch(setToChain(newToChain)); + // dispatch(setSelectedToken(newToken)); + // updateToToken({ + // fromChainId: newFromChain?.id, + // toChainId: newToChain?.id, + // token: newToken, + // }); }; const selectFromChain = async (tmpFromChain: IBridgeChain) => { // After selecting fromChain, if toChain becomes incompatible, reselect the first compatible network in toChain list. - const toChains = getToChains({ + const toChains = bridgeSDK.getToChains({ fromChainId: tmpFromChain.id, }); const tmpToChain = - toChains.find((c) => isChainOrTokenCompatible(c) && c.id === toChain?.id) ?? - toChains.find((c) => isChainOrTokenCompatible(c) && c.chainType !== 'link'); + toChains.find((c) => c.isCompatible && c.id === toChain?.id) ?? + toChains.find((c) => c.isCompatible && c.chainType !== 'link'); const tmpTokens = await getSortedTokens({ chainType: tmpFromChain.chainType, fromChainId: tmpFromChain.id, - tokens: getTokens({ + tokens: bridgeSDK.getTokens({ fromChainId: tmpFromChain.id, - toChainId: tmpToChain?.id, + toChainId: tmpToChain!.id, }), }); const newToken = tmpTokens.find( (t) => - isChainOrTokenCompatible(t) && + t.isCompatible && t.displaySymbol.toUpperCase() === selectedToken?.displaySymbol.toUpperCase(), - ) ?? tmpTokens.find((t) => isChainOrTokenCompatible(t)); + ) ?? tmpTokens.find((t) => t.isCompatible); updateSelectedInfo({ tmpToken: newToken, @@ -123,13 +122,6 @@ export function useSelection() { toChainId: number; tokenSymbol: string; }) { - const { adapters } = bridgeSDK.getSDKOptions(); - const bridgeTypes = adapters.map((item) => item.bridgeType); - - const token = Object.fromEntries( - bridgeTypes.map((item) => [item, { symbol: tokenSymbol }]), - ) as any as IBridgeToken; - if (chainId) { const fromChains = bridgeSDK.getFromChains(); const chain = fromChains.find((chain) => chain.id === chainId); @@ -140,15 +132,11 @@ export function useSelection() { if (fromChain?.id) return; } - const fromChains = getFromChains({ - toChainId, - token, - }); - const toChains = getToChains({ + const fromChains = bridgeSDK.getFromChains(); + const toChains = bridgeSDK.getToChains({ fromChainId, - token, }); - const tokens = getTokens({ + const tokens = bridgeSDK.getTokens({ fromChainId, toChainId, }); @@ -163,11 +151,11 @@ export function useSelection() { dispatch(setToChain(newToChain)); dispatch(setSelectedToken(newToken)); - updateToToken({ - fromChainId: newFromChain?.id, - toChainId: newToChain?.id, - token: newToken, - }); + // updateToToken({ + // fromChainId: newFromChain?.id, + // toChainId: newToChain?.id, + // token: newToken, + // }); }, selectFromChain, async selectToChain(tmpToChain: IBridgeChain) { @@ -175,7 +163,7 @@ export function useSelection() { const tmpTokens = await getSortedTokens({ fromChainId, - tokens: getTokens({ + tokens: bridgeSDK.getTokens({ fromChainId, toChainId: tmpToChain?.id, }), @@ -184,9 +172,9 @@ export function useSelection() { const newToken = tmpTokens.find( (t) => - isChainOrTokenCompatible(t) && + t.isCompatible && t.displaySymbol.toUpperCase() === selectedToken?.displaySymbol.toUpperCase(), - ) ?? tmpTokens.find((t) => isChainOrTokenCompatible(t)); + ) ?? tmpTokens.find((t) => t.isCompatible); updateSelectedInfo({ tmpToken: newToken, diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/shared/sortTokens.ts b/packages/canonical-bridge-widget/src/modules/aggregator/shared/sortTokens.ts index bed5f977..18590b0f 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/shared/sortTokens.ts +++ b/packages/canonical-bridge-widget/src/modules/aggregator/shared/sortTokens.ts @@ -1,23 +1,15 @@ -import { isSameAddress } from '@bnb-chain/canonical-bridge-sdk'; - import { IBridgeTokenWithBalance } from '@/modules/aggregator/types'; export function sortTokens({ tokens = [], orders = [], - selectedTokenAddress, }: { tokens?: IBridgeTokenWithBalance[]; orders?: string[]; - selectedTokenAddress?: string; }) { const tokenOrders = orders.map((item) => item.toUpperCase()); const sortedTokens = [...tokens].sort((a, b) => { - if (isSameAddress(a.address, selectedTokenAddress) && a.isCompatible) { - return -1; - } - const aSymbol = a.displaySymbol.toUpperCase(); const bSymbol = b.displaySymbol.toUpperCase(); From c90711effa46e732d610000d6692f5cbb61095a9 Mon Sep 17 00:00:00 2001 From: wenty22 Date: Thu, 14 Nov 2024 12:26:16 +0800 Subject: [PATCH 6/7] refactor: Refactor sdk --- .../token-config/mainnet/useTransferConfig.ts | 2 +- .../testnet/useTestnetTransferConfig.ts | 2 +- .../src/adapters/base/index.ts | 112 ++-- .../src/adapters/base/types.ts | 7 +- .../src/adapters/cBridge/index.ts | 22 +- .../src/adapters/deBridge/index.ts | 33 +- .../src/adapters/deBridge/types.ts | 2 +- .../src/adapters/layerZero/index.ts | 4 +- .../src/adapters/meson/index.ts | 22 +- .../src/adapters/stargate/index.ts | 22 +- .../src/aggregator/index.ts | 624 +++++++++++------- .../src/aggregator/utils/sortChains.ts | 42 ++ .../src/aggregator/utils/sortTokens.ts | 43 ++ .../src/constants/index.ts | 1 + .../components/AggregatorProvider.tsx | 5 +- .../SelectModal/DestinationNetworkModal.tsx | 5 +- .../SelectModal/SourceNetworkModal.tsx | 5 +- .../SelectModal/hooks/useChainList.ts | 59 -- .../modules/aggregator/hooks/useSelection.ts | 213 +++--- .../src/modules/aggregator/types.ts | 2 +- .../transfer/hooks/useLoadingBridgeFees.ts | 101 +-- .../transfer/hooks/usePreSelectRoute.ts | 8 +- 22 files changed, 723 insertions(+), 613 deletions(-) create mode 100644 packages/canonical-bridge-sdk/src/aggregator/utils/sortChains.ts create mode 100644 packages/canonical-bridge-sdk/src/aggregator/utils/sortTokens.ts delete mode 100644 packages/canonical-bridge-widget/src/modules/aggregator/components/SelectModal/hooks/useChainList.ts diff --git a/apps/canonical-bridge-ui/token-config/mainnet/useTransferConfig.ts b/apps/canonical-bridge-ui/token-config/mainnet/useTransferConfig.ts index ad81eac0..f5af6a98 100644 --- a/apps/canonical-bridge-ui/token-config/mainnet/useTransferConfig.ts +++ b/apps/canonical-bridge-ui/token-config/mainnet/useTransferConfig.ts @@ -28,7 +28,7 @@ export function useTransferConfig() { defaultSelectedInfo: { fromChainId: 1, toChainId: 56, - tokenSymbol: 'USDT', // USDT + tokenAddress: '0xdac17f958d2ee523a2206206994597c13d831ec7', // USDT amount: '', }, order: { diff --git a/apps/canonical-bridge-ui/token-config/testnet/useTestnetTransferConfig.ts b/apps/canonical-bridge-ui/token-config/testnet/useTestnetTransferConfig.ts index 35f9f757..906db63e 100644 --- a/apps/canonical-bridge-ui/token-config/testnet/useTestnetTransferConfig.ts +++ b/apps/canonical-bridge-ui/token-config/testnet/useTestnetTransferConfig.ts @@ -12,7 +12,7 @@ export function useTestnetTransferConfig() { defaultSelectedInfo: { fromChainId: 97, toChainId: 3448148188, - tokenSymbol: 'USDT', // USDT + tokenAddress: '0x337610d27c682E347C9cD60BD4b3b107C9d34dDd', // USDT amount: '', }, order: { diff --git a/packages/canonical-bridge-sdk/src/adapters/base/index.ts b/packages/canonical-bridge-sdk/src/adapters/base/index.ts index 9d168ccf..3e862970 100644 --- a/packages/canonical-bridge-sdk/src/adapters/base/index.ts +++ b/packages/canonical-bridge-sdk/src/adapters/base/index.ts @@ -16,6 +16,8 @@ import { isSameAddress } from '@/shared/address'; export abstract class BaseAdapter { public abstract bridgeType: BridgeType; + protected options: IBaseAdapterOptions; + protected config: G; protected excludedChains: number[] = []; protected excludedTokens: Record> = {}; @@ -27,6 +29,7 @@ export abstract class BaseAdapter { protected brandChains: number[] = []; protected externalChains: IExternalChain[] = []; protected displayTokenSymbols: Record> = {}; + protected chainConfigs: IChainConfig[] = []; protected chains: C[] = []; protected chainMap = new Map(); @@ -38,6 +41,7 @@ export abstract class BaseAdapter { >(); constructor(options: IBaseAdapterOptions) { + this.options = options; this.config = options.config ?? {}; this.excludedChains = options.excludedChains ?? []; this.excludedTokens = options.excludedTokens ?? {}; @@ -59,6 +63,7 @@ export abstract class BaseAdapter { this.brandChains = initialOptions?.brandChains ?? []; this.externalChains = initialOptions?.externalChains ?? []; this.displayTokenSymbols = initialOptions?.displayTokenSymbols ?? {}; + this.chainConfigs = initialOptions?.chainConfigs ?? []; } protected abstract initChains(): void; @@ -98,7 +103,7 @@ export abstract class BaseAdapter { public abstract getChainId(chain: C): number; - public abstract getTokenInfo({ + public abstract getTokenBaseInfo({ chainId, token, }: { @@ -106,13 +111,9 @@ export abstract class BaseAdapter { token: T; }): IBridgeTokenBaseInfo; - public getChainInfo({ - chainId, - chainConfig, - }: { - chainId: number; - chainConfig: IChainConfig; - }) { + public getChainBaseInfo({ chainId }: { chainId: number }) { + const chainConfig = this.chainConfigs.find((e) => e.id === chainId); + const explorerUrl = chainConfig?.explorer.url?.replace(/\/$/, '') ?? ''; const tmpUrlPattern = explorerUrl ? `${explorerUrl}/token/{0}` : ''; const tokenUrlPattern = @@ -183,7 +184,7 @@ export abstract class BaseAdapter { // 1. Native currency is ETH -> Native currency is ETH, all transfer to ETH // 2. Native currency is ETH -> Native currency is NOT ETH, transfer to ETH first, if not, WETH // 3. Native currency is NOT ETH -> Native currency is ETH, all transfer to ETH - protected getToToken({ + protected getTransferToToken({ fromChainId, toChainId, fromTokenSymbol, @@ -228,44 +229,53 @@ export abstract class BaseAdapter { return toToken; } - public getRealTokenSymbol({ - fromChainId, - toChainId, - tokenSymbol, + public getTokenSymbolByTokenAddress({ + chainId, + tokenAddress, }: { - fromChainId: number; - toChainId: number; - tokenSymbol: string; + chainId: number; + tokenAddress: string; }) { - return tokenSymbol; + const tokens = this.tokenMap.get(chainId); + + let baseInfo: IBridgeTokenBaseInfo | undefined; + const token = tokens?.find((t) => { + baseInfo = this.getTokenBaseInfo({ + chainId, + token: t, + }); + return isSameAddress(baseInfo.address, tokenAddress); + }); + + if (token) { + return baseInfo?.symbol?.toUpperCase(); + } } - public getChain({ chainId }: { chainId: number }) { + public getChainById(chainId: number) { return this.chainMap.get(chainId); } public getTokenPair({ fromChainId, toChainId, - tokenSymbol, + tokenAddress, }: { fromChainId: number; toChainId: number; - tokenSymbol: string; + tokenAddress: string; }) { - const realTokenSymbol = this.getRealTokenSymbol({ - fromChainId, - toChainId, - tokenSymbol, + const tokenSymbol = this.getTokenSymbolByTokenAddress({ + chainId: fromChainId, + tokenAddress, }); - const tokenPair = this.transferMap - .get(fromChainId) - ?.get(toChainId) - ?.get(realTokenSymbol); - return { - tokenPair, - }; + if (tokenSymbol) { + return this.transferMap + .get(fromChainId) + ?.get(toChainId) + ?.get(tokenSymbol); + } } public getFromChains() { @@ -291,14 +301,23 @@ export abstract class BaseAdapter { fromChainId: number; toChainId: number; }) { - const tokenPairs: ITransferTokenPair[] = []; - - const tokenPairsMap = this.transferMap.get(fromChainId)?.get(toChainId); - tokenPairsMap?.forEach((tokenPair) => { - tokenPairs.push(tokenPair); + const allMap = new Map>(); + this.transferMap.get(fromChainId)?.forEach((toMap) => { + toMap.forEach((tokenPair, tokenSymbol) => { + allMap.set(tokenSymbol.toUpperCase(), tokenPair); + }); }); - return tokenPairs; + // The tokenPairs in `allMap` may not be the `tokenPair` in the toChain, + // so it needs to be overwritten + this.transferMap + .get(fromChainId) + ?.get(toChainId) + ?.forEach((tokenPair, tokenSymbol) => { + allMap.set(tokenSymbol.toUpperCase(), tokenPair); + }); + + return Array.from(allMap.values()); } public isToChainCompatible({ @@ -311,24 +330,11 @@ export abstract class BaseAdapter { return !!this.transferMap.get(fromChainId)?.get(toChainId); } - public isTokenCompatible({ - fromChainId, - toChainId, - tokenSymbol, - }: { + public isTokenCompatible(params: { fromChainId: number; toChainId: number; - tokenSymbol: string; + tokenAddress: string; }) { - const realTokenSymbol = this.getRealTokenSymbol({ - fromChainId, - toChainId, - tokenSymbol, - }); - - return !!this.transferMap - .get(fromChainId) - ?.get(toChainId) - ?.get(realTokenSymbol); + return !!this.getTokenPair(params); } } diff --git a/packages/canonical-bridge-sdk/src/adapters/base/types.ts b/packages/canonical-bridge-sdk/src/adapters/base/types.ts index e3d800c9..1b0c1f87 100644 --- a/packages/canonical-bridge-sdk/src/adapters/base/types.ts +++ b/packages/canonical-bridge-sdk/src/adapters/base/types.ts @@ -1,4 +1,8 @@ -import { INativeCurrency, IExternalChain } from '@/aggregator/types'; +import { + INativeCurrency, + IExternalChain, + IChainConfig, +} from '@/aggregator/types'; export interface ITransferTokenPair { fromChainId: number; @@ -25,6 +29,7 @@ export interface IInitialOptions { brandChains?: number[]; externalChains?: IExternalChain[]; displayTokenSymbols?: Record>; + chainConfigs?: IChainConfig[]; } export interface IBridgeTokenBaseInfo { diff --git a/packages/canonical-bridge-sdk/src/adapters/cBridge/index.ts b/packages/canonical-bridge-sdk/src/adapters/cBridge/index.ts index 79a8f187..1f21caeb 100644 --- a/packages/canonical-bridge-sdk/src/adapters/cBridge/index.ts +++ b/packages/canonical-bridge-sdk/src/adapters/cBridge/index.ts @@ -34,6 +34,7 @@ export class CBridgeAdapter extends BaseAdapter< ICBridgeChain, ICBridgeToken > { + protected options: ICBridgeAdapterOptions; private client: AxiosInstance; public bridgeType: BridgeType = 'cBridge'; @@ -41,17 +42,18 @@ export class CBridgeAdapter extends BaseAdapter< private burnPairConfigs: ICBridgeBurnPairConfig[] = []; constructor(options: ICBridgeAdapterOptions) { - const { - timeout = CLIENT_TIME_OUT, - endpoint = env.CBRIDGE_ENDPOINT, - ...baseOptions - } = options; + const finalOptions = { + timeout: CLIENT_TIME_OUT, + endpoint: env.CBRIDGE_ENDPOINT, + ...options, + }; - super(baseOptions); + super(finalOptions); + this.options = finalOptions; this.client = axios.create({ - timeout, - baseURL: endpoint, + timeout: this.options.timeout, + baseURL: this.options.endpoint, }); } @@ -521,7 +523,7 @@ export class CBridgeAdapter extends BaseAdapter< ITransferTokenPair >(); fromTokens.forEach((fromToken) => { - const toToken = this.getToToken({ + const toToken = this.getTransferToToken({ fromChainId: fromChain.id, toChainId: toChain.id, fromTokenSymbol: fromToken.token.symbol?.toUpperCase(), @@ -706,7 +708,7 @@ export class CBridgeAdapter extends BaseAdapter< return chain.id; } - public getTokenInfo({ + public getTokenBaseInfo({ chainId, token, }: { diff --git a/packages/canonical-bridge-sdk/src/adapters/deBridge/index.ts b/packages/canonical-bridge-sdk/src/adapters/deBridge/index.ts index 6f8dd309..6a67fbbc 100644 --- a/packages/canonical-bridge-sdk/src/adapters/deBridge/index.ts +++ b/packages/canonical-bridge-sdk/src/adapters/deBridge/index.ts @@ -20,28 +20,30 @@ export class DeBridgeAdapter extends BaseAdapter< IDeBridgeChain, IDeBridgeToken > { + protected options: IDeBridgeAdapterOptions; private client: AxiosInstance; private statsClient: AxiosInstance; public bridgeType: BridgeType = 'deBridge'; constructor(options: IDeBridgeAdapterOptions) { - const { - timeout = CLIENT_TIME_OUT, - endpoint = env.DEBRIDGE_ENDPOINT, - statsEndpoint = env.DEBRIDGE_STATS_ENDPOINT, - ...baseOptions - } = options; + const finalOptions = { + timeout: CLIENT_TIME_OUT, + endpoint: env.DEBRIDGE_ENDPOINT, + statsEndpoint: env.DEBRIDGE_STATS_ENDPOINT, + ...options, + }; - super(baseOptions); + super(finalOptions); + this.options = finalOptions; this.client = axios.create({ - timeout, - baseURL: endpoint, + timeout: this.options.timeout, + baseURL: this.options.endpoint, }); this.statsClient = axios.create({ - timeout, - baseURL: statsEndpoint, + timeout: this.options.timeout, + baseURL: this.options.statsEndpoint, }); } @@ -72,7 +74,6 @@ export class DeBridgeAdapter extends BaseAdapter< userAddress, toUserAddress, affiliateFeePercent = 0, - accesstoken = '', prependOperatingExpenses = false, }: IDeBridgeEstimatedFeesInput): Promise { try { @@ -89,8 +90,8 @@ export class DeBridgeAdapter extends BaseAdapter< srcChainOrderAuthorityAddress: userAddress, } as any; - if (accesstoken) { - deBridgeParams.accesstoken = accesstoken; + if (this.options.accessToken) { + deBridgeParams.accesstoken = this.options.accessToken; } const urlParams = new URLSearchParams(deBridgeParams as any); const deBridgeQuote = await this.createTxQuote(urlParams); @@ -239,7 +240,7 @@ export class DeBridgeAdapter extends BaseAdapter< ITransferTokenPair >(); fromTokens.forEach((fromToken) => { - const toToken = this.getToToken({ + const toToken = this.getTransferToToken({ fromChainId: fromChain.chainId, toChainId: toChain.chainId, fromTokenSymbol: fromToken.symbol?.toUpperCase(), @@ -286,7 +287,7 @@ export class DeBridgeAdapter extends BaseAdapter< return chain.chainId; } - public getTokenInfo({ + public getTokenBaseInfo({ chainId, token, }: { diff --git a/packages/canonical-bridge-sdk/src/adapters/deBridge/types.ts b/packages/canonical-bridge-sdk/src/adapters/deBridge/types.ts index fab2a8c6..206af174 100644 --- a/packages/canonical-bridge-sdk/src/adapters/deBridge/types.ts +++ b/packages/canonical-bridge-sdk/src/adapters/deBridge/types.ts @@ -6,6 +6,7 @@ export interface IDeBridgeAdapterOptions timeout?: number; endpoint?: string; statsEndpoint?: string; + accessToken?: string; } export interface IDeBridgeChain { @@ -150,7 +151,6 @@ export interface IDeBridgeEstimatedFeesInput { toUserAddress?: string; affiliateFeePercent?: number; prependOperatingExpenses?: boolean; - accesstoken?: string; } export interface ISendDebridgeTokenInput { diff --git a/packages/canonical-bridge-sdk/src/adapters/layerZero/index.ts b/packages/canonical-bridge-sdk/src/adapters/layerZero/index.ts index bf1c3fec..df239967 100644 --- a/packages/canonical-bridge-sdk/src/adapters/layerZero/index.ts +++ b/packages/canonical-bridge-sdk/src/adapters/layerZero/index.ts @@ -212,7 +212,7 @@ export class LayerZeroAdapter extends BaseAdapter< ITransferTokenPair >(); fromTokens.forEach((fromToken) => { - const toToken = this.getToToken({ + const toToken = this.getTransferToToken({ fromChainId: fromChain.chainId, toChainId: toChain.chainId, fromTokenSymbol: fromToken.symbol?.toUpperCase(), @@ -256,7 +256,7 @@ export class LayerZeroAdapter extends BaseAdapter< return chain.chainId; } - public getTokenInfo({ + public getTokenBaseInfo({ chainId, token, }: { diff --git a/packages/canonical-bridge-sdk/src/adapters/meson/index.ts b/packages/canonical-bridge-sdk/src/adapters/meson/index.ts index ebc0913f..0b01681c 100644 --- a/packages/canonical-bridge-sdk/src/adapters/meson/index.ts +++ b/packages/canonical-bridge-sdk/src/adapters/meson/index.ts @@ -18,21 +18,23 @@ export class MesonAdapter extends BaseAdapter< IMesonChain, IMesonToken > { + protected options: IMesonAdapterOptions; private client: AxiosInstance; public bridgeType: BridgeType = 'meson'; constructor(options: IMesonAdapterOptions) { - const { - timeout = CLIENT_TIME_OUT, - endpoint = env.MESON_ENDPOINT, - ...baseOptions - } = options; + const finalOptions = { + timeout: CLIENT_TIME_OUT, + endpoint: env.MESON_ENDPOINT, + ...options, + }; - super(baseOptions); + super(finalOptions); + this.options = finalOptions; this.client = axios.create({ - timeout, - baseURL: endpoint, + timeout: this.options.timeout, + baseURL: this.options.endpoint, }); } @@ -190,7 +192,7 @@ export class MesonAdapter extends BaseAdapter< ITransferTokenPair >(); fromTokens.forEach((fromToken) => { - const toToken = this.getToToken({ + const toToken = this.getTransferToToken({ fromChainId: Number(fromChain.chainId), toChainId: Number(toChain.chainId), fromTokenSymbol: fromToken.id?.toUpperCase(), @@ -231,7 +233,7 @@ export class MesonAdapter extends BaseAdapter< return Number(chain.chainId); } - public getTokenInfo({ + public getTokenBaseInfo({ chainId, token, }: { diff --git a/packages/canonical-bridge-sdk/src/adapters/stargate/index.ts b/packages/canonical-bridge-sdk/src/adapters/stargate/index.ts index b81f06aa..7dbc78af 100644 --- a/packages/canonical-bridge-sdk/src/adapters/stargate/index.ts +++ b/packages/canonical-bridge-sdk/src/adapters/stargate/index.ts @@ -12,7 +12,7 @@ import { IStarGateBusDriveSettings, } from '@/adapters/stargate/types'; import { BridgeType } from '@/aggregator/types'; -import { CLIENT_TIME_OUT } from '@/constants'; +import { CLIENT_TIME_OUT, env } from '@/constants'; import axios, { AxiosInstance } from 'axios'; import { Hash, pad } from 'viem'; @@ -21,17 +21,23 @@ export class StargateAdapter extends BaseAdapter< IStargateChain, IStargateToken > { + protected options: IStargateAdapterOptions; private client: AxiosInstance; public bridgeType: BridgeType = 'stargate'; constructor(options: IStargateAdapterOptions) { - const { timeout = CLIENT_TIME_OUT, endpoint, ...baseOptions } = options; + const finalOptions = { + timeout: CLIENT_TIME_OUT, + endpoint: env.STARGATE_ENDPOINT, + ...options, + }; - super(baseOptions); + super(finalOptions); + this.options = finalOptions; this.client = axios.create({ - timeout, - baseURL: endpoint, + timeout: this.options.timeout, + baseURL: this.options.endpoint, }); } @@ -45,7 +51,7 @@ export class StargateAdapter extends BaseAdapter< }) { return ( await this.client.get( - `${fromEndpointId}/${toEndpointId}` + `/buses/bus-drive-settings/${fromEndpointId}/${toEndpointId}` ) ).data; } @@ -251,7 +257,7 @@ export class StargateAdapter extends BaseAdapter< ITransferTokenPair >(); fromTokens.forEach((fromToken) => { - const toToken = this.getToToken({ + const toToken = this.getTransferToToken({ fromChainId: fromChain.chainId, toChainId: toChain.chainId, fromTokenSymbol: fromToken.symbol?.toUpperCase(), @@ -298,7 +304,7 @@ export class StargateAdapter extends BaseAdapter< return chain.chainId; } - public getTokenInfo({ + public getTokenBaseInfo({ chainId, token, }: { diff --git a/packages/canonical-bridge-sdk/src/aggregator/index.ts b/packages/canonical-bridge-sdk/src/aggregator/index.ts index e268d9d3..2eb993f6 100644 --- a/packages/canonical-bridge-sdk/src/aggregator/index.ts +++ b/packages/canonical-bridge-sdk/src/aggregator/index.ts @@ -1,12 +1,13 @@ import { ERC20_TOKEN } from '@/abi/erc20Token'; import { ITransferTokenPair } from '@/adapters/base/types'; import { CBridgeAdapter } from '@/adapters/cBridge'; -import { ICBridgePeggedPairConfig } from '@/adapters/cBridge/types'; +import { + ICBridgePeggedPairConfig, + ICBridgeToken, +} from '@/adapters/cBridge/types'; import { DeBridgeAdapter } from '@/adapters/deBridge'; -import { IDeBridgeEstimatedFeesInput } from '@/adapters/deBridge/types'; import { LayerZeroAdapter } from '@/adapters/layerZero'; import { MesonAdapter } from '@/adapters/meson'; -import { IGetMesonEstimateFeeInput } from '@/adapters/meson/types'; import { StargateAdapter } from '@/adapters/stargate'; import { IBridgeChain, @@ -18,10 +19,10 @@ import { IApproveTokenInput, IGetAllowanceInput, IGetTokenBalanceInput, - IBridgeEndpointId, - IBridgeAddress, } from '@/aggregator/types'; -import { Hash, PublicClient, WalletClient } from 'viem'; +import { sortChains } from '@/aggregator/utils/sortChains'; +import { sortTokens } from '@/aggregator/utils/sortTokens'; +import { formatUnits, Hash, PublicClient } from 'viem'; export interface CanonicalBridgeSDKOptions { chains: IChainConfig[]; @@ -90,6 +91,7 @@ export class CanonicalBridgeSDK { externalChains: this.options.externalChains, displayTokenSymbols: this.options.displayTokenSymbols, assetPrefix: this.options.assetPrefix, + chainConfigs: this.options.chains, }); }); @@ -102,9 +104,10 @@ export class CanonicalBridgeSDK { if (adapter) { return adapter; } else { - console.warn( - `${bridgeType} adapter is not found, you should initialize an adapter before using` - ); + // TODO + // console.warn( + // `${bridgeType} adapter is not found, you should initialize the adapter before using it` + // ); } }, }); @@ -211,234 +214,167 @@ export class CanonicalBridgeSDK { } /** - * Load bridge fees and return fee information in the following order - * [deBridge, cBridge, stargate, layerZero, meson] + * Load bridge fees and return fee information */ async loadBridgeFees({ - bridgeType, + publicClient, fromChainId, - fromAccount, toChainId, - sendValue, - fromTokenSymbol, - publicClient, - endPointId, - bridgeAddress, - isPegged, - slippage, - mesonOpts, - deBridgeOpts, + tokenAddress, + userAddress, + toUserAddress, + amount, + slippage = 10000, }: { - bridgeType: BridgeType[]; + publicClient: PublicClient; fromChainId: number; - fromAccount: `0x${string}`; toChainId: number; - sendValue: bigint; - fromTokenSymbol: string; - publicClient?: PublicClient; - endPointId?: IBridgeEndpointId; - bridgeAddress?: IBridgeAddress; - isPegged?: boolean; + tokenAddress: string; + userAddress: string; + toUserAddress?: string; + amount: bigint; slippage?: number; - mesonOpts?: IGetMesonEstimateFeeInput; - deBridgeOpts?: IDeBridgeEstimatedFeesInput; }) { + const { fromChain, toChain, fromToken, toToken } = this.getParamDetails({ + fromChainId, + toChainId, + tokenAddress, + }); + + if (!fromChain || !toChain || !fromToken || !toToken) { + console.log(fromChain, toChain, fromToken, toToken); + throw new Error('Missing parameters'); + } + + const promiseArr: Array<{ bridgeType: BridgeType; apiCall: Promise }> = + []; + // deBridge - const promiseArr = []; - if (this.deBridge && deBridgeOpts && bridgeType.includes('deBridge')) { - const debridgeFeeAPICall = this.deBridge.getEstimatedFees(deBridgeOpts); - promiseArr.push(debridgeFeeAPICall); + if ( + this.deBridge && + fromToken?.deBridge?.address && + toToken?.deBridge?.address + ) { + const debridgeFeeAPICall = this.deBridge.getEstimatedFees({ + fromChainId, + toChainId, + fromTokenAddress: fromToken.deBridge.address as `0x${string}`, + toTokenAddress: toToken.deBridge.address as `0x${string}`, + amount, + userAddress, + toUserAddress: toUserAddress || userAddress, + }); + promiseArr.push({ + bridgeType: 'deBridge', + apiCall: debridgeFeeAPICall, + }); } else { - promiseArr.push(new Promise((reject) => reject(null))); + promiseArr.push({ + bridgeType: 'deBridge', + apiCall: new Promise((reject) => reject(null)), + }); } + // cBridge - if (this.cBridge && slippage && bridgeType.includes('cBridge')) { + if (this.cBridge && fromToken.cBridge?.raw?.token.symbol) { + const fromTokenSymbol = fromToken.cBridge?.raw?.token.symbol; + const tokenSymbol = fromTokenSymbol === 'ETH' ? 'WETH' : fromTokenSymbol; + const cBridgeFeeAPICall = this.cBridge.getEstimatedAmount({ src_chain_id: fromChainId, dst_chain_id: toChainId, - token_symbol: fromTokenSymbol, - amt: String(sendValue), - user_addr: fromAccount, + token_symbol: tokenSymbol, + amt: String(amount), + user_addr: userAddress, slippage_tolerance: slippage, - is_pegged: isPegged, + is_pegged: fromToken.isPegged, + }); + promiseArr.push({ + bridgeType: 'cBridge', + apiCall: cBridgeFeeAPICall, }); - promiseArr.push(cBridgeFeeAPICall); } else { - promiseArr.push(new Promise((reject) => reject(null))); + promiseArr.push({ + bridgeType: 'cBridge', + apiCall: new Promise((reject) => reject(null)), + }); } + // stargate if ( this.stargate && - bridgeAddress?.stargate && - endPointId?.layerZeroV2 && - bridgeType.includes('stargate') && - !!publicClient + fromToken?.stargate?.raw?.bridgeAddress && + toToken?.stargate?.raw?.endpointID ) { const stargateFeeAPICall = this.stargate.getQuoteOFT({ publicClient: publicClient, - bridgeAddress: bridgeAddress.stargate, - endPointId: endPointId.layerZeroV2, - receiver: fromAccount, - amount: sendValue, + bridgeAddress: fromToken.stargate.raw.bridgeAddress as `0x${string}`, + endPointId: toToken?.stargate?.raw?.endpointID, + receiver: userAddress as `0x${string}`, + amount, + }); + promiseArr.push({ + bridgeType: 'stargate', + apiCall: stargateFeeAPICall, }); - promiseArr.push(stargateFeeAPICall); } else { - promiseArr.push(new Promise((reject) => reject(null))); + promiseArr.push({ + bridgeType: 'stargate', + apiCall: new Promise((reject) => reject(null)), + }); } + // layerZero if ( this.layerZero && - bridgeAddress?.layerZero && - endPointId?.layerZeroV1 && - bridgeType.includes('layerZero') && - !!publicClient + fromToken?.layerZero?.raw?.bridgeAddress && + toToken?.layerZero?.raw?.endpointID ) { const layerZeroFeeAPICall = this.layerZero.getEstimateFee({ - bridgeAddress: bridgeAddress.layerZero, - amount: sendValue, - dstEndpoint: endPointId.layerZeroV1, - userAddress: fromAccount, - publicClient, - }); - promiseArr.push(layerZeroFeeAPICall); - } else { - promiseArr.push(new Promise((reject) => reject(null))); - } - // meson - if (this.meson && mesonOpts && bridgeType.includes('meson')) { - const mesonFeeAPICall = this.meson.getEstimatedFees(mesonOpts); - promiseArr.push(mesonFeeAPICall); - } else { - promiseArr.push(new Promise((reject) => reject(null))); - } - return await Promise.allSettled(promiseArr); - } - - /** - * Send token through different bridge mode - */ - async sendToken({ - bridgeType, - fromChainId, - amount, - userAddress, - tokenAddress, - toChainId, - bridgeAddress, - walletClient, - publicClient, - slippage, - peggedConfig, - bridgeEndPointId, - debridgeOpts, - cBridgeOpts, - }: { - bridgeType: BridgeType; - fromChainId: number; - amount: bigint; - userAddress: `0x${string}`; - tokenAddress: `0x${string}`; - toChainId: number; - bridgeAddress: `0x${string}`; - walletClient: WalletClient; - publicClient: PublicClient; - slippage?: number; - peggedConfig?: ICBridgePeggedPairConfig; - deBridgeData?: `0x${string}`; - bridgeEndPointId?: IBridgeEndpointId; - debridgeOpts?: { - data?: `0x${string}`; - }; - cBridgeOpts?: { - isPegged?: boolean; - }; - }) { - // deBridge - if ( - this.deBridge && - bridgeType === 'deBridge' && - debridgeOpts && - debridgeOpts?.data - ) { - if (!debridgeOpts?.data) { - throw new Error('Invalid deBridge data'); - } - return await this.deBridge.sendToken({ - walletClient, - bridgeAddress, - data: debridgeOpts?.data, + bridgeAddress: fromToken?.layerZero?.raw + ?.bridgeAddress as `0x${string}`, amount, - address: userAddress, - }); - } - // cBridge - else if ( - this.cBridge && - bridgeType === 'cBridge' && - cBridgeOpts && - cBridgeOpts?.isPegged && - slippage - ) { - const transferType = this.cBridge.getTransferType({ - peggedConfig, - fromChainId, + dstEndpoint: toToken?.layerZero?.raw?.endpointID, + userAddress: userAddress as `0x${string}`, + publicClient, }); - const nonce = new Date().getTime(); - const transferArgs = this.cBridge.getTransferParams({ - amount, - isPegged: cBridgeOpts.isPegged, - toChainId, - tokenAddress, - address: userAddress, - maxSlippage: slippage, - transferType: transferType, - peggedConfig: peggedConfig, - nonce, + promiseArr.push({ + bridgeType: 'layerZero', + apiCall: layerZeroFeeAPICall, }); - return await this.cBridge.sendToken({ - walletClient, - publicClient, - bridgeAddress, - fromChainId, - address: userAddress, - isPegged: cBridgeOpts.isPegged, - peggedConfig, - args: transferArgs, + } else { + promiseArr.push({ + bridgeType: 'layerZero', + apiCall: new Promise((reject) => reject(null)), }); } - // stargate - else if ( - this.stargate && - bridgeType === 'stargate' && - bridgeEndPointId?.layerZeroV2 - ) { - return await this.stargate.sendToken({ - walletClient, - publicClient, - bridgeAddress, - tokenAddress, - endPointId: bridgeEndPointId?.layerZeroV2, - receiver: userAddress, - amount, + + // meson + if (this.meson && fromToken?.meson?.raw?.id && toToken?.meson?.raw?.id) { + const mesonAmount = formatUnits(amount, fromToken.meson.raw.decimals); + const mesonFeeAPICall = this.meson.getEstimatedFees({ + fromToken: `${fromChain?.meson?.raw?.id}:${fromToken?.meson?.raw?.id}`, + toToken: `${toChain?.meson?.raw?.id}:${toToken?.meson?.raw?.id}`, + amount: mesonAmount, + fromAddr: userAddress, }); - } - // May implement LayerZero v2 in the future - else if ( - this.layerZero && - bridgeType === 'layerZero' && - bridgeEndPointId?.layerZeroV1 - ) { - return await this.layerZero.sendToken({ - userAddress, - bridgeAddress, - amount, - dstEndpoint: bridgeEndPointId?.layerZeroV1 as number, - publicClient, - walletClient, + promiseArr.push({ + bridgeType: 'meson', + apiCall: mesonFeeAPICall, }); } else { - throw new Error('Invalid bridge inputs'); + promiseArr.push({ + bridgeType: 'meson', + apiCall: new Promise((reject) => reject(null)), + }); } + + const apiCalls = promiseArr.map((e) => e.apiCall); + const results = await Promise.allSettled(apiCalls); + + return Object.fromEntries( + promiseArr.map((e, index) => [e.bridgeType, results[index]]) + ) as Record>; } public getFromChains() { @@ -449,25 +385,26 @@ export class CanonicalBridgeSDK { fromChains.forEach((item: any) => { const chainId = adapter.getChainId(item); - const chainConfig = this.options.chains.find((e) => e.id === chainId); - - if (chainConfig) { - let bridgeChain = chainMap.get(chainId); - if (!bridgeChain) { - bridgeChain = { - isCompatible: true, - ...adapter.getChainInfo({ - chainId, - chainConfig, - }), - }; - chainMap.set(chainId, bridgeChain); - } + + let bridgeChain = chainMap.get(chainId); + if (!bridgeChain) { + bridgeChain = { + isCompatible: true, + ...adapter.getChainBaseInfo({ + chainId, + }), + }; + chainMap.set(chainId, bridgeChain); } }); }); - return Array.from(chainMap.values()); + const chains = Array.from(chainMap.values()); + return sortChains({ + direction: 'from', + chains, + chainOrder: this.options.chainOrder, + }); } public getToChains({ fromChainId }: { fromChainId: number }) { @@ -480,36 +417,37 @@ export class CanonicalBridgeSDK { toChains.forEach((item: any) => { const chainId = adapter.getChainId(item); - const chainConfig = this.options.chains.find((e) => e.id === chainId); - - if (chainConfig) { - const isCompatible = adapter.isToChainCompatible({ - fromChainId, - toChainId: chainId, - }); - - let bridgeChain = chainMap.get(chainId); - if (!bridgeChain) { - bridgeChain = { - isCompatible, - ...adapter.getChainInfo({ - chainId, - chainConfig, - }), - }; - } else { - bridgeChain = { - ...bridgeChain, - isCompatible: bridgeChain.isCompatible || isCompatible, - }; - } - chainMap.set(chainId, bridgeChain); + const isCompatible = adapter.isToChainCompatible({ + fromChainId, + toChainId: chainId, + }); + + let bridgeChain = chainMap.get(chainId); + if (!bridgeChain) { + bridgeChain = { + isCompatible, + ...adapter.getChainBaseInfo({ + chainId, + }), + }; + } else { + bridgeChain = { + ...bridgeChain, + isCompatible: bridgeChain.isCompatible || isCompatible, + }; } + + chainMap.set(chainId, bridgeChain); }); }); - return Array.from(chainMap.values()); + const chains = Array.from(chainMap.values()); + return sortChains({ + direction: 'to', + chains, + chainOrder: this.options.chainOrder, + }); } public getTokens({ @@ -528,17 +466,15 @@ export class CanonicalBridgeSDK { }); tokenPairs.forEach((item: ITransferTokenPair) => { - const { fromToken, fromChainId, toChainId } = item; - - const baseInfo = adapter.getTokenInfo({ + const baseInfo = adapter.getTokenBaseInfo({ chainId: fromChainId, - token: fromToken, + token: item.fromToken, }); const isCompatible = adapter.isTokenCompatible({ fromChainId, toChainId, - tokenSymbol: baseInfo.displaySymbol, + tokenAddress: baseInfo.address, }); let bridgeToken = tokenMap.get(baseInfo.displaySymbol.toUpperCase()); @@ -559,10 +495,214 @@ export class CanonicalBridgeSDK { }); }); - return Array.from(tokenMap.values()); + const tokens = Array.from(tokenMap.values()); + return sortTokens({ + tokens, + tokenOrder: this.options.tokenOrder, + }); + } + + public getFromChainDetail({ + fromChainId, + toChainId, + tokenAddress, + }: { + fromChainId: number; + toChainId: number; + tokenAddress: string; + }) { + let chainDetail: IBridgeChain | undefined; + + this.options.adapters.forEach((adapter) => { + const tokenPair = adapter.getTokenPair({ + fromChainId, + toChainId, + tokenAddress, + }); + + if (tokenPair) { + if (!chainDetail) { + chainDetail = { + ...adapter.getChainBaseInfo({ chainId: fromChainId }), + isCompatible: true, + }; + } + chainDetail = { + ...chainDetail, + [adapter.bridgeType]: { + raw: adapter.getChainById(fromChainId), + }, + }; + } + }); + + return chainDetail; + } + + public getToChainDetail({ + fromChainId, + toChainId, + tokenAddress, + }: { + fromChainId: number; + toChainId: number; + tokenAddress: string; + }) { + let chainDetail: IBridgeChain | undefined; + + this.options.adapters.forEach((adapter) => { + const tokenPair = adapter.getTokenPair({ + fromChainId, + toChainId, + tokenAddress, + }); + + if (tokenPair) { + const isCompatible = adapter.isToChainCompatible({ + fromChainId, + toChainId, + }); + + if (!chainDetail) { + chainDetail = { + ...adapter.getChainBaseInfo({ chainId: toChainId }), + isCompatible, + }; + } + chainDetail = { + ...chainDetail, + [adapter.bridgeType]: { + raw: adapter.getChainById(toChainId), + }, + }; + } + }); + + return chainDetail; } - public getChainDetail() {} + public getTokenDetail({ + fromChainId, + toChainId, + tokenAddress, + }: { + fromChainId: number; + toChainId: number; + tokenAddress: string; + }) { + let tokenDetail: IBridgeToken | undefined; - public getTokenDetail() {} + this.options.adapters.forEach((adapter) => { + const tokenPair = adapter.getTokenPair({ + fromChainId, + toChainId, + tokenAddress, + }); + + if (tokenPair) { + const isCompatible = adapter.isTokenCompatible({ + fromChainId, + toChainId, + tokenAddress, + }); + + const baseInfo = adapter.getTokenBaseInfo({ + chainId: fromChainId, + token: tokenPair.fromToken as any, + }); + + if (!tokenDetail) { + tokenDetail = { + ...baseInfo, + isPegged: !!tokenPair.isPegged, + isCompatible, + }; + } + + if (adapter.bridgeType === 'cBridge' && tokenPair.peggedConfig) { + tokenDetail.cBridge = { + ...baseInfo, + peggedConfig: tokenPair.peggedConfig as ICBridgePeggedPairConfig, + raw: tokenPair.fromToken as ICBridgeToken, + }; + } else { + tokenDetail = { + ...tokenDetail, + [adapter.bridgeType]: { + ...baseInfo, + raw: tokenPair.fromToken, + }, + }; + } + } + }); + + return tokenDetail; + } + + public getToTokenDetail({ + fromChainId, + toChainId, + tokenAddress, + }: { + fromChainId: number; + toChainId: number; + tokenAddress: string; + }) { + let tokenDetail: IBridgeToken | undefined; + + this.options.adapters.forEach((adapter) => { + const tokenPair = adapter.getTokenPair({ + fromChainId, + toChainId, + tokenAddress, + }); + + if (tokenPair) { + const baseInfo = adapter.getTokenBaseInfo({ + chainId: toChainId, + token: tokenPair.toToken as any, + }); + + if (!tokenDetail) { + tokenDetail = { + ...baseInfo, + isPegged: !!tokenPair.isPegged, + isCompatible: true, + }; + } + + if (adapter.bridgeType === 'cBridge' && tokenPair.peggedConfig) { + tokenDetail.cBridge = { + ...baseInfo, + peggedConfig: tokenPair.peggedConfig as ICBridgePeggedPairConfig, + raw: tokenPair.toToken as ICBridgeToken, + }; + } else { + tokenDetail = { + ...tokenDetail, + [adapter.bridgeType]: { + ...baseInfo, + raw: tokenPair.toToken, + }, + }; + } + } + }); + + return tokenDetail; + } + + private getParamDetails(params: { + fromChainId: number; + toChainId: number; + tokenAddress: string; + }) { + return { + fromChain: this.getFromChainDetail(params), + toChain: this.getToChainDetail(params), + fromToken: this.getTokenDetail(params), + toToken: this.getToTokenDetail(params), + }; + } } diff --git a/packages/canonical-bridge-sdk/src/aggregator/utils/sortChains.ts b/packages/canonical-bridge-sdk/src/aggregator/utils/sortChains.ts new file mode 100644 index 00000000..4d45d82d --- /dev/null +++ b/packages/canonical-bridge-sdk/src/aggregator/utils/sortChains.ts @@ -0,0 +1,42 @@ +import { IBridgeChain } from '@/aggregator/types'; + +export function sortChains({ + chains, + direction, + chainOrder = [], +}: { + direction: 'to' | 'from'; + chains: IBridgeChain[]; + chainOrder?: number[]; +}) { + const sortedChains = chains.sort((a, b) => { + if (direction === 'to') { + const isA = a.isCompatible; + const isB = b.isCompatible; + + if (isA && !isB) { + return -1; + } + if (!isA && isB) { + return 1; + } + } + + const indexA = chainOrder.indexOf(a.id); + const indexB = chainOrder.indexOf(b.id); + + if (indexA > -1 && indexB === -1) { + return -1; + } + if (indexA === -1 && indexB > -1) { + return 1; + } + if (indexA > -1 && indexB > -1) { + return indexA - indexB; + } + + return a.name < b.name ? -1 : 1; + }); + + return sortedChains; +} diff --git a/packages/canonical-bridge-sdk/src/aggregator/utils/sortTokens.ts b/packages/canonical-bridge-sdk/src/aggregator/utils/sortTokens.ts new file mode 100644 index 00000000..adbca524 --- /dev/null +++ b/packages/canonical-bridge-sdk/src/aggregator/utils/sortTokens.ts @@ -0,0 +1,43 @@ +import { IBridgeToken } from '@/aggregator/types'; + +export function sortTokens({ + tokens = [], + tokenOrder = [], +}: { + tokens?: IBridgeToken[]; + tokenOrder?: string[]; +}) { + const order = tokenOrder.map((item) => item.toUpperCase()); + + const sortedTokens = [...tokens].sort((a, b) => { + const aSymbol = a.displaySymbol.toUpperCase(); + const bSymbol = b.displaySymbol.toUpperCase(); + + const isA = a.isCompatible; + const isB = b.isCompatible; + + const indexA = order.indexOf(aSymbol); + const indexB = order.indexOf(bSymbol); + + if (isA && !isB) { + return -1; + } + if (!isA && isB) { + return 1; + } + + if (indexA > -1 && indexB === -1) { + return -1; + } + if (indexA === -1 && indexB > -1) { + return 1; + } + if (indexA > -1 && indexB > -1) { + return indexA - indexB; + } + + return aSymbol < bSymbol ? -1 : 1; + }); + + return sortedTokens; +} diff --git a/packages/canonical-bridge-sdk/src/constants/index.ts b/packages/canonical-bridge-sdk/src/constants/index.ts index c86735da..c6a9f5a5 100644 --- a/packages/canonical-bridge-sdk/src/constants/index.ts +++ b/packages/canonical-bridge-sdk/src/constants/index.ts @@ -3,6 +3,7 @@ export const env = { DEBRIDGE_ENDPOINT: 'https://deswap.debridge.finance/v1.0', DEBRIDGE_STATS_ENDPOINT: 'https://stats-api.dln.trade/api', MESON_ENDPOINT: 'https://relayer.meson.fi/api/v1', + STARGATE_ENDPOINT: 'https://mainnet.stargate-api.com/v1', }; export const CLIENT_TIME_OUT = 60 * 1000; diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/components/AggregatorProvider.tsx b/packages/canonical-bridge-widget/src/modules/aggregator/components/AggregatorProvider.tsx index 04620f19..5afaf4ed 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/components/AggregatorProvider.tsx +++ b/packages/canonical-bridge-widget/src/modules/aggregator/components/AggregatorProvider.tsx @@ -59,6 +59,7 @@ export function AggregatorProvider(props: AggregatorProviderProps) { bridgeType: 'deBridge', getAdapter(params: GetAdapterParams) { return new DeBridgeAdapter({ + accessToken: bridgeConfig.http.deBridgeAccessToken, ...params, }); }, @@ -109,6 +110,8 @@ export function AggregatorProvider(props: AggregatorProviderProps) { brandChains: transferConfig?.brandChains, externalChains: transferConfig?.externalChains, displayTokenSymbols: transferConfig?.displayTokenSymbols, + chainOrder: transferConfig?.order?.chains, + tokenOrder: transferConfig?.order?.tokens, adapters, }); @@ -119,7 +122,7 @@ export function AggregatorProvider(props: AggregatorProviderProps) { chainConfigs: bridgeSDK.getSDKOptions().chains, nativeCurrencies: bridgeSDK.getNativeCurrencies(), }; - }, [chains, bridgeConfig.assetPrefix, transferConfig]); + }, [chains, bridgeConfig.assetPrefix, bridgeConfig.http.deBridgeAccessToken, transferConfig]); return {children}; } diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/components/SelectModal/DestinationNetworkModal.tsx b/packages/canonical-bridge-widget/src/modules/aggregator/components/SelectModal/DestinationNetworkModal.tsx index e7794bb5..f47707c7 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/components/SelectModal/DestinationNetworkModal.tsx +++ b/packages/canonical-bridge-widget/src/modules/aggregator/components/SelectModal/DestinationNetworkModal.tsx @@ -10,7 +10,6 @@ import { reportEvent } from '@/core/utils/gtm'; import { useSearch } from '@/modules/aggregator/components/SelectModal/hooks/useSearch'; import { BaseModal } from '@/modules/aggregator/components/SelectModal/components/BaseModal'; import { ListItem } from '@/modules/aggregator/components/SelectModal/components/ListItem'; -import { useChainList } from '@/modules/aggregator/components/SelectModal/hooks/useChainList'; interface DestinationNetworkModalProps { isOpen: boolean; @@ -34,9 +33,9 @@ export function DestinationNetworkModal(props: DestinationNetworkModalProps) { const { isNoResult, result, onSearch } = useSearch({ filter: (item, keyword) => item.name.toLowerCase().includes(keyword), + sorter: (a) => (a.id === toChain?.id ? -1 : 0), data: toChains, }); - const { data } = useChainList('to', result); return ( - + {(item) => ( item.name.toLowerCase().includes(keyword), + sorter: (a) => (a.id === fromChain?.id ? -1 : 0), data: fromChains, }); - const { data } = useChainList('from', result); return ( - + {(item) => ( state.transfer.fromChain); - const toChain = useAppSelector((state) => state.transfer.toChain); - - const sortedChains = useMemo(() => { - const chainOrder = transferConfig.order?.chains ?? []; - - const sortedChains = chains - .sort((a, b) => { - if (direction === 'to') { - const isA = a.isCompatible; - const isB = b.isCompatible; - - if (isA && !isB) { - return -1; - } - if (!isA && isB) { - return 1; - } - } - - const indexA = chainOrder.indexOf(a.id); - const indexB = chainOrder.indexOf(b.id); - - if (indexA > -1 && indexB === -1) { - return -1; - } - if (indexA === -1 && indexB > -1) { - return 1; - } - if (indexA > -1 && indexB > -1) { - return indexA - indexB; - } - - return a.name < b.name ? -1 : 1; - }) - .sort((a) => { - if (direction === 'from' && a.id === fromChain?.id) { - return -1; - } - if (direction === 'to' && a.id === toChain?.id) { - return -1; - } - return 0; - }); - - return sortedChains; - }, [transferConfig.order?.chains, chains, direction, fromChain?.id, toChain?.id]); - - return { data: sortedChains }; -} diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useSelection.ts b/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useSelection.ts index 5eb8b549..7572b0ab 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useSelection.ts +++ b/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useSelection.ts @@ -26,168 +26,121 @@ export function useSelection() { const { getSortedTokens } = useSortedTokens(); - const updateToToken = ({ - fromChainId = fromChain?.id, - toChainId = toChain?.id, - token = selectedToken, - }: { - fromChainId?: number; - toChainId?: number; - token?: IBridgeToken; + const updateSelectedInfo = (params: { + fromChainId: number; + toChainId: number; + tokenAddress: string; }) => { - // const newToToken = getToToken({ - // fromChainId: fromChainId!, - // toChainId: toChainId!, - // token: token!, - // }); - // dispatch(setToToken(newToToken)); + const newFromChain = bridgeSDK.getFromChainDetail(params); + const newToChain = bridgeSDK.getToChainDetail(params); + const newToken = bridgeSDK.getTokenDetail(params); + const newToToken = bridgeSDK.getToTokenDetail(params); + + dispatch(setFromChain(newFromChain)); + dispatch(setToChain(newToChain)); + dispatch(setSelectedToken(newToken)); + dispatch(setToToken(newToToken)); }; - const updateSelectedInfo = ({ - tmpFromChain = fromChain, - tmpToChain = toChain, - tmpToken = selectedToken, + const selectDefault = async ({ + fromChainId, + toChainId, + tokenAddress, }: { - tmpFromChain?: IBridgeChain; - tmpToChain?: IBridgeChain; - tmpToken?: IBridgeToken; + fromChainId: number; + toChainId: number; + tokenAddress: string; }) => { - // const newToken = bridgeSDK - // .getTokens({ - // fromChainId: tmpFromChain?.id, - // toChainId: tmpToChain?.id, - // }) - // .find((t) => t.displaySymbol?.toUpperCase() === tmpToken?.displaySymbol?.toUpperCase()); - // const newFromChain = bridgeSDK - // .getFromChains({ - // toChainId: tmpToChain?.id, - // token: newToken, - // }) - // .find((c) => c.id === tmpFromChain?.id); - // const newToChain = bridgeSDK - // .getToChains({ - // fromChainId: tmpFromChain?.id, - // token: newToken, - // }) - // .find((c) => c.id === tmpToChain?.id); - // dispatch(setFromChain(newFromChain)); - // dispatch(setToChain(newToChain)); - // dispatch(setSelectedToken(newToken)); - // updateToToken({ - // fromChainId: newFromChain?.id, - // toChainId: newToChain?.id, - // token: newToken, - // }); + if (chainId) { + const fromChains = bridgeSDK.getFromChains(); + const chain = fromChains.find((chain) => chain.id === chainId); + if (chain) { + selectFromChain(chain); + return; + } + if (fromChain?.id) return; + } + + updateSelectedInfo({ + fromChainId, + toChainId, + tokenAddress, + }); }; - const selectFromChain = async (tmpFromChain: IBridgeChain) => { - // After selecting fromChain, if toChain becomes incompatible, reselect the first compatible network in toChain list. + const selectFromChain = async (newFromChain: IBridgeChain) => { + // After selecting fromChain, if toChain becomes incompatible, + // reselect the first compatible network in toChain list. const toChains = bridgeSDK.getToChains({ - fromChainId: tmpFromChain.id, + fromChainId: newFromChain.id, }); - const tmpToChain = + + const newToChain = toChains.find((c) => c.isCompatible && c.id === toChain?.id) ?? toChains.find((c) => c.isCompatible && c.chainType !== 'link'); - const tmpTokens = await getSortedTokens({ - chainType: tmpFromChain.chainType, - fromChainId: tmpFromChain.id, + const sortedTokens = await getSortedTokens({ + chainType: newFromChain.chainType, + fromChainId: newFromChain.id, tokens: bridgeSDK.getTokens({ - fromChainId: tmpFromChain.id, - toChainId: tmpToChain!.id, + fromChainId: newFromChain.id, + toChainId: newToChain!.id, }), }); const newToken = - tmpTokens.find( + sortedTokens.find( (t) => t.isCompatible && t.displaySymbol.toUpperCase() === selectedToken?.displaySymbol.toUpperCase(), - ) ?? tmpTokens.find((t) => t.isCompatible); + ) ?? sortedTokens.find((t) => t.isCompatible); updateSelectedInfo({ - tmpToken: newToken, - tmpFromChain, - tmpToChain, + tokenAddress: newToken!.address, + fromChainId: newFromChain.id, + toChainId: newToChain!.id, }); }; - return { - async selectDefault({ - fromChainId, - toChainId, - tokenSymbol, - }: { - fromChainId: number; - toChainId: number; - tokenSymbol: string; - }) { - if (chainId) { - const fromChains = bridgeSDK.getFromChains(); - const chain = fromChains.find((chain) => chain.id === chainId); - if (chain) { - selectFromChain(chain); - return; - } - if (fromChain?.id) return; - } + const selectToChain = async (newToChain: IBridgeChain) => { + const fromChainId = fromChain!.id; + const toChainId = newToChain.id; - const fromChains = bridgeSDK.getFromChains(); - const toChains = bridgeSDK.getToChains({ - fromChainId, - }); - const tokens = bridgeSDK.getTokens({ + const sortedTokens = await getSortedTokens({ + fromChainId, + tokens: bridgeSDK.getTokens({ fromChainId, toChainId, - }); + }), + }); - const newFromChain = fromChains.find((item) => item.id === fromChainId); - const newToChain = toChains.find((item) => item.id === toChainId); - const newToken = tokens.find( - (item) => item.displaySymbol.toUpperCase() === tokenSymbol.toUpperCase(), - ); - - dispatch(setFromChain(newFromChain)); - dispatch(setToChain(newToChain)); - dispatch(setSelectedToken(newToken)); - - // updateToToken({ - // fromChainId: newFromChain?.id, - // toChainId: newToChain?.id, - // token: newToken, - // }); - }, - selectFromChain, - async selectToChain(tmpToChain: IBridgeChain) { - const fromChainId = fromChain!.id; + const newToken = + sortedTokens.find( + (t) => + t.isCompatible && + t.displaySymbol.toUpperCase() === selectedToken?.displaySymbol.toUpperCase(), + ) ?? sortedTokens.find((t) => t.isCompatible); - const tmpTokens = await getSortedTokens({ - fromChainId, - tokens: bridgeSDK.getTokens({ - fromChainId, - toChainId: tmpToChain?.id, - }), - }); + updateSelectedInfo({ + tokenAddress: newToken!.address, + fromChainId, + toChainId, + }); + }; - const newToken = - tmpTokens.find( - (t) => - t.isCompatible && - t.displaySymbol.toUpperCase() === selectedToken?.displaySymbol.toUpperCase(), - ) ?? tmpTokens.find((t) => t.isCompatible); - - updateSelectedInfo({ - tmpToken: newToken, - tmpFromChain: fromChain, - tmpToChain, - }); - }, + const selectToken = async (newToken: IBridgeToken) => { + updateSelectedInfo({ + tokenAddress: newToken.address, + fromChainId: fromChain!.id, + toChainId: toChain!.id, + }); + }; - async selectToken(newToken: IBridgeToken) { - updateSelectedInfo({ - tmpToken: newToken, - }); - }, + return { + selectDefault, + selectFromChain, + selectToChain, + selectToken, }; } diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/types.ts b/packages/canonical-bridge-widget/src/modules/aggregator/types.ts index cac84720..2b11983b 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/types.ts +++ b/packages/canonical-bridge-widget/src/modules/aggregator/types.ts @@ -17,7 +17,7 @@ export interface ITransferConfig { defaultSelectedInfo: { fromChainId: number; toChainId: number; - tokenSymbol: string; + tokenAddress: string; amount: string; }; order?: { diff --git a/packages/canonical-bridge-widget/src/modules/transfer/hooks/useLoadingBridgeFees.ts b/packages/canonical-bridge-widget/src/modules/transfer/hooks/useLoadingBridgeFees.ts index ea9b24bf..c25fd0d4 100644 --- a/packages/canonical-bridge-widget/src/modules/transfer/hooks/useLoadingBridgeFees.ts +++ b/packages/canonical-bridge-widget/src/modules/transfer/hooks/useLoadingBridgeFees.ts @@ -25,7 +25,6 @@ import { useGetCBridgeFees } from '@/modules/aggregator/adapters/cBridge/hooks/u import { useGetDeBridgeFees } from '@/modules/aggregator/adapters/deBridge/hooks/useGetDeBridgeFees'; import { useGetStargateFees } from '@/modules/aggregator/adapters/stargate/hooks/useGetStarGateFees'; import { useBridgeSDK } from '@/core/hooks/useBridgeSDK'; -import { useBridgeConfig } from '@/index'; import { useGetLayerZeroFees } from '@/modules/aggregator/adapters/layerZero/hooks/useGetLayerZeroFees'; import { usePreSelectRoute } from '@/modules/transfer/hooks/usePreSelectRoute'; import { useGetNativeToken } from '@/modules/transfer/hooks/useGetNativeToken'; @@ -47,9 +46,6 @@ export const useLoadingBridgeFees = () => { const { isSolanaAvailableToAccount } = useSolanaTransferInfo(); const bridgeSDK = useBridgeSDK(); - const { - http: { deBridgeAccessToken }, - } = useBridgeConfig(); const nativeToken = useGetNativeToken(); const { deBridgeFeeSorting: _deBridgeFeeSorting } = useGetDeBridgeFees(); const deBridgeFeeSorting = useRef(_deBridgeFeeSorting); @@ -78,7 +74,6 @@ export const useLoadingBridgeFees = () => { const { formatMessage } = useIntl(); - const toToken = useAppSelector((state) => state.transfer.toToken); const selectedToken = useAppSelector((state) => state.transfer.selectedToken); const sendValue = useAppSelector((state) => state.transfer.sendValue); const fromChain = useAppSelector((state) => state.transfer.fromChain); @@ -115,80 +110,51 @@ export const useLoadingBridgeFees = () => { meson: undefined, }), ); - const bridgeTypeList: BridgeType[] = []; const valueArr = []; - const adapters = bridgeSDK.getSDKOptions().adapters; - adapters.forEach((adapter) => { - if (selectedToken[adapter.bridgeType]) { - bridgeTypeList.push(adapter.bridgeType); - } - }); try { const amount = parseUnits(debouncedSendValue, selectedToken.decimals); const now = Date.now(); lastTime = now; + const response = await bridgeSDK.loadBridgeFees({ - bridgeType: bridgeTypeList, + publicClient, fromChainId: fromChain.id, - fromAccount: address || DEFAULT_ADDRESS, toChainId: toChain?.id, - sendValue: amount, - fromTokenSymbol: selectedToken.symbol === 'ETH' ? 'WETH' : selectedToken.symbol, - publicClient, - endPointId: { - layerZeroV1: toToken?.layerZero?.raw?.endpointID, - layerZeroV2: toToken?.stargate?.raw?.endpointID, - }, - bridgeAddress: { - stargate: selectedToken?.stargate?.raw?.bridgeAddress as `0x${string}`, - layerZero: selectedToken?.layerZero?.raw?.bridgeAddress as `0x${string}`, - }, - isPegged: selectedToken?.isPegged, + tokenAddress: selectedToken.address, + amount, slippage: max_slippage, - mesonOpts: { - fromToken: `${fromChain?.meson?.raw?.id}:${selectedToken?.meson?.raw?.id}`, - toToken: `${toChain?.meson?.raw?.id}:${toToken?.meson?.raw?.id}`, - amount: debouncedSendValue, - fromAddr: - fromChain?.chainType === 'tron' - ? tronAddress ?? DEFAULT_TRON_ADDRESS - : address ?? DEFAULT_ADDRESS, - }, - deBridgeOpts: { - fromChainId: fromChain.id, - fromTokenAddress: selectedToken.address as `0x${string}`, - amount, - toChainId: toChain?.id, - toTokenAddress: toToken?.address as `0x${string}`, - accesstoken: deBridgeAccessToken, - userAddress: - fromChain.chainType === 'solana' - ? solanaAddress || DEFAULT_SOLANA_ADDRESS - : address || DEFAULT_ADDRESS, - toUserAddress: - fromChain.chainType === 'solana' - ? isSolanaAvailableToAccount - ? toAccountRef.current - : DEFAULT_ADDRESS - : toChain.chainType === 'solana' - ? isSolanaAvailableToAccount - ? toAccountRef.current - : DEFAULT_SOLANA_ADDRESS - : undefined, - }, + userAddress: + fromChain?.chainType === 'solana' + ? solanaAddress ?? DEFAULT_SOLANA_ADDRESS + : fromChain?.chainType === 'tron' + ? tronAddress ?? DEFAULT_TRON_ADDRESS + : address ?? DEFAULT_ADDRESS, + toUserAddress: + fromChain.chainType === 'solana' + ? isSolanaAvailableToAccount + ? toAccountRef.current + : DEFAULT_ADDRESS + : toChain.chainType === 'solana' + ? isSolanaAvailableToAccount + ? toAccountRef.current + : DEFAULT_SOLANA_ADDRESS + : undefined, }); // eslint-disable-next-line no-console - console.log( - 'API response deBridge[0], cBridge[1], stargate[2], layerZero[3], meson[4]', - response, - ); + console.log('API response', response); if (lastTime > now) { return; } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const [debridgeEst, cbridgeEst, stargateEst, layerZeroEst, mesonEst] = response as any; + const { + deBridge: debridgeEst, + cBridge: cbridgeEst, + stargate: stargateEst, + layerZero: layerZeroEst, + meson: mesonEst, + } = response; + // meson if (mesonEst.status === 'fulfilled' && mesonEst?.value) { if (mesonEst?.value?.error) { @@ -419,16 +385,11 @@ export const useLoadingBridgeFees = () => { toChain, debouncedSendValue, bridgeSDK, - address, publicClient, - toToken?.layerZero?.raw?.endpointID, - toToken?.stargate?.raw?.endpointID, - toToken?.meson?.raw?.id, - toToken?.address, max_slippage, - tronAddress, - deBridgeAccessToken, solanaAddress, + tronAddress, + address, isSolanaAvailableToAccount, formatMessage, getToDecimals, diff --git a/packages/canonical-bridge-widget/src/modules/transfer/hooks/usePreSelectRoute.ts b/packages/canonical-bridge-widget/src/modules/transfer/hooks/usePreSelectRoute.ts index ad2a96d4..05c47c59 100644 --- a/packages/canonical-bridge-widget/src/modules/transfer/hooks/usePreSelectRoute.ts +++ b/packages/canonical-bridge-widget/src/modules/transfer/hooks/usePreSelectRoute.ts @@ -15,7 +15,13 @@ export const usePreSelectRoute = () => { const preSelectRoute = useCallback( // eslint-disable-next-line @typescript-eslint/no-explicit-any (response: any, bridgeType: BridgeType) => { - const [debridgeEst, cbridgeEst, stargateEst, layerZeroEst, mesonEst] = response; + const { + deBridge: debridgeEst, + cBridge: cbridgeEst, + stargate: stargateEst, + layerZero: layerZeroEst, + meson: mesonEst, + } = response; if (bridgeType === 'deBridge' && debridgeEst.status === 'fulfilled') { dispatch( From d05066db171f5b3249a337f49c625fd03524bdad Mon Sep 17 00:00:00 2001 From: wenty22 Date: Fri, 15 Nov 2024 10:03:02 +0800 Subject: [PATCH 7/7] refactor: Refactor sdk --- common/config/rush/pnpm-lock.yaml | 87 +++-- packages/canonical-bridge-sdk/package.json | 7 +- .../src/adapters/cBridge/index.ts | 137 +++++++- .../src/adapters/cBridge/types.ts | 20 +- .../src/adapters/layerZero/index.ts | 30 +- .../src/aggregator/index.ts | 237 ++++++++++++- .../src/constants/index.ts | 2 + .../src/shared/address.ts | 11 +- .../canonical-bridge-sdk/src/shared/string.ts | 5 + .../src/core/utils/address.ts | 3 +- .../canonical-bridge-widget/src/index.tsx | 4 + .../cBridge/hooks/useCBridgeSendMaxMin.ts | 2 +- .../cBridge/hooks/useCBridgeTransferParams.ts | 143 +------- .../aggregator/adapters/cBridge/types.ts | 121 ------- .../aggregator/adapters/deBridge/types.ts | 53 --- .../aggregator/adapters/layerZero/types.ts | 22 -- .../stargate/hooks/useStargateTransfer.ts | 133 ------- .../hooks/useStargateTransferParams.ts | 2 +- .../aggregator/adapters/stargate/types.ts | 10 - .../src/modules/aggregator/index.ts | 2 - .../src/modules/aggregator/reducer.ts | 3 +- .../components/Button/TransferButton.tsx | 326 ++++++++---------- .../RouteInfo/TokenInfoTooltip.tsx | 2 +- .../transfer/hooks/useLoadingBridgeFees.ts | 3 +- 24 files changed, 639 insertions(+), 726 deletions(-) create mode 100644 packages/canonical-bridge-sdk/src/shared/string.ts delete mode 100644 packages/canonical-bridge-widget/src/modules/aggregator/adapters/cBridge/types.ts delete mode 100644 packages/canonical-bridge-widget/src/modules/aggregator/adapters/layerZero/types.ts delete mode 100644 packages/canonical-bridge-widget/src/modules/aggregator/adapters/stargate/hooks/useStargateTransfer.ts diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 52fc79bc..fd2b9ee2 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -233,6 +233,12 @@ importers: ../../packages/canonical-bridge-sdk: devDependencies: + '@solana/web3.js': + specifier: ~1.95.4 + version: 1.95.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.9) + '@types/node': + specifier: ~22.9.0 + version: 22.9.0 '@types/react': specifier: ^18 version: 18.3.8 @@ -241,7 +247,7 @@ importers: version: 18.3.0 '@vitejs/plugin-react': specifier: ^4.2.0 - version: 4.3.1(vite@4.5.3(@types/node@22.7.5)(terser@5.31.6)) + version: 4.3.1(vite@4.5.3(@types/node@22.9.0)(terser@5.31.6)) axios: specifier: ~0.27.2 version: 0.27.2 @@ -262,10 +268,10 @@ importers: version: 2.21.14(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.9)(zod@3.22.4) vite: specifier: ^4.5.0 - version: 4.5.3(@types/node@22.7.5)(terser@5.31.6) + version: 4.5.3(@types/node@22.9.0)(terser@5.31.6) vite-plugin-dts: specifier: ^3.6.3 - version: 3.9.1(@types/node@22.7.5)(rollup@3.29.4)(typescript@5.5.4)(vite@4.5.3(@types/node@22.7.5)(terser@5.31.6)) + version: 3.9.1(@types/node@22.9.0)(rollup@3.29.4)(typescript@5.5.4)(vite@4.5.3(@types/node@22.9.0)(terser@5.31.6)) ../../packages/canonical-bridge-widget: dependencies: @@ -304,8 +310,8 @@ importers: specifier: ~11.13.0 version: 11.13.0(@emotion/react@11.13.3(@types/react@18.3.8)(react@18.3.1)(supports-color@9.4.0))(@types/react@18.3.8)(react@18.3.1)(supports-color@9.4.0) '@node-real/walletkit': - specifier: 2.4.1-alpha.0 - version: 2.4.1-alpha.0(@babel/runtime@7.25.0)(@react-native-async-storage/async-storage@1.24.0)(@tanstack/react-query@5.50.1(react@18.3.1))(@types/react@18.3.8)(bs58@6.0.0)(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.7.0)(typescript@5.5.4)(utf-8-validate@5.0.9)(viem@2.21.14(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.9)(zod@3.22.4))(wagmi@2.12.14(@react-native-async-storage/async-storage@1.24.0)(@tanstack/query-core@5.50.1)(@tanstack/react-query@5.50.1(react@18.3.1))(@types/react@18.3.8)(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@3.29.4)(typescript@5.5.4)(utf-8-validate@5.0.9)(viem@2.21.14(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.9)(zod@3.22.4))(zod@3.22.4))(zod@3.22.4) + specifier: 2.4.1-alpha.2 + version: 2.4.1-alpha.2(@babel/core@7.24.9)(@babel/runtime@7.25.0)(@react-native-async-storage/async-storage@1.24.0)(@tanstack/react-query@5.50.1(react@18.3.1))(@types/react@18.3.8)(bs58@6.0.0)(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.7.0)(typescript@5.5.4)(utf-8-validate@5.0.9)(viem@2.21.14(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.9)(zod@3.22.4))(wagmi@2.12.14(@react-native-async-storage/async-storage@1.24.0)(@tanstack/query-core@5.50.1)(@tanstack/react-query@5.50.1(react@18.3.1))(@types/react@18.3.8)(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@3.29.4)(typescript@5.5.4)(utf-8-validate@5.0.9)(viem@2.21.14(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.9)(zod@3.22.4))(zod@3.22.4))(zod@3.22.4) '@tanstack/react-query': specifier: ~5.50.1 version: 5.50.1(react@18.3.1) @@ -2993,8 +2999,8 @@ packages: viem: ^2 wagmi: ^2 - '@node-real/walletkit@2.4.1-alpha.0': - resolution: {integrity: sha512-rl/uXrktICOROw4cGWXAmIdA9Hw8QlBW/GVYts2ucSJuwrST0EInM10Z7jwNCdJyn1ANDwzB5L/lA41BDJPafA==} + '@node-real/walletkit@2.4.1-alpha.2': + resolution: {integrity: sha512-0j3L7z8hWG26JyT6aUg6lEl9y2+rxRfpQHk61cQ68rL4UF58h4ZTkpNvENz6IW19adeLb4Yzcjzjf4wViS9aUA==} peerDependencies: '@tanstack/react-query': ^5 react: '>=17' @@ -4499,6 +4505,9 @@ packages: '@types/node@22.7.5': resolution: {integrity: sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==} + '@types/node@22.9.0': + resolution: {integrity: sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==} + '@types/normalize-package-data@2.4.1': resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} @@ -15590,11 +15599,11 @@ snapshots: transitivePeerDependencies: - '@types/node' - '@microsoft/api-extractor-model@7.28.13(@types/node@22.7.5)': + '@microsoft/api-extractor-model@7.28.13(@types/node@22.9.0)': dependencies: '@microsoft/tsdoc': 0.14.2 '@microsoft/tsdoc-config': 0.16.2 - '@rushstack/node-core-library': 4.0.2(@types/node@22.7.5) + '@rushstack/node-core-library': 4.0.2(@types/node@22.9.0) transitivePeerDependencies: - '@types/node' @@ -15616,15 +15625,15 @@ snapshots: transitivePeerDependencies: - '@types/node' - '@microsoft/api-extractor@7.43.0(@types/node@22.7.5)': + '@microsoft/api-extractor@7.43.0(@types/node@22.9.0)': dependencies: - '@microsoft/api-extractor-model': 7.28.13(@types/node@22.7.5) + '@microsoft/api-extractor-model': 7.28.13(@types/node@22.9.0) '@microsoft/tsdoc': 0.14.2 '@microsoft/tsdoc-config': 0.16.2 - '@rushstack/node-core-library': 4.0.2(@types/node@22.7.5) + '@rushstack/node-core-library': 4.0.2(@types/node@22.9.0) '@rushstack/rig-package': 0.5.2 - '@rushstack/terminal': 0.10.0(@types/node@22.7.5) - '@rushstack/ts-command-line': 4.19.1(@types/node@22.7.5) + '@rushstack/terminal': 0.10.0(@types/node@22.9.0) + '@rushstack/ts-command-line': 4.19.1(@types/node@22.9.0) lodash: 4.17.21 minimatch: 3.0.5 resolve: 1.22.8 @@ -15978,11 +15987,11 @@ snapshots: - utf-8-validate - zod - '@node-real/walletkit@2.4.1-alpha.0(@babel/runtime@7.25.0)(@react-native-async-storage/async-storage@1.24.0)(@tanstack/react-query@5.50.1(react@18.3.1))(@types/react@18.3.8)(bs58@6.0.0)(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.7.0)(typescript@5.5.4)(utf-8-validate@5.0.9)(viem@2.21.14(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.9)(zod@3.22.4))(wagmi@2.12.14(@react-native-async-storage/async-storage@1.24.0)(@tanstack/query-core@5.50.1)(@tanstack/react-query@5.50.1(react@18.3.1))(@types/react@18.3.8)(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@3.29.4)(typescript@5.5.4)(utf-8-validate@5.0.9)(viem@2.21.14(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.9)(zod@3.22.4))(zod@3.22.4))(zod@3.22.4)': + '@node-real/walletkit@2.4.1-alpha.2(@babel/core@7.24.9)(@babel/runtime@7.25.0)(@react-native-async-storage/async-storage@1.24.0)(@tanstack/react-query@5.50.1(react@18.3.1))(@types/react@18.3.8)(bs58@6.0.0)(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.7.0)(typescript@5.5.4)(utf-8-validate@5.0.9)(viem@2.21.14(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.9)(zod@3.22.4))(wagmi@2.12.14(@react-native-async-storage/async-storage@1.24.0)(@tanstack/query-core@5.50.1)(@tanstack/react-query@5.50.1(react@18.3.1))(@types/react@18.3.8)(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@3.29.4)(typescript@5.5.4)(utf-8-validate@5.0.9)(viem@2.21.14(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.9)(zod@3.22.4))(zod@3.22.4))(zod@3.22.4)': dependencies: '@metamask/jazzicon': 2.0.0 '@solana/wallet-adapter-react': 0.15.35(@solana/web3.js@1.95.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.9))(bs58@6.0.0)(react@18.3.1) - '@solana/wallet-adapter-wallets': 0.19.32(@babel/runtime@7.25.0)(@react-native-async-storage/async-storage@1.24.0)(@solana/web3.js@1.95.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.9))(bs58@6.0.0)(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.7.0)(utf-8-validate@5.0.9) + '@solana/wallet-adapter-wallets': 0.19.32(@babel/core@7.24.9)(@babel/runtime@7.25.0)(@react-native-async-storage/async-storage@1.24.0)(@solana/web3.js@1.95.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.9))(bs58@6.0.0)(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.7.0)(utf-8-validate@5.0.9) '@solana/web3.js': 1.95.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.9) '@tanstack/react-query': 5.50.1(react@18.3.1) '@tronweb3/tronwallet-abstract-adapter': 1.1.7 @@ -16260,7 +16269,7 @@ snapshots: optionalDependencies: '@types/node': 20.11.21 - '@rushstack/node-core-library@4.0.2(@types/node@22.7.5)': + '@rushstack/node-core-library@4.0.2(@types/node@22.9.0)': dependencies: fs-extra: 7.0.1 import-lazy: 4.0.0 @@ -16269,7 +16278,7 @@ snapshots: semver: 7.5.4 z-schema: 5.0.5 optionalDependencies: - '@types/node': 22.7.5 + '@types/node': 22.9.0 '@rushstack/rig-package@0.5.2': dependencies: @@ -16283,12 +16292,12 @@ snapshots: optionalDependencies: '@types/node': 20.11.21 - '@rushstack/terminal@0.10.0(@types/node@22.7.5)': + '@rushstack/terminal@0.10.0(@types/node@22.9.0)': dependencies: - '@rushstack/node-core-library': 4.0.2(@types/node@22.7.5) + '@rushstack/node-core-library': 4.0.2(@types/node@22.9.0) supports-color: 8.1.1 optionalDependencies: - '@types/node': 22.7.5 + '@types/node': 22.9.0 '@rushstack/ts-command-line@4.19.1(@types/node@20.11.21)': dependencies: @@ -16299,9 +16308,9 @@ snapshots: transitivePeerDependencies: - '@types/node' - '@rushstack/ts-command-line@4.19.1(@types/node@22.7.5)': + '@rushstack/ts-command-line@4.19.1(@types/node@22.9.0)': dependencies: - '@rushstack/terminal': 0.10.0(@types/node@22.7.5) + '@rushstack/terminal': 0.10.0(@types/node@22.9.0) '@types/argparse': 1.0.38 argparse: 1.0.10 string-argv: 0.3.1 @@ -16753,11 +16762,11 @@ snapshots: - tslib - utf-8-validate - '@solana/wallet-adapter-trezor@0.1.2(@solana/web3.js@1.95.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.9))(bufferutil@4.0.8)(encoding@0.1.13)(tslib@2.7.0)(utf-8-validate@5.0.9)': + '@solana/wallet-adapter-trezor@0.1.2(@babel/core@7.24.9)(@solana/web3.js@1.95.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.9))(bufferutil@4.0.8)(encoding@0.1.13)(tslib@2.7.0)(utf-8-validate@5.0.9)': dependencies: '@solana/wallet-adapter-base': 0.9.23(@solana/web3.js@1.95.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.9)) '@solana/web3.js': 1.95.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.9) - '@trezor/connect-web': 9.3.0(bufferutil@4.0.8)(encoding@0.1.13)(tslib@2.7.0)(utf-8-validate@5.0.9) + '@trezor/connect-web': 9.3.0(@babel/core@7.24.9)(bufferutil@4.0.8)(encoding@0.1.13)(tslib@2.7.0)(utf-8-validate@5.0.9) buffer: 6.0.3 transitivePeerDependencies: - '@babel/core' @@ -16873,7 +16882,7 @@ snapshots: - tslib - utf-8-validate - '@solana/wallet-adapter-wallets@0.19.32(@babel/runtime@7.25.0)(@react-native-async-storage/async-storage@1.24.0)(@solana/web3.js@1.95.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.9))(bs58@6.0.0)(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.7.0)(utf-8-validate@5.0.9)': + '@solana/wallet-adapter-wallets@0.19.32(@babel/core@7.24.9)(@babel/runtime@7.25.0)(@react-native-async-storage/async-storage@1.24.0)(@solana/web3.js@1.95.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.9))(bs58@6.0.0)(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.7.0)(utf-8-validate@5.0.9)': dependencies: '@solana/wallet-adapter-alpha': 0.1.10(@solana/web3.js@1.95.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.9)) '@solana/wallet-adapter-avana': 0.1.13(@solana/web3.js@1.95.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.9)) @@ -16906,7 +16915,7 @@ snapshots: '@solana/wallet-adapter-tokenary': 0.1.12(@solana/web3.js@1.95.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.9)) '@solana/wallet-adapter-tokenpocket': 0.4.19(@solana/web3.js@1.95.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.9)) '@solana/wallet-adapter-torus': 0.11.28(@babel/runtime@7.25.0)(@solana/web3.js@1.95.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.9))(bufferutil@4.0.8)(encoding@0.1.13)(supports-color@9.4.0)(utf-8-validate@5.0.9) - '@solana/wallet-adapter-trezor': 0.1.2(@solana/web3.js@1.95.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.9))(bufferutil@4.0.8)(encoding@0.1.13)(tslib@2.7.0)(utf-8-validate@5.0.9) + '@solana/wallet-adapter-trezor': 0.1.2(@babel/core@7.24.9)(@solana/web3.js@1.95.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.9))(bufferutil@4.0.8)(encoding@0.1.13)(tslib@2.7.0)(utf-8-validate@5.0.9) '@solana/wallet-adapter-trust': 0.1.13(@solana/web3.js@1.95.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.9)) '@solana/wallet-adapter-unsafe-burner': 0.1.7(@solana/web3.js@1.95.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.9)) '@solana/wallet-adapter-walletconnect': 0.1.16(@react-native-async-storage/async-storage@1.24.0)(@solana/web3.js@1.95.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.9))(bufferutil@4.0.8)(encoding@0.1.13)(supports-color@9.4.0)(utf-8-validate@5.0.9) @@ -18418,9 +18427,9 @@ snapshots: - supports-color - utf-8-validate - '@trezor/connect-web@9.3.0(bufferutil@4.0.8)(encoding@0.1.13)(tslib@2.7.0)(utf-8-validate@5.0.9)': + '@trezor/connect-web@9.3.0(@babel/core@7.24.9)(bufferutil@4.0.8)(encoding@0.1.13)(tslib@2.7.0)(utf-8-validate@5.0.9)': dependencies: - '@trezor/connect': 9.3.0(bufferutil@4.0.8)(encoding@0.1.13)(tslib@2.7.0)(utf-8-validate@5.0.9) + '@trezor/connect': 9.3.0(@babel/core@7.24.9)(bufferutil@4.0.8)(encoding@0.1.13)(tslib@2.7.0)(utf-8-validate@5.0.9) '@trezor/connect-common': 0.1.0(tslib@2.7.0) '@trezor/utils': 9.1.0(tslib@2.7.0) tslib: 2.7.0 @@ -18465,7 +18474,7 @@ snapshots: - supports-color - utf-8-validate - '@trezor/connect@9.3.0(bufferutil@4.0.8)(encoding@0.1.13)(tslib@2.7.0)(utf-8-validate@5.0.9)': + '@trezor/connect@9.3.0(@babel/core@7.24.9)(bufferutil@4.0.8)(encoding@0.1.13)(tslib@2.7.0)(utf-8-validate@5.0.9)': dependencies: '@babel/preset-typescript': 7.24.7(@babel/core@7.24.9) '@ethereumjs/common': 4.3.0 @@ -18873,6 +18882,10 @@ snapshots: dependencies: undici-types: 6.19.8 + '@types/node@22.9.0': + dependencies: + undici-types: 6.19.8 + '@types/normalize-package-data@2.4.1': {} '@types/npmlog@4.1.4': {} @@ -19279,14 +19292,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@vitejs/plugin-react@4.3.1(vite@4.5.3(@types/node@22.7.5)(terser@5.31.6))': + '@vitejs/plugin-react@4.3.1(vite@4.5.3(@types/node@22.9.0)(terser@5.31.6))': dependencies: '@babel/core': 7.24.9 '@babel/plugin-transform-react-jsx-self': 7.24.7(@babel/core@7.24.9) '@babel/plugin-transform-react-jsx-source': 7.24.7(@babel/core@7.24.9) '@types/babel__core': 7.20.5 react-refresh: 0.14.2 - vite: 4.5.3(@types/node@22.7.5)(terser@5.31.6) + vite: 4.5.3(@types/node@22.9.0)(terser@5.31.6) transitivePeerDependencies: - supports-color @@ -28606,9 +28619,9 @@ snapshots: - rollup - supports-color - vite-plugin-dts@3.9.1(@types/node@22.7.5)(rollup@3.29.4)(typescript@5.5.4)(vite@4.5.3(@types/node@22.7.5)(terser@5.31.6)): + vite-plugin-dts@3.9.1(@types/node@22.9.0)(rollup@3.29.4)(typescript@5.5.4)(vite@4.5.3(@types/node@22.9.0)(terser@5.31.6)): dependencies: - '@microsoft/api-extractor': 7.43.0(@types/node@22.7.5) + '@microsoft/api-extractor': 7.43.0(@types/node@22.9.0) '@rollup/pluginutils': 5.1.0(rollup@3.29.4) '@vue/language-core': 1.8.27(typescript@5.5.4) debug: 4.3.4(supports-color@9.4.0) @@ -28617,7 +28630,7 @@ snapshots: typescript: 5.5.4 vue-tsc: 1.8.27(typescript@5.5.4) optionalDependencies: - vite: 4.5.3(@types/node@22.7.5)(terser@5.31.6) + vite: 4.5.3(@types/node@22.9.0)(terser@5.31.6) transitivePeerDependencies: - '@types/node' - rollup @@ -28633,13 +28646,13 @@ snapshots: fsevents: 2.3.3 terser: 5.31.6 - vite@4.5.3(@types/node@22.7.5)(terser@5.31.6): + vite@4.5.3(@types/node@22.9.0)(terser@5.31.6): dependencies: esbuild: 0.18.20 postcss: 8.4.31 rollup: 3.29.4 optionalDependencies: - '@types/node': 22.7.5 + '@types/node': 22.9.0 fsevents: 2.3.3 terser: 5.31.6 diff --git a/packages/canonical-bridge-sdk/package.json b/packages/canonical-bridge-sdk/package.json index cf4bb52a..b1520bf0 100644 --- a/packages/canonical-bridge-sdk/package.json +++ b/packages/canonical-bridge-sdk/package.json @@ -30,9 +30,11 @@ }, "peerDependencies": { "axios": "^0", - "viem": "^2" + "viem": "^2", + "@solana/web3.js": "^1" }, "devDependencies": { + "@solana/web3.js": "~1.95.4", "@types/react": "^18", "@types/react-dom": "^18", "@vitejs/plugin-react": "^4.2.0", @@ -43,6 +45,7 @@ "typescript": "^5", "viem": "~2.21.14", "vite": "^4.5.0", - "vite-plugin-dts": "^3.6.3" + "vite-plugin-dts": "^3.6.3", + "@types/node": "~22.9.0" } } diff --git a/packages/canonical-bridge-sdk/src/adapters/cBridge/index.ts b/packages/canonical-bridge-sdk/src/adapters/cBridge/index.ts index 1f21caeb..7e307258 100644 --- a/packages/canonical-bridge-sdk/src/adapters/cBridge/index.ts +++ b/packages/canonical-bridge-sdk/src/adapters/cBridge/index.ts @@ -1,4 +1,4 @@ -import { getContract, Hash, isAddress } from 'viem'; +import { getContract, Hash, isAddress, parseUnits } from 'viem'; import { BaseAdapter } from '@/adapters/base'; import { IInitialOptions, ITransferTokenPair } from '@/adapters/base/types'; import { @@ -18,7 +18,7 @@ import { IGetCBridgeTransferParamsInput, ISendCBridgeToken, } from '@/adapters/cBridge/types'; -import { BridgeType } from '@/aggregator/types'; +import { BridgeType, IBridgeChain, IBridgeToken } from '@/aggregator/types'; import axios, { AxiosInstance } from 'axios'; import { CLIENT_TIME_OUT, env } from '@/constants'; import { @@ -28,6 +28,7 @@ import { PEGGED_TOKEN_BRIDGE, PEGGED_TOKEN_BRIDGE_V2, } from '@/adapters/cBridge/exports'; +import { isNativeToken } from '@/shared/address'; export class CBridgeAdapter extends BaseAdapter< ICBridgeTransferConfig, @@ -392,6 +393,138 @@ export class CBridgeAdapter extends BaseAdapter< }; } + public getTransactionParams({ + userAddress, + fromChain, + toChain, + fromToken, + sendValue, + slippage, + }: { + userAddress: string; + fromChain: IBridgeChain; + toChain: IBridgeChain; + fromToken: IBridgeToken; + sendValue: string; + slippage: number; + }) { + const isPegged = !!fromToken.isPegged; + + const bridgeAddress = (() => { + try { + if ( + !fromChain || + (isPegged && !fromToken?.cBridge?.peggedConfig) || + (!isPegged && !fromChain?.cBridge?.raw) + ) { + return null; + } + return this.getTransferAddress({ + fromChainId: fromChain?.id as number, + isPegged, + peggedConfig: fromToken?.cBridge?.peggedConfig, + chainConfig: fromChain?.cBridge?.raw, + }); + } catch (e: any) { + // eslint-disable-next-line no-console + console.log(e); + } + })(); + + // Mint/deposit or burn/withdraw + const transferType = (() => { + if (fromToken?.cBridge?.peggedConfig?.org_chain_id === fromChain?.id) { + return 'deposit'; + } + if (fromToken?.cBridge?.peggedConfig?.pegged_chain_id === fromChain?.id) { + return 'withdraw'; + } + return ''; + })(); + + const argument = (() => { + if ( + !sendValue || + sendValue === '0' || + !toChain || + !fromToken || + !userAddress + ) { + return null; + } + const nonce = new Date().getTime(); + + let amount = 0n; + try { + if (fromToken.isPegged === false) { + amount = parseUnits( + sendValue, + fromToken?.cBridge?.raw?.token.decimal as number + ); // Convert to big number + } else if (transferType === 'deposit') { + amount = parseUnits( + sendValue, + fromToken?.cBridge?.peggedConfig?.org_token.token.decimal as number + ); + } else if (transferType === 'withdraw') { + amount = parseUnits( + sendValue, + fromToken?.cBridge?.peggedConfig?.pegged_token.token + .decimal as number + ); + } + } catch (e: any) { + // eslint-disable-next-line no-console + console.log(e); + } + + return this.getTransferParams({ + amount, + isPegged, + toChainId: toChain.id, + tokenAddress: fromToken?.address as `0x${string}`, + address: userAddress as `0x${string}`, + maxSlippage: slippage, + transferType: transferType ? transferType : undefined, + peggedConfig: fromToken.cBridge?.peggedConfig, + isNativeToken: isNativeToken(fromToken.address), + nonce, + }); + })(); + + // Arguments for bridge smart contract + const args = (() => { + const peggedConfig = fromToken?.cBridge?.peggedConfig; + if ( + !argument || + (isPegged && !transferType) || + !userAddress || + !bridgeAddress || + !fromToken + ) { + return null; + } + const abi = this.getABI({ + isPegged, + transferType: transferType || undefined, + peggedConfig, + }); + const functionName = this.getTransferFunction({ + isPegged, + isNativeToken: isNativeToken(fromToken?.address), + transferType: transferType || undefined, + }); + return { + address: bridgeAddress as `0x${string}`, + abi: abi, + functionName: functionName, + account: userAddress as `0x${string}`, + args: argument, + }; + })(); + return { args, bridgeAddress }; + } + public init(initialOptions?: IInitialOptions) { this.initOptions(initialOptions); diff --git a/packages/canonical-bridge-sdk/src/adapters/cBridge/types.ts b/packages/canonical-bridge-sdk/src/adapters/cBridge/types.ts index ed47b046..0ad79e4e 100644 --- a/packages/canonical-bridge-sdk/src/adapters/cBridge/types.ts +++ b/packages/canonical-bridge-sdk/src/adapters/cBridge/types.ts @@ -142,16 +142,6 @@ export interface ICBridgeTransactionResponse { error: null | unknown; } -export interface ICBridgeEstimateAmountRequest { - src_chain_id: number; - dst_chain_id: number; - token_symbol: string; - amt: string; - user_addr?: string; - slippage_tolerance: number; - is_pegged?: boolean; -} - export interface ICBridgeEstimateAmountResponse { err: object; eq_value_token_amt: string; @@ -222,3 +212,13 @@ export interface ICBridgeMaxMinSendAmt { max: string; min: string; } + +export interface ICBridgeEstimateAmountRequest { + src_chain_id: number; + dst_chain_id: number; + token_symbol: string; + amt: string; + user_addr?: string; + slippage_tolerance: number; + is_pegged?: boolean; +} diff --git a/packages/canonical-bridge-sdk/src/adapters/layerZero/index.ts b/packages/canonical-bridge-sdk/src/adapters/layerZero/index.ts index df239967..addbb696 100644 --- a/packages/canonical-bridge-sdk/src/adapters/layerZero/index.ts +++ b/packages/canonical-bridge-sdk/src/adapters/layerZero/index.ts @@ -41,13 +41,22 @@ export class LayerZeroAdapter extends BaseAdapter< walletClient, gasAmount = 200000n, version = 1, + airDropGas = 0n, + dstAddress = '0x', }: ISendCakeTokenInput): Promise { try { const address32Bytes = pad(userAddress, { size: 32 }); - const adapterParams = encodePacked( - ['uint16', 'uint256'], - [version, gasAmount] - ); + /* version 1 - send token + * version 2 - send token and air drop native gas on destination chain + * https://docs.layerzero.network/v1/developers/evm/evm-guides/advanced/relayer-adapter-parameters#airdrop + */ + const adapterParams = + version === 1 + ? encodePacked(['uint16', 'uint256'], [version, gasAmount]) + : encodePacked( + ['uint16', 'uint', 'uint', 'address'], + [2, gasAmount, airDropGas, dstAddress] + ); const fees = await publicClient.readContract({ address: bridgeAddress, abi: CAKE_PROXY_OFT_ABI, @@ -117,13 +126,18 @@ export class LayerZeroAdapter extends BaseAdapter< publicClient, gasAmount = 200000n, version = 1, + airDropGas = 0n, + dstAddress = '0x', }: IGetEstimateFeeInput) { try { const address32Bytes = pad(userAddress, { size: 32 }); - const adapterParams = encodePacked( - ['uint16', 'uint256'], - [version, gasAmount] - ); + const adapterParams = + version === 1 + ? encodePacked(['uint16', 'uint256'], [version, gasAmount]) + : encodePacked( + ['uint16', 'uint', 'uint', 'address'], + [2, gasAmount, airDropGas, dstAddress] + ); const fees = await publicClient.readContract({ address: bridgeAddress, abi: CAKE_PROXY_OFT_ABI, diff --git a/packages/canonical-bridge-sdk/src/aggregator/index.ts b/packages/canonical-bridge-sdk/src/aggregator/index.ts index 2eb993f6..28b4b78d 100644 --- a/packages/canonical-bridge-sdk/src/aggregator/index.ts +++ b/packages/canonical-bridge-sdk/src/aggregator/index.ts @@ -22,7 +22,15 @@ import { } from '@/aggregator/types'; import { sortChains } from '@/aggregator/utils/sortChains'; import { sortTokens } from '@/aggregator/utils/sortTokens'; -import { formatUnits, Hash, PublicClient } from 'viem'; +import { DEFAULT_SLIPPAGE } from '@/constants'; +import { isNativeToken } from '@/shared/address'; +import { utf8ToHex } from '@/shared/string'; +import { Hash, parseUnits, PublicClient, WalletClient } from 'viem'; +import { + VersionedTransaction, + Connection, + TransactionSignature, +} from '@solana/web3.js'; export interface CanonicalBridgeSDKOptions { chains: IChainConfig[]; @@ -223,8 +231,8 @@ export class CanonicalBridgeSDK { tokenAddress, userAddress, toUserAddress, - amount, - slippage = 10000, + sendValue, + slippage = DEFAULT_SLIPPAGE, }: { publicClient: PublicClient; fromChainId: number; @@ -232,7 +240,7 @@ export class CanonicalBridgeSDK { tokenAddress: string; userAddress: string; toUserAddress?: string; - amount: bigint; + sendValue: string; slippage?: number; }) { const { fromChain, toChain, fromToken, toToken } = this.getParamDetails({ @@ -246,6 +254,7 @@ export class CanonicalBridgeSDK { throw new Error('Missing parameters'); } + const amount = parseUnits(sendValue, fromToken.decimals); const promiseArr: Array<{ bridgeType: BridgeType; apiCall: Promise }> = []; @@ -351,11 +360,10 @@ export class CanonicalBridgeSDK { // meson if (this.meson && fromToken?.meson?.raw?.id && toToken?.meson?.raw?.id) { - const mesonAmount = formatUnits(amount, fromToken.meson.raw.decimals); const mesonFeeAPICall = this.meson.getEstimatedFees({ fromToken: `${fromChain?.meson?.raw?.id}:${fromToken?.meson?.raw?.id}`, toToken: `${toChain?.meson?.raw?.id}:${toToken?.meson?.raw?.id}`, - amount: mesonAmount, + amount: sendValue, fromAddr: userAddress, }); promiseArr.push({ @@ -377,6 +385,223 @@ export class CanonicalBridgeSDK { ) as Record>; } + async sendToken({ + bridgeType, + publicClient, + walletClient, + fromChainId, + toChainId, + tokenAddress, + userAddress, + toUserAddress, + sendValue, + slippage = DEFAULT_SLIPPAGE, + solanaOpts, + mesonOpts, + }: { + bridgeType: BridgeType; + publicClient: PublicClient; + walletClient: WalletClient; + fromChainId: number; + toChainId: number; + tokenAddress: string; + userAddress: string; + toUserAddress?: string; + sendValue: string; + slippage?: number; + solanaOpts?: { + connection: Connection; + sendTransaction: ( + transaction: VersionedTransaction, + connection: Connection + ) => Promise; + }; + mesonOpts?: { + signMessage: (message: string) => Promise; + signTransaction: (message: string) => Promise; + }; + }) { + const { fromChain, toChain, fromToken, toToken } = this.getParamDetails({ + fromChainId, + toChainId, + tokenAddress, + }); + + if (!fromChain || !toChain || !fromToken || !toToken) { + console.log(fromChain, toChain, fromToken, toToken); + throw new Error('Missing parameters'); + } + + const amount = parseUnits(sendValue, fromToken.decimals); + + if (bridgeType === 'cBridge' && this.cBridge) { + const { args, bridgeAddress } = this.cBridge.getTransactionParams({ + fromChain, + toChain, + fromToken, + userAddress, + sendValue, + slippage, + }); + + const hash = await this.cBridge.sendToken({ + walletClient, + publicClient, + bridgeAddress: bridgeAddress as string, + fromChainId, + isPegged: fromToken.isPegged, + isNativeToken: isNativeToken(fromToken.address), + address: userAddress as `0x${string}`, + peggedConfig: fromToken.cBridge?.peggedConfig, + args, + }); + + await publicClient.waitForTransactionReceipt({ + hash, + }); + + return { + hash, + }; + } + + if (bridgeType === 'deBridge' && this.deBridge) { + const txQuote = await this.deBridge.getEstimatedFees({ + fromChainId, + toChainId, + fromTokenAddress: fromToken.deBridge?.raw?.address as `0x${string}`, + toTokenAddress: toToken.deBridge?.raw?.address as `0x${string}`, + amount, + userAddress, + toUserAddress: toUserAddress || userAddress, + }); + + if (fromChain.chainType === 'evm') { + const hash = await this.deBridge?.sendToken({ + walletClient, + bridgeAddress: txQuote.tx.to as string, + data: txQuote.tx.data as `0x${string}`, + amount: BigInt(txQuote.tx.value), + address: userAddress as `0x${string}`, + }); + + await publicClient.waitForTransactionReceipt({ + hash, + }); + + return { + hash, + }; + } + + if (fromChain?.chainType === 'solana') { + if (solanaOpts) { + const { connection, sendTransaction } = solanaOpts; + + const { blockhash } = await connection.getLatestBlockhash(); + const data = (txQuote.tx.data as string)?.slice(2); + const tx = VersionedTransaction.deserialize(Buffer.from(data, 'hex')); + + tx.message.recentBlockhash = blockhash; + const hash = await sendTransaction(tx, connection); + + return { + hash, + }; + } else { + throw Error('Parameter [solanaOpts] is required'); + } + } + } + + if (bridgeType === 'stargate' && this.stargate) { + const hash = await this.stargate?.sendToken({ + walletClient, + publicClient, + bridgeAddress: fromToken.stargate?.raw?.bridgeAddress as `0x${string}`, + tokenAddress: fromToken.stargate?.raw?.address as `0x${string}`, + endPointId: toToken.stargate?.raw?.endpointID as number, + receiver: userAddress as `0x${string}`, + amount, + }); + + return { + hash, + }; + } + + if (bridgeType === 'layerZero' && this.layerZero) { + const hash = await this.layerZero?.sendToken({ + walletClient, + publicClient, + bridgeAddress: fromToken.layerZero?.raw?.bridgeAddress as `0x${string}`, + dstEndpoint: toToken.layerZero?.raw?.endpointID as number, + userAddress: userAddress as `0x${string}`, + amount, + }); + + return { + hash, + }; + } + + if (bridgeType === 'meson' && this.meson) { + if (mesonOpts) { + const { signMessage, signTransaction } = mesonOpts; + + let message = ''; + let signature = ''; + + // get unsigned message + const unsignedMessage = await this.meson.getUnsignedMessage({ + fromToken: `${fromChain?.meson?.raw?.id}:${fromToken?.meson?.raw?.id}`, + toToken: `${toChain?.meson?.raw?.id}:${toToken?.meson?.raw?.id}`, + amount: sendValue, + fromAddress: userAddress, + recipient: toUserAddress || userAddress, + }); + + if (unsignedMessage?.result) { + const result = unsignedMessage.result; + const encodedData = result.encoded; + const signingMessage = result.signingRequest.message; + + if (fromChain?.chainType === 'tron') { + const hexTronHeader = utf8ToHex('\x19TRON Signed Message:\n32'); + message = signingMessage.replace(hexTronHeader, ''); + } else { + const hexEthHeader = utf8ToHex('\x19Ethereum Signed Message:\n52'); + message = signingMessage.replace(hexEthHeader, ''); + } + + if (fromChain?.chainType != 'tron') { + signature = await signMessage(message); + } else { + // TODO + signature = await signTransaction(message); + } + + const swapId = await this.meson?.sendToken({ + fromAddress: userAddress, + recipient: toUserAddress || userAddress, + signature: signature, + encodedData: encodedData, + }); + + return { + swapId, + }; + } else { + throw new Error(unsignedMessage?.error.message); + } + } else { + throw Error('Parameter [mesonOpts] is required'); + } + } + + return {}; + } + public getFromChains() { const chainMap = new Map(); diff --git a/packages/canonical-bridge-sdk/src/constants/index.ts b/packages/canonical-bridge-sdk/src/constants/index.ts index c6a9f5a5..45646b2b 100644 --- a/packages/canonical-bridge-sdk/src/constants/index.ts +++ b/packages/canonical-bridge-sdk/src/constants/index.ts @@ -16,3 +16,5 @@ export const EXPLORER_URL: Record<(typeof ExplorerList)[number], string> = { cBridge: 'https://celerscan.com/tx/', deBridge: 'https://app.debridge.finance/orders?s=', }; + +export const DEFAULT_SLIPPAGE = 10000; diff --git a/packages/canonical-bridge-sdk/src/shared/address.ts b/packages/canonical-bridge-sdk/src/shared/address.ts index 570e7b0a..c76bfbc8 100644 --- a/packages/canonical-bridge-sdk/src/shared/address.ts +++ b/packages/canonical-bridge-sdk/src/shared/address.ts @@ -1,3 +1,5 @@ +import { ChainType } from '@/aggregator/types'; + export function isSameAddress(A?: string, B?: string) { if (!A || !B) return false; @@ -12,7 +14,14 @@ export function isEvmAddress(address?: string) { return !!address && /^0x[a-f0-9]{40}$/i.test(address); } -export function isNativeToken(tokenAddress: string) { +export function isNativeToken( + tokenAddress: string, + chainType: ChainType = 'evm' +) { + if (chainType === 'solana') { + return tokenAddress === '11111111111111111111111111111111'; + } + return tokenAddress === '0x0000000000000000000000000000000000000000'; } diff --git a/packages/canonical-bridge-sdk/src/shared/string.ts b/packages/canonical-bridge-sdk/src/shared/string.ts new file mode 100644 index 00000000..ea7f7492 --- /dev/null +++ b/packages/canonical-bridge-sdk/src/shared/string.ts @@ -0,0 +1,5 @@ +export function utf8ToHex(utf8Str: any) { + return Array.from(utf8Str) + .map((char: any) => char.charCodeAt(0).toString(16).padStart(2, '0')) + .join(''); +} diff --git a/packages/canonical-bridge-widget/src/core/utils/address.ts b/packages/canonical-bridge-widget/src/core/utils/address.ts index 89bf1b60..3a5cfa7c 100644 --- a/packages/canonical-bridge-widget/src/core/utils/address.ts +++ b/packages/canonical-bridge-widget/src/core/utils/address.ts @@ -1,5 +1,6 @@ +import { ChainType } from '@bnb-chain/canonical-bridge-sdk'; + import { truncateHash } from '@/core/utils/string'; -import { ChainType } from '@/modules/aggregator'; export function formatAppAddress(params: { address?: string; diff --git a/packages/canonical-bridge-widget/src/index.tsx b/packages/canonical-bridge-widget/src/index.tsx index 8406f0f8..1f2f8edc 100644 --- a/packages/canonical-bridge-widget/src/index.tsx +++ b/packages/canonical-bridge-widget/src/index.tsx @@ -8,3 +8,7 @@ export * from '@/core/utils/gtm'; // locales export * from '@/core/locales'; + +// TODO +// sdk +export * from '@bnb-chain/canonical-bridge-sdk'; diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/cBridge/hooks/useCBridgeSendMaxMin.ts b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/cBridge/hooks/useCBridgeSendMaxMin.ts index 23ea7029..0ec086fe 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/cBridge/hooks/useCBridgeSendMaxMin.ts +++ b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/cBridge/hooks/useCBridgeSendMaxMin.ts @@ -1,10 +1,10 @@ import { useEffect, useState } from 'react'; import { usePublicClient } from 'wagmi'; import { formatUnits } from 'viem'; +import { ICBridgeMaxMinSendAmt } from '@bnb-chain/canonical-bridge-sdk'; import { useAppSelector } from '@/modules/store/StoreProvider'; import { useCBridgeTransferParams } from '@/modules/aggregator/adapters/cBridge/hooks/useCBridgeTransferParams'; -import { ICBridgeMaxMinSendAmt } from '@/modules/aggregator/adapters/cBridge/types'; import { useBridgeSDK } from '@/core/hooks/useBridgeSDK'; export const useCBridgeSendMaxMin = (isDisabled = false) => { diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/cBridge/hooks/useCBridgeTransferParams.ts b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/cBridge/hooks/useCBridgeTransferParams.ts index 210b8389..4cddbe22 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/cBridge/hooks/useCBridgeTransferParams.ts +++ b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/cBridge/hooks/useCBridgeTransferParams.ts @@ -1,143 +1,36 @@ import { useMemo } from 'react'; -import { parseUnits } from 'viem'; import { useAccount } from 'wagmi'; import { useAppSelector } from '@/modules/store/StoreProvider'; import { useBridgeSDK } from '@/core/hooks/useBridgeSDK'; -import { isNativeToken } from '@/core/utils/address'; export const useCBridgeTransferParams = () => { - const { address } = useAccount(); const bridgeSDK = useBridgeSDK(); - const selectedToken = useAppSelector((state) => state.transfer.selectedToken); + const { address } = useAccount(); + const fromChain = useAppSelector((state) => state.transfer.fromChain); const toChain = useAppSelector((state) => state.transfer.toChain); + const selectedToken = useAppSelector((state) => state.transfer.selectedToken); const sendValue = useAppSelector((state) => state.transfer.sendValue); - const max_slippage = useAppSelector((state) => state.transfer.slippage); - const isPegged = useMemo(() => selectedToken?.isPegged || false, [selectedToken]); - const bridgeAddress = useMemo(() => { - try { - if ( - !fromChain || - (isPegged && !selectedToken?.cBridge?.peggedConfig) || - (!isPegged && !fromChain?.cBridge?.raw) || - !bridgeSDK?.cBridge - ) { - return null; - } - return bridgeSDK.cBridge.getTransferAddress({ - fromChainId: fromChain?.id as number, - isPegged, - peggedConfig: selectedToken?.cBridge?.peggedConfig, - chainConfig: fromChain?.cBridge?.raw, + const slippage = useAppSelector((state) => state.transfer.slippage); + + return useMemo(() => { + if (fromChain && toChain && selectedToken && address && bridgeSDK.cBridge) { + const { args, bridgeAddress } = bridgeSDK.cBridge.getTransactionParams({ + fromChain, + toChain, + fromToken: selectedToken, + userAddress: address, + sendValue, + slippage, }); - } catch (e: any) { - // eslint-disable-next-line no-console - console.log(e); - } - }, [selectedToken, fromChain, isPegged, bridgeSDK?.cBridge]); - - // Mint/deposit or burn/withdraw - const transferType = useMemo(() => { - if (selectedToken?.cBridge?.peggedConfig?.org_chain_id === fromChain?.id) { - return 'deposit'; + return { args, bridgeAddress }; } - if (selectedToken?.cBridge?.peggedConfig?.pegged_chain_id === fromChain?.id) { - return 'withdraw'; - } - return ''; - }, [selectedToken, fromChain?.id]); - - const argument = useMemo(() => { - if ( - !sendValue || - sendValue === '0' || - !toChain || - !selectedToken || - !address || - !bridgeSDK?.cBridge - ) { - return null; - } - const nonce = new Date().getTime(); - let amount = 0n; - try { - if (selectedToken.isPegged === false) { - amount = parseUnits( - String(sendValue), - selectedToken?.cBridge?.raw?.token.decimal as number, - ); // Convert to big number - } else if (transferType === 'deposit') { - amount = parseUnits( - String(sendValue), - selectedToken?.cBridge?.peggedConfig?.org_token.token.decimal as number, - ); - } else if (transferType === 'withdraw') { - amount = parseUnits( - String(sendValue), - selectedToken?.cBridge?.peggedConfig?.pegged_token.token.decimal as number, - ); - } - } catch (e: any) { - // eslint-disable-next-line no-console - console.log(e); - } - - return bridgeSDK.cBridge.getTransferParams({ - amount, - isPegged, - toChainId: toChain.id, - tokenAddress: selectedToken?.address as `0x${string}`, - address: address as `0x${string}`, - maxSlippage: max_slippage, - transferType: transferType ? transferType : undefined, - peggedConfig: selectedToken.cBridge?.peggedConfig, - isNativeToken: isNativeToken(selectedToken.address), - nonce, - }); - }, [ - sendValue, - toChain, - selectedToken, - address, - isPegged, - transferType, - max_slippage, - bridgeSDK?.cBridge, - ]); - - // Arguments for bridge smart contract - const args = useMemo(() => { - const peggedConfig = selectedToken?.cBridge?.peggedConfig; - if ( - !argument || - (isPegged && !transferType) || - !address || - !bridgeAddress || - !bridgeSDK?.cBridge || - !selectedToken - ) { - return null; - } - const abi = bridgeSDK.cBridge.getABI({ - isPegged, - transferType: transferType || undefined, - peggedConfig, - }); - const functionName = bridgeSDK.cBridge.getTransferFunction({ - isPegged, - isNativeToken: isNativeToken(selectedToken?.address), - transferType: transferType || undefined, - }); return { - address: bridgeAddress as `0x${string}`, - abi: abi, - functionName: functionName, - account: address as `0x${string}`, - args: argument, + args: null, + bridgeAddress: null, }; - }, [bridgeAddress, transferType, selectedToken, isPegged, address, argument, bridgeSDK?.cBridge]); - return { args, bridgeAddress }; + }, [address, bridgeSDK.cBridge, fromChain, selectedToken, sendValue, slippage, toChain]); }; diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/cBridge/types.ts b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/cBridge/types.ts deleted file mode 100644 index 1d75c6d0..00000000 --- a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/cBridge/types.ts +++ /dev/null @@ -1,121 +0,0 @@ -import { ICBridgeChain, ICBridgePeggedPairConfig } from '@bnb-chain/canonical-bridge-sdk'; -import { type PublicClient, type WalletClient } from 'viem'; - -export interface ICBridgeTransferInfo { - chain: any; - token: any; - amount: string; -} - -export interface ICBridgeTransferHistory { - transfer_id: string; - src_send_info: ICBridgeTransferInfo; - dst_received_info: ICBridgeTransferInfo; - ts: number; - src_block_tx_link: string; - dst_block_tx_link: string; - status: string; - refund_reason: string; -} - -export interface ICBridgeTransferHistoryResponse { - err: object; - history: ICBridgeTransferHistory[]; - next_page_token: string; - current_size: string; -} - -export interface ICBridgeTransferEstimatedTime { - err: object; - median_transfer_latency_in_second: number; -} - -export interface ICBridgeTransactionResponse { - data: null | { - gasFee: bigint; - gasPrice: bigint; - transferId: string; - send: () => Promise<`0x${string}`>; - }; - isLoading: boolean; - isError: boolean; - error: null | unknown; -} - -export interface ICBridgeEstimateAmountRequest { - src_chain_id: number; - dst_chain_id: number; - token_symbol: string; - amt: string; - user_addr?: string; - slippage_tolerance: number; - is_pegged?: boolean; -} - -export interface ICBridgeEstimateAmountResponse { - err: object; - eq_value_token_amt: string; - bridge_rate: number; - perc_fee: string; - base_fee: string; - slippage_tolerance: number; - max_slippage: number; - estimated_receive_amt: string; - drop_gas_amt: string; - op_fee_rebate: number; - op_fee_rebate_portion: number; - op_fee_rebate_end_time: string; -} - -export interface ICBridgeSendRangeInput { - bridgeAddress: `0x${string}`; - tokenAddress: `0x${string}`; - isPegged?: boolean; - client: PublicClient; -} - -export interface ISendCBridgeToken { - walletClient: WalletClient; - publicClient: PublicClient; - bridgeAddress: string; - fromChainId: number; - address: `0x${string}`; - peggedConfig?: ICBridgePeggedPairConfig; - isPegged: boolean; - args: any; -} - -export interface IGetCBridgeTransferAddressInput { - fromChainId: number; - isPegged: boolean; - peggedConfig?: ICBridgePeggedPairConfig; - chainConfig?: ICBridgeChain; -} - -export interface IGetCBridgeTransferParamsInput { - amount: bigint; - isPegged: boolean; - toChainId: number; - address: `0x${string}`; - tokenAddress: `0x${string}`; - maxSlippage: number; - transferType?: 'deposit' | 'withdraw'; - peggedConfig?: ICBridgePeggedPairConfig; - nonce: number; -} - -export interface IGetCBridgeABI { - isPegged: boolean; - transferType?: 'deposit' | 'withdraw'; - peggedConfig?: ICBridgePeggedPairConfig; -} - -export interface IGetCBridgeTransferFunction { - isPegged: boolean; - transferType?: 'deposit' | 'withdraw'; -} - -export interface ICBridgeMaxMinSendAmt { - max: string; - min: string; -} diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/deBridge/types.ts b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/deBridge/types.ts index dde03ecc..46d384e9 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/deBridge/types.ts +++ b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/deBridge/types.ts @@ -1,56 +1,3 @@ -// https://deswap.debridge.finance/v1.0/#/DLN -export type IQuoteResponse = { - estimation: { - srcChainTokenIn: { - address: `0x${string}`; - name: string; - symbol: string; - chainId: number; - decimals: number; - amount: string; - approximateOperatingExpense: string; - mutatedWithOperatingExpense: boolean; - }; - srcChainTokenOut: { - address: `0x${string}`; - name: string; - symbol: string; - decimals: number; - amount: string; - chainId: number; - maxRefundAmount: string; - }; - dstChainTokenOut: { - address: `0x${string}`; - name: string; - symbol: string; - decimals: number; - amount: string; - recommendedAmount: string; - withoutAdditionalTakerRewardsAmount: string; - maxTheoreticalAmount: string; - chainId: number; - }; - costsDetails: any[]; - recommendedSlippage: number; - }; - tx: { - allowanceTarget: `0x${string}`; - allowanceValue: string; - data: `0x${string}`; - to: `0x${string}`; // Bridge address - value: string; - }; - prependedOperatingExpenseCost: string; - order: { - approximateFulfillmentDelay: number; - }; - fixFee: string; - userPoints: number; - integratorPoints: number; - orderId: string; -}; - export interface TokenQueryParams { chainId: number; address: string; diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/layerZero/types.ts b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/layerZero/types.ts deleted file mode 100644 index b2216f1a..00000000 --- a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/layerZero/types.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { type PublicClient, type WalletClient } from 'viem'; - -export interface ISendCakeTokenInput { - userAddress: `0x${string}`; - bridgeAddress: `0x${string}`; - amount: bigint; - dstEndpoint: number; - gasAmount?: bigint; - version?: number; - publicClient: PublicClient; - walletClient: WalletClient; -} - -export interface IGetEstimateFeeInput { - userAddress: `0x${string}`; - bridgeAddress: `0x${string}`; - amount: bigint; - dstEndpoint: number; - gasAmount?: bigint; - version?: number; - publicClient: PublicClient; -} diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/stargate/hooks/useStargateTransfer.ts b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/stargate/hooks/useStargateTransfer.ts deleted file mode 100644 index eb783dd5..00000000 --- a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/stargate/hooks/useStargateTransfer.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { useAccount, usePublicClient, useWalletClient } from 'wagmi'; -import { useCallback } from 'react'; -import { parseUnits } from 'viem'; - -import { useAppDispatch, useAppSelector } from '@/modules/store/StoreProvider'; -import { setEstimatedAmount } from '@/modules/transfer/action'; -import { toObject } from '@/core/utils/string'; -import { useDebounce } from '@/core/hooks/useDebounce'; -import { DEBOUNCE_DELAY, DEFAULT_ADDRESS } from '@/core/constants'; -import { useToTokenInfo } from '@/modules/transfer/hooks/useToTokenInfo'; -import { useStargateTransferParams } from '@/modules/aggregator/adapters/stargate/hooks/useStargateTransferParams'; -import { useBridgeSDK } from '@/core/hooks/useBridgeSDK'; - -export const useStargateTransfer = () => { - const { data: walletClient } = useWalletClient(); - const { address } = useAccount(); - const dispatch = useAppDispatch(); - const bridgeSDK = useBridgeSDK(); - - const selectedToken = useAppSelector((state) => state.transfer.selectedToken); - const sendValue = useAppSelector((state) => state.transfer.sendValue); - const estimatedAmount = useAppSelector((state) => state.transfer.estimatedAmount); - const fromChain = useAppSelector((state) => state.transfer.fromChain); - const toChain = useAppSelector((state) => state.transfer.toChain); - const { toTokenInfo } = useToTokenInfo(); - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const publicClient = usePublicClient({ chainId: fromChain?.id }) as any; - const debouncedSendValue = useDebounce(sendValue, DEBOUNCE_DELAY); - const { args } = useStargateTransferParams(); - - const getQuoteOFT = useCallback(async () => { - if ( - !args || - !fromChain || - !toChain || - !selectedToken || - !debouncedSendValue || - !Number(debouncedSendValue) || - !toTokenInfo || - !publicClient || - !bridgeSDK - ) { - return; - } - try { - const bridgeAddress = selectedToken.stargate?.raw?.bridgeAddress as `0x${string}`; - - const quoteOFTResponse = await bridgeSDK.stargate?.getQuoteOFT({ - publicClient: publicClient, - bridgeAddress, - endPointId: args.dstEid, - receiver: address || DEFAULT_ADDRESS, - amount: parseUnits(debouncedSendValue, selectedToken.decimals), - }); - - dispatch(setEstimatedAmount({ stargate: toObject(quoteOFTResponse) })); - return { quoteOFT: quoteOFTResponse }; - // eslint-disable-next-line - } catch (error: any) { - // eslint-disable-next-line no-console - console.log(error, error.message); - dispatch(setEstimatedAmount({ stargate: undefined })); - } - }, [ - args, - fromChain, - toChain, - selectedToken, - debouncedSendValue, - toTokenInfo, - dispatch, - publicClient, - address, - bridgeSDK, - ]); - - const sendToken = useCallback( - async ({ onOpenFailedModal }: { onOpenFailedModal: () => void }) => { - if ( - !address || - !estimatedAmount?.stargate || - !args || - !selectedToken || - !publicClient || - !walletClient - ) - return; - try { - const bridgeAddress = selectedToken.stargate?.raw?.bridgeAddress as `0x${string}`; - const amountReceivedLD = estimatedAmount?.stargate[2].amountReceivedLD; - const sendParams = { ...args }; - if (amountReceivedLD) { - sendParams.minAmountLD = BigInt(amountReceivedLD); - } - const hash = await bridgeSDK.stargate?.sendToken({ - walletClient: walletClient as any, - publicClient: publicClient, - bridgeAddress, - tokenAddress: selectedToken.address as `0x${string}`, - endPointId: args.dstEid, - receiver: address, - amount: parseUnits(sendValue, selectedToken.decimals), - }); - const tx = await publicClient.waitForTransactionReceipt({ - hash: hash as `0x${string}`, - }); - // eslint-disable-next-line no-console - console.log('send token response', tx); - return hash; - } catch (e: any) { - // eslint-disable-next-line no-console - console.log(e, e.message); - onOpenFailedModal(); - } - }, - [ - address, - args, - estimatedAmount, - publicClient, - selectedToken, - walletClient, - sendValue, - bridgeSDK?.stargate, - ], - ); - - return { - getQuoteOFT, - sendToken, - }; -}; diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/stargate/hooks/useStargateTransferParams.ts b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/stargate/hooks/useStargateTransferParams.ts index 9d22653e..a5e33370 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/stargate/hooks/useStargateTransferParams.ts +++ b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/stargate/hooks/useStargateTransferParams.ts @@ -2,12 +2,12 @@ import { useAccount } from 'wagmi'; import { useMemo } from 'react'; import { parseUnits } from 'viem'; import { ethers } from 'ethers'; +import { IStargateParams } from '@bnb-chain/canonical-bridge-sdk'; import { useAppSelector } from '@/modules/store/StoreProvider'; import { useDebounce } from '@/core/hooks/useDebounce'; import { DEBOUNCE_DELAY, DEFAULT_ADDRESS } from '@/core/constants'; import { useToTokenInfo } from '@/modules/transfer/hooks/useToTokenInfo'; -import { IStargateParams } from '@/modules/aggregator/adapters/stargate/types'; export const useStargateTransferParams = (): { args: IStargateParams | null } => { const { address } = useAccount(); diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/stargate/types.ts b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/stargate/types.ts index de4e54d4..101322bf 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/stargate/types.ts +++ b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/stargate/types.ts @@ -1,13 +1,3 @@ -export interface IStargateParams { - dstEid: number; - to: `0x${string}`; - amountLD: bigint; - minAmountLD: bigint; - extraOptions: `0x${string}`; - composeMsg: `0x${string}`; - oftCmd: `0x${string}`; -} - export interface IStargateQuoteSend { nativeFee: bigint; lzTokenFee: bigint; diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/index.ts b/packages/canonical-bridge-widget/src/modules/aggregator/index.ts index 8541ef67..de689ef7 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/index.ts +++ b/packages/canonical-bridge-widget/src/modules/aggregator/index.ts @@ -1,5 +1,3 @@ export * from '@/modules/aggregator/types'; -export * from '@/modules/aggregator/adapters/cBridge/types'; export * from '@/modules/aggregator/adapters/deBridge/types'; -export * from '@/modules/aggregator/adapters/layerZero/types'; export * from '@/modules/aggregator/adapters/stargate/types'; diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/reducer.ts b/packages/canonical-bridge-widget/src/modules/aggregator/reducer.ts index b267d1ea..93160bf8 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/reducer.ts +++ b/packages/canonical-bridge-widget/src/modules/aggregator/reducer.ts @@ -1,6 +1,7 @@ +import { ICBridgeMaxMinSendAmt } from '@bnb-chain/canonical-bridge-sdk'; + import { createReducer } from '@/modules/store/createReducer'; import * as actions from '@/modules/aggregator/action'; -import { ICBridgeMaxMinSendAmt } from '@/modules/aggregator/adapters/cBridge/types'; export interface IAggregatorState { tokenPrices: { cmcPrices: Record; diff --git a/packages/canonical-bridge-widget/src/modules/transfer/components/Button/TransferButton.tsx b/packages/canonical-bridge-widget/src/modules/transfer/components/Button/TransferButton.tsx index a81a3a8d..f84fa603 100644 --- a/packages/canonical-bridge-widget/src/modules/transfer/components/Button/TransferButton.tsx +++ b/packages/canonical-bridge-widget/src/modules/transfer/components/Button/TransferButton.tsx @@ -1,11 +1,10 @@ import { Button, Flex, useColorMode, useIntl, useTheme } from '@bnb-chain/space'; import { useCallback, useState } from 'react'; import { useAccount, useBytecode, usePublicClient, useSignMessage, useWalletClient } from 'wagmi'; -import { formatUnits, parseUnits } from 'viem'; +import { formatUnits } from 'viem'; import { useTronWallet } from '@node-real/walletkit/tron'; import { useConnection } from '@solana/wallet-adapter-react'; import { useSolanaWallet } from '@node-real/walletkit/solana'; -import { VersionedTransaction } from '@solana/web3.js'; import { useAppSelector } from '@/modules/store/StoreProvider'; import { useGetAllowance } from '@/core/contract/hooks/useGetAllowance'; @@ -15,10 +14,9 @@ import { reportEvent } from '@/core/utils/gtm'; import { useGetTronAllowance } from '@/modules/aggregator/adapters/meson/hooks/useGetTronAllowance'; import { useCurrentWallet } from '@/modules/wallet/CurrentWalletProvider'; import { useTronTransferInfo } from '@/modules/transfer/hooks/tron/useTronTransferInfo'; -import { utf8ToHex } from '@/core/utils/string'; import { useTronContract } from '@/modules/aggregator/adapters/meson/hooks/useTronContract'; -import { isNativeToken } from '@/core/utils/address'; import { useSolanaTransferInfo } from '@/modules/transfer/hooks/solana/useSolanaTransferInfo'; +import { useSolanaAccount } from '@/modules/wallet/hooks/useSolanaAccount'; export function TransferButton({ onOpenSubmittedModal, @@ -52,13 +50,13 @@ export function TransferButton({ const { isSolanaTransfer, isSolanaAvailableToAccount } = useSolanaTransferInfo(); const { connection } = useConnection(); const { sendTransaction: sendSolanaTransaction } = useSolanaWallet(); + const { address: solanaAddress } = useSolanaAccount(); const sendValue = useAppSelector((state) => state.transfer.sendValue); const transferActionInfo = useAppSelector((state) => state.transfer.transferActionInfo); const selectedToken = useAppSelector((state) => state.transfer.selectedToken); const isGlobalFeeLoading = useAppSelector((state) => state.transfer.isGlobalFeeLoading); const isTransferable = useAppSelector((state) => state.transfer.isTransferable); - const toToken = useAppSelector((state) => state.transfer.toToken); const fromChain = useAppSelector((state) => state.transfer.fromChain); const toChain = useAppSelector((state) => state.transfer.toChain); const toAccount = useAppSelector((state) => state.transfer.toAccount); @@ -167,104 +165,85 @@ export function TransferButton({ }); if (transferActionInfo.bridgeType === 'cBridge' && cBridgeArgs && fromChain && address) { - try { - const cBridgeHash = await bridgeSDK.cBridge?.sendToken({ - // eslint-disable-next-line @typescript-eslint/no-explicit-any - walletClient: walletClient as any, - publicClient, - bridgeAddress: transferActionInfo.bridgeAddress as string, - fromChainId: fromChain?.id, - isPegged: selectedToken.isPegged, - isNativeToken: isNativeToken(selectedToken.address), - address, - peggedConfig: selectedToken?.cBridge?.peggedConfig, - args: cBridgeArgs.args, - }); - await publicClient.waitForTransactionReceipt({ - hash: cBridgeHash, + const { hash: cBridgeHash } = await bridgeSDK.sendToken({ + bridgeType: 'cBridge', + publicClient, + walletClient: walletClient as any, + fromChainId: fromChain.id, + toChainId: toChain!.id, + tokenAddress: selectedToken.address, + userAddress: address, + sendValue, + }); + if (cBridgeHash) { + reportEvent({ + id: 'transaction_bridge_success', + params: { + item_category: fromChain?.name, + item_category2: toChain?.name, + token: selectedToken.displaySymbol, + value: sendValue, + item_variant: 'cBridge', + }, }); - if (cBridgeHash) { - reportEvent({ - id: 'transaction_bridge_success', - params: { - item_category: fromChain?.name, - item_category2: toChain?.name, - token: selectedToken.displaySymbol, - value: sendValue, - item_variant: 'cBridge', - }, - }); - onCloseConfirmingModal(); - setHash(cBridgeHash); - setChosenBridge('cBridge'); - onOpenSubmittedModal(); - } - // eslint-disable-next-line no-console - console.log('cBridge tx', cBridgeHash); - } catch (e) { - // eslint-disable-next-line no-console - console.log(e); - handleFailure(e); + onCloseConfirmingModal(); + setHash(cBridgeHash); + setChosenBridge('cBridge'); + onOpenSubmittedModal(); } + // eslint-disable-next-line no-console + console.log('cBridge tx', cBridgeHash); } else if (transferActionInfo.bridgeType === 'deBridge') { - try { - let deBridgeHash: string | undefined; - - if (fromChain?.chainType === 'evm' && transferActionInfo.value && address) { - deBridgeHash = await bridgeSDK.deBridge?.sendToken({ - // eslint-disable-next-line @typescript-eslint/no-explicit-any - walletClient: walletClient as any, - bridgeAddress: transferActionInfo.bridgeAddress as string, - data: transferActionInfo.data as `0x${string}`, - amount: BigInt(transferActionInfo.value), - address, - }); - await publicClient.waitForTransactionReceipt({ - hash: deBridgeHash, - }); - } + const userAddress = (fromChain?.chainType === 'solana' ? solanaAddress : address) as string; + const toUserAddress = + fromChain?.chainType === 'solana' || toChain?.chainType === 'solana' + ? toAccount?.address + : undefined; - if (fromChain?.chainType === 'solana') { - const { blockhash } = await connection.getLatestBlockhash(); - const data = (transferActionInfo.data as string)?.slice(2); - const tx = VersionedTransaction.deserialize(Buffer.from(data, 'hex')); - - tx.message.recentBlockhash = blockhash; - deBridgeHash = await sendSolanaTransaction(tx, connection); - } + const { hash: deBridgeHash } = await bridgeSDK.sendToken({ + bridgeType: 'deBridge', + publicClient, + walletClient: walletClient as any, + fromChainId: fromChain!.id, + toChainId: toChain!.id, + tokenAddress: selectedToken.address, + userAddress, + toUserAddress, + sendValue, + solanaOpts: { + connection, + sendTransaction: sendSolanaTransaction, + }, + }); - if (deBridgeHash) { - reportEvent({ - id: 'transaction_bridge_success', - params: { - item_category: fromChain?.name, - item_category2: toChain?.name, - token: selectedToken.displaySymbol, - value: sendValue, - item_variant: 'deBridge', - }, - }); - onCloseConfirmingModal(); - setChosenBridge('deBridge'); - setHash(deBridgeHash); - onOpenSubmittedModal(); - } - } catch (e) { - // eslint-disable-next-line no-console - console.log(e); - handleFailure(e); + if (deBridgeHash) { + reportEvent({ + id: 'transaction_bridge_success', + params: { + item_category: fromChain?.name, + item_category2: toChain?.name, + token: selectedToken.displaySymbol, + value: sendValue, + item_variant: 'deBridge', + }, + }); + onCloseConfirmingModal(); + setChosenBridge('deBridge'); + setHash(deBridgeHash); + onOpenSubmittedModal(); } } else if (transferActionInfo.bridgeType === 'stargate' && address) { - const stargateHash = await bridgeSDK.stargate?.sendToken({ - // eslint-disable-next-line @typescript-eslint/no-explicit-any - walletClient: walletClient as any, + const { hash: stargateHash } = await bridgeSDK.sendToken({ + bridgeType: 'stargate', publicClient, - bridgeAddress: transferActionInfo.bridgeAddress as `0x${string}`, - tokenAddress: selectedToken.address as `0x${string}`, - endPointId: toToken?.stargate?.raw?.endpointID as number, - receiver: address, - amount: parseUnits(sendValue, selectedToken.decimals), + walletClient: walletClient as any, + fromChainId: fromChain!.id, + toChainId: toChain!.id, + tokenAddress: selectedToken.address, + userAddress: address as string, + sendValue, }); + if (stargateHash) { reportEvent({ id: 'transaction_bridge_success', @@ -282,14 +261,15 @@ export function TransferButton({ onOpenSubmittedModal(); } } else if (transferActionInfo.bridgeType === 'layerZero' && address) { - const layerZeroHash = await bridgeSDK.layerZero?.sendToken({ - bridgeAddress: transferActionInfo.bridgeAddress as `0x${string}`, - dstEndpoint: toToken?.layerZero?.raw?.endpointID as number, - userAddress: address, - amount: parseUnits(sendValue, selectedToken.decimals), - // eslint-disable-next-line @typescript-eslint/no-explicit-any - walletClient: walletClient as any, + const { hash: layerZeroHash } = await bridgeSDK.sendToken({ + bridgeType: 'layerZero', publicClient, + walletClient: walletClient as any, + fromChainId: fromChain!.id, + toChainId: toChain!.id, + tokenAddress: selectedToken.address, + userAddress: address as string, + sendValue, }); if (layerZeroHash) { reportEvent({ @@ -308,90 +288,68 @@ export function TransferButton({ onOpenSubmittedModal(); } } else if (transferActionInfo.bridgeType === 'meson') { - let fromAddress = ''; - let toAddress = ''; - let msg = ''; - let signature = ''; + let userAddress = ''; + let toUserAddress = ''; if (fromChain?.chainType === 'tron' && tronAddress) { - fromAddress = tronAddress; + userAddress = tronAddress; } else if (fromChain?.chainType !== 'tron' && address) { - fromAddress = address; + userAddress = address; } if (isTronTransfer && isTronAvailableToAccount && toAccount?.address) { - toAddress = toAccount.address; + toUserAddress = toAccount.address; } else if (address) { - toAddress = address; + toUserAddress = address; } - // get unsigned message - const unsignedMessage = await bridgeSDK.meson?.getUnsignedMessage({ - fromToken: `${fromChain?.meson?.raw?.id}:${selectedToken?.meson?.raw?.id}`, - toToken: `${toChain?.meson?.raw?.id}:${toToken?.meson?.raw?.id}`, - amount: sendValue, - fromAddress: fromAddress, - recipient: toAddress, + const { swapId } = await bridgeSDK.sendToken({ + bridgeType: 'meson', + publicClient, + walletClient: walletClient as any, + fromChainId: fromChain!.id, + toChainId: toChain!.id, + tokenAddress: selectedToken.address, + userAddress, + toUserAddress, + sendValue, + mesonOpts: { + signTransaction: async (message) => { + return String(await signTransaction(message as any)); + }, + signMessage: async (message) => { + return await signMessageAsync({ + account: userAddress as `0x${string}`, + message: { + raw: message as `0x${string}`, + }, + }); + }, + }, }); + // eslint-disable-next-line no-console + console.log(swapId); + if (swapId?.result?.swapId) { + setChosenBridge('meson'); + setHash(swapId?.result?.swapId); + } + if (swapId?.error) { + throw new Error(swapId?.error.message); + } - if (unsignedMessage?.result) { - const result = unsignedMessage.result; - const encodedData = result.encoded; - const message = result.signingRequest.message; - - if (fromChain?.chainType === 'tron') { - const hexTronHeader = utf8ToHex('\x19TRON Signed Message:\n32'); - msg = message.replace(hexTronHeader, ''); - } else { - const hexEthHeader = utf8ToHex('\x19Ethereum Signed Message:\n52'); - msg = message.replace(hexEthHeader, ''); - } - - if (fromChain?.chainType != 'tron') { - signature = await signMessageAsync({ - account: address, - message: { - raw: msg as `0x${string}`, - }, - }); - } else { - // TODO - signature = String(await signTransaction(msg as any)); - } - - const swapId = await bridgeSDK.meson?.sendToken({ - fromAddress: fromAddress, - recipient: toAddress, - signature: signature, - encodedData: encodedData, - }); - - // eslint-disable-next-line no-console - console.log(swapId); - if (swapId?.result?.swapId) { - setChosenBridge('meson'); - setHash(swapId?.result?.swapId); - } - if (swapId?.error) { - throw new Error(swapId?.error.message); - } - - reportEvent({ - id: 'transaction_bridge_success', - params: { - item_category: fromChain?.name, - item_category2: toChain?.name, - token: selectedToken.displaySymbol, - value: sendValue, - item_variant: 'meson', - }, - }); + reportEvent({ + id: 'transaction_bridge_success', + params: { + item_category: fromChain?.name, + item_category2: toChain?.name, + token: selectedToken.displaySymbol, + value: sendValue, + item_variant: 'meson', + }, + }); - onCloseConfirmingModal(); - onOpenSubmittedModal(); - } else { - throw new Error(unsignedMessage?.error.message); - } + onCloseConfirmingModal(); + onOpenSubmittedModal(); } } catch (e: any) { // eslint-disable-next-line no-console @@ -403,18 +361,18 @@ export function TransferButton({ } }, [ selectedToken, - transferActionInfo, + transferActionInfo?.bridgeType, + transferActionInfo?.bridgeAddress, + fromChain, walletClient, publicClient, address, allowance, isEvmConnected, - fromChain, isTronConnected, tronAddress, tronAllowance, - toChain?.name, - toChain?.meson?.raw?.id, + toChain, sendValue, onOpenFailedModal, setHash, @@ -423,23 +381,17 @@ export function TransferButton({ onOpenConfirmingModal, cBridgeArgs, onOpenApproveModal, - bridgeSDK.cBridge, - bridgeSDK.deBridge, - bridgeSDK.stargate, - bridgeSDK.layerZero, - bridgeSDK.meson, + bridgeSDK, onCloseConfirmingModal, onOpenSubmittedModal, + solanaAddress, + toAccount.address, connection, sendSolanaTransaction, - toToken?.stargate?.raw?.endpointID, - toToken?.layerZero?.raw?.endpointID, - toToken?.meson?.raw?.id, isTronTransfer, isTronAvailableToAccount, - toAccount.address, - signMessageAsync, signTransaction, + signMessageAsync, ]); return ( diff --git a/packages/canonical-bridge-widget/src/modules/transfer/components/TransferOverview/RouteInfo/TokenInfoTooltip.tsx b/packages/canonical-bridge-widget/src/modules/transfer/components/TransferOverview/RouteInfo/TokenInfoTooltip.tsx index fc1e6183..e9ed57b7 100644 --- a/packages/canonical-bridge-widget/src/modules/transfer/components/TransferOverview/RouteInfo/TokenInfoTooltip.tsx +++ b/packages/canonical-bridge-widget/src/modules/transfer/components/TransferOverview/RouteInfo/TokenInfoTooltip.tsx @@ -12,9 +12,9 @@ import { Portal, } from '@bnb-chain/space'; import { useMemo } from 'react'; +import { ChainType } from '@bnb-chain/canonical-bridge-sdk'; import { isNativeToken } from '@/core/utils/address.ts'; -import { ChainType } from '@/modules/aggregator'; interface TokenTooltipProps { tokenLinkUrl: string; diff --git a/packages/canonical-bridge-widget/src/modules/transfer/hooks/useLoadingBridgeFees.ts b/packages/canonical-bridge-widget/src/modules/transfer/hooks/useLoadingBridgeFees.ts index c25fd0d4..1c61e843 100644 --- a/packages/canonical-bridge-widget/src/modules/transfer/hooks/useLoadingBridgeFees.ts +++ b/packages/canonical-bridge-widget/src/modules/transfer/hooks/useLoadingBridgeFees.ts @@ -113,7 +113,6 @@ export const useLoadingBridgeFees = () => { const valueArr = []; try { - const amount = parseUnits(debouncedSendValue, selectedToken.decimals); const now = Date.now(); lastTime = now; @@ -122,7 +121,7 @@ export const useLoadingBridgeFees = () => { fromChainId: fromChain.id, toChainId: toChain?.id, tokenAddress: selectedToken.address, - amount, + sendValue: debouncedSendValue, slippage: max_slippage, userAddress: fromChain?.chainType === 'solana'