diff --git a/.env b/.env index aebb6d081b2..ca0648e3b6e 100644 --- a/.env +++ b/.env @@ -176,6 +176,7 @@ VITE_UNICHAIN_NODE_URL=https://mainnet.unichain.org VITE_BOB_NODE_URL=https://rpc.gobob.xyz VITE_MODE_NODE_URL=https://mainnet.mode.network VITE_SONEIUM_NODE_URL=https://rpc.soneium.org/ +VITE_SEI_NODE_URL=https://evm-rpc.sei-apis.com VITE_THORCHAIN_NODE_URL=https://api.thorchain.shapeshift.com/lcd VITE_MAYACHAIN_NODE_URL=https://api.mayachain.shapeshift.com/lcd VITE_SOLANA_NODE_URL=https://api.solana.shapeshift.com/api/v1/jsonrpc @@ -342,6 +343,7 @@ VITE_FEATURE_UNICHAIN=false VITE_FEATURE_BOB=false VITE_FEATURE_MODE=false VITE_FEATURE_SONEIUM=false +VITE_FEATURE_SEI=false # Yield.xyz Feature Flag VITE_FEATURE_YIELD_XYZ=true @@ -358,6 +360,10 @@ VITE_FEATURE_ACROSS_SWAP=false VITE_ACROSS_API_URL=https://app.across.to/api VITE_ACROSS_INTEGRATOR_ID= +# deBridge DLN +VITE_FEATURE_DEBRIDGE_SWAP=false +VITE_DEBRIDGE_API_URL=https://dln.debridge.finance/v1.0 + # Userback feedback widget VITE_FEATURE_USERBACK=true VITE_USERBACK_TOKEN=A-3gHopRTd55QqxXGsJd0XLVVG3 diff --git a/.env.development b/.env.development index 421e6b5c78d..456a61a89b6 100644 --- a/.env.development +++ b/.env.development @@ -82,6 +82,7 @@ VITE_LINEA_NODE_URL=https://rpc.linea.build VITE_BERACHAIN_NODE_URL=https://rpc.berachain.com VITE_SCROLL_NODE_URL=https://rpc.scroll.io/ VITE_SONEIUM_NODE_URL=https://rpc.soneium.org/ +VITE_SEI_NODE_URL=https://evm-rpc.sei-apis.com # Swap me back to 9R as-needed # VITE_THORCHAIN_NODE_URL=https://thornode.ninerealms.com VITE_THORCHAIN_NODE_URL=https://dev-api.thorchain.shapeshift.com/lcd @@ -137,6 +138,8 @@ VITE_FEATURE_SONEIUM=true VITE_FEATURE_TON=true VITE_FEATURE_STONFI_SWAP=true VITE_FEATURE_ACROSS_SWAP=true +VITE_FEATURE_SEI=true +VITE_FEATURE_DEBRIDGE_SWAP=true VITE_FEATURE_YIELDS_PAGE=true VITE_FEATURE_EARN_TAB=true VITE_FEATURE_YIELD_MULTI_ACCOUNT=true diff --git a/headers/csps/chains/sei.ts b/headers/csps/chains/sei.ts new file mode 100644 index 00000000000..8ef58ca746b --- /dev/null +++ b/headers/csps/chains/sei.ts @@ -0,0 +1,10 @@ +import { loadEnv } from 'vite' + +import type { Csp } from '../../types' + +const mode = process.env.MODE ?? process.env.NODE_ENV ?? 'development' +const env = loadEnv(mode, process.cwd(), '') + +export const csp: Csp = { + 'connect-src': [env.VITE_SEI_NODE_URL], +} diff --git a/headers/csps/debridge.ts b/headers/csps/debridge.ts new file mode 100644 index 00000000000..cfdd619ec6a --- /dev/null +++ b/headers/csps/debridge.ts @@ -0,0 +1,13 @@ +import { loadEnv } from 'vite' + +import type { Csp } from '../types' + +const mode = process.env.MODE ?? process.env.NODE_ENV ?? 'development' +const env = loadEnv(mode, process.cwd(), '') + +export const csp: Csp = { + 'connect-src': [ + env.VITE_DEBRIDGE_API_URL ? `${env.VITE_DEBRIDGE_API_URL}/` : 'https://dln.debridge.finance/', + 'https://stats-api.dln.trade/', + ], +} diff --git a/headers/csps/index.ts b/headers/csps/index.ts index 6fb0351f9d3..008f4d5ee6a 100644 --- a/headers/csps/index.ts +++ b/headers/csps/index.ts @@ -37,6 +37,7 @@ import { csp as plasma } from './chains/plasma' import { csp as plume } from './chains/plume' import { csp as polygon } from './chains/polygon' import { csp as scroll } from './chains/scroll' +import { csp as sei } from './chains/sei' import { csp as solana } from './chains/solana' import { csp as soneium } from './chains/soneium' import { csp as sonic } from './chains/sonic' @@ -52,6 +53,7 @@ import { csp as zcash } from './chains/zcash' import { csp as zksyncera } from './chains/zksyncera' import { csp as chatwoot } from './chatwoot' import { csp as customTokenImport } from './customTokenImport' +import { csp as debridge } from './debridge' import { csp as foxy } from './defi/foxy' import { csp as idle } from './defi/idle' import { csp as mtpelerin } from './defi/mtpelerin' @@ -169,6 +171,7 @@ export const csps = [ story, worldchain, sonic, + sei, trustwallet, coincap, coingecko, @@ -199,6 +202,7 @@ export const csps = [ walletConnect, walletMigration, relay, + debridge, railway, discord, yieldxyz, diff --git a/packages/caip/src/adapters/coingecko/generated/eip155_1329/adapter.json b/packages/caip/src/adapters/coingecko/generated/eip155_1329/adapter.json new file mode 100644 index 00000000000..bd79d950c34 --- /dev/null +++ b/packages/caip/src/adapters/coingecko/generated/eip155_1329/adapter.json @@ -0,0 +1 @@ +{"eip155:1329/slip44:60":"sei-network","eip155:1329/erc20:0xa193d87b0d5e23ef45379f90066891cbd6013226":"archer-hunter","eip155:1329/erc20:0x93919784c523f39cacaa98ee0a9d96c3f32b593e":"bedrock-btc","eip155:1329/erc20:0x443ac9f358226f5f48f2cd10bc0121e7a6176323":"bitbat-token","eip155:1329/erc20:0x71052bae71c25c78e37fd12e5ff1101a71d9018f":"chainlink","eip155:1329/erc20:0xbd82f3bfe1df0c84faec88a22ebc34c9a86595dc":"chips-2","eip155:1329/erc20:0x0a526e425809aea71eb279d24ae22dee6c92a4fe":"dragonswap","eip155:1329/erc20:0x60c230c38af6d86b0277a98a1caeaa345a7b061f":"fiamma-btc","eip155:1329/erc20:0x805679729df385815c57c24b20f4161bd34b655f":"fishwar","eip155:1329/erc20:0xff7f8f301f7a706e3cfd3d2275f5dc0b9ee8009b":"folks","eip155:1329/erc20:0x80eede496655fb9047dd39d9f418d5483ed600df":"frax-usd","eip155:1329/erc20:0xf9bdbf259ece5ae17e29bf92eb7abd7b8b465db9":"froggy-2","eip155:1329/erc20:0x58e11d8ed38a2061361e90916540c5c32281a380":"grand-gangsta-city","eip155:1329/erc20:0x3894085ef7ff0f0aedf52e2a2704928d1ec074f1":"ibc-bridged-usdc","eip155:1329/erc20:0xb75d0b03c06a926e488e2659df1a861f860bd3d1":"ibc-bridged-usdt","eip155:1329/erc20:0xcc1b8207853662c5cfabfb028806ec06ea1f6ac6":"kindred-labs","eip155:1329/erc20:0x89aec21572f6637ccbd0d66861aaac46dd775ed1":"kryptonite","eip155:1329/erc20:0x9bfa177621119e64cecbeabe184ab9993e2ef727":"merlin-s-seal-btc","eip155:1329/erc20:0x4a1ea452c598363188bccc83fb4f69dc14b54acd":"metaarena","eip155:1329/erc20:0x95597eb8d227a7c4b4f5e807a815c5178ee6dbe1":"milli-coin","eip155:1329/erc20:0x54cd901491aef397084453f4372b93c33260e2a6":"ondo-us-dollar-yield","eip155:1329/erc20:0x8ee050f6af49a6b7fd8557d0e75219d66f5f6094":"oxium","eip155:1329/erc20:0x481fe356df88169f5f38203dd7f3c67b7559fda5":"raiinmaker","eip155:1329/erc20:0x09d4214c03d01f49544c0448dbe3a27f768f2b34":"reservoir-rusd","eip155:1329/erc20:0x9faaea2cdd810b21594e54309dc847842ae301ce":"seiyaneth","eip155:1329/erc20:0x541fd749419ca806a8bc7da8ac23d346f2df8b77":"solv-btc","eip155:1329/erc20:0xcc0966d8418d412c599a6421b760a847eb169a8c":"solv-protocol-solvbtc-bbn","eip155:1329/erc20:0xc257361320f4514d91c05f461006ce6a0300e2d2":"splashing-staked-sei","eip155:1329/erc20:0x5bff88ca1442c2496f7e475e9e7786383bc070c0":"staked-frax-usd","eip155:1329/erc20:0x80137510979822322193fc997d400d5a6c747bf7":"stakestone-ether","eip155:1329/erc20:0x160345fc359604fc6e70e3c5facbde5f7a9342d8":"stargate-bridged-weth","eip155:1329/erc20:0xa8a3a5013104e093245164ea56588dbe10a3eb48":"super-seyian-eth","eip155:1329/erc20:0x059a6b0ba116c63191182a0956cf697d0d2213ec":"syusd","eip155:1329/erc20:0xedf7732991b3de8f46fb97dcd4c5cdb28e6ad859":"tea-rex","eip155:1329/erc20:0x78e26e8b953c7c78a58d69d8b9a91745c2bbb258":"ubtc","eip155:1329/erc20:0xe15fc38f6d8c56af07bbcbe3baf5708a2bf42392":"usd-coin","eip155:1329/erc20:0x9151434b16b9763660705744891fa906f660ecc5":"usdt0","eip155:1329/erc20:0x5b8034f6346a81a1387ea21cdd36c48f6e05eb5f":"vertex-protocol","eip155:1329/erc20:0xc53ac24320e3a54c7211e4993c8095078a0cb3cf":"wild-goat-coin-2","eip155:1329/erc20:0x0555e30da8f98308edb960aa94c0db47230d2b9c":"wrapped-bitcoin","eip155:1329/erc20:0x64445f0aecc51e94ad52d8ac56b7190e764e561a":"wrapped-frax","eip155:1329/erc20:0x4809010926aec940b550d34a46a52739f996d75d":"wrapped-savings-rusd","eip155:1329/erc20:0xe30fedd158a2e3b13e9badaeabafc5516e95e8c7":"wrapped-sei","eip155:1329/erc20:0x51121bcae92e302f19d06c193c95e1f7b81a444b":"yaka","eip155:1329/erc20:0x81d3a238b02827f62b9f390f947d36d4a5bf89d2":"yei-finance","eip155:1329/erc20:0x83c82f0f959ad3eff528ee513b43808aa53f4b37":"zenrock"} \ No newline at end of file diff --git a/packages/caip/src/adapters/coingecko/generated/index.ts b/packages/caip/src/adapters/coingecko/generated/index.ts index 142b6fbf7fb..be8fc0b3988 100644 --- a/packages/caip/src/adapters/coingecko/generated/index.ts +++ b/packages/caip/src/adapters/coingecko/generated/index.ts @@ -34,6 +34,7 @@ import unichain from "./eip155_130/adapter.json"; import soneium from "./eip155_1868/adapter.json"; import flowevm from "./eip155_747/adapter.json"; import celo from "./eip155_42220/adapter.json"; +import sei from "./eip155_1329/adapter.json"; import cosmos from "./cosmos_cosmoshub-4/adapter.json"; import thorchain from "./cosmos_thorchain-1/adapter.json"; import mayachain from "./cosmos_mayachain-mainnet-v1/adapter.json"; @@ -82,6 +83,7 @@ export { soneium, flowevm, celo, + sei, cosmos, thorchain, mayachain, diff --git a/packages/caip/src/adapters/coingecko/index.test.ts b/packages/caip/src/adapters/coingecko/index.test.ts index 95b3781ea85..e4a93a2c10e 100644 --- a/packages/caip/src/adapters/coingecko/index.test.ts +++ b/packages/caip/src/adapters/coingecko/index.test.ts @@ -297,6 +297,12 @@ describe('adapters:coingecko', () => { assetNamespace: 'token', assetReference: '0x33068f6539f8e6e6b131e6b2b814e6c34a5224bc66947c47dab9dfee93b35fb', }) + const usdcOnSei = toAssetId({ + chainNamespace: CHAIN_NAMESPACE.Evm, + chainReference: CHAIN_REFERENCE.SeiMainnet, + assetNamespace: 'erc20', + assetReference: '0xe15fc38f6d8c56af07bbcbe3baf5708a2bf42392', + }) const usdcOnInk = toAssetId({ chainNamespace: CHAIN_NAMESPACE.Evm, chainReference: CHAIN_REFERENCE.InkMainnet, @@ -336,6 +342,7 @@ describe('adapters:coingecko', () => { usdcOnInk, usdcOnSonic, usdcOnUnichain, + usdcOnSei, usdcOnSolana, usdcOnStarknet, usdcOnSui, diff --git a/packages/caip/src/adapters/coingecko/index.ts b/packages/caip/src/adapters/coingecko/index.ts index 2ce4c1b3e73..b8ee9ff4617 100644 --- a/packages/caip/src/adapters/coingecko/index.ts +++ b/packages/caip/src/adapters/coingecko/index.ts @@ -36,6 +36,7 @@ import { plumeChainId, polygonChainId, scrollChainId, + seiChainId, solanaChainId, soneiumChainId, sonicChainId, @@ -90,6 +91,7 @@ export enum CoingeckoAssetPlatform { Scroll = 'scroll', Cronos = 'cronos', Soneium = 'soneium', + Sei = 'sei-v2', Solana = 'solana', Starknet = 'starknet', Tron = 'tron', @@ -190,6 +192,8 @@ export const chainIdToCoingeckoAssetPlatform = (chainId: ChainId): string => { return CoingeckoAssetPlatform.Cronos case CHAIN_REFERENCE.SoneiumMainnet: return CoingeckoAssetPlatform.Soneium + case CHAIN_REFERENCE.SeiMainnet: + return CoingeckoAssetPlatform.Sei default: throw new Error( `chainNamespace ${chainNamespace}, chainReference ${chainReference} not supported.`, @@ -337,6 +341,8 @@ export const coingeckoAssetPlatformToChainId = ( return cronosChainId case CoingeckoAssetPlatform.Soneium: return soneiumChainId + case CoingeckoAssetPlatform.Sei: + return seiChainId case CoingeckoAssetPlatform.Cosmos: return cosmosChainId case CoingeckoAssetPlatform.Thorchain: diff --git a/packages/caip/src/constants.ts b/packages/caip/src/constants.ts index 65151228395..e3af52eea29 100644 --- a/packages/caip/src/constants.ts +++ b/packages/caip/src/constants.ts @@ -39,6 +39,7 @@ export const unichainAssetId: AssetId = 'eip155:130/slip44:60' export const bobAssetId: AssetId = 'eip155:60808/slip44:60' export const modeAssetId: AssetId = 'eip155:34443/slip44:60' export const soneiumAssetId: AssetId = 'eip155:1868/slip44:60' +export const seiAssetId: AssetId = 'eip155:1329/slip44:60' export const solAssetId: AssetId = 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501' export const wrappedSolAssetId: AssetId = 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/token:So11111111111111111111111111111111111111112' @@ -119,6 +120,7 @@ export const unichainChainId: ChainId = 'eip155:130' export const bobChainId: ChainId = 'eip155:60808' export const modeChainId: ChainId = 'eip155:34443' export const soneiumChainId: ChainId = 'eip155:1868' +export const seiChainId: ChainId = 'eip155:1329' export const cosmosChainId: ChainId = 'cosmos:cosmoshub-4' export const thorchainChainId: ChainId = 'cosmos:thorchain-1' @@ -190,6 +192,7 @@ export const CHAIN_REFERENCE = { BobMainnet: '60808', // https://docs.gobob.xyz ModeMainnet: '34443', // https://docs.mode.network SoneiumMainnet: '1868', // https://soneium.org + SeiMainnet: '1329', SolanaMainnet: '5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp', // https://namespaces.chainagnostic.org/solana/caip2 TronMainnet: '0x2b6653dc', // https://developers.tron.network/docs/networks SuiMainnet: '35834a8a', // First 8 chars of SUI mainnet genesis hash @@ -254,6 +257,7 @@ export const ASSET_REFERENCE = { Bob: '60', // evm chain which uses ethereum derivation path as common practice Mode: '60', // evm chain which uses ethereum derivation path as common practice Soneium: '60', // evm chain which uses ethereum derivation path as common practice + Sei: '60', // evm chain which uses ethereum derivation path as common practice Solana: '501', Tron: '195', Sui: '784', @@ -302,6 +306,8 @@ export const VALID_CHAIN_IDS: ValidChainMap = Object.freeze({ CHAIN_REFERENCE.BobMainnet, CHAIN_REFERENCE.ModeMainnet, CHAIN_REFERENCE.SoneiumMainnet, + CHAIN_REFERENCE.SeiMainnet, + CHAIN_REFERENCE.CeloMainnet, ], [CHAIN_NAMESPACE.CosmosSdk]: [ CHAIN_REFERENCE.CosmosHubMainnet, @@ -372,6 +378,7 @@ export const FEE_ASSET_IDS = [ sonicAssetId, unichainAssetId, soneiumAssetId, + seiAssetId, solAssetId, tronAssetId, suiAssetId, diff --git a/packages/chain-adapters/src/evm/EvmBaseAdapter.ts b/packages/chain-adapters/src/evm/EvmBaseAdapter.ts index 203edad90a3..b67c554ba0a 100644 --- a/packages/chain-adapters/src/evm/EvmBaseAdapter.ts +++ b/packages/chain-adapters/src/evm/EvmBaseAdapter.ts @@ -34,6 +34,7 @@ import { supportsPlume, supportsPolygon, supportsScroll, + supportsSei, supportsSoneium, supportsSonic, supportsStory, @@ -126,6 +127,7 @@ export const evmChainIds = [ KnownChainIds.BobMainnet, KnownChainIds.ModeMainnet, KnownChainIds.SoneiumMainnet, + KnownChainIds.SeiMainnet, ] as const export type EvmChainAdapter = EvmBaseAdapter @@ -268,6 +270,8 @@ export abstract class EvmBaseAdapter implements IChainAdap return supportsMode(wallet) case Number(fromChainId(KnownChainIds.SoneiumMainnet).chainReference): return supportsSoneium(wallet) + case Number(fromChainId(KnownChainIds.SeiMainnet).chainReference): + return supportsSei(wallet) default: return false } @@ -460,6 +464,11 @@ export abstract class EvmBaseAdapter implements IChainAdap symbol: 'ETH', explorer: 'https://soneium.blockscout.com', }, + [KnownChainIds.SeiMainnet]: { + name: 'SEI', + symbol: 'SEI', + explorer: 'https://seitrace.com', + }, }[this.chainId] try { diff --git a/packages/chain-adapters/src/evm/SecondClassEvmAdapter.ts b/packages/chain-adapters/src/evm/SecondClassEvmAdapter.ts index aaa8ecaff3c..a8a211d2509 100644 --- a/packages/chain-adapters/src/evm/SecondClassEvmAdapter.ts +++ b/packages/chain-adapters/src/evm/SecondClassEvmAdapter.ts @@ -11,6 +11,7 @@ import { hyperEvmChainId, mantleChainId, modeChainId, + seiChainId, soneiumChainId, sonicChainId, storyChainId, @@ -70,6 +71,7 @@ const WRAPPED_NATIVE_CONTRACT_BY_CHAIN_ID: Partial> = { [blastChainId]: '0x4300000000000000000000000000000000000004', [zkSyncEraChainId]: '0x5AEa5775959fBC2557Cc8789bC1bf90A239D9a91', [storyChainId]: '0x1514000000000000000000000000000000000000', + [seiChainId]: '0xE30feDd158A2e3b13e9badaeABaFc5516e95e8C7', } const BATCH_SIZE = 500 @@ -414,7 +416,8 @@ export abstract class SecondClassEvmAdapter extends EvmBas if ( this.chainId === hyperEvmChainId || this.chainId === blastChainId || - this.chainId === zkSyncEraChainId + this.chainId === zkSyncEraChainId || + this.chainId === seiChainId ) { return [] } @@ -472,6 +475,17 @@ export abstract class SecondClassEvmAdapter extends EvmBas } const wrappedNativeContract = WRAPPED_NATIVE_CONTRACT_BY_CHAIN_ID[this.chainId] + console.log( + '[SecondClassEvmAdapter parseTx]', + JSON.stringify({ + chainId: this.chainId, + hash, + pubkey, + wrappedNativeContract, + internalTxsCount: internalTxs.length, + logsCount: receipt.logs.length, + }), + ) if (wrappedNativeContract && internalTxs.length === 0) { const wrappedNativeBurnLogs = parseEventLogs({ abi: erc20Abi, @@ -482,6 +496,17 @@ export abstract class SecondClassEvmAdapter extends EvmBas isAddressEqual(getAddress(log.address), getAddress(wrappedNativeContract)) && isAddressEqual(log.args.to, zeroAddress), ) + console.log( + '[SecondClassEvmAdapter parseTx] wrappedNativeBurnLogs', + JSON.stringify({ + wrappedNativeBurnLogs: wrappedNativeBurnLogs.map(l => ({ + address: l.address, + from: l.args.from, + to: l.args.to, + value: l.args.value.toString(), + })), + }), + ) for (const log of wrappedNativeBurnLogs) { internalTxs.push({ @@ -501,6 +526,16 @@ export abstract class SecondClassEvmAdapter extends EvmBas isAddressEqual(getAddress(log.address), getAddress(wrappedNativeContract)) && log.topics[0] === WITHDRAWAL_TOPIC, ) + console.log( + '[SecondClassEvmAdapter parseTx] withdrawalLogs', + JSON.stringify({ + withdrawalLogs: withdrawalLogs.map(l => ({ + address: l.address, + topics: l.topics, + data: l.data, + })), + }), + ) for (const log of withdrawalLogs) { internalTxs.push({ @@ -511,6 +546,10 @@ export abstract class SecondClassEvmAdapter extends EvmBas } } } + console.log( + '[SecondClassEvmAdapter parseTx] final internalTxs', + JSON.stringify({ internalTxs, tokenTransferAddresses: receipt.logs.map(l => l.address) }), + ) const block = receipt.blockHash ? await viemClient.getBlock({ blockHash: receipt.blockHash }).catch(() => null) diff --git a/packages/chain-adapters/src/evm/index.ts b/packages/chain-adapters/src/evm/index.ts index db68d6b1377..e74af5ec1cf 100644 --- a/packages/chain-adapters/src/evm/index.ts +++ b/packages/chain-adapters/src/evm/index.ts @@ -37,3 +37,4 @@ export * as unichain from './unichain' export * as bob from './bob' export * as mode from './mode' export * as soneium from './soneium' +export * as sei from './sei' diff --git a/packages/chain-adapters/src/evm/sei/SeiChainAdapter.ts b/packages/chain-adapters/src/evm/sei/SeiChainAdapter.ts new file mode 100644 index 00000000000..17ebabfcd1d --- /dev/null +++ b/packages/chain-adapters/src/evm/sei/SeiChainAdapter.ts @@ -0,0 +1,58 @@ +import type { AssetId } from '@shapeshiftoss/caip' +import { ASSET_REFERENCE, seiAssetId } from '@shapeshiftoss/caip' +import type { RootBip44Params } from '@shapeshiftoss/types' +import { KnownChainIds } from '@shapeshiftoss/types' + +import { ChainAdapterDisplayName } from '../../types' +import type { TokenInfo } from '../SecondClassEvmAdapter' +import { SecondClassEvmAdapter } from '../SecondClassEvmAdapter' + +const SUPPORTED_CHAIN_IDS = [KnownChainIds.SeiMainnet] +const DEFAULT_CHAIN_ID = KnownChainIds.SeiMainnet + +export type ChainAdapterArgs = { + rpcUrl: string + getKnownTokens: () => TokenInfo[] +} + +export const isSeiChainAdapter = (adapter: unknown): adapter is ChainAdapter => { + if (!adapter) return false + return (adapter as ChainAdapter).getType() === KnownChainIds.SeiMainnet +} + +export class ChainAdapter extends SecondClassEvmAdapter { + public static readonly rootBip44Params: RootBip44Params = { + purpose: 44, + coinType: Number(ASSET_REFERENCE.Sei), + accountNumber: 0, + } + + constructor(args: ChainAdapterArgs) { + super({ + assetId: seiAssetId, + chainId: DEFAULT_CHAIN_ID, + rootBip44Params: ChainAdapter.rootBip44Params, + supportedChainIds: SUPPORTED_CHAIN_IDS, + rpcUrl: args.rpcUrl, + getKnownTokens: args.getKnownTokens, + }) + } + + getDisplayName() { + return ChainAdapterDisplayName.Sei + } + + getName() { + return 'Sei' + } + + getType(): KnownChainIds.SeiMainnet { + return KnownChainIds.SeiMainnet + } + + getFeeAssetId(): AssetId { + return this.assetId + } +} + +export type { TokenInfo } diff --git a/packages/chain-adapters/src/evm/sei/index.ts b/packages/chain-adapters/src/evm/sei/index.ts new file mode 100644 index 00000000000..dbe2a6c26b3 --- /dev/null +++ b/packages/chain-adapters/src/evm/sei/index.ts @@ -0,0 +1 @@ +export * from './SeiChainAdapter' diff --git a/packages/chain-adapters/src/types.ts b/packages/chain-adapters/src/types.ts index f4bd59af9fe..640efb03fe3 100644 --- a/packages/chain-adapters/src/types.ts +++ b/packages/chain-adapters/src/types.ts @@ -68,6 +68,7 @@ type ChainSpecificAccount = ChainSpecific< [KnownChainIds.BobMainnet]: evm.Account [KnownChainIds.ModeMainnet]: evm.Account [KnownChainIds.SoneiumMainnet]: evm.Account + [KnownChainIds.SeiMainnet]: evm.Account [KnownChainIds.BitcoinMainnet]: utxo.Account [KnownChainIds.BitcoinCashMainnet]: utxo.Account [KnownChainIds.DogecoinMainnet]: utxo.Account @@ -139,6 +140,7 @@ type ChainSpecificFeeData = ChainSpecific< [KnownChainIds.BobMainnet]: evm.FeeData [KnownChainIds.ModeMainnet]: evm.FeeData [KnownChainIds.SoneiumMainnet]: evm.FeeData + [KnownChainIds.SeiMainnet]: evm.FeeData [KnownChainIds.BitcoinMainnet]: utxo.FeeData [KnownChainIds.BitcoinCashMainnet]: utxo.FeeData [KnownChainIds.DogecoinMainnet]: utxo.FeeData @@ -243,6 +245,7 @@ export type ChainSignTx = { [KnownChainIds.BobMainnet]: ETHSignTx [KnownChainIds.ModeMainnet]: ETHSignTx [KnownChainIds.SoneiumMainnet]: ETHSignTx + [KnownChainIds.SeiMainnet]: ETHSignTx [KnownChainIds.BitcoinMainnet]: BTCSignTx [KnownChainIds.BitcoinCashMainnet]: BTCSignTx [KnownChainIds.DogecoinMainnet]: BTCSignTx @@ -319,6 +322,7 @@ export type ChainSpecificBuildTxData = ChainSpecific< [KnownChainIds.BobMainnet]: evm.BuildTxInput [KnownChainIds.ModeMainnet]: evm.BuildTxInput [KnownChainIds.SoneiumMainnet]: evm.BuildTxInput + [KnownChainIds.SeiMainnet]: evm.BuildTxInput [KnownChainIds.BitcoinMainnet]: utxo.BuildTxInput [KnownChainIds.BitcoinCashMainnet]: utxo.BuildTxInput [KnownChainIds.DogecoinMainnet]: utxo.BuildTxInput @@ -445,6 +449,7 @@ type ChainSpecificGetFeeDataInput = ChainSpecific< [KnownChainIds.BobMainnet]: evm.GetFeeDataInput [KnownChainIds.ModeMainnet]: evm.GetFeeDataInput [KnownChainIds.SoneiumMainnet]: evm.GetFeeDataInput + [KnownChainIds.SeiMainnet]: evm.GetFeeDataInput [KnownChainIds.BitcoinMainnet]: utxo.GetFeeDataInput [KnownChainIds.BitcoinCashMainnet]: utxo.GetFeeDataInput [KnownChainIds.DogecoinMainnet]: utxo.GetFeeDataInput @@ -536,6 +541,7 @@ export enum ChainAdapterDisplayName { Bob = 'BOB', Mode = 'Mode', Soneium = 'Soneium', + Sei = 'Sei', Cosmos = 'Cosmos', Bitcoin = 'Bitcoin', BitcoinCash = 'Bitcoin Cash', diff --git a/packages/contracts/src/ethersProviderSingleton.ts b/packages/contracts/src/ethersProviderSingleton.ts index e342f25de61..86388888704 100644 --- a/packages/contracts/src/ethersProviderSingleton.ts +++ b/packages/contracts/src/ethersProviderSingleton.ts @@ -72,6 +72,8 @@ export const rpcUrlByChainId = (chainId: EvmChainId): string => { return process.env.VITE_MODE_NODE_URL case KnownChainIds.SoneiumMainnet: return process.env.VITE_SONEIUM_NODE_URL + case KnownChainIds.SeiMainnet: + return process.env.VITE_SEI_NODE_URL default: return assertUnreachable(chainId) } diff --git a/packages/contracts/src/viemClient.ts b/packages/contracts/src/viemClient.ts index 1e6b67ff3cd..3e3f9c5a4e7 100644 --- a/packages/contracts/src/viemClient.ts +++ b/packages/contracts/src/viemClient.ts @@ -29,6 +29,7 @@ import { plumeMainnet, polygon, scroll, + sei, soneium, sonic, story, @@ -211,6 +212,11 @@ export const viemHemiClient = createPublicClient({ transport: fallback([process.env.VITE_HEMI_NODE_URL].filter(Boolean).map(url => http(url))), }) as PublicClient +export const viemSeiClient = createPublicClient({ + chain: sei, + transport: fallback([process.env.VITE_SEI_NODE_URL].filter(Boolean).map(url => http(url))), +}) as PublicClient + export const viemLineaClient = createPublicClient({ chain: linea, transport: fallback([process.env.VITE_LINEA_NODE_URL].filter(Boolean).map(url => http(url))), @@ -274,6 +280,7 @@ export const viemClientByChainId: Record = { [KnownChainIds.BobMainnet]: viemBobClient, [KnownChainIds.ModeMainnet]: viemModeClient, [KnownChainIds.SoneiumMainnet]: viemSoneiumClient, + [KnownChainIds.SeiMainnet]: viemSeiClient, } export const viemNetworkIdByChainId: Record = { @@ -309,6 +316,7 @@ export const viemNetworkIdByChainId: Record = { [KnownChainIds.BobMainnet]: bob.id, [KnownChainIds.ModeMainnet]: mode.id, [KnownChainIds.SoneiumMainnet]: soneium.id, + [KnownChainIds.SeiMainnet]: sei.id, } export const viemClientByNetworkId: Record = { @@ -344,6 +352,7 @@ export const viemClientByNetworkId: Record = { [bob.id]: viemBobClient, [mode.id]: viemModeClient, [soneium.id]: viemSoneiumClient, + [sei.id]: viemSeiClient, } export const assertGetViemClient = (chainId: ChainId): PublicClient => { diff --git a/packages/hdwallet-coinbase/src/coinbase.ts b/packages/hdwallet-coinbase/src/coinbase.ts index 2e81ef1d687..b6637b7e0e8 100644 --- a/packages/hdwallet-coinbase/src/coinbase.ts +++ b/packages/hdwallet-coinbase/src/coinbase.ts @@ -115,6 +115,7 @@ export class CoinbaseHDWallet implements core.HDWallet, core.ETHWallet { readonly _supportsSonic = false readonly _supportsBob = false readonly _supportsMode = false + readonly _supportsSei = false readonly _supportsHyperEvm = false readonly _supportsMantle = false readonly _supportsInk = false diff --git a/packages/hdwallet-core/src/ethereum.ts b/packages/hdwallet-core/src/ethereum.ts index e62de913c62..6a39d8aba59 100644 --- a/packages/hdwallet-core/src/ethereum.ts +++ b/packages/hdwallet-core/src/ethereum.ts @@ -213,6 +213,7 @@ export interface ETHWallet extends ETHWalletInfo, HDWallet { readonly _supportsSonic: boolean readonly _supportsBob: boolean readonly _supportsMode: boolean + readonly _supportsSei: boolean readonly _supportsHyperEvm: boolean readonly _supportsMantle: boolean readonly _supportsInk: boolean diff --git a/packages/hdwallet-core/src/wallet.ts b/packages/hdwallet-core/src/wallet.ts index 4d55a8e0b7b..2698be9452b 100644 --- a/packages/hdwallet-core/src/wallet.ts +++ b/packages/hdwallet-core/src/wallet.ts @@ -201,6 +201,10 @@ export function supportsMode(wallet: HDWallet): wallet is ETHWallet { return isObject(wallet) && (wallet as any)._supportsMode } +export function supportsSei(wallet: HDWallet): wallet is ETHWallet { + return isObject(wallet) && (wallet as any)._supportsSei +} + export function supportsHyperEvm(wallet: HDWallet): wallet is ETHWallet { return isObject(wallet) && (wallet as any)._supportsHyperEvm } diff --git a/packages/hdwallet-gridplus/src/gridplus.ts b/packages/hdwallet-gridplus/src/gridplus.ts index 21ae5047bfd..22e5406662a 100644 --- a/packages/hdwallet-gridplus/src/gridplus.ts +++ b/packages/hdwallet-gridplus/src/gridplus.ts @@ -368,6 +368,7 @@ export class GridPlusHDWallet readonly _supportsSonic = false readonly _supportsBob = false readonly _supportsMode = false + readonly _supportsSei = false readonly _supportsHyperEvm = true readonly _supportsMantle = true readonly _supportsInk = true diff --git a/packages/hdwallet-keepkey/src/keepkey.ts b/packages/hdwallet-keepkey/src/keepkey.ts index 71f71e42a74..5e0faf39c2c 100644 --- a/packages/hdwallet-keepkey/src/keepkey.ts +++ b/packages/hdwallet-keepkey/src/keepkey.ts @@ -557,6 +557,7 @@ export class KeepKeyHDWallet readonly _supportsSonic = false readonly _supportsBob = false readonly _supportsMode = false + readonly _supportsSei = false readonly _supportsHyperEvm = false readonly _supportsMantle = false readonly _supportsInk = false diff --git a/packages/hdwallet-ledger/src/ledger.ts b/packages/hdwallet-ledger/src/ledger.ts index 5df5a6a14cf..4cbbde1926a 100644 --- a/packages/hdwallet-ledger/src/ledger.ts +++ b/packages/hdwallet-ledger/src/ledger.ts @@ -434,6 +434,7 @@ export class LedgerHDWallet readonly _supportsSonic = true readonly _supportsBob = true readonly _supportsMode = true + readonly _supportsSei = true readonly _supportsHyperEvm = true readonly _supportsMantle = true readonly _supportsInk = true diff --git a/packages/hdwallet-metamask-multichain/src/shapeshift-multichain.ts b/packages/hdwallet-metamask-multichain/src/shapeshift-multichain.ts index f3c4b2e4639..d9f21ea8c05 100644 --- a/packages/hdwallet-metamask-multichain/src/shapeshift-multichain.ts +++ b/packages/hdwallet-metamask-multichain/src/shapeshift-multichain.ts @@ -281,6 +281,7 @@ export class MetaMaskMultiChainHDWallet readonly _supportsSonic = true readonly _supportsBob = true readonly _supportsMode = true + readonly _supportsSei = true readonly _supportsHyperEvm = true readonly _supportsMantle = true readonly _supportsInk = true diff --git a/packages/hdwallet-native/src/ethereum.ts b/packages/hdwallet-native/src/ethereum.ts index 801b26d2d73..63791887198 100644 --- a/packages/hdwallet-native/src/ethereum.ts +++ b/packages/hdwallet-native/src/ethereum.ts @@ -73,6 +73,7 @@ export function MixinNativeETHWallet ({ VITE_SUI_NODE_URL: process.env.SUI_NODE_URL || 'https://fullnode.mainnet.sui.io', VITE_ACROSS_API_URL: process.env.ACROSS_API_URL || 'https://app.across.to/api', VITE_ACROSS_INTEGRATOR_ID: process.env.ACROSS_INTEGRATOR_ID || '', + VITE_DEBRIDGE_API_URL: process.env.DEBRIDGE_API_URL || 'https://dln.debridge.finance/v1.0', }) // Default affiliate fee in basis points diff --git a/packages/swapper/src/constants.ts b/packages/swapper/src/constants.ts index e92d6855a18..93b5d95025e 100644 --- a/packages/swapper/src/constants.ts +++ b/packages/swapper/src/constants.ts @@ -16,6 +16,8 @@ import { chainflipSwapper } from './swappers/ChainflipSwapper/ChainflipSwapper' import { chainflipApi } from './swappers/ChainflipSwapper/endpoints' import { cowSwapper } from './swappers/CowSwapper/CowSwapper' import { cowApi } from './swappers/CowSwapper/endpoints' +import { debridgeSwapper } from './swappers/DebridgeSwapper' +import { debridgeApi } from './swappers/DebridgeSwapper/endpoints' import { jupiterApi } from './swappers/JupiterSwapper/endpoints' import { jupiterSwapper } from './swappers/JupiterSwapper/JupiterSwapper' import { mayachainApi } from './swappers/MayachainSwapper/endpoints' @@ -116,6 +118,10 @@ export const swappers: Record = ...acrossSwapper, ...acrossApi, }, + [SwapperName.Debridge]: { + ...debridgeSwapper, + ...debridgeApi, + }, [SwapperName.Test]: undefined, } @@ -133,6 +139,8 @@ const DEFAULT_SUNIO_SLIPPAGE_DECIMAL_PERCENTAGE = '0.005' // Starknet swaps can have more latency, so use higher default slippage const DEFAULT_AVNU_SLIPPAGE_DECIMAL_PERCENTAGE = '0.02' const DEFAULT_STONFI_SLIPPAGE_DECIMAL_PERCENTAGE = '0.01' +// deBridge API off-chain simulation overestimates output on some chains (e.g. SEI ~2.4%), so auto slippage (1%) is insufficient +const DEFAULT_DEBRIDGE_SLIPPAGE_DECIMAL_PERCENTAGE = '0.03' export const getDefaultSlippageDecimalPercentageForSwapper = ( swapperName: SwapperName | undefined, @@ -161,6 +169,8 @@ export const getDefaultSlippageDecimalPercentageForSwapper = ( throw new Error('Default slippage not supported by Relay') case SwapperName.Across: throw new Error('Default slippage not supported by Across') + case SwapperName.Debridge: + return DEFAULT_DEBRIDGE_SLIPPAGE_DECIMAL_PERCENTAGE case SwapperName.ButterSwap: return DEFAULT_BUTTERSWAP_SLIPPAGE_DECIMAL_PERCENTAGE case SwapperName.NearIntents: @@ -184,6 +194,7 @@ export const isAutoSlippageSupportedBySwapper = (swapperName: SwapperName): bool return true case SwapperName.Relay: case SwapperName.Across: + case SwapperName.Debridge: return true default: return false diff --git a/packages/swapper/src/index.ts b/packages/swapper/src/index.ts index eb6c216caa6..284a46fc70a 100644 --- a/packages/swapper/src/index.ts +++ b/packages/swapper/src/index.ts @@ -9,6 +9,7 @@ export * from './swappers/CetusSwapper' export * from './swappers/ChainflipSwapper' export * from './swappers/SunioSwapper' export * from './swappers/CowSwapper' +export * from './swappers/DebridgeSwapper' export * from './swappers/JupiterSwapper' export * from './swappers/PortalsSwapper' export * from './swappers/ThorchainSwapper' diff --git a/packages/swapper/src/swappers/DebridgeSwapper/DebridgeSwapper.ts b/packages/swapper/src/swappers/DebridgeSwapper/DebridgeSwapper.ts new file mode 100644 index 00000000000..152fcd1cbc5 --- /dev/null +++ b/packages/swapper/src/swappers/DebridgeSwapper/DebridgeSwapper.ts @@ -0,0 +1,6 @@ +import type { Swapper } from '../../types' +import { executeEvmTransaction } from '../../utils' + +export const debridgeSwapper: Swapper = { + executeEvmTransaction, +} diff --git a/packages/swapper/src/swappers/DebridgeSwapper/constant.ts b/packages/swapper/src/swappers/DebridgeSwapper/constant.ts new file mode 100644 index 00000000000..4d15d6c3eb3 --- /dev/null +++ b/packages/swapper/src/swappers/DebridgeSwapper/constant.ts @@ -0,0 +1,39 @@ +import { + arbitrumChainId, + avalancheChainId, + baseChainId, + bscChainId, + ethChainId, + gnosisChainId, + hyperEvmChainId, + monadChainId, + optimismChainId, + plasmaChainId, + polygonChainId, + seiChainId, +} from '@shapeshiftoss/caip' +import invert from 'lodash/invert' +import { zeroAddress } from 'viem' + +export const chainIdToDebridgeChainId: Record = { + [ethChainId]: 1, + [optimismChainId]: 10, + [bscChainId]: 56, + [gnosisChainId]: 100000002, + [polygonChainId]: 137, + [monadChainId]: 100000030, + [hyperEvmChainId]: 100000022, + [seiChainId]: 100000027, + [baseChainId]: 8453, + [plasmaChainId]: 100000028, + [arbitrumChainId]: 42161, + [avalancheChainId]: 43114, +} + +export const debridgeChainIdToChainId = invert(chainIdToDebridgeChainId) + +export const DEBRIDGE_SUPPORTED_CHAIN_IDS = Object.keys(chainIdToDebridgeChainId) + +export const DEFAULT_DEBRIDGE_TOKEN_ADDRESS = zeroAddress + +export const DEFAULT_DEBRIDGE_USER_ADDRESS = '0x000000000000000000000000000000000000dead' diff --git a/packages/swapper/src/swappers/DebridgeSwapper/endpoints.ts b/packages/swapper/src/swappers/DebridgeSwapper/endpoints.ts new file mode 100644 index 00000000000..a26f9d71889 --- /dev/null +++ b/packages/swapper/src/swappers/DebridgeSwapper/endpoints.ts @@ -0,0 +1,270 @@ +import { evm, isEvmChainId } from '@shapeshiftoss/chain-adapters' +import { TxStatus } from '@shapeshiftoss/unchained-client' +import BigNumber from 'bignumber.js' + +import type { SwapperApi } from '../../types' +import { checkEvmSwapStatus, getExecutableTradeStep, isExecutableTradeQuote } from '../../utils' +import { getTradeQuote } from './getTradeQuote/getTradeQuote' +import { getTradeRate } from './getTradeRate/getTradeRate' +import { debridgeService } from './utils/debridgeService' +import type { + DebridgeOrderDetail, + DebridgeOrderIdsResponse, + DebridgeOrderStatus, +} from './utils/types' + +const DEBRIDGE_STATS_API_URL = 'https://stats-api.dln.trade' + +export const debridgeApi: SwapperApi = { + getTradeQuote: (input, deps) => getTradeQuote(input, deps), + getTradeRate: (input, deps) => getTradeRate(input, deps), + getEvmTransactionFees: async ({ + from, + stepIndex, + tradeQuote, + supportsEIP1559, + assertGetEvmChainAdapter, + }) => { + if (!isExecutableTradeQuote(tradeQuote)) throw new Error('Unable to execute a trade rate quote') + + const step = getExecutableTradeStep(tradeQuote, stepIndex) + + const { debridgeTransactionMetadata, sellAsset } = step + if (!debridgeTransactionMetadata) throw new Error('Missing deBridge transaction metadata') + + const { to, value, data, gasLimit: gasLimitFromApi } = debridgeTransactionMetadata + + const adapter = assertGetEvmChainAdapter(sellAsset.chainId) + + try { + const feeData = await evm.getFees({ adapter, data, to, value, from, supportsEIP1559 }) + return feeData.networkFeeCryptoBaseUnit + } catch (e) { + console.error('[deBridge] getEvmTransactionFees: gas estimation failed, using API fallback', { + error: e, + chainId: sellAsset.chainId, + to, + value, + }) + if (!gasLimitFromApi) throw new Error('Gas estimation failed and no API gas limit fallback') + const { average } = await adapter.getGasFeeData() + return evm.calcNetworkFeeCryptoBaseUnit({ + ...average, + supportsEIP1559, + gasLimit: gasLimitFromApi, + }) + } + }, + getUnsignedEvmTransaction: async ({ + from, + stepIndex, + tradeQuote, + supportsEIP1559, + assertGetEvmChainAdapter, + }) => { + if (!isExecutableTradeQuote(tradeQuote)) throw new Error('Unable to execute a trade rate quote') + + const step = getExecutableTradeStep(tradeQuote, stepIndex) + + const { accountNumber, debridgeTransactionMetadata, sellAsset } = step + if (!debridgeTransactionMetadata) throw new Error('Missing deBridge transaction metadata') + + const { to, value, data, gasLimit: gasLimitFromApi } = debridgeTransactionMetadata + + const adapter = assertGetEvmChainAdapter(sellAsset.chainId) + + const feeData = await (async () => { + try { + const fees = await evm.getFees({ adapter, data, to, value, from, supportsEIP1559 }) + const result = { + ...fees, + gasLimit: BigNumber(fees.gasLimit).times('1.2').toFixed(0), + } + console.log( + '[deBridge] getUnsignedEvmTransaction feeData', + JSON.stringify({ + chainId: sellAsset.chainId, + to, + value, + supportsEIP1559, + feesFromEstimate: fees, + gasLimitWithBuffer: result.gasLimit, + }), + ) + return result + } catch (e) { + console.error( + '[deBridge] getUnsignedEvmTransaction: gas estimation failed, using API fallback', + { error: e, chainId: sellAsset.chainId, to, value }, + ) + if (!gasLimitFromApi) throw new Error('Gas estimation failed and no API gas limit fallback') + const { average } = await adapter.getGasFeeData() + const networkFeeCryptoBaseUnit = evm.calcNetworkFeeCryptoBaseUnit({ + ...average, + supportsEIP1559, + gasLimit: gasLimitFromApi, + }) + const { gasPrice, maxFeePerGas, maxPriorityFeePerGas } = average + if (supportsEIP1559 && maxFeePerGas && maxPriorityFeePerGas) { + return { + networkFeeCryptoBaseUnit, + gasLimit: gasLimitFromApi, + maxFeePerGas, + maxPriorityFeePerGas, + } + } + return { networkFeeCryptoBaseUnit, gasLimit: gasLimitFromApi, gasPrice } + } + })() + + const unsignedTx = await adapter.buildCustomApiTx({ + accountNumber, + data, + from, + to, + value, + ...feeData, + }) + + return unsignedTx + }, + checkTradeStatus: async ({ + txHash, + chainId, + address, + swap, + config, + fetchIsSmartContractAddressQuery, + assertGetEvmChainAdapter, + }) => { + const isSameChainSwap = swap?.metadata.debridgeTransactionMetadata?.isSameChainSwap === true + + if (isSameChainSwap) { + return checkEvmSwapStatus({ + txHash, + chainId, + address, + assertGetEvmChainAdapter, + fetchIsSmartContractAddressQuery, + }) + } + + let resolvedTxHash = txHash + + if (isEvmChainId(chainId)) { + const sourceTxStatus = await checkEvmSwapStatus({ + txHash, + chainId, + address, + assertGetEvmChainAdapter, + fetchIsSmartContractAddressQuery, + }) + + if (sourceTxStatus.status !== TxStatus.Confirmed) return sourceTxStatus + + resolvedTxHash = sourceTxStatus.buyTxHash ?? txHash + } + + const maybeOrderIdsResponse = await debridgeService.get( + `${DEBRIDGE_STATS_API_URL}/api/Transaction/${resolvedTxHash}/orderIds`, + ) + + if (maybeOrderIdsResponse.isErr()) { + console.error('[deBridge] checkTradeStatus: failed to fetch orderIds', { + error: maybeOrderIdsResponse.unwrapErr(), + txHash: resolvedTxHash, + chainId, + }) + return { + buyTxHash: undefined, + status: TxStatus.Pending, + message: 'Waiting for order confirmation...', + } + } + + const { data: orderIdsResponse } = maybeOrderIdsResponse.unwrap() + + if (!orderIdsResponse.orderIds?.length) { + return { + buyTxHash: undefined, + status: TxStatus.Pending, + message: 'Waiting for order confirmation...', + } + } + + const orderId = orderIdsResponse.orderIds[0].stringValue + + const maybeStatusResponse = await debridgeService.get( + `${config.VITE_DEBRIDGE_API_URL}/dln/order/${orderId}/status`, + ) + + if (maybeStatusResponse.isErr()) { + console.error('[deBridge] checkTradeStatus: failed to fetch order status', { + error: maybeStatusResponse.unwrapErr(), + orderId, + txHash: resolvedTxHash, + chainId, + }) + return { + buyTxHash: undefined, + status: TxStatus.Unknown, + message: undefined, + } + } + + const { data: statusResponse } = maybeStatusResponse.unwrap() + + const status = (() => { + switch (statusResponse.status) { + case 'Fulfilled': + case 'SentUnlock': + case 'ClaimedUnlock': + return TxStatus.Confirmed + case 'Created': + case 'None': + return TxStatus.Pending + case 'OrderCancelled': + case 'SentOrderCancel': + case 'ClaimedOrderCancel': + return TxStatus.Failed + default: + return TxStatus.Unknown + } + })() + + const message = (() => { + switch (statusResponse.status) { + case 'Created': + case 'None': + return 'Order created, waiting for solver fulfillment...' + case 'Fulfilled': + return 'Order fulfilled, unlocking funds...' + case 'SentUnlock': + return 'Unlock transaction sent...' + case 'OrderCancelled': + return 'Order was cancelled' + case 'SentOrderCancel': + return 'Cancellation in progress...' + case 'ClaimedOrderCancel': + return 'Order cancelled and refunded' + default: + return undefined + } + })() + + const buyTxHash = await (async () => { + if (status !== TxStatus.Confirmed) return undefined + const maybeOrderDetail = await debridgeService.get( + `${DEBRIDGE_STATS_API_URL}/api/Orders/${orderId}`, + ) + if (maybeOrderDetail.isErr()) return undefined + return maybeOrderDetail.unwrap().data.fulfilledDstEventMetadata?.transactionHash?.stringValue + })() + + return { + status, + buyTxHash, + message, + } + }, +} diff --git a/packages/swapper/src/swappers/DebridgeSwapper/getTradeQuote/getTradeQuote.ts b/packages/swapper/src/swappers/DebridgeSwapper/getTradeQuote/getTradeQuote.ts new file mode 100644 index 00000000000..f2eda4e400a --- /dev/null +++ b/packages/swapper/src/swappers/DebridgeSwapper/getTradeQuote/getTradeQuote.ts @@ -0,0 +1,46 @@ +import type { Result } from '@sniptt/monads' +import { Err } from '@sniptt/monads' + +import type { CommonTradeQuoteInput, SwapErrorRight, SwapperDeps, TradeQuote } from '../../../types' +import { makeSwapErrorRight } from '../../../utils' +import { getTrade } from '../utils/getTrade' + +export const getTradeQuote = ( + input: CommonTradeQuoteInput, + deps: SwapperDeps, +): Promise> => { + if (!input.sendAddress) { + return Promise.resolve( + Err( + makeSwapErrorRight({ + message: 'sendAddress is required', + }), + ), + ) + } + + if (!input.receiveAddress) { + return Promise.resolve( + Err( + makeSwapErrorRight({ + message: 'receiveAddress is required', + }), + ), + ) + } + + const args = { + buyAsset: input.buyAsset, + receiveAddress: input.receiveAddress, + sellAmountIncludingProtocolFeesCryptoBaseUnit: + input.sellAmountIncludingProtocolFeesCryptoBaseUnit, + sellAsset: input.sellAsset, + sendAddress: input.sendAddress, + quoteOrRate: 'quote' as const, + accountNumber: input.accountNumber, + affiliateBps: input.affiliateBps, + slippageTolerancePercentageDecimal: input.slippageTolerancePercentageDecimal, + } + + return getTrade({ input: args, deps }) +} diff --git a/packages/swapper/src/swappers/DebridgeSwapper/getTradeRate/getTradeRate.ts b/packages/swapper/src/swappers/DebridgeSwapper/getTradeRate/getTradeRate.ts new file mode 100644 index 00000000000..3a33de2f038 --- /dev/null +++ b/packages/swapper/src/swappers/DebridgeSwapper/getTradeRate/getTradeRate.ts @@ -0,0 +1,24 @@ +import type { Result } from '@sniptt/monads' + +import type { GetTradeRateInput, SwapErrorRight, SwapperDeps, TradeRate } from '../../../types' +import { getTrade } from '../utils/getTrade' + +export const getTradeRate = ( + input: GetTradeRateInput, + deps: SwapperDeps, +): Promise> => { + const args = { + quoteOrRate: 'rate' as const, + buyAsset: input.buyAsset, + receiveAddress: input.receiveAddress, + sellAmountIncludingProtocolFeesCryptoBaseUnit: + input.sellAmountIncludingProtocolFeesCryptoBaseUnit, + sellAsset: input.sellAsset, + sendAddress: input.sendAddress, + accountNumber: input.accountNumber, + affiliateBps: input.affiliateBps, + slippageTolerancePercentageDecimal: input.slippageTolerancePercentageDecimal, + } + + return getTrade({ input: args, deps }) +} diff --git a/packages/swapper/src/swappers/DebridgeSwapper/index.ts b/packages/swapper/src/swappers/DebridgeSwapper/index.ts new file mode 100644 index 00000000000..09557661ff7 --- /dev/null +++ b/packages/swapper/src/swappers/DebridgeSwapper/index.ts @@ -0,0 +1 @@ +export { debridgeSwapper } from './DebridgeSwapper' diff --git a/packages/swapper/src/swappers/DebridgeSwapper/utils/debridgeService.ts b/packages/swapper/src/swappers/DebridgeSwapper/utils/debridgeService.ts new file mode 100644 index 00000000000..906f67a70a0 --- /dev/null +++ b/packages/swapper/src/swappers/DebridgeSwapper/utils/debridgeService.ts @@ -0,0 +1,15 @@ +import axios from 'axios' + +import { makeSwapperAxiosServiceMonadic } from '../../../utils' + +const axiosConfig = { + timeout: 30000, + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, +} + +const debridgeServiceBase = axios.create(axiosConfig) + +export const debridgeService = makeSwapperAxiosServiceMonadic(debridgeServiceBase) diff --git a/packages/swapper/src/swappers/DebridgeSwapper/utils/fetchDebridgeSingleChainTrade.ts b/packages/swapper/src/swappers/DebridgeSwapper/utils/fetchDebridgeSingleChainTrade.ts new file mode 100644 index 00000000000..cdd9500df39 --- /dev/null +++ b/packages/swapper/src/swappers/DebridgeSwapper/utils/fetchDebridgeSingleChainTrade.ts @@ -0,0 +1,42 @@ +import type { Result } from '@sniptt/monads' +import type { AxiosResponse } from 'axios' + +import type { SwapErrorRight, SwapperConfig } from '../../../types' +import { debridgeService } from './debridgeService' +import type { DebridgeSingleChainTransactionResponse } from './types' + +export type DebridgeSingleChainFetchParams = { + chainId: number + tokenIn: string + tokenInAmount: string + tokenOut: string + tokenOutRecipient?: string + senderAddress?: string + slippage?: string + affiliateFeePercent?: string + affiliateFeeRecipient?: string +} + +export const fetchDebridgeSingleChainTrade = ( + params: DebridgeSingleChainFetchParams, + config: SwapperConfig, +): Promise, SwapErrorRight>> => { + const searchParams = new URLSearchParams() + + searchParams.set('chainId', params.chainId.toString()) + searchParams.set('tokenIn', params.tokenIn) + searchParams.set('tokenInAmount', params.tokenInAmount) + searchParams.set('tokenOut', params.tokenOut) + searchParams.set('slippage', params.slippage ?? 'auto') + + if (params.tokenOutRecipient) searchParams.set('tokenOutRecipient', params.tokenOutRecipient) + if (params.senderAddress) searchParams.set('senderAddress', params.senderAddress) + if (params.affiliateFeePercent && params.affiliateFeeRecipient) { + searchParams.set('affiliateFeePercent', params.affiliateFeePercent) + searchParams.set('affiliateFeeRecipient', params.affiliateFeeRecipient) + } + + const url = `${config.VITE_DEBRIDGE_API_URL}/chain/transaction?${searchParams.toString()}` + + return debridgeService.get(url) +} diff --git a/packages/swapper/src/swappers/DebridgeSwapper/utils/fetchDebridgeTrade.ts b/packages/swapper/src/swappers/DebridgeSwapper/utils/fetchDebridgeTrade.ts new file mode 100644 index 00000000000..f59fb129b10 --- /dev/null +++ b/packages/swapper/src/swappers/DebridgeSwapper/utils/fetchDebridgeTrade.ts @@ -0,0 +1,53 @@ +import type { Result } from '@sniptt/monads' +import type { AxiosResponse } from 'axios' + +import type { SwapErrorRight, SwapperConfig } from '../../../types' +import { debridgeService } from './debridgeService' +import type { DebridgeCreateTxResponse } from './types' + +export type DebridgeFetchQuoteParams = { + srcChainId: number + srcChainTokenIn: string + srcChainTokenInAmount: string + dstChainId: number + dstChainTokenOut: string + dstChainTokenOutAmount: 'auto' + dstChainTokenOutRecipient?: string + srcChainOrderAuthorityAddress?: string + dstChainOrderAuthorityAddress?: string + senderAddress?: string + prependOperatingExpenses: 'true' + affiliateFeePercent?: string + affiliateFeeRecipient?: string +} + +export const fetchDebridgeTrade = ( + params: DebridgeFetchQuoteParams, + config: SwapperConfig, +): Promise, SwapErrorRight>> => { + const searchParams = new URLSearchParams() + + searchParams.set('srcChainId', params.srcChainId.toString()) + searchParams.set('srcChainTokenIn', params.srcChainTokenIn) + searchParams.set('srcChainTokenInAmount', params.srcChainTokenInAmount) + searchParams.set('dstChainId', params.dstChainId.toString()) + searchParams.set('dstChainTokenOut', params.dstChainTokenOut) + searchParams.set('dstChainTokenOutAmount', params.dstChainTokenOutAmount) + searchParams.set('prependOperatingExpenses', params.prependOperatingExpenses) + + if (params.dstChainTokenOutRecipient) + searchParams.set('dstChainTokenOutRecipient', params.dstChainTokenOutRecipient) + if (params.srcChainOrderAuthorityAddress) + searchParams.set('srcChainOrderAuthorityAddress', params.srcChainOrderAuthorityAddress) + if (params.dstChainOrderAuthorityAddress) + searchParams.set('dstChainOrderAuthorityAddress', params.dstChainOrderAuthorityAddress) + if (params.senderAddress) searchParams.set('senderAddress', params.senderAddress) + if (params.affiliateFeePercent && params.affiliateFeeRecipient) { + searchParams.set('affiliateFeePercent', params.affiliateFeePercent) + searchParams.set('affiliateFeeRecipient', params.affiliateFeeRecipient) + } + + const url = `${config.VITE_DEBRIDGE_API_URL}/dln/order/create-tx?${searchParams.toString()}` + + return debridgeService.get(url) +} diff --git a/packages/swapper/src/swappers/DebridgeSwapper/utils/getTrade.ts b/packages/swapper/src/swappers/DebridgeSwapper/utils/getTrade.ts new file mode 100644 index 00000000000..63ef7d77926 --- /dev/null +++ b/packages/swapper/src/swappers/DebridgeSwapper/utils/getTrade.ts @@ -0,0 +1,508 @@ +import { fromAssetId, isAssetReference } from '@shapeshiftoss/caip' +import { evm, isEvmChainId } from '@shapeshiftoss/chain-adapters' +import { + BigAmount, + bnOrZero, + chainIdToFeeAssetId, + getBaseAsset, + isTreasuryChainId, +} from '@shapeshiftoss/utils' +import type { Result } from '@sniptt/monads' +import { Err, Ok } from '@sniptt/monads' +import axios from 'axios' +import { v4 as uuid } from 'uuid' +import { zeroAddress } from 'viem' + +import type { + SwapErrorRight, + SwapperDeps, + TradeQuote, + TradeQuoteStep, + TradeRate, + TradeRateStep, +} from '../../../types' +import { SwapperName, TradeQuoteError } from '../../../types' +import { getInputOutputRate, makeSwapErrorRight } from '../../../utils' +import { getTreasuryAddressFromChainId, isNativeEvmAsset } from '../../utils/helpers/helpers' +import { + chainIdToDebridgeChainId, + debridgeChainIdToChainId, + DEFAULT_DEBRIDGE_TOKEN_ADDRESS, + DEFAULT_DEBRIDGE_USER_ADDRESS, +} from '../constant' +import { fetchDebridgeSingleChainTrade } from './fetchDebridgeSingleChainTrade' +import { fetchDebridgeTrade } from './fetchDebridgeTrade' +import type { DebridgeTradeInputParams, DebridgeTransactionMetadata } from './types' +import { isDebridgeError } from './types' + +const getDebridgeAssetAddress = (assetId: string): string => { + if (isNativeEvmAsset(assetId)) return DEFAULT_DEBRIDGE_TOKEN_ADDRESS + + const { assetReference } = fromAssetId(assetId) + return isAssetReference(assetReference) ? zeroAddress : assetReference +} + +export async function getTrade(args: { + input: DebridgeTradeInputParams<'quote'> + deps: SwapperDeps +}): Promise> + +export async function getTrade(args: { + input: DebridgeTradeInputParams<'rate'> + deps: SwapperDeps +}): Promise> + +export async function getTrade({ + input, + deps, +}: { + input: DebridgeTradeInputParams + deps: SwapperDeps +}): Promise> { + const { sellAsset, buyAsset, sellAmountIncludingProtocolFeesCryptoBaseUnit, affiliateBps } = input + + const isSameChainSwap = sellAsset.chainId === buyAsset.chainId + + const sellDebridgeChainId = chainIdToDebridgeChainId[sellAsset.chainId] + + if (sellDebridgeChainId === undefined) { + return Err( + makeSwapErrorRight({ + message: `Sell asset chain '${sellAsset.chainId}' not supported by deBridge`, + code: TradeQuoteError.UnsupportedChain, + }), + ) + } + + if (!isSameChainSwap) { + const buyDebridgeChainId = chainIdToDebridgeChainId[buyAsset.chainId] + if (buyDebridgeChainId === undefined) { + return Err( + makeSwapErrorRight({ + message: `Buy asset chain '${buyAsset.chainId}' not supported by deBridge`, + code: TradeQuoteError.UnsupportedChain, + }), + ) + } + } + + const senderAddress = (() => { + if (input.quoteOrRate === 'rate') { + if (input.sendAddress) return input.sendAddress + return DEFAULT_DEBRIDGE_USER_ADDRESS + } + return input.sendAddress ?? DEFAULT_DEBRIDGE_USER_ADDRESS + })() + + const recipientAddress = (() => { + if (input.quoteOrRate === 'rate') { + if (input.receiveAddress) return input.receiveAddress + return DEFAULT_DEBRIDGE_USER_ADDRESS + } + return input.receiveAddress + })() + + const affiliateFeePercent = (() => { + if (!isTreasuryChainId(buyAsset.chainId)) return undefined + const bps = bnOrZero(affiliateBps) + if (!bps.isFinite() || bps.lte(0)) return undefined + return bps.div(100).toFixed() + })() + + const affiliateFeeRecipient = (() => { + if (affiliateFeePercent === undefined) return undefined + try { + return getTreasuryAddressFromChainId(buyAsset.chainId).toLowerCase() + } catch (e) { + console.error( + `[getTrade] Failed to get treasury address for chainId ${buyAsset.chainId}, affiliate fee will not be applied`, + e, + ) + return undefined + } + })() + + if (isSameChainSwap) { + return getSameChainTrade({ + input, + deps, + sellDebridgeChainId, + senderAddress, + recipientAddress, + affiliateFeePercent, + affiliateFeeRecipient, + }) + } + + const buyDebridgeChainId = chainIdToDebridgeChainId[buyAsset.chainId] + if (buyDebridgeChainId === undefined) { + return Err( + makeSwapErrorRight({ + message: `Buy asset chain '${buyAsset.chainId}' not supported by deBridge`, + code: TradeQuoteError.UnsupportedChain, + }), + ) + } + + const maybeQuote = await fetchDebridgeTrade( + { + srcChainId: sellDebridgeChainId, + srcChainTokenIn: getDebridgeAssetAddress(sellAsset.assetId), + srcChainTokenInAmount: sellAmountIncludingProtocolFeesCryptoBaseUnit, + dstChainId: buyDebridgeChainId, + dstChainTokenOut: getDebridgeAssetAddress(buyAsset.assetId), + dstChainTokenOutAmount: 'auto', + dstChainTokenOutRecipient: recipientAddress, + srcChainOrderAuthorityAddress: senderAddress, + dstChainOrderAuthorityAddress: recipientAddress, + senderAddress, + prependOperatingExpenses: 'true', + affiliateFeePercent, + affiliateFeeRecipient, + }, + deps.config, + ) + + if (maybeQuote.isErr()) { + return handleDebridgeError(maybeQuote.unwrapErr()) + } + + const { data: quote } = maybeQuote.unwrap() + + const buyAmountAfterFeesCryptoBaseUnit = quote.estimation.dstChainTokenOut.recommendedAmount + + const rate = getInputOutputRate({ + sellAmountCryptoBaseUnit: sellAmountIncludingProtocolFeesCryptoBaseUnit, + buyAmountCryptoBaseUnit: buyAmountAfterFeesCryptoBaseUnit, + sellAsset, + buyAsset, + }) + + const nativePreFee = bnOrZero(quote.fixFee) + .plus(bnOrZero(quote.prependedOperatingExpenseCost)) + .toFixed() + + const buyAmountBeforeFeesCryptoBaseUnit = buyAmountAfterFeesCryptoBaseUnit + + const allowanceContract = isEvmChainId(sellAsset.chainId) ? quote.tx.to : '' + + const debridgeTransactionMetadata: DebridgeTransactionMetadata = { + to: quote.tx.to, + data: quote.tx.data, + value: quote.tx.value, + orderId: quote.orderId, + gasLimit: quote.estimatedTransactionFee?.details.gasLimit, + } + + const networkFeeCryptoBaseUnit = await getNetworkFee({ + sellAsset, + debridgeTransactionMetadata, + senderAddress, + deps, + }) + + const protocolFeeAssetCaipChainId = debridgeChainIdToChainId[sellDebridgeChainId.toString()] + + const protocolFeeAssetIdForFees = (() => { + if (!protocolFeeAssetCaipChainId) return undefined + return chainIdToFeeAssetId(protocolFeeAssetCaipChainId) + })() + + const accountNumber = input.accountNumber + + const step: TradeQuoteStep | TradeRateStep = { + allowanceContract, + rate, + buyAmountBeforeFeesCryptoBaseUnit, + buyAmountAfterFeesCryptoBaseUnit, + sellAmountIncludingProtocolFeesCryptoBaseUnit, + buyAsset, + sellAsset, + accountNumber, + feeData: { + networkFeeCryptoBaseUnit, + protocolFees: + protocolFeeAssetIdForFees && bnOrZero(nativePreFee).gt(0) + ? { + [protocolFeeAssetIdForFees]: { + amountCryptoBaseUnit: nativePreFee, + asset: getBaseAsset(sellAsset.chainId), + requiresBalance: true, + }, + } + : {}, + }, + source: SwapperName.Debridge, + estimatedExecutionTimeMs: quote.order.approximateFulfillmentDelay * 1000, + debridgeTransactionMetadata, + } + + const baseQuoteOrRate = { + id: quote.orderId, + rate, + swapperName: SwapperName.Debridge, + affiliateBps, + slippageTolerancePercentageDecimal: input.slippageTolerancePercentageDecimal, + } + + if (input.quoteOrRate === 'quote') { + if (!input.receiveAddress) { + return Err( + makeSwapErrorRight({ + message: 'Receive address is required for quote', + code: TradeQuoteError.InternalError, + }), + ) + } + + const tradeQuote: TradeQuote = { + ...baseQuoteOrRate, + steps: [step as TradeQuoteStep], + receiveAddress: input.receiveAddress, + quoteOrRate: 'quote' as const, + } + + return Ok([tradeQuote]) + } + + const tradeRate: TradeRate = { + ...baseQuoteOrRate, + steps: [step as TradeRateStep], + receiveAddress: recipientAddress, + quoteOrRate: 'rate' as const, + } + + return Ok([tradeRate]) +} + +function handleDebridgeError(error: SwapErrorRight): Result { + if (!axios.isAxiosError(error.cause)) { + return Err( + makeSwapErrorRight({ + message: 'Unknown error', + code: TradeQuoteError.UnknownError, + }), + ) + } + + const debridgeError = error.cause?.response?.data + + if (!isDebridgeError(debridgeError)) { + return Err( + makeSwapErrorRight({ + message: 'Unknown error', + code: TradeQuoteError.UnknownError, + }), + ) + } + + const errorMessage = debridgeError.errorMessage ?? debridgeError.message ?? 'Unknown error' + + return Err( + makeSwapErrorRight({ + message: errorMessage, + code: TradeQuoteError.UnknownError, + }), + ) +} + +async function getNetworkFee({ + sellAsset, + debridgeTransactionMetadata, + senderAddress, + deps, +}: { + sellAsset: { chainId: string } + debridgeTransactionMetadata: DebridgeTransactionMetadata + senderAddress: string + deps: SwapperDeps +}): Promise { + if (!isEvmChainId(sellAsset.chainId)) return undefined + + const adapter = deps.assertGetEvmChainAdapter(sellAsset.chainId) + const { average } = await adapter.getGasFeeData() + const supportsEIP1559 = 'maxFeePerGas' in average + + try { + const feeData = await evm.getFees({ + adapter, + data: debridgeTransactionMetadata.data, + to: debridgeTransactionMetadata.to, + value: debridgeTransactionMetadata.value, + from: senderAddress, + supportsEIP1559, + }) + + return feeData.networkFeeCryptoBaseUnit + } catch { + const gasLimitFromApi = debridgeTransactionMetadata.gasLimit + if (!gasLimitFromApi) return undefined + + return evm.calcNetworkFeeCryptoBaseUnit({ + ...average, + gasLimit: gasLimitFromApi, + supportsEIP1559, + }) + } +} + +async function getSameChainTrade({ + input, + deps, + sellDebridgeChainId, + senderAddress, + recipientAddress, + affiliateFeePercent, + affiliateFeeRecipient, +}: { + input: DebridgeTradeInputParams + deps: SwapperDeps + sellDebridgeChainId: number + senderAddress: string + recipientAddress: string | undefined + affiliateFeePercent: string | undefined + affiliateFeeRecipient: string | undefined +}): Promise> { + const { sellAsset, buyAsset, sellAmountIncludingProtocolFeesCryptoBaseUnit, affiliateBps } = input + + const slippage = input.slippageTolerancePercentageDecimal + ? bnOrZero(input.slippageTolerancePercentageDecimal).times(100).toFixed() + : '5' + + const maybeQuote = await fetchDebridgeSingleChainTrade( + { + chainId: sellDebridgeChainId, + tokenIn: getDebridgeAssetAddress(sellAsset.assetId), + tokenInAmount: sellAmountIncludingProtocolFeesCryptoBaseUnit, + tokenOut: getDebridgeAssetAddress(buyAsset.assetId), + tokenOutRecipient: recipientAddress, + senderAddress, + slippage, + affiliateFeePercent, + affiliateFeeRecipient, + }, + deps.config, + ) + + if (maybeQuote.isErr()) { + return handleDebridgeError(maybeQuote.unwrapErr()) + } + + const { data: quote } = maybeQuote.unwrap() + + const buyAmountAfterFeesCryptoBaseUnit = quote.tokenOut.amount + + const rate = getInputOutputRate({ + sellAmountCryptoBaseUnit: sellAmountIncludingProtocolFeesCryptoBaseUnit, + buyAmountCryptoBaseUnit: buyAmountAfterFeesCryptoBaseUnit, + sellAsset, + buyAsset, + }) + + const protocolFeeAmount = quote.protocolFee ?? '0' + + const buyAmountBeforeFeesCryptoBaseUnit = (() => { + if (protocolFeeAmount === '0') return buyAmountAfterFeesCryptoBaseUnit + + return BigAmount.fromBaseUnit({ + value: buyAmountAfterFeesCryptoBaseUnit, + precision: buyAsset.precision, + }) + .plus( + BigAmount.fromBaseUnit({ + value: protocolFeeAmount, + precision: buyAsset.precision, + }), + ) + .toBaseUnit() + })() + + const allowanceContract = isEvmChainId(sellAsset.chainId) ? quote.tx.to : '' + + const debridgeTransactionMetadata: DebridgeTransactionMetadata = { + to: quote.tx.to, + data: quote.tx.data, + value: quote.tx.value, + gasLimit: quote.estimatedTransactionFee?.details.gasLimit, + isSameChainSwap: true, + } + + const networkFeeCryptoBaseUnit = await getNetworkFee({ + sellAsset, + debridgeTransactionMetadata, + senderAddress, + deps, + }) + + const tradeId = uuid() + + const step: TradeQuoteStep | TradeRateStep = { + allowanceContract, + rate, + buyAmountBeforeFeesCryptoBaseUnit, + buyAmountAfterFeesCryptoBaseUnit, + sellAmountIncludingProtocolFeesCryptoBaseUnit, + buyAsset, + sellAsset, + accountNumber: input.accountNumber, + feeData: { + networkFeeCryptoBaseUnit, + protocolFees: + protocolFeeAmount !== '0' + ? { + [buyAsset.assetId]: { + amountCryptoBaseUnit: protocolFeeAmount, + asset: { + symbol: quote.tokenOut.symbol, + chainId: buyAsset.chainId, + precision: buyAsset.precision, + }, + requiresBalance: false, + }, + } + : {}, + }, + source: SwapperName.Debridge, + estimatedExecutionTimeMs: 15_000, + debridgeTransactionMetadata, + } + + const baseQuoteOrRate = { + id: tradeId, + rate, + swapperName: SwapperName.Debridge, + affiliateBps, + slippageTolerancePercentageDecimal: + input.slippageTolerancePercentageDecimal ?? + (quote.slippage ? bnOrZero(quote.slippage).div(100).toString() : undefined), + } + + if (input.quoteOrRate === 'quote') { + if (!input.receiveAddress) { + return Err( + makeSwapErrorRight({ + message: 'Receive address is required for quote', + code: TradeQuoteError.InternalError, + }), + ) + } + + const tradeQuote: TradeQuote = { + ...baseQuoteOrRate, + steps: [step as TradeQuoteStep], + receiveAddress: input.receiveAddress, + quoteOrRate: 'quote' as const, + } + + return Ok([tradeQuote]) + } + + const tradeRate: TradeRate = { + ...baseQuoteOrRate, + steps: [step as TradeRateStep], + receiveAddress: recipientAddress, + quoteOrRate: 'rate' as const, + } + + return Ok([tradeRate]) +} diff --git a/packages/swapper/src/swappers/DebridgeSwapper/utils/types.ts b/packages/swapper/src/swappers/DebridgeSwapper/utils/types.ts new file mode 100644 index 00000000000..bb7088fa579 --- /dev/null +++ b/packages/swapper/src/swappers/DebridgeSwapper/utils/types.ts @@ -0,0 +1,195 @@ +import type { Asset } from '@shapeshiftoss/types' + +export type DebridgeTradeBaseParams = { + buyAsset: Asset + sellAsset: Asset + sellAmountIncludingProtocolFeesCryptoBaseUnit: string + affiliateBps: string +} + +export type DebridgeTradeInputParams = DebridgeTradeBaseParams & { + quoteOrRate: T + receiveAddress: T extends 'rate' ? string | undefined : string + sendAddress: T extends 'rate' ? undefined : string + accountNumber: T extends 'rate' ? undefined : number + slippageTolerancePercentageDecimal?: string +} + +export type DebridgeTransactionMetadata = { + to: string + data: string + value: string + gasLimit?: string + orderId?: string + isSameChainSwap?: boolean +} + +export type DebridgeTokenInfo = { + address: string + chainId: number + decimals: number + name: string + symbol: string + amount: string + approximateOperatingExpense?: string + mutatedWithOperatingExpense?: boolean + approximateUsdValue?: number + maxRefundAmount?: string +} + +export type DebridgeCostDetail = { + type: + | 'DlnProtocolFee' + | 'TakerMargin' + | 'EstimatedOperatingExpenses' + | 'InputTokenFee' + | 'OutputTokenFee' + payload: { + feeAmount: string + feeBps?: string + chain?: string + tokenAddress?: string + } +} + +export type DebridgeEstimation = { + srcChainTokenIn: DebridgeTokenInfo + srcChainTokenOut?: DebridgeTokenInfo + dstChainTokenOut: DebridgeTokenInfo & { + recommendedAmount: string + } + costsDetails: DebridgeCostDetail[] + recommendedSlippage: number +} + +export type DebridgeTx = { + to: string + data: string + value: string +} + +export type DebridgeOrder = { + approximateFulfillmentDelay: number + salt: number + metadata: string +} + +export type DebridgeCreateTxResponse = { + estimation: DebridgeEstimation + tx: DebridgeTx + orderId: string + order: DebridgeOrder + fixFee: string + prependedOperatingExpenseCost?: string + estimatedTransactionFee?: DebridgeSingleChainEstimatedTransactionFee +} + +export type DebridgeOrderStatus = { + orderId: string + status: + | 'None' + | 'Created' + | 'Fulfilled' + | 'SentUnlock' + | 'ClaimedUnlock' + | 'OrderCancelled' + | 'SentOrderCancel' + | 'ClaimedOrderCancel' +} + +export type DebridgeHashObject = { + stringValue: string + bytesValue: string + bytesArrayValue: string +} + +export type DebridgeOrderDetail = { + orderId: DebridgeHashObject + fulfilledDstEventMetadata?: { + transactionHash?: DebridgeHashObject + } +} + +export type DebridgeOrderId = { + bytesValue: string + bytesArrayValue: string + stringValue: string +} + +export type DebridgeOrderIdsResponse = { + orderIds: DebridgeOrderId[] +} + +export type DebridgeSingleChainTokenInfo = { + address: string + name: string + symbol: string + decimals: number + amount: string + approximateUsdValue?: number +} + +export type DebridgeSingleChainTokenOut = DebridgeSingleChainTokenInfo & { + minAmount: string +} + +export type DebridgeSingleChainCostDetail = { + chain: string + tokenIn: string + tokenOut: string + amountIn: string + amountOut: string + type: string + payload: { + feeAmount?: string + feeBps?: string + amountOutBeforeCorrection?: string + estimatedVolatilityBps?: string + actualFeeAmount?: string + actualFeeBps?: string + subsidyAmount?: string + feeApproximateUsdValue?: string + } +} + +export type DebridgeSingleChainEstimatedTransactionFee = { + total: string + details: { + gasLimit: string + baseFee?: string + maxFeePerGas?: string + maxPriorityFeePerGas?: string + } + approximateUsdValue?: number +} + +export type DebridgeSingleChainTransactionResponse = { + tokenIn: DebridgeSingleChainTokenInfo + tokenOut: DebridgeSingleChainTokenOut + slippage: number + recommendedSlippage: number + protocolFee?: string + costsDetails: DebridgeSingleChainCostDetail[] + estimatedTransactionFee?: DebridgeSingleChainEstimatedTransactionFee + tx: { + to: string + data: string + value: string + } +} + +export type DebridgeError = { + errorCode?: number + errorId?: string + errorMessage?: string + statusCode?: number + message?: string +} + +export const isDebridgeError = (error: unknown): error is DebridgeError => { + return ( + typeof error === 'object' && + error !== null && + ('errorCode' in error || 'errorId' in error || 'errorMessage' in error) + ) +} diff --git a/packages/swapper/src/swappers/RelaySwapper/constant.ts b/packages/swapper/src/swappers/RelaySwapper/constant.ts index 3439b2676a0..92e1b99d5e8 100644 --- a/packages/swapper/src/swappers/RelaySwapper/constant.ts +++ b/packages/swapper/src/swappers/RelaySwapper/constant.ts @@ -26,6 +26,7 @@ import { plumeChainId, polygonChainId, scrollChainId, + seiChainId, solanaChainId, soneiumChainId, sonicChainId, @@ -62,6 +63,7 @@ import { plumeMainnet, polygon, scroll, + sei, soneium, sonic, story, @@ -115,6 +117,7 @@ export const chainIdToRelayChainId = { [bobChainId]: bob.id, [modeChainId]: mode.id, [soneiumChainId]: soneium.id, + [seiChainId]: sei.id, } export enum RelayStatusMessage { diff --git a/packages/swapper/src/swappers/RelaySwapper/utils/relayTokenToAssetId.ts b/packages/swapper/src/swappers/RelaySwapper/utils/relayTokenToAssetId.ts index fb7ce108209..7e819b1c861 100644 --- a/packages/swapper/src/swappers/RelaySwapper/utils/relayTokenToAssetId.ts +++ b/packages/swapper/src/swappers/RelaySwapper/utils/relayTokenToAssetId.ts @@ -241,6 +241,11 @@ export const relayTokenToAssetId = (relayToken: RelayToken): AssetId => { assetReference: ASSET_REFERENCE.Soneium, assetNamespace: ASSET_NAMESPACE.slip44, } + case CHAIN_REFERENCE.SeiMainnet: + return { + assetReference: ASSET_REFERENCE.Sei, + assetNamespace: ASSET_NAMESPACE.slip44, + } default: throw Error(`chainId '${relayToken.chainId}' not supported`) } diff --git a/packages/swapper/src/types.ts b/packages/swapper/src/types.ts index a946986a844..582d4e1456a 100644 --- a/packages/swapper/src/types.ts +++ b/packages/swapper/src/types.ts @@ -45,6 +45,7 @@ import type { Address, Hex } from 'viem' import type { AcrossTransactionMetadata } from './swappers/AcrossSwapper/utils/types' import type { CowMessageToSign } from './swappers/CowSwapper/types' +import type { DebridgeTransactionMetadata } from './swappers/DebridgeSwapper/utils/types' import type { RelayTransactionMetadata } from './swappers/RelaySwapper/utils/types' import type { makeSwapperAxiosServiceMonadic } from './utils' @@ -84,6 +85,7 @@ export type SwapperConfig = { VITE_SUI_NODE_URL: string VITE_ACROSS_API_URL: string VITE_ACROSS_INTEGRATOR_ID: string + VITE_DEBRIDGE_API_URL: string } export enum SwapperName { @@ -105,6 +107,7 @@ export enum SwapperName { Avnu = 'AVNU', Stonfi = 'STON.fi', Across = 'Across', + Debridge = 'deBridge', } export type SwapSource = SwapperName | `${SwapperName} • ${string}` @@ -482,6 +485,7 @@ export type TradeQuoteStep = { params?: unknown } acrossTransactionMetadata?: AcrossTransactionMetadata + debridgeTransactionMetadata?: DebridgeTransactionMetadata } export type TradeRateStep = Omit & { @@ -543,6 +547,7 @@ export type SwapperSpecificMetadata = { } relayTransactionMetadata: RelayTransactionMetadata | undefined acrossTransactionMetadata: AcrossTransactionMetadata | undefined + debridgeTransactionMetadata: DebridgeTransactionMetadata | undefined relayerExplorerTxLink: string | undefined relayerTxHash: string | undefined stepIndex: SupportedTradeQuoteStepIndex diff --git a/packages/types/src/base.ts b/packages/types/src/base.ts index 416ad68c24f..be6700bacce 100644 --- a/packages/types/src/base.ts +++ b/packages/types/src/base.ts @@ -39,6 +39,7 @@ export enum KnownChainIds { BlastMainnet = 'eip155:81457', WorldChainMainnet = 'eip155:480', HemiMainnet = 'eip155:43111', + SeiMainnet = 'eip155:1329', LineaMainnet = 'eip155:59144', ScrollMainnet = 'eip155:534352', SonicMainnet = 'eip155:146', @@ -88,6 +89,7 @@ export type EvmChainId = | KnownChainIds.BlastMainnet | KnownChainIds.WorldChainMainnet | KnownChainIds.HemiMainnet + | KnownChainIds.SeiMainnet | KnownChainIds.LineaMainnet | KnownChainIds.ScrollMainnet | KnownChainIds.SonicMainnet diff --git a/packages/types/src/zerion.ts b/packages/types/src/zerion.ts index 157ddbc9e53..82653d1c707 100644 --- a/packages/types/src/zerion.ts +++ b/packages/types/src/zerion.ts @@ -21,6 +21,7 @@ import { optimismChainId, plasmaChainId, polygonChainId, + seiChainId, solanaChainId, soneiumChainId, sonicChainId, @@ -57,6 +58,7 @@ export const ZERION_CHAINS = [ 'mantle', 'linea', 'berachain', + 'sei', 'ink', 'cronos', 'sonic', @@ -95,6 +97,7 @@ export const ZERION_CHAINS_MAP: Record = { mantle: mantleChainId, linea: lineaChainId, berachain: berachainChainId, + sei: seiChainId, ink: inkChainId, cronos: cronosChainId, sonic: sonicChainId, diff --git a/packages/utils/src/assetData/baseAssets.ts b/packages/utils/src/assetData/baseAssets.ts index db4c13fad31..6152903be69 100644 --- a/packages/utils/src/assetData/baseAssets.ts +++ b/packages/utils/src/assetData/baseAssets.ts @@ -592,6 +592,23 @@ export const hemi: Readonly = Object.freeze({ relatedAssetKey: 'eip155:1/slip44:60', }) +export const sei: Readonly = Object.freeze({ + assetId: caip.seiAssetId, + chainId: caip.seiChainId, + name: 'SEI', + networkName: 'Sei', + symbol: 'SEI', + precision: 18, + color: '#9B1B30', + networkColor: '#9B1B30', + icon: 'https://assets.coingecko.com/coins/images/28205/large/Sei_Logo_-_Transparent.png', + networkIcon: 'https://assets.coingecko.com/coins/images/28205/small/Sei_Logo_-_Transparent.png', + explorer: 'https://seitrace.com', + explorerAddressLink: 'https://seitrace.com/address/', + explorerTxLink: 'https://seitrace.com/tx/', + relatedAssetKey: null, +}) + export const linea: Readonly = Object.freeze({ assetId: caip.lineaAssetId, chainId: caip.lineaChainId, diff --git a/packages/utils/src/assetData/getBaseAsset.ts b/packages/utils/src/assetData/getBaseAsset.ts index 1a20adac99a..be46d42bbc5 100644 --- a/packages/utils/src/assetData/getBaseAsset.ts +++ b/packages/utils/src/assetData/getBaseAsset.ts @@ -37,6 +37,7 @@ import { plume, polygon, scroll, + sei, solana, soneium, sonic, @@ -127,6 +128,8 @@ export const getBaseAsset = (chainId: ChainId): Readonly => { return worldchain case KnownChainIds.HemiMainnet: return hemi + case KnownChainIds.SeiMainnet: + return sei case KnownChainIds.LineaMainnet: return linea case KnownChainIds.ScrollMainnet: diff --git a/packages/utils/src/chainIdToFeeAssetId.ts b/packages/utils/src/chainIdToFeeAssetId.ts index fc3a25e1f78..1042c129ad5 100644 --- a/packages/utils/src/chainIdToFeeAssetId.ts +++ b/packages/utils/src/chainIdToFeeAssetId.ts @@ -33,6 +33,7 @@ import { plumeAssetId, polygonAssetId, scrollAssetId, + seiAssetId, solAssetId, soneiumAssetId, sonicAssetId, @@ -126,6 +127,8 @@ export const chainIdToFeeAssetId = (_chainId: ChainId): AssetId => { return worldChainAssetId case KnownChainIds.HemiMainnet: return hemiAssetId + case KnownChainIds.SeiMainnet: + return seiAssetId case KnownChainIds.LineaMainnet: return lineaAssetId case KnownChainIds.ScrollMainnet: diff --git a/packages/utils/src/getAssetNamespaceFromChainId.ts b/packages/utils/src/getAssetNamespaceFromChainId.ts index 2fd9dd12509..e9c27be1bb1 100644 --- a/packages/utils/src/getAssetNamespaceFromChainId.ts +++ b/packages/utils/src/getAssetNamespaceFromChainId.ts @@ -37,6 +37,7 @@ export const getAssetNamespaceFromChainId = (chainId: KnownChainIds): AssetNames case KnownChainIds.BlastMainnet: case KnownChainIds.WorldChainMainnet: case KnownChainIds.HemiMainnet: + case KnownChainIds.SeiMainnet: case KnownChainIds.LineaMainnet: case KnownChainIds.ScrollMainnet: case KnownChainIds.SonicMainnet: diff --git a/packages/utils/src/getChainShortName.ts b/packages/utils/src/getChainShortName.ts index db3a897c50b..a486b7991a3 100644 --- a/packages/utils/src/getChainShortName.ts +++ b/packages/utils/src/getChainShortName.ts @@ -76,6 +76,8 @@ export const getChainShortName = (chainId: KnownChainIds) => { return 'WLD' case KnownChainIds.HemiMainnet: return 'HEM' + case KnownChainIds.SeiMainnet: + return 'SEI' case KnownChainIds.LineaMainnet: return 'LIN' case KnownChainIds.ScrollMainnet: diff --git a/packages/utils/src/getNativeFeeAssetReference.ts b/packages/utils/src/getNativeFeeAssetReference.ts index ae76adb2a00..97f2df61f78 100644 --- a/packages/utils/src/getNativeFeeAssetReference.ts +++ b/packages/utils/src/getNativeFeeAssetReference.ts @@ -74,6 +74,8 @@ export const getNativeFeeAssetReference = ( return ASSET_REFERENCE.WorldChain case CHAIN_REFERENCE.HemiMainnet: return ASSET_REFERENCE.Hemi + case CHAIN_REFERENCE.SeiMainnet: + return ASSET_REFERENCE.Sei case CHAIN_REFERENCE.LineaMainnet: return ASSET_REFERENCE.Linea case CHAIN_REFERENCE.ScrollMainnet: diff --git a/public/generated/asset-manifest.json b/public/generated/asset-manifest.json index 5b177d0f8d5..ce9ce6f5b59 100644 --- a/public/generated/asset-manifest.json +++ b/public/generated/asset-manifest.json @@ -1,4 +1,4 @@ { - "assetData": "f451bef9", - "relatedAssetIndex": "074b1120" + "assetData": "848146b7", + "relatedAssetIndex": "78acda28" } \ No newline at end of file diff --git a/public/generated/asset-manifest.json.br b/public/generated/asset-manifest.json.br index 29126fed843..a83bff5fae4 100644 Binary files a/public/generated/asset-manifest.json.br and b/public/generated/asset-manifest.json.br differ diff --git a/public/generated/asset-manifest.json.gz b/public/generated/asset-manifest.json.gz index d1308877071..0f538061b6b 100644 Binary files a/public/generated/asset-manifest.json.gz and b/public/generated/asset-manifest.json.gz differ diff --git a/public/generated/generatedAssetData.json b/public/generated/generatedAssetData.json index 711a8c33517..7ccd8932036 100644 --- a/public/generated/generatedAssetData.json +++ b/public/generated/generatedAssetData.json @@ -270597,7 +270597,7 @@ "explorer": "https://bscscan.com", "explorerAddressLink": "https://bscscan.com/address/", "explorerTxLink": "https://bscscan.com/tx/", - "relatedAssetKey": null + "relatedAssetKey": "eip155:1329/erc20:0x81d3a238b02827f62b9f390f947d36d4a5bf89d2" }, "eip155:56/erc20:0x81de84e51f49983e043a8527ddfae08238acc330": { "assetId": "eip155:56/erc20:0x81de84e51f49983e043a8527ddfae08238acc330", @@ -288802,7 +288802,7 @@ "explorer": "https://bscscan.com", "explorerAddressLink": "https://bscscan.com/address/", "explorerTxLink": "https://bscscan.com/tx/", - "relatedAssetKey": null + "relatedAssetKey": "eip155:1329/erc20:0xcc1b8207853662c5cfabfb028806ec06ea1f6ac6" }, "eip155:56/erc20:0xcc1db43a06d97f736c7b045aedd03c6707c09bdf": { "explorer": "https://bscscan.com", @@ -437692,6 +437692,659 @@ "explorerAddressLink": "https://celoscan.io/address/", "explorerTxLink": "https://celoscan.io/tx/", "relatedAssetKey": null + }, + "eip155:1329/erc20:0x0555e30da8f98308edb960aa94c0db47230d2b9c": { + "assetId": "eip155:1329/erc20:0x0555e30da8f98308edb960aa94c0db47230d2b9c", + "chainId": "eip155:1329", + "name": "Wrapped Bitcoin", + "precision": 8, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/7598/large/WBTCLOGO.png?1764496367", + "symbol": "WBTC", + "explorer": "https://seitrace.com", + "explorerAddressLink": "https://seitrace.com/address/", + "explorerTxLink": "https://seitrace.com/tx/", + "relatedAssetKey": "eip155:1/erc20:0x2260fac5e5542a773aa44fbcfedf7c193bc2c599" + }, + "eip155:1329/erc20:0x059a6b0ba116c63191182a0956cf697d0d2213ec": { + "assetId": "eip155:1329/erc20:0x059a6b0ba116c63191182a0956cf697d0d2213ec", + "chainId": "eip155:1329", + "name": "Synnax Stablecoin", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/50179/large/7.png?1726164913", + "symbol": "SYUSD", + "explorer": "https://seitrace.com", + "explorerAddressLink": "https://seitrace.com/address/", + "explorerTxLink": "https://seitrace.com/tx/", + "relatedAssetKey": null + }, + "eip155:1329/erc20:0x09d4214c03d01f49544c0448dbe3a27f768f2b34": { + "assetId": "eip155:1329/erc20:0x09d4214c03d01f49544c0448dbe3a27f768f2b34", + "chainId": "eip155:1329", + "name": "Reservoir rUSD", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/54117/large/rusd_bera2.png?1738311629", + "symbol": "RUSD", + "explorer": "https://seitrace.com", + "explorerAddressLink": "https://seitrace.com/address/", + "explorerTxLink": "https://seitrace.com/tx/", + "relatedAssetKey": "eip155:1/erc20:0x09d4214c03d01f49544c0448dbe3a27f768f2b34" + }, + "eip155:1329/erc20:0x0a526e425809aea71eb279d24ae22dee6c92a4fe": { + "assetId": "eip155:1329/erc20:0x0a526e425809aea71eb279d24ae22dee6c92a4fe", + "chainId": "eip155:1329", + "name": "DragonSwap", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/67207/large/_DRG_token_.png?1752088626", + "symbol": "DRG", + "explorer": "https://seitrace.com", + "explorerAddressLink": "https://seitrace.com/address/", + "explorerTxLink": "https://seitrace.com/tx/", + "relatedAssetKey": null + }, + "eip155:1329/erc20:0x160345fc359604fc6e70e3c5facbde5f7a9342d8": { + "assetId": "eip155:1329/erc20:0x160345fc359604fc6e70e3c5facbde5f7a9342d8", + "chainId": "eip155:1329", + "name": "Stargate Bridged WETH", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/69466/large/weth_2.jpg?1758701596", + "symbol": "WETH", + "explorer": "https://seitrace.com", + "explorerAddressLink": "https://seitrace.com/address/", + "explorerTxLink": "https://seitrace.com/tx/", + "relatedAssetKey": "eip155:1/erc20:0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" + }, + "eip155:1329/erc20:0x3894085ef7ff0f0aedf52e2a2704928d1ec074f1": { + "assetId": "eip155:1329/erc20:0x3894085ef7ff0f0aedf52e2a2704928d1ec074f1", + "chainId": "eip155:1329", + "name": "Noble USDC", + "precision": 6, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/38604/large/usdc.png?1718098770", + "symbol": "USDC.N", + "explorer": "https://seitrace.com", + "explorerAddressLink": "https://seitrace.com/address/", + "explorerTxLink": "https://seitrace.com/tx/", + "relatedAssetKey": "eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" + }, + "eip155:1329/erc20:0x443ac9f358226f5f48f2cd10bc0121e7a6176323": { + "assetId": "eip155:1329/erc20:0x443ac9f358226f5f48f2cd10bc0121e7a6176323", + "chainId": "eip155:1329", + "name": "BitBat Token", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/69010/large/logo.png?1757254589", + "symbol": "BAT", + "explorer": "https://seitrace.com", + "explorerAddressLink": "https://seitrace.com/address/", + "explorerTxLink": "https://seitrace.com/tx/", + "relatedAssetKey": null + }, + "eip155:1329/erc20:0x4809010926aec940b550d34a46a52739f996d75d": { + "assetId": "eip155:1329/erc20:0x4809010926aec940b550d34a46a52739f996d75d", + "chainId": "eip155:1329", + "name": "Wrapped Savings rUSD", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/66163/large/wsrUSD.png?1748500965", + "symbol": "WSRUSD", + "explorer": "https://seitrace.com", + "explorerAddressLink": "https://seitrace.com/address/", + "explorerTxLink": "https://seitrace.com/tx/", + "relatedAssetKey": "eip155:42161/erc20:0x4809010926aec940b550d34a46a52739f996d75d" + }, + "eip155:1329/erc20:0x481fe356df88169f5f38203dd7f3c67b7559fda5": { + "assetId": "eip155:1329/erc20:0x481fe356df88169f5f38203dd7f3c67b7559fda5", + "chainId": "eip155:1329", + "name": "Raiinmaker", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/67517/large/200x200_Logo.png?1753820843", + "symbol": "RAIIN", + "explorer": "https://seitrace.com", + "explorerAddressLink": "https://seitrace.com/address/", + "explorerTxLink": "https://seitrace.com/tx/", + "relatedAssetKey": null + }, + "eip155:1329/erc20:0x4a1ea452c598363188bccc83fb4f69dc14b54acd": { + "assetId": "eip155:1329/erc20:0x4a1ea452c598363188bccc83fb4f69dc14b54acd", + "chainId": "eip155:1329", + "name": "MetaArena", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/70582/large/metareana.png?1762670673", + "symbol": "TIMI", + "explorer": "https://seitrace.com", + "explorerAddressLink": "https://seitrace.com/address/", + "explorerTxLink": "https://seitrace.com/tx/", + "relatedAssetKey": null + }, + "eip155:1329/erc20:0x51121bcae92e302f19d06c193c95e1f7b81a444b": { + "assetId": "eip155:1329/erc20:0x51121bcae92e302f19d06c193c95e1f7b81a444b", + "chainId": "eip155:1329", + "name": "YAKA", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/50889/large/IMG_6242.png?1741333572", + "symbol": "YAKA", + "explorer": "https://seitrace.com", + "explorerAddressLink": "https://seitrace.com/address/", + "explorerTxLink": "https://seitrace.com/tx/", + "relatedAssetKey": null + }, + "eip155:1329/erc20:0x541fd749419ca806a8bc7da8ac23d346f2df8b77": { + "assetId": "eip155:1329/erc20:0x541fd749419ca806a8bc7da8ac23d346f2df8b77", + "chainId": "eip155:1329", + "name": "Solv Protocol BTC", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/36800/large/solvBTC.png?1719810684", + "symbol": "SOLVBTC", + "explorer": "https://seitrace.com", + "explorerAddressLink": "https://seitrace.com/address/", + "explorerTxLink": "https://seitrace.com/tx/", + "relatedAssetKey": "eip155:42161/erc20:0x3647c54c4c2c65bc7a2d63c0da2809b399dbbdc0" + }, + "eip155:1329/erc20:0x54cd901491aef397084453f4372b93c33260e2a6": { + "assetId": "eip155:1329/erc20:0x54cd901491aef397084453f4372b93c33260e2a6", + "chainId": "eip155:1329", + "name": "Ondo US Dollar Yield", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/31700/large/usdy_%281%29.png?1696530524", + "symbol": "USDY", + "explorer": "https://seitrace.com", + "explorerAddressLink": "https://seitrace.com/address/", + "explorerTxLink": "https://seitrace.com/tx/", + "relatedAssetKey": "eip155:42161/erc20:0x35e050d3c0ec2d29d269a8ecea763a183bdf9a9d" + }, + "eip155:1329/erc20:0x58e11d8ed38a2061361e90916540c5c32281a380": { + "assetId": "eip155:1329/erc20:0x58e11d8ed38a2061361e90916540c5c32281a380", + "chainId": "eip155:1329", + "name": "Grand Gangsta City", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/67656/large/200pixels.png?1753423916", + "symbol": "$GGC", + "explorer": "https://seitrace.com", + "explorerAddressLink": "https://seitrace.com/address/", + "explorerTxLink": "https://seitrace.com/tx/", + "relatedAssetKey": null + }, + "eip155:1329/erc20:0x5b8034f6346a81a1387ea21cdd36c48f6e05eb5f": { + "assetId": "eip155:1329/erc20:0x5b8034f6346a81a1387ea21cdd36c48f6e05eb5f", + "chainId": "eip155:1329", + "name": "Vertex", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/27927/large/vrtx.png?1696526947", + "symbol": "VRTX", + "explorer": "https://seitrace.com", + "explorerAddressLink": "https://seitrace.com/address/", + "explorerTxLink": "https://seitrace.com/tx/", + "relatedAssetKey": "eip155:42161/erc20:0x95146881b86b3ee99e63705ec87afe29fcc044d9" + }, + "eip155:1329/erc20:0x5bff88ca1442c2496f7e475e9e7786383bc070c0": { + "assetId": "eip155:1329/erc20:0x5bff88ca1442c2496f7e475e9e7786383bc070c0", + "chainId": "eip155:1329", + "name": "Frax Staked frxUSD", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/53964/large/sfrxUSD.png?1737792232", + "symbol": "SFRXUSD", + "explorer": "https://seitrace.com", + "explorerAddressLink": "https://seitrace.com/address/", + "explorerTxLink": "https://seitrace.com/tx/", + "relatedAssetKey": "eip155:1/erc20:0xcf62f905562626cfcdd2261162a51fd02fc9c5b6" + }, + "eip155:1329/erc20:0x5cf6826140c1c56ff49c808a1a75407cd1df9423": { + "assetId": "eip155:1329/erc20:0x5cf6826140c1c56ff49c808a1a75407cd1df9423", + "chainId": "eip155:1329", + "name": "Silo Staked SEI", + "precision": 6, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/38591/large/iSEI_logo_200_200.png?1718091709", + "symbol": "ISEI", + "explorer": "https://seitrace.com", + "explorerAddressLink": "https://seitrace.com/address/", + "explorerTxLink": "https://seitrace.com/tx/", + "relatedAssetKey": null + }, + "eip155:1329/erc20:0x5f0e07dfee5832faa00c63f2d33a0d79150e8598": { + "assetId": "eip155:1329/erc20:0x5f0e07dfee5832faa00c63f2d33a0d79150e8598", + "chainId": "eip155:1329", + "name": "SEIYAN", + "precision": 6, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/33830/large/seiyanlogo.png?1703069383", + "symbol": "SEIYAN", + "explorer": "https://seitrace.com", + "explorerAddressLink": "https://seitrace.com/address/", + "explorerTxLink": "https://seitrace.com/tx/", + "relatedAssetKey": null + }, + "eip155:1329/erc20:0x60c230c38af6d86b0277a98a1caeaa345a7b061f": { + "assetId": "eip155:1329/erc20:0x60c230c38af6d86b0277a98a1caeaa345a7b061f", + "chainId": "eip155:1329", + "name": "Fiamma BTC", + "precision": 8, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/68062/large/fiabtc_200-200.png?1754642511", + "symbol": "FIABTC", + "explorer": "https://seitrace.com", + "explorerAddressLink": "https://seitrace.com/address/", + "explorerTxLink": "https://seitrace.com/tx/", + "relatedAssetKey": "eip155:1/erc20:0x2260fac5e5542a773aa44fbcfedf7c193bc2c599" + }, + "eip155:1329/erc20:0x64445f0aecc51e94ad52d8ac56b7190e764e561a": { + "assetId": "eip155:1329/erc20:0x64445f0aecc51e94ad52d8ac56b7190e764e561a", + "chainId": "eip155:1329", + "name": "Wrapped FRAX", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/55432/large/wfrax_64.png?1745993624", + "symbol": "WFRAX", + "explorer": "https://seitrace.com", + "explorerAddressLink": "https://seitrace.com/address/", + "explorerTxLink": "https://seitrace.com/tx/", + "relatedAssetKey": "eip155:98866/erc20:0x64445f0aecc51e94ad52d8ac56b7190e764e561a" + }, + "eip155:1329/erc20:0x71052bae71c25c78e37fd12e5ff1101a71d9018f": { + "assetId": "eip155:1329/erc20:0x71052bae71c25c78e37fd12e5ff1101a71d9018f", + "chainId": "eip155:1329", + "name": "Chainlink", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/877/large/Chainlink_Logo_500.png?1760023405", + "symbol": "LINK", + "explorer": "https://seitrace.com", + "explorerAddressLink": "https://seitrace.com/address/", + "explorerTxLink": "https://seitrace.com/tx/", + "relatedAssetKey": null + }, + "eip155:1329/erc20:0x78e26e8b953c7c78a58d69d8b9a91745c2bbb258": { + "assetId": "eip155:1329/erc20:0x78e26e8b953c7c78a58d69d8b9a91745c2bbb258", + "chainId": "eip155:1329", + "name": "uBTC", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/55079/large/uBTC.png?1743613297", + "symbol": "UBTC", + "explorer": "https://seitrace.com", + "explorerAddressLink": "https://seitrace.com/address/", + "explorerTxLink": "https://seitrace.com/tx/", + "relatedAssetKey": null + }, + "eip155:1329/erc20:0x80137510979822322193fc997d400d5a6c747bf7": { + "assetId": "eip155:1329/erc20:0x80137510979822322193fc997d400d5a6c747bf7", + "chainId": "eip155:1329", + "name": "StakeStone ETH", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/33103/large/200_200.png?1702602672", + "symbol": "STONE", + "explorer": "https://seitrace.com", + "explorerAddressLink": "https://seitrace.com/address/", + "explorerTxLink": "https://seitrace.com/tx/", + "relatedAssetKey": "eip155:42161/erc20:0x80137510979822322193fc997d400d5a6c747bf7" + }, + "eip155:1329/erc20:0x805679729df385815c57c24b20f4161bd34b655f": { + "assetId": "eip155:1329/erc20:0x805679729df385815c57c24b20f4161bd34b655f", + "chainId": "eip155:1329", + "name": "Fishwar", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/54843/large/logo_fishwar_no_bg200x200.png?1742019218", + "symbol": "FISHW", + "explorer": "https://seitrace.com", + "explorerAddressLink": "https://seitrace.com/address/", + "explorerTxLink": "https://seitrace.com/tx/", + "relatedAssetKey": null + }, + "eip155:1329/erc20:0x80eede496655fb9047dd39d9f418d5483ed600df": { + "assetId": "eip155:1329/erc20:0x80eede496655fb9047dd39d9f418d5483ed600df", + "chainId": "eip155:1329", + "name": "Frax USD", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/53963/large/frxUSD.png?1737792154", + "symbol": "FRXUSD", + "explorer": "https://seitrace.com", + "explorerAddressLink": "https://seitrace.com/address/", + "explorerTxLink": "https://seitrace.com/tx/", + "relatedAssetKey": "eip155:1/erc20:0xcacd6fd266af91b8aed52accc382b4e165586e29" + }, + "eip155:1329/erc20:0x81d3a238b02827f62b9f390f947d36d4a5bf89d2": { + "assetId": "eip155:1329/erc20:0x81d3a238b02827f62b9f390f947d36d4a5bf89d2", + "chainId": "eip155:1329", + "name": "Yei Finance", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/69987/large/CLO.png?1760503082", + "symbol": "CLO", + "explorer": "https://seitrace.com", + "explorerAddressLink": "https://seitrace.com/address/", + "explorerTxLink": "https://seitrace.com/tx/", + "relatedAssetKey": "eip155:1329/erc20:0x81d3a238b02827f62b9f390f947d36d4a5bf89d2" + }, + "eip155:1329/erc20:0x83c82f0f959ad3eff528ee513b43808aa53f4b37": { + "assetId": "eip155:1329/erc20:0x83c82f0f959ad3eff528ee513b43808aa53f4b37", + "chainId": "eip155:1329", + "name": "Zenrock", + "precision": 6, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/52032/large/Zenrock_Token_%282%29.png?1732376562", + "symbol": "ROCK", + "explorer": "https://seitrace.com", + "explorerAddressLink": "https://seitrace.com/address/", + "explorerTxLink": "https://seitrace.com/tx/", + "relatedAssetKey": null + }, + "eip155:1329/erc20:0x89aec21572f6637ccbd0d66861aaac46dd775ed1": { + "assetId": "eip155:1329/erc20:0x89aec21572f6637ccbd0d66861aaac46dd775ed1", + "chainId": "eip155:1329", + "name": "Kryptonite", + "precision": 6, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/31252/large/Kryptonite_PFP-03.png?1696530076", + "symbol": "SEILOR", + "explorer": "https://seitrace.com", + "explorerAddressLink": "https://seitrace.com/address/", + "explorerTxLink": "https://seitrace.com/tx/", + "relatedAssetKey": null + }, + "eip155:1329/erc20:0x8ee050f6af49a6b7fd8557d0e75219d66f5f6094": { + "assetId": "eip155:1329/erc20:0x8ee050f6af49a6b7fd8557d0e75219d66f5f6094", + "chainId": "eip155:1329", + "name": "Oxium", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/102171811/large/oxi.png?1769848020", + "symbol": "OXI", + "explorer": "https://seitrace.com", + "explorerAddressLink": "https://seitrace.com/address/", + "explorerTxLink": "https://seitrace.com/tx/", + "relatedAssetKey": null + }, + "eip155:1329/erc20:0x9151434b16b9763660705744891fa906f660ecc5": { + "assetId": "eip155:1329/erc20:0x9151434b16b9763660705744891fa906f660ecc5", + "chainId": "eip155:1329", + "name": "USDT0", + "precision": 6, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/53705/large/usdt0.jpg?1737086183", + "symbol": "USDT0", + "explorer": "https://seitrace.com", + "explorerAddressLink": "https://seitrace.com/address/", + "explorerTxLink": "https://seitrace.com/tx/", + "relatedAssetKey": "eip155:1/erc20:0xdac17f958d2ee523a2206206994597c13d831ec7" + }, + "eip155:1329/erc20:0x93919784c523f39cacaa98ee0a9d96c3f32b593e": { + "assetId": "eip155:1329/erc20:0x93919784c523f39cacaa98ee0a9d96c3f32b593e", + "chainId": "eip155:1329", + "name": "Bedrock BTC", + "precision": 8, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/53039/large/brBTC_200.png?1735074374", + "symbol": "BRBTC", + "explorer": "https://seitrace.com", + "explorerAddressLink": "https://seitrace.com/address/", + "explorerTxLink": "https://seitrace.com/tx/", + "relatedAssetKey": "eip155:8453/erc20:0x3376ebca0a85fc8d791b1001a571c41fdd61514a" + }, + "eip155:1329/erc20:0x95597eb8d227a7c4b4f5e807a815c5178ee6dbe1": { + "assetId": "eip155:1329/erc20:0x95597eb8d227a7c4b4f5e807a815c5178ee6dbe1", + "chainId": "eip155:1329", + "name": "MILLI", + "precision": 6, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/35724/large/milli_new.PNG?1731239997", + "symbol": "MILLI", + "explorer": "https://seitrace.com", + "explorerAddressLink": "https://seitrace.com/address/", + "explorerTxLink": "https://seitrace.com/tx/", + "relatedAssetKey": null + }, + "eip155:1329/erc20:0x9bfa177621119e64cecbeabe184ab9993e2ef727": { + "assetId": "eip155:1329/erc20:0x9bfa177621119e64cecbeabe184ab9993e2ef727", + "chainId": "eip155:1329", + "name": "Merlin's Seal BTC", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/36566/large/photo_2024-03-25_22-04-42.jpg?1711936162", + "symbol": "M-BTC", + "explorer": "https://seitrace.com", + "explorerAddressLink": "https://seitrace.com/address/", + "explorerTxLink": "https://seitrace.com/tx/", + "relatedAssetKey": "sui:35834a8a/coin:0xd1a91b46bd6d966b62686263609074ad16cfdffc63c31a4775870a2d54d20c6b::mbtc::MBTC" + }, + "eip155:1329/erc20:0x9faaea2cdd810b21594e54309dc847842ae301ce": { + "assetId": "eip155:1329/erc20:0x9faaea2cdd810b21594e54309dc847842ae301ce", + "chainId": "eip155:1329", + "name": "seiyanETH", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/51474/large/seiyanETH_%281%29.png?1731401763", + "symbol": "SETH", + "explorer": "https://seitrace.com", + "explorerAddressLink": "https://seitrace.com/address/", + "explorerTxLink": "https://seitrace.com/tx/", + "relatedAssetKey": null + }, + "eip155:1329/erc20:0xa193d87b0d5e23ef45379f90066891cbd6013226": { + "assetId": "eip155:1329/erc20:0xa193d87b0d5e23ef45379f90066891cbd6013226", + "chainId": "eip155:1329", + "name": "Archer Hunter", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/70339/large/Token_FrontClean_%282%29.png?1761638821", + "symbol": "FASTER", + "explorer": "https://seitrace.com", + "explorerAddressLink": "https://seitrace.com/address/", + "explorerTxLink": "https://seitrace.com/tx/", + "relatedAssetKey": null + }, + "eip155:1329/erc20:0xa8a3a5013104e093245164ea56588dbe10a3eb48": { + "assetId": "eip155:1329/erc20:0xa8a3a5013104e093245164ea56588dbe10a3eb48", + "chainId": "eip155:1329", + "name": "Super Seyian ETH", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/50331/large/2024-09-18_19.52.53.jpg?1727164288", + "symbol": "SSETH", + "explorer": "https://seitrace.com", + "explorerAddressLink": "https://seitrace.com/address/", + "explorerTxLink": "https://seitrace.com/tx/", + "relatedAssetKey": null + }, + "eip155:1329/erc20:0xb75d0b03c06a926e488e2659df1a861f860bd3d1": { + "assetId": "eip155:1329/erc20:0xb75d0b03c06a926e488e2659df1a861f860bd3d1", + "chainId": "eip155:1329", + "name": "IBC Bridged USDT (Sei)", + "precision": 6, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/38607/large/usdt_%281%29.png?1718099237", + "symbol": "KAVAUSDT", + "explorer": "https://seitrace.com", + "explorerAddressLink": "https://seitrace.com/address/", + "explorerTxLink": "https://seitrace.com/tx/", + "relatedAssetKey": "eip155:1/erc20:0xdac17f958d2ee523a2206206994597c13d831ec7" + }, + "eip155:1329/erc20:0xbd82f3bfe1df0c84faec88a22ebc34c9a86595dc": { + "assetId": "eip155:1329/erc20:0xbd82f3bfe1df0c84faec88a22ebc34c9a86595dc", + "chainId": "eip155:1329", + "name": "Chips", + "precision": 6, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/52405/large/logo.png?1733303663", + "symbol": "CHIPS", + "explorer": "https://seitrace.com", + "explorerAddressLink": "https://seitrace.com/address/", + "explorerTxLink": "https://seitrace.com/tx/", + "relatedAssetKey": null + }, + "eip155:1329/erc20:0xc18b6a15fb0ceaf5eb18696eefcb5bc7b9107149": { + "assetId": "eip155:1329/erc20:0xc18b6a15fb0ceaf5eb18696eefcb5bc7b9107149", + "chainId": "eip155:1329", + "name": "Popo The Cat", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/39686/large/8a872ee5-dfc6-4139-a157-9cdcbbeb2f59.jpeg?1723658023", + "symbol": "POPO", + "explorer": "https://seitrace.com", + "explorerAddressLink": "https://seitrace.com/address/", + "explorerTxLink": "https://seitrace.com/tx/", + "relatedAssetKey": null + }, + "eip155:1329/erc20:0xc257361320f4514d91c05f461006ce6a0300e2d2": { + "assetId": "eip155:1329/erc20:0xc257361320f4514d91c05f461006ce6a0300e2d2", + "chainId": "eip155:1329", + "name": "Splashing Staked SEI", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/68734/large/logo_spSEI.png?1756409469", + "symbol": "SPSEI", + "explorer": "https://seitrace.com", + "explorerAddressLink": "https://seitrace.com/address/", + "explorerTxLink": "https://seitrace.com/tx/", + "relatedAssetKey": null + }, + "eip155:1329/erc20:0xc53ac24320e3a54c7211e4993c8095078a0cb3cf": { + "assetId": "eip155:1329/erc20:0xc53ac24320e3a54c7211e4993c8095078a0cb3cf", + "chainId": "eip155:1329", + "name": "Wild Goat Coin", + "precision": 6, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/37966/large/wgcCoin-256px.png?1768798052", + "symbol": "WGC", + "explorer": "https://seitrace.com", + "explorerAddressLink": "https://seitrace.com/address/", + "explorerTxLink": "https://seitrace.com/tx/", + "relatedAssetKey": null + }, + "eip155:1329/erc20:0xcc0966d8418d412c599a6421b760a847eb169a8c": { + "assetId": "eip155:1329/erc20:0xcc0966d8418d412c599a6421b760a847eb169a8c", + "chainId": "eip155:1329", + "name": "Solv Protocol Staked BTC", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/39384/large/xSolvBTC.png?1744170824", + "symbol": "XSOLVBTC", + "explorer": "https://seitrace.com", + "explorerAddressLink": "https://seitrace.com/address/", + "explorerTxLink": "https://seitrace.com/tx/", + "relatedAssetKey": "eip155:42161/erc20:0x346c574c56e1a4aaa8dc88cda8f7eb12b39947ab" + }, + "eip155:1329/erc20:0xcc1b8207853662c5cfabfb028806ec06ea1f6ac6": { + "assetId": "eip155:1329/erc20:0xcc1b8207853662c5cfabfb028806ec06ea1f6ac6", + "chainId": "eip155:1329", + "name": "Kindred Labs", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/102171798/large/kindred-labs.jpg?1769759004", + "symbol": "KIN", + "explorer": "https://seitrace.com", + "explorerAddressLink": "https://seitrace.com/address/", + "explorerTxLink": "https://seitrace.com/tx/", + "relatedAssetKey": "eip155:1329/erc20:0xcc1b8207853662c5cfabfb028806ec06ea1f6ac6" + }, + "eip155:1329/erc20:0xdd7d5e4ea2125d43c16eed8f1ffefffa2f4b4af6": { + "assetId": "eip155:1329/erc20:0xdd7d5e4ea2125d43c16eed8f1ffefffa2f4b4af6", + "chainId": "eip155:1329", + "name": "Jellyverse", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/31805/large/short.png?1719199901", + "symbol": "JLY", + "explorer": "https://seitrace.com", + "explorerAddressLink": "https://seitrace.com/address/", + "explorerTxLink": "https://seitrace.com/tx/", + "relatedAssetKey": null + }, + "eip155:1329/erc20:0xe15fc38f6d8c56af07bbcbe3baf5708a2bf42392": { + "assetId": "eip155:1329/erc20:0xe15fc38f6d8c56af07bbcbe3baf5708a2bf42392", + "chainId": "eip155:1329", + "name": "USDC", + "precision": 6, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/6319/large/USDC.png?1769615602", + "symbol": "USDC", + "explorer": "https://seitrace.com", + "explorerAddressLink": "https://seitrace.com/address/", + "explorerTxLink": "https://seitrace.com/tx/", + "relatedAssetKey": null + }, + "eip155:1329/erc20:0xe30fedd158a2e3b13e9badaeabafc5516e95e8c7": { + "assetId": "eip155:1329/erc20:0xe30fedd158a2e3b13e9badaeabafc5516e95e8c7", + "chainId": "eip155:1329", + "name": "Wrapped SEI", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/38470/large/sei.png?1717644025", + "symbol": "WSEI", + "explorer": "https://seitrace.com", + "explorerAddressLink": "https://seitrace.com/address/", + "explorerTxLink": "https://seitrace.com/tx/", + "relatedAssetKey": null + }, + "eip155:1329/erc20:0xedf7732991b3de8f46fb97dcd4c5cdb28e6ad859": { + "assetId": "eip155:1329/erc20:0xedf7732991b3de8f46fb97dcd4c5cdb28e6ad859", + "chainId": "eip155:1329", + "name": "Tea-REX", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/70782/large/tREX.png?1763709291", + "symbol": "TREX", + "explorer": "https://seitrace.com", + "explorerAddressLink": "https://seitrace.com/address/", + "explorerTxLink": "https://seitrace.com/tx/", + "relatedAssetKey": null + }, + "eip155:1329/erc20:0xf9bdbf259ece5ae17e29bf92eb7abd7b8b465db9": { + "assetId": "eip155:1329/erc20:0xf9bdbf259ece5ae17e29bf92eb7abd7b8b465db9", + "chainId": "eip155:1329", + "name": "Froggy", + "precision": 18, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/67889/large/Logo200x200.png?1754194519", + "symbol": "FROG", + "explorer": "https://seitrace.com", + "explorerAddressLink": "https://seitrace.com/address/", + "explorerTxLink": "https://seitrace.com/tx/", + "relatedAssetKey": null + }, + "eip155:1329/erc20:0xff7f8f301f7a706e3cfd3d2275f5dc0b9ee8009b": { + "assetId": "eip155:1329/erc20:0xff7f8f301f7a706e3cfd3d2275f5dc0b9ee8009b", + "chainId": "eip155:1329", + "name": "FOLKS", + "precision": 6, + "color": "#FFFFFF", + "icon": "https://assets.coingecko.com/coins/images/70268/large/1000331462.png?1761335326", + "symbol": "FOLKS", + "explorer": "https://seitrace.com", + "explorerAddressLink": "https://seitrace.com/address/", + "explorerTxLink": "https://seitrace.com/tx/", + "relatedAssetKey": "eip155:42161/erc20:0xff7f8f301f7a706e3cfd3d2275f5dc0b9ee8009b" + }, + "eip155:1329/slip44:60": { + "assetId": "eip155:1329/slip44:60", + "chainId": "eip155:1329", + "name": "SEI", + "networkName": "Sei", + "symbol": "SEI", + "precision": 18, + "color": "#9B1B30", + "networkColor": "#9B1B30", + "icon": "https://assets.coingecko.com/coins/images/28205/large/Sei_Logo_-_Transparent.png", + "networkIcon": "https://assets.coingecko.com/coins/images/28205/small/Sei_Logo_-_Transparent.png", + "explorer": "https://seitrace.com", + "explorerAddressLink": "https://seitrace.com/address/", + "explorerTxLink": "https://seitrace.com/tx/", + "relatedAssetKey": null } }, "ids": [ @@ -468897,6 +469550,56 @@ "eip155:747/erc20:0xf1815bd50389c46847f0bda824ec8da914045d14", "eip155:747/erc20:0xf45cbe30bd953590c9917799142edb05be3f293f", "eip155:747/slip44:60", - "eip155:42220/slip44:60" + "eip155:42220/slip44:60", + "eip155:1329/erc20:0x0555e30da8f98308edb960aa94c0db47230d2b9c", + "eip155:1329/erc20:0x059a6b0ba116c63191182a0956cf697d0d2213ec", + "eip155:1329/erc20:0x09d4214c03d01f49544c0448dbe3a27f768f2b34", + "eip155:1329/erc20:0x0a526e425809aea71eb279d24ae22dee6c92a4fe", + "eip155:1329/erc20:0x160345fc359604fc6e70e3c5facbde5f7a9342d8", + "eip155:1329/erc20:0x3894085ef7ff0f0aedf52e2a2704928d1ec074f1", + "eip155:1329/erc20:0x443ac9f358226f5f48f2cd10bc0121e7a6176323", + "eip155:1329/erc20:0x4809010926aec940b550d34a46a52739f996d75d", + "eip155:1329/erc20:0x481fe356df88169f5f38203dd7f3c67b7559fda5", + "eip155:1329/erc20:0x4a1ea452c598363188bccc83fb4f69dc14b54acd", + "eip155:1329/erc20:0x51121bcae92e302f19d06c193c95e1f7b81a444b", + "eip155:1329/erc20:0x541fd749419ca806a8bc7da8ac23d346f2df8b77", + "eip155:1329/erc20:0x54cd901491aef397084453f4372b93c33260e2a6", + "eip155:1329/erc20:0x58e11d8ed38a2061361e90916540c5c32281a380", + "eip155:1329/erc20:0x5b8034f6346a81a1387ea21cdd36c48f6e05eb5f", + "eip155:1329/erc20:0x5bff88ca1442c2496f7e475e9e7786383bc070c0", + "eip155:1329/erc20:0x5cf6826140c1c56ff49c808a1a75407cd1df9423", + "eip155:1329/erc20:0x5f0e07dfee5832faa00c63f2d33a0d79150e8598", + "eip155:1329/erc20:0x60c230c38af6d86b0277a98a1caeaa345a7b061f", + "eip155:1329/erc20:0x64445f0aecc51e94ad52d8ac56b7190e764e561a", + "eip155:1329/erc20:0x71052bae71c25c78e37fd12e5ff1101a71d9018f", + "eip155:1329/erc20:0x78e26e8b953c7c78a58d69d8b9a91745c2bbb258", + "eip155:1329/erc20:0x80137510979822322193fc997d400d5a6c747bf7", + "eip155:1329/erc20:0x805679729df385815c57c24b20f4161bd34b655f", + "eip155:1329/erc20:0x80eede496655fb9047dd39d9f418d5483ed600df", + "eip155:1329/erc20:0x81d3a238b02827f62b9f390f947d36d4a5bf89d2", + "eip155:1329/erc20:0x83c82f0f959ad3eff528ee513b43808aa53f4b37", + "eip155:1329/erc20:0x89aec21572f6637ccbd0d66861aaac46dd775ed1", + "eip155:1329/erc20:0x8ee050f6af49a6b7fd8557d0e75219d66f5f6094", + "eip155:1329/erc20:0x9151434b16b9763660705744891fa906f660ecc5", + "eip155:1329/erc20:0x93919784c523f39cacaa98ee0a9d96c3f32b593e", + "eip155:1329/erc20:0x95597eb8d227a7c4b4f5e807a815c5178ee6dbe1", + "eip155:1329/erc20:0x9bfa177621119e64cecbeabe184ab9993e2ef727", + "eip155:1329/erc20:0x9faaea2cdd810b21594e54309dc847842ae301ce", + "eip155:1329/erc20:0xa193d87b0d5e23ef45379f90066891cbd6013226", + "eip155:1329/erc20:0xa8a3a5013104e093245164ea56588dbe10a3eb48", + "eip155:1329/erc20:0xb75d0b03c06a926e488e2659df1a861f860bd3d1", + "eip155:1329/erc20:0xbd82f3bfe1df0c84faec88a22ebc34c9a86595dc", + "eip155:1329/erc20:0xc18b6a15fb0ceaf5eb18696eefcb5bc7b9107149", + "eip155:1329/erc20:0xc257361320f4514d91c05f461006ce6a0300e2d2", + "eip155:1329/erc20:0xc53ac24320e3a54c7211e4993c8095078a0cb3cf", + "eip155:1329/erc20:0xcc0966d8418d412c599a6421b760a847eb169a8c", + "eip155:1329/erc20:0xcc1b8207853662c5cfabfb028806ec06ea1f6ac6", + "eip155:1329/erc20:0xdd7d5e4ea2125d43c16eed8f1ffefffa2f4b4af6", + "eip155:1329/erc20:0xe15fc38f6d8c56af07bbcbe3baf5708a2bf42392", + "eip155:1329/erc20:0xe30fedd158a2e3b13e9badaeabafc5516e95e8c7", + "eip155:1329/erc20:0xedf7732991b3de8f46fb97dcd4c5cdb28e6ad859", + "eip155:1329/erc20:0xf9bdbf259ece5ae17e29bf92eb7abd7b8b465db9", + "eip155:1329/erc20:0xff7f8f301f7a706e3cfd3d2275f5dc0b9ee8009b", + "eip155:1329/slip44:60" ] } \ No newline at end of file diff --git a/public/generated/generatedAssetData.json.br b/public/generated/generatedAssetData.json.br index efbee6e7cd8..a4f3b821a7d 100644 Binary files a/public/generated/generatedAssetData.json.br and b/public/generated/generatedAssetData.json.br differ diff --git a/public/generated/generatedAssetData.json.gz b/public/generated/generatedAssetData.json.gz index 728ead7ae0d..faa1c5f73bb 100644 Binary files a/public/generated/generatedAssetData.json.gz and b/public/generated/generatedAssetData.json.gz differ diff --git a/public/generated/relatedAssetIndex.json b/public/generated/relatedAssetIndex.json index 33bf186d035..74a669b77cb 100644 --- a/public/generated/relatedAssetIndex.json +++ b/public/generated/relatedAssetIndex.json @@ -503,7 +503,8 @@ "eip155:146/erc20:0x09d4214c03d01f49544c0448dbe3a27f768f2b34", "eip155:130/erc20:0x09d4214c03d01f49544c0448dbe3a27f768f2b34", "eip155:480/erc20:0x09d4214c03d01f49544c0448dbe3a27f768f2b34", - "eip155:98866/erc20:0x09d4214c03d01f49544c0448dbe3a27f768f2b34" + "eip155:98866/erc20:0x09d4214c03d01f49544c0448dbe3a27f768f2b34", + "eip155:1329/erc20:0x09d4214c03d01f49544c0448dbe3a27f768f2b34" ], "eip155:42161/erc20:0x8d9ba570d6cb60c7e3e0f31343efe75ab8e65fb1": [ "eip155:43114/erc20:0x321e7092a180bb43555132ec53aaa65a5bf84251", @@ -1458,7 +1459,9 @@ "eip155:1868/erc20:0x0555e30da8f98308edb960aa94c0db47230d2b9c", "eip155:43111/erc20:0x03c7054bcb39f7b2e5b2c7acb37583e32d70cfa3", "eip155:480/erc20:0x03c7054bcb39f7b2e5b2c7acb37583e32d70cfa3", - "eip155:324/erc20:0xbbeb516fb02a01611cbbe0453fe3c580d7281011" + "eip155:324/erc20:0xbbeb516fb02a01611cbbe0453fe3c580d7281011", + "eip155:1329/erc20:0x0555e30da8f98308edb960aa94c0db47230d2b9c", + "eip155:1329/erc20:0x60c230c38af6d86b0277a98a1caeaa345a7b061f" ], "eip155:8453/erc20:0xc5fed7c8ccc75d8a72b601a66dffd7a489073f0b": [ "eip155:56/erc20:0x6ef2ffb38d64afe18ce782da280b300e358cfeaf", @@ -1784,7 +1787,8 @@ "eip155:57073/erc20:0xa161132371c94299d215915d4cbc3b629e2059be", "eip155:80094/erc20:0x93919784c523f39cacaa98ee0a9d96c3f32b593e", "eip155:130/erc20:0xa161132371c94299d215915d4cbc3b629e2059be", - "eip155:43111/erc20:0x93919784c523f39cacaa98ee0a9d96c3f32b593e" + "eip155:43111/erc20:0x93919784c523f39cacaa98ee0a9d96c3f32b593e", + "eip155:1329/erc20:0x93919784c523f39cacaa98ee0a9d96c3f32b593e" ], "eip155:42161/erc20:0x14297be295ab922458277be046e89f73382bdf8e": [ "eip155:8453/erc20:0xb3358ab71f784910de0ca766ebdab527323731b9", @@ -4558,7 +4562,8 @@ "eip155:59144/erc20:0x93f4d0ab6a8b4271f4a28db399b5e30612d21116", "eip155:80094/erc20:0xec901da9c68e90798bbbb74c11406a32a70652c3", "eip155:5000/erc20:0x2fde62942759d7c0aaf25952da4098423bc1264c", - "eip155:34443/erc20:0x80137510979822322193fc997d400d5a6c747bf7" + "eip155:34443/erc20:0x80137510979822322193fc997d400d5a6c747bf7", + "eip155:1329/erc20:0x80137510979822322193fc997d400d5a6c747bf7" ], "eip155:56/erc20:0x405f38b90bebf1259062cf29da299f3398662bcb": [ "eip155:1/erc20:0x74a03d741226f738098c35da8188e57aca50d146", @@ -4845,7 +4850,8 @@ "eip155:60808/erc20:0x541fd749419ca806a8bc7da8ac23d346f2df8b77", "eip155:34443/erc20:0x541fd749419ca806a8bc7da8ac23d346f2df8b77", "eip155:1868/erc20:0x541fd749419ca806a8bc7da8ac23d346f2df8b77", - "eip155:324/erc20:0x74ed17608cc2b5f30a59d6af07c9ad1b1ab3a5b1" + "eip155:324/erc20:0x74ed17608cc2b5f30a59d6af07c9ad1b1ab3a5b1", + "eip155:1329/erc20:0x541fd749419ca806a8bc7da8ac23d346f2df8b77" ], "eip155:42161/erc20:0x7bbcf1b600565ae023a1806ef637af4739de3255": [ "eip155:8453/erc20:0x7bbcf1b600565ae023a1806ef637af4739de3255", @@ -5863,7 +5869,8 @@ "eip155:42161/erc20:0x35e050d3c0ec2d29d269a8ecea763a183bdf9a9d", "sui:35834a8a/coin:0x960b531667636f39e85867775f52f6b1f220a058c4de786905bdf761e06a56bb::usdy::USDY", "eip155:5000/erc20:0x5be26527e817998a7206475496fde1e68957c5a6", - "eip155:98866/erc20:0xd2b65e851be3d80d3c2ce795eb2e78f16cb088b2" + "eip155:98866/erc20:0xd2b65e851be3d80d3c2ce795eb2e78f16cb088b2", + "eip155:1329/erc20:0x54cd901491aef397084453f4372b93c33260e2a6" ], "eip155:1/erc20:0x9695e0114e12c0d3a3636fab5a18e6b737529023": [ "eip155:137/erc20:0xc168e40227e4ebd8c1cae80f7a55a4f0e6d66c97", @@ -6245,7 +6252,8 @@ "eip155:98866/erc20:0x222365ef19f7947e5484218551b56bb3965aa7af", "eip155:98866/erc20:0x78add880a697070c1e765ac44d65323a0dcce913", "eip155:747/erc20:0xf1815bd50389c46847f0bda824ec8da914045d14", - "eip155:747/erc20:0x7f27352d5f83db87a5a3e00f4b07cc2138d8ee52" + "eip155:747/erc20:0x7f27352d5f83db87a5a3e00f4b07cc2138d8ee52", + "eip155:1329/erc20:0x3894085ef7ff0f0aedf52e2a2704928d1ec074f1" ], "eip155:8453/erc20:0x9c632e6aaa3ea73f91554f8a3cb2ed2f29605e0c": [ "eip155:56/erc20:0x7324c7c0d95cebc73eea7e85cbaac0dbdf88a05b", @@ -7512,7 +7520,8 @@ "eip155:81457/erc20:0x4300000000000000000000000000000000000004", "eip155:1514/erc20:0xbab93b7ad7fe8692a878b95a8e689423437cc500", "eip155:98866/erc20:0xca59ca09e5602fae8b629dee83ffa819741f14be", - "eip155:747/erc20:0x2f6f07cdcf3588944bf4c42ac74ff24bf56e7590" + "eip155:747/erc20:0x2f6f07cdcf3588944bf4c42ac74ff24bf56e7590", + "eip155:1329/erc20:0x160345fc359604fc6e70e3c5facbde5f7a9342d8" ], "eip155:42161/erc20:0xea986d33ef8a20a96120ecc44dbdd49830192043": [ "eip155:56/erc20:0x3028b4395f98777123c7da327010c40f3c7cc4ef", @@ -7970,7 +7979,8 @@ "eip155:34443/erc20:0x80eede496655fb9047dd39d9f418d5483ed600df", "eip155:81457/erc20:0x80eede496655fb9047dd39d9f418d5483ed600df", "eip155:324/erc20:0xea77c590bb36c43ef7139ce649cfbcfd6163170d", - "eip155:98866/erc20:0x80eede496655fb9047dd39d9f418d5483ed600df" + "eip155:98866/erc20:0x80eede496655fb9047dd39d9f418d5483ed600df", + "eip155:1329/erc20:0x80eede496655fb9047dd39d9f418d5483ed600df" ], "eip155:56/erc20:0xcd6926193308d3b371fdd6a6219067e550000000": [ "eip155:1/erc20:0xcd6926193308d3b371fdd6a6219067e550000000", @@ -8156,7 +8166,8 @@ "eip155:1/erc20:0xcf62f905562626cfcdd2261162a51fd02fc9c5b6", "eip155:747474/erc20:0x5bff88ca1442c2496f7e475e9e7786383bc070c0", "eip155:146/erc20:0x5bff88ca1442c2496f7e475e9e7786383bc070c0", - "eip155:34443/erc20:0x5bff88ca1442c2496f7e475e9e7786383bc070c0" + "eip155:34443/erc20:0x5bff88ca1442c2496f7e475e9e7786383bc070c0", + "eip155:1329/erc20:0x5bff88ca1442c2496f7e475e9e7786383bc070c0" ], "eip155:42161/erc20:0x071e1fd14fbacb2b856867043eb12ace1ee3cb52": [ "eip155:8453/erc20:0xba921e840fb60adb0f92b2a977427fe8e5dd208c", @@ -8283,7 +8294,8 @@ "eip155:146/erc20:0x4809010926aec940b550d34a46a52739f996d75d", "eip155:130/erc20:0x4809010926aec940b550d34a46a52739f996d75d", "eip155:480/erc20:0x4809010926aec940b550d34a46a52739f996d75d", - "eip155:98866/erc20:0x0bbcc2c1991d0af8ec6a5ed922e6f5606923fe15" + "eip155:98866/erc20:0x0bbcc2c1991d0af8ec6a5ed922e6f5606923fe15", + "eip155:1329/erc20:0x4809010926aec940b550d34a46a52739f996d75d" ], "eip155:1/erc20:0xd457b379801433ea5c3a8542b61902023ae39dcd": [ "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/token:XsicTFC7G68d74FaYDctWggkKDgkMx8LpNoFNr81TrF", @@ -8551,7 +8563,8 @@ "eip155:5000/erc20:0x1d40bafc49c37cda49f2a5427e2fb95e1e3fcf20", "eip155:146/erc20:0xcc0966d8418d412c599a6421b760a847eb169a8c", "eip155:60808/erc20:0xcc0966d8418d412c599a6421b760a847eb169a8c", - "eip155:1868/erc20:0xcc0966d8418d412c599a6421b760a847eb169a8c" + "eip155:1868/erc20:0xcc0966d8418d412c599a6421b760a847eb169a8c", + "eip155:1329/erc20:0xcc0966d8418d412c599a6421b760a847eb169a8c" ], "eip155:42161/erc20:0x462cd9e0247b2e63831c3189ae738e5e9a5a4b64": [ "eip155:43114/erc20:0x9ceed3a7f753608372eeab300486cc7c2f38ac68", @@ -8615,7 +8628,9 @@ "eip155:43111/erc20:0xbb0d083fb1be0a9f6157ec484b6c79e0a4e31c2e", "eip155:480/erc20:0x102d758f688a4c1c5a80b116bd945d4455460282", "eip155:324/erc20:0x493257fd37edb34451f62edf8d2a0c418852ba4c", - "eip155:98866/erc20:0xda6087e69c51e7d31b6dbad276a3c44703dfdcad" + "eip155:98866/erc20:0xda6087e69c51e7d31b6dbad276a3c44703dfdcad", + "eip155:1329/erc20:0x9151434b16b9763660705744891fa906f660ecc5", + "eip155:1329/erc20:0xb75d0b03c06a926e488e2659df1a861f860bd3d1" ], "eip155:1/erc20:0xdb9783ca04bbd64fe2c6d7b9503a979b3de30729": [ "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/token:XsAsZLF4MmsvS1sDxRMrUz7REjHfwbC9UAMXSRBqgEB", @@ -9867,7 +9882,8 @@ "eip155:1/erc20:0xff7f8f301f7a706e3cfd3d2275f5dc0b9ee8009b", "eip155:143/erc20:0xff7f8f301f7a706e3cfd3d2275f5dc0b9ee8009b", "eip155:137/erc20:0xff7f8f301f7a706e3cfd3d2275f5dc0b9ee8009b", - "eip155:42161/erc20:0xff7f8f301f7a706e3cfd3d2275f5dc0b9ee8009b" + "eip155:42161/erc20:0xff7f8f301f7a706e3cfd3d2275f5dc0b9ee8009b", + "eip155:1329/erc20:0xff7f8f301f7a706e3cfd3d2275f5dc0b9ee8009b" ], "eip155:42161/erc20:0x06e90a57d1ece8752d6ce92d1ad348ead5eae4f4": [ "eip155:1/erc20:0xff836a5821e69066c87e268bc51b849fab94240c", @@ -10380,7 +10396,8 @@ "eip155:42161/erc20:0x95146881b86b3ee99e63705ec87afe29fcc044d9", "eip155:5000/erc20:0xd0728f5b1f53a834f8dcd1b86f62ceb8726eb0a0", "eip155:146/erc20:0xad747e3cf4e31b8897b96c81c6c74152de52f614", - "eip155:81457/erc20:0x6cd20f11470e9c9d1458a69c8f7b330b99577ef9" + "eip155:81457/erc20:0x6cd20f11470e9c9d1458a69c8f7b330b99577ef9", + "eip155:1329/erc20:0x5b8034f6346a81a1387ea21cdd36c48f6e05eb5f" ], "eip155:42161/erc20:0x9cf7eebb75b751dc8fdd2268ae8c9b570b4c97b9": [ "eip155:43114/erc20:0xab236ed82a0184ab754534f3952b48408468c09b", @@ -12745,7 +12762,8 @@ "sui:35834a8a/coin:0xd1a91b46bd6d966b62686263609074ad16cfdffc63c31a4775870a2d54d20c6b::mbtc::MBTC": [ "eip155:59144/erc20:0xe4d584ae9b753e549cae66200a6475d2f00705f7", "eip155:34443/erc20:0x59889b7021243db5b1e065385f918316cd90d46c", - "eip155:324/erc20:0xe757355edba7ced7b8c0271bba4efda184ad75ab" + "eip155:324/erc20:0xe757355edba7ced7b8c0271bba4efda184ad75ab", + "eip155:1329/erc20:0x9bfa177621119e64cecbeabe184ab9993e2ef727" ], "eip155:1868/erc20:0x61f2993a644762a345b483adf0d6351c5edfb3b5": [ "eip155:56/erc20:0x2a3dc2d5daf9c8c46c954b8669f4643c6b1c081a", @@ -12815,10 +12833,19 @@ ], "eip155:98866/erc20:0x64445f0aecc51e94ad52d8ac56b7190e764e561a": [ "eip155:59144/erc20:0x5217ab28ece654aab2c68efedb6a22739df6c3d5", - "eip155:98866/erc20:0x64445f0aecc51e94ad52d8ac56b7190e764e561a" + "eip155:98866/erc20:0x64445f0aecc51e94ad52d8ac56b7190e764e561a", + "eip155:1329/erc20:0x64445f0aecc51e94ad52d8ac56b7190e764e561a" ], "eip155:747/erc20:0x00f4ce400130c9383115f3858f9ca54677426583": [ "eip155:1/erc20:0x0c65b7094e9cedebf9ad5b6a631e57961e0680cf", "eip155:747/erc20:0x00f4ce400130c9383115f3858f9ca54677426583" + ], + "eip155:1329/erc20:0x81d3a238b02827f62b9f390f947d36d4a5bf89d2": [ + "eip155:56/erc20:0x81d3a238b02827f62b9f390f947d36d4a5bf89d2", + "eip155:1329/erc20:0x81d3a238b02827f62b9f390f947d36d4a5bf89d2" + ], + "eip155:1329/erc20:0xcc1b8207853662c5cfabfb028806ec06ea1f6ac6": [ + "eip155:56/erc20:0xcc1b8207853662c5cfabfb028806ec06ea1f6ac6", + "eip155:1329/erc20:0xcc1b8207853662c5cfabfb028806ec06ea1f6ac6" ] } \ No newline at end of file diff --git a/public/generated/relatedAssetIndex.json.br b/public/generated/relatedAssetIndex.json.br index 380c97203af..68e546d8000 100644 Binary files a/public/generated/relatedAssetIndex.json.br and b/public/generated/relatedAssetIndex.json.br differ diff --git a/public/generated/relatedAssetIndex.json.gz b/public/generated/relatedAssetIndex.json.gz index 8e4b2f87b8e..bbb9af97532 100644 Binary files a/public/generated/relatedAssetIndex.json.gz and b/public/generated/relatedAssetIndex.json.gz differ diff --git a/scripts/generateAssetData/coingecko.ts b/scripts/generateAssetData/coingecko.ts index 53b67cdf096..ec0159460cd 100644 --- a/scripts/generateAssetData/coingecko.ts +++ b/scripts/generateAssetData/coingecko.ts @@ -29,6 +29,7 @@ import { plumeChainId, polygonChainId, scrollChainId, + seiChainId, solanaChainId, soneiumChainId, sonicChainId, @@ -71,6 +72,7 @@ import { plume, polygon, scroll, + sei, solana, soneium, sonic, @@ -315,6 +317,14 @@ export async function getAssets(chainId: ChainId): Promise { explorerAddressLink: hemi.explorerAddressLink, explorerTxLink: hemi.explorerTxLink, } + case seiChainId: + return { + assetNamespace: ASSET_NAMESPACE.erc20, + category: adapters.chainIdToCoingeckoAssetPlatform(chainId), + explorer: sei.explorer, + explorerAddressLink: sei.explorerAddressLink, + explorerTxLink: sei.explorerTxLink, + } case berachainChainId: return { assetNamespace: ASSET_NAMESPACE.erc20, diff --git a/scripts/generateAssetData/generateAssetData.ts b/scripts/generateAssetData/generateAssetData.ts index 18cec0dcc07..2223d8add92 100644 --- a/scripts/generateAssetData/generateAssetData.ts +++ b/scripts/generateAssetData/generateAssetData.ts @@ -54,6 +54,7 @@ import * as plasma from './plasma' import * as plume from './plume' import * as polygon from './polygon' import * as scroll from './scroll' +import * as sei from './sei' import * as solana from './solana' import * as soneium from './soneium' import * as sonic from './sonic' @@ -111,6 +112,7 @@ const generateAssetData = async () => { const worldchainAssets = await worldchainModule.getAssets() const sonicAssets = await sonic.getAssets() const soneiumAssets = await soneium.getAssets() + const seiAssets = await sei.getAssets() const solanaAssets = await solana.getAssets() const starknetAssets = await starknet.getAssets() const tronAssets = await tronModule.getAssets() @@ -163,6 +165,7 @@ const generateAssetData = async () => { ...worldchainAssets, ...sonicAssets, ...soneiumAssets, + ...seiAssets, ...solanaAssets, ...starknetAssets, ...tronAssets, diff --git a/scripts/generateAssetData/sei/index.ts b/scripts/generateAssetData/sei/index.ts new file mode 100644 index 00000000000..dcc16dd5e1f --- /dev/null +++ b/scripts/generateAssetData/sei/index.ts @@ -0,0 +1,11 @@ +import { seiChainId } from '@shapeshiftoss/caip' +import type { Asset } from '@shapeshiftoss/types' +import { sei, unfreeze } from '@shapeshiftoss/utils' + +import * as coingecko from '../coingecko' + +export const getAssets = async (): Promise => { + const assets = await coingecko.getAssets(seiChainId) + + return [...assets, unfreeze(sei)] +} diff --git a/src/Routes/RoutesCommon.tsx b/src/Routes/RoutesCommon.tsx index 1fb96062dce..f3a1caa7935 100644 --- a/src/Routes/RoutesCommon.tsx +++ b/src/Routes/RoutesCommon.tsx @@ -216,7 +216,7 @@ export const routes: Route[] = [ priority: 2, main: TradeTab, category: RouteCategory.Featured, - relatedPaths: ['/trade', '/limit'], + relatedPaths: ['/trade', '/limit', '/ramp/trade', '/earn'], routes: [ { path: TRADE_ROUTE_ASSET_SPECIFIC, diff --git a/src/components/MultiHopTrade/components/SharedTradeInput/SharedTradeInputHeader.tsx b/src/components/MultiHopTrade/components/SharedTradeInput/SharedTradeInputHeader.tsx index d2e36bfca7c..118f6c86de1 100644 --- a/src/components/MultiHopTrade/components/SharedTradeInput/SharedTradeInputHeader.tsx +++ b/src/components/MultiHopTrade/components/SharedTradeInput/SharedTradeInputHeader.tsx @@ -200,7 +200,7 @@ export const SharedTradeInputHeader = ({ - {translate('transactionRow.swap')} + {translate('common.trade')} {rightContent} diff --git a/src/components/MultiHopTrade/components/TradeConfirm/hooks/useTradeButtonProps.tsx b/src/components/MultiHopTrade/components/TradeConfirm/hooks/useTradeButtonProps.tsx index 27917c3e624..29a0331b714 100644 --- a/src/components/MultiHopTrade/components/TradeConfirm/hooks/useTradeButtonProps.tsx +++ b/src/components/MultiHopTrade/components/TradeConfirm/hooks/useTradeButtonProps.tsx @@ -123,6 +123,7 @@ export const useTradeButtonProps = ({ relayerTxHash, relayTransactionMetadata: firstStep?.relayTransactionMetadata, acrossTransactionMetadata: firstStep?.acrossTransactionMetadata, + debridgeTransactionMetadata: firstStep?.debridgeTransactionMetadata, stepIndex: currentHopIndex, quoteId: firstStep?.stonfiSpecific?.quoteId ?? activeQuote.id, streamingSwapMetadata: { diff --git a/src/components/MultiHopTrade/components/TradeInput/components/SwapperIcon/SwapperIcon.tsx b/src/components/MultiHopTrade/components/TradeInput/components/SwapperIcon/SwapperIcon.tsx index 8f97c90be85..acd08ac853e 100644 --- a/src/components/MultiHopTrade/components/TradeInput/components/SwapperIcon/SwapperIcon.tsx +++ b/src/components/MultiHopTrade/components/TradeInput/components/SwapperIcon/SwapperIcon.tsx @@ -11,6 +11,7 @@ import ButterSwapIcon from './butterswap.png' import CetusIcon from './cetus-icon.jpg' import ChainflipIcon from './chainflip-icon.png' import CowIcon from './cow-icon.png' +import DebridgeIcon from './debridge-icon.svg' import JupiterIcon from './jupiter-icon.svg' import MayachainIcon from './maya_logo.png' import NearIntentsIcon from './near-intents-icon.png' @@ -66,6 +67,8 @@ export const SwapperIcon = ({ return StonfiIcon case SwapperName.Across: return AcrossIcon + case SwapperName.Debridge: + return DebridgeIcon case SwapperName.Test: return '' default: diff --git a/src/components/MultiHopTrade/components/TradeInput/components/SwapperIcon/debridge-icon.svg b/src/components/MultiHopTrade/components/TradeInput/components/SwapperIcon/debridge-icon.svg new file mode 100644 index 00000000000..60a37c89265 --- /dev/null +++ b/src/components/MultiHopTrade/components/TradeInput/components/SwapperIcon/debridge-icon.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/src/components/TradeAssetSearch/hooks/useGetPopularAssetsQuery.tsx b/src/components/TradeAssetSearch/hooks/useGetPopularAssetsQuery.tsx index 04da293adaf..a5aea798f9a 100644 --- a/src/components/TradeAssetSearch/hooks/useGetPopularAssetsQuery.tsx +++ b/src/components/TradeAssetSearch/hooks/useGetPopularAssetsQuery.tsx @@ -20,6 +20,7 @@ import { plasmaAssetId, plumeAssetId, scrollAssetId, + seiAssetId, soneiumAssetId, sonicAssetId, starknetAssetId, @@ -72,6 +73,7 @@ export const queryFn = async () => { if (enabledFlags.Blast) assetIds.push(blastAssetId) if (enabledFlags.WorldChain) assetIds.push(worldChainAssetId) if (enabledFlags.Hemi) assetIds.push(hemiAssetId) + if (enabledFlags.Sei) assetIds.push(seiAssetId) if (enabledFlags.Linea) assetIds.push(lineaAssetId) if (enabledFlags.Cronos) assetIds.push(cronosAssetId) if (enabledFlags.Sonic) assetIds.push(sonicAssetId) diff --git a/src/config.ts b/src/config.ts index 55f9b0e2eb3..8daae7d76f2 100644 --- a/src/config.ts +++ b/src/config.ts @@ -76,6 +76,7 @@ const validators = { VITE_BLAST_NODE_URL: url(), VITE_WORLDCHAIN_NODE_URL: url(), VITE_HEMI_NODE_URL: url(), + VITE_SEI_NODE_URL: url(), VITE_LINEA_NODE_URL: url(), VITE_SCROLL_NODE_URL: url(), VITE_SONIC_NODE_URL: url(), @@ -137,6 +138,7 @@ const validators = { VITE_FEATURE_BLAST: bool({ default: false }), VITE_FEATURE_WORLDCHAIN: bool({ default: false }), VITE_FEATURE_HEMI: bool({ default: false }), + VITE_FEATURE_SEI: bool({ default: false }), VITE_FEATURE_LINEA: bool({ default: false }), VITE_FEATURE_SCROLL: bool({ default: false }), VITE_FEATURE_SONIC: bool({ default: false }), @@ -274,6 +276,8 @@ const validators = { VITE_FEATURE_ACROSS_SWAP: bool({ default: false }), VITE_ACROSS_API_URL: url({ default: 'https://app.across.to/api' }), VITE_ACROSS_INTEGRATOR_ID: str({ default: '' }), + VITE_FEATURE_DEBRIDGE_SWAP: bool({ default: false }), + VITE_DEBRIDGE_API_URL: url({ default: 'https://dln.debridge.finance/v1.0' }), VITE_FEATURE_TX_HISTORY_BYE_BYE: bool({ default: false }), VITE_AFFILIATE_REVENUE_URL: url(), VITE_FEATURE_LEDGER_READ_ONLY: bool({ default: false }), diff --git a/src/constants/chains.ts b/src/constants/chains.ts index d647a2a0bd0..bd579d22518 100644 --- a/src/constants/chains.ts +++ b/src/constants/chains.ts @@ -17,6 +17,7 @@ export const SECOND_CLASS_CHAINS: readonly KnownChainIds[] = [ KnownChainIds.StoryMainnet, KnownChainIds.WorldChainMainnet, KnownChainIds.MantleMainnet, + KnownChainIds.SeiMainnet, KnownChainIds.InkMainnet, KnownChainIds.LineaMainnet, KnownChainIds.SonicMainnet, @@ -55,6 +56,7 @@ export const knownChainIds = Object.values(KnownChainIds).filter(chainId => { if (chainId === KnownChainIds.StoryMainnet && !enabledFlags.Story) return false if (chainId === KnownChainIds.WorldChainMainnet && !enabledFlags.WorldChain) return false if (chainId === KnownChainIds.MantleMainnet && !enabledFlags.Mantle) return false + if (chainId === KnownChainIds.SeiMainnet && !enabledFlags.Sei) return false if (chainId === KnownChainIds.InkMainnet && !enabledFlags.Ink) return false if (chainId === KnownChainIds.LineaMainnet && !enabledFlags.Linea) return false if (chainId === KnownChainIds.SonicMainnet && !enabledFlags.Sonic) return false diff --git a/src/context/PluginProvider/PluginProvider.tsx b/src/context/PluginProvider/PluginProvider.tsx index 540d4f6301f..6cd3e8f39d6 100644 --- a/src/context/PluginProvider/PluginProvider.tsx +++ b/src/context/PluginProvider/PluginProvider.tsx @@ -135,6 +135,7 @@ export const PluginProvider = ({ children }: PluginProviderProps): JSX.Element = if (!featureFlags.ZkSyncEra && chainId === KnownChainIds.ZkSyncEraMainnet) return false if (!featureFlags.Blast && chainId === KnownChainIds.BlastMainnet) return false if (!featureFlags.Hemi && chainId === KnownChainIds.HemiMainnet) return false + if (!featureFlags.Sei && chainId === KnownChainIds.SeiMainnet) return false if (!featureFlags.Linea && chainId === KnownChainIds.LineaMainnet) return false if (!featureFlags.Scroll && chainId === KnownChainIds.ScrollMainnet) return false if (!featureFlags.Berachain && chainId === KnownChainIds.BerachainMainnet) return false diff --git a/src/context/WalletProvider/WalletConnectV2/config.ts b/src/context/WalletProvider/WalletConnectV2/config.ts index af1b01a95fb..e76cf74e88d 100644 --- a/src/context/WalletProvider/WalletConnectV2/config.ts +++ b/src/context/WalletProvider/WalletConnectV2/config.ts @@ -12,6 +12,7 @@ import { mainnet, optimism, polygon, + sei, } from 'viem/chains' import type { EthereumProviderOptions } from './constants' @@ -61,6 +62,7 @@ export const walletConnectV2OptionalChains: AtLeastOneViemChain = (() => { base, ink, flowEvmChain, + sei, ] if (optionalViemChains.length === 0) throw new Error('Array must contain at least one element.') return optionalViemChains as AtLeastOneViemChain diff --git a/src/hooks/useWalletSupportsChain/useWalletSupportsChain.ts b/src/hooks/useWalletSupportsChain/useWalletSupportsChain.ts index a3197a8a342..a95f1833c9c 100644 --- a/src/hooks/useWalletSupportsChain/useWalletSupportsChain.ts +++ b/src/hooks/useWalletSupportsChain/useWalletSupportsChain.ts @@ -33,6 +33,7 @@ import { plumeChainId, polygonChainId, scrollChainId, + seiChainId, solanaChainId, soneiumChainId, sonicChainId, @@ -83,6 +84,7 @@ import { supportsPlume, supportsPolygon, supportsScroll, + supportsSei, supportsSolana, supportsSoneium, supportsSonic, @@ -207,6 +209,7 @@ export const walletSupportsChain = ({ const isNearEnabled = selectFeatureFlag(store.getState(), 'Near') const isPlasmaEnabled = selectFeatureFlag(store.getState(), 'Plasma') const isPlumeEnabled = selectFeatureFlag(store.getState(), 'Plume') + const isSeiEnabled = selectFeatureFlag(store.getState(), 'Sei') const isScrollEnabled = selectFeatureFlag(store.getState(), 'Scroll') const isStarknetEnabled = selectFeatureFlag(store.getState(), 'Starknet') const isWorldChainEnabled = selectFeatureFlag(store.getState(), 'WorldChain') @@ -275,6 +278,8 @@ export const walletSupportsChain = ({ return isWorldChainEnabled && supportsWorldChain(wallet) case hemiChainId: return isHemiEnabled && supportsHemi(wallet) + case seiChainId: + return isSeiEnabled && supportsSei(wallet) case lineaChainId: return isLineaEnabled && supportsLinea(wallet) case scrollChainId: diff --git a/src/lib/account/evm.ts b/src/lib/account/evm.ts index 89a16187235..be93db04e02 100644 --- a/src/lib/account/evm.ts +++ b/src/lib/account/evm.ts @@ -27,6 +27,7 @@ import { plumeChainId, polygonChainId, scrollChainId, + seiChainId, soneiumChainId, sonicChainId, storyChainId, @@ -64,6 +65,7 @@ import { supportsPlume, supportsPolygon, supportsScroll, + supportsSei, supportsSoneium, supportsSonic, supportsStory, @@ -178,6 +180,7 @@ export const deriveEvmAccountIdsAndMetadata: DeriveAccountIdsAndMetadata = async if (chainId === blastChainId && !supportsBlast(wallet)) continue if (chainId === worldChainChainId && !supportsWorldChain(wallet)) continue if (chainId === hemiChainId && !supportsHemi(wallet)) continue + if (chainId === seiChainId && !supportsSei(wallet)) continue if (chainId === lineaChainId && !supportsLinea(wallet)) continue if (chainId === sonicChainId && !supportsSonic(wallet)) continue if (chainId === unichainChainId && !supportsUnichain(wallet)) continue diff --git a/src/lib/asset-service/service/AssetService.ts b/src/lib/asset-service/service/AssetService.ts index 93d5c321399..ebaaa43c744 100644 --- a/src/lib/asset-service/service/AssetService.ts +++ b/src/lib/asset-service/service/AssetService.ts @@ -26,6 +26,7 @@ import { plumeChainId, polygonChainId, scrollChainId, + seiChainId, solanaChainId, soneiumChainId, sonicChainId, @@ -156,6 +157,7 @@ class _AssetService { if (!config.VITE_FEATURE_BOB && asset.chainId === bobChainId) return false if (!config.VITE_FEATURE_MODE && asset.chainId === modeChainId) return false if (!config.VITE_FEATURE_MEGAETH && asset.chainId === megaethChainId) return false + if (!config.VITE_FEATURE_SEI && asset.chainId === seiChainId) return false if (!config.VITE_FEATURE_LINEA && asset.chainId === lineaChainId) return false if (!config.VITE_FEATURE_BERACHAIN && asset.chainId === berachainChainId) return false if (!config.VITE_FEATURE_SCROLL && asset.chainId === scrollChainId) return false diff --git a/src/lib/coingecko/utils.ts b/src/lib/coingecko/utils.ts index 2d8aa1bfacf..e1349eda749 100644 --- a/src/lib/coingecko/utils.ts +++ b/src/lib/coingecko/utils.ts @@ -27,6 +27,7 @@ import { optimismChainId, plumeChainId, polygonChainId, + seiChainId, solanaChainId, soneiumChainId, sonicChainId, @@ -241,6 +242,7 @@ export const getCoingeckoSupportedChainIds = () => { ...(getConfig().VITE_FEATURE_WORLDCHAIN ? [worldChainChainId] : []), ...(getConfig().VITE_FEATURE_HEMI ? [hemiChainId] : []), ...(getConfig().VITE_FEATURE_MANTLE ? [mantleChainId] : []), + ...(getConfig().VITE_FEATURE_SEI ? [seiChainId] : []), ...(getConfig().VITE_FEATURE_LINEA ? [lineaChainId] : []), ...(getConfig().VITE_FEATURE_SONIC ? [sonicChainId] : []), ...(getConfig().VITE_FEATURE_UNICHAIN ? [unichainChainId] : []), diff --git a/src/pages/Markets/components/MarketsRow.tsx b/src/pages/Markets/components/MarketsRow.tsx index 4ffcc1b7e4a..7b5ac4d72ef 100644 --- a/src/pages/Markets/components/MarketsRow.tsx +++ b/src/pages/Markets/components/MarketsRow.tsx @@ -103,6 +103,7 @@ export const MarketsRow: React.FC = ({ const isSoneiumEnabled = useAppSelector(state => selectFeatureFlag(state, 'Soneium')) const isFlowEvmEnabled = useAppSelector(state => selectFeatureFlag(state, 'FlowEvm')) const isCeloEnabled = useAppSelector(state => selectFeatureFlag(state, 'Celo')) + const isSeiEnabled = useAppSelector(state => selectFeatureFlag(state, 'Sei')) const [isSmallerThanLg] = useMediaQuery(`(max-width: ${breakpoints.lg})`) const chainIds = useMemo(() => { @@ -124,6 +125,7 @@ export const MarketsRow: React.FC = ({ if (!isBlastEnabled && chainId === KnownChainIds.BlastMainnet) return false if (!isWorldChainEnabled && chainId === KnownChainIds.WorldChainMainnet) return false if (!isHemiEnabled && chainId === KnownChainIds.HemiMainnet) return false + if (!isSeiEnabled && chainId === KnownChainIds.SeiMainnet) return false if (!isBerachainEnabled && chainId === KnownChainIds.BerachainMainnet) return false if (!isCronosEnabled && chainId === KnownChainIds.CronosMainnet) return false if (!isSonicEnabled && chainId === KnownChainIds.SonicMainnet) return false @@ -161,6 +163,7 @@ export const MarketsRow: React.FC = ({ isSoneiumEnabled, isFlowEvmEnabled, isCeloEnabled, + isSeiEnabled, ]) const Title = useMemo(() => { diff --git a/src/pages/TCY/components/Unstake/Unstake.tsx b/src/pages/TCY/components/Unstake/Unstake.tsx index 045f2183116..65202e7038e 100644 --- a/src/pages/TCY/components/Unstake/Unstake.tsx +++ b/src/pages/TCY/components/Unstake/Unstake.tsx @@ -38,6 +38,7 @@ export type UnstakeFormValues = { amountCryptoPrecision: string fiatAmount: string accountId: string + unstakePercent: number } export const Unstake: React.FC = ({ @@ -53,6 +54,7 @@ export const Unstake: React.FC { const dispatch = useAppDispatch() const amountCryptoPrecision = watch('amountCryptoPrecision') const accountId = watch('accountId') + const unstakePercent = watch('unstakePercent') const tcyMarketData = useAppSelector(state => selectMarketDataByFilter(state, { assetId: tcyAssetId }), ) @@ -47,30 +46,17 @@ export const UnstakeConfirm: React.FC = () => { [amountCryptoPrecision, tcyMarketData], ) - const { data: tcyStaker } = useTcyStaker(accountId) - const { isDrawerOpen, openActionCenter } = useActionCenterContext() const toast = useNotificationToast({ duration: isDrawerOpen ? 5000 : null, }) - const amountThorBaseUnit = useMemo( - () => - BigAmount.fromPrecision({ - value: amountCryptoPrecision, - precision: THOR_PRECISION, - }).toBaseUnit(), - [amountCryptoPrecision], + const withdrawBps = useMemo( + () => convertPercentageToBasisPoints(unstakePercent).toFixed(0), + [unstakePercent], ) - const withdrawBps = useMemo(() => { - if (!tcyStaker?.amount || !amountThorBaseUnit) return '0' - const stakedAmountThorBaseUnit = tcyStaker.amount - const withdrawRatio = bnOrZero(amountThorBaseUnit).div(stakedAmountThorBaseUnit) - return withdrawRatio.times(BASE_BPS_POINTS).toFixed(0) - }, [tcyStaker?.amount, amountThorBaseUnit]) - const { executeTransaction, estimatedFeesData, diff --git a/src/pages/TCY/components/Unstake/UnstakeInput.tsx b/src/pages/TCY/components/Unstake/UnstakeInput.tsx index 48c65d3a378..8f84d905309 100644 --- a/src/pages/TCY/components/Unstake/UnstakeInput.tsx +++ b/src/pages/TCY/components/Unstake/UnstakeInput.tsx @@ -13,10 +13,10 @@ import { } from '@chakra-ui/react' import type { AssetId } from '@shapeshiftoss/caip' import { tcyAssetId, thorchainChainId } from '@shapeshiftoss/caip' -import { BigAmount, bnOrZero } from '@shapeshiftoss/utils' +import { BigAmount, bnOrZero, convertPercentageToBasisPoints } from '@shapeshiftoss/utils' import noop from 'lodash/noop' import { useCallback, useEffect, useMemo, useState } from 'react' -import { useFormContext, useWatch } from 'react-hook-form' +import { useFormContext } from 'react-hook-form' import { useTranslate } from 'react-polyglot' import { useNavigate } from 'react-router' @@ -34,7 +34,7 @@ import { Row } from '@/components/Row/Row' import { RawText } from '@/components/Text' import { useWallet } from '@/hooks/useWallet/useWallet' import { bn } from '@/lib/bignumber/bignumber' -import { BASE_BPS_POINTS, THOR_PRECISION } from '@/lib/utils/thorchain/constants' +import { THOR_PRECISION } from '@/lib/utils/thorchain/constants' import { useIsChainHalted } from '@/lib/utils/thorchain/hooks/useIsChainHalted' import { useSendThorTx } from '@/lib/utils/thorchain/hooks/useSendThorTx' import { selectAssetById } from '@/state/slices/assetsSlice/selectors' @@ -85,14 +85,11 @@ export const UnstakeInput: React.FC() const [fieldName, setFieldName] = useState('amountCryptoPrecision') - const [unstakePercent, setUnstakePercent] = useState(100) + const [selectedUnstakePercent, setSelectedUnstakePercent] = useState( + undefined, + ) - const amountCryptoPrecision = useWatch({ - name: 'amountCryptoPrecision', - }) - const fiatAmount = useWatch({ - name: 'fiatAmount', - }) + const currentUnstakePercent = selectedUnstakePercent ?? 100 const { price: assetUserCurrencyRate = '0' } = useAppSelector(state => @@ -113,57 +110,30 @@ export const UnstakeInput: React.FC { - if (!tcyStaker?.amount) return '0' - const amountThorBaseUnit = BigAmount.fromPrecision({ - value: amountCryptoPrecision, - precision: THOR_PRECISION, - }).toBaseUnit() - // tcyStaker.amount is already in THOR base units from the API - const withdrawRatio = bnOrZero(amountThorBaseUnit).div(tcyStaker.amount) - return withdrawRatio.times(BASE_BPS_POINTS).toFixed(0) - }, [tcyStaker?.amount, amountCryptoPrecision]) - - const handleAmountChange = useCallback( - (inputValue: string) => { - if (inputValue === '') { - setValue('amountCryptoPrecision', '') - setValue('fiatAmount', '') - return - } - - const price = assetUserCurrencyRate ?? 0 - const cryptoAmount = inputValue - const fiatAmount = bnOrZero(cryptoAmount).times(bnOrZero(price)) + const amountCryptoPrecision = useMemo( + () => bnOrZero(stakedAmountCryptoPrecision).times(currentUnstakePercent).div(100).toFixed(), + [stakedAmountCryptoPrecision, currentUnstakePercent], + ) - setValue('amountCryptoPrecision', cryptoAmount.toString()) - setValue('fiatAmount', fiatAmount.toString()) - }, - [setValue, assetUserCurrencyRate], + const fiatAmount = useMemo( + () => bnOrZero(amountCryptoPrecision).times(assetUserCurrencyRate).toFixed(2), + [amountCryptoPrecision, assetUserCurrencyRate], ) - const handleUnstakePercentChange = useCallback( - (value: number) => { - setUnstakePercent(value) - if (!tcyStaker?.amount) { - setValue('amountCryptoPrecision', '0') - setValue('fiatAmount', '0') - return - } - const stakedAmount = BigAmount.fromBaseUnit({ - value: tcyStaker.amount, - precision: THOR_PRECISION, - }).toPrecision() - const unstakeAmount = bnOrZero(stakedAmount).times(value).div(100).toString() - handleAmountChange(unstakeAmount) - }, - [tcyStaker?.amount, handleAmountChange, setValue], + const withdrawBps = useMemo( + () => convertPercentageToBasisPoints(currentUnstakePercent).toFixed(0), + [currentUnstakePercent], ) - // Set initial values when component mounts or when staked amount changes + const handleUnstakePercentChange = useCallback((value: number) => { + setSelectedUnstakePercent(value) + }, []) + useEffect(() => { - handleUnstakePercentChange(unstakePercent) - }, [unstakePercent, tcyStaker?.amount, handleUnstakePercentChange]) + setValue('amountCryptoPrecision', amountCryptoPrecision) + setValue('fiatAmount', fiatAmount) + setValue('unstakePercent', currentUnstakePercent) + }, [amountCryptoPrecision, fiatAmount, currentUnstakePercent, setValue]) const { estimatedFeesData, isEstimatedFeesDataLoading, isEstimatedFeesDataError } = useSendThorTx( { @@ -233,7 +203,7 @@ export const UnstakeInput: React.FC - + - + diff --git a/src/plugins/activePlugins.ts b/src/plugins/activePlugins.ts index f5a19052594..f013af3d3fc 100644 --- a/src/plugins/activePlugins.ts +++ b/src/plugins/activePlugins.ts @@ -32,6 +32,7 @@ import plasma from '@/plugins/plasma' import plume from '@/plugins/plume' import polygon from '@/plugins/polygon' import scroll from '@/plugins/scroll' +import sei from '@/plugins/sei' import solana from '@/plugins/solana' import soneium from '@/plugins/soneium' import sonic from '@/plugins/sonic' @@ -82,6 +83,7 @@ export const activePlugins = [ plasma, plume, polygon, + sei, scroll, soneium, solana, diff --git a/src/plugins/sei/index.tsx b/src/plugins/sei/index.tsx new file mode 100644 index 00000000000..eed1f302a30 --- /dev/null +++ b/src/plugins/sei/index.tsx @@ -0,0 +1,49 @@ +import { fromAssetId, seiChainId } from '@shapeshiftoss/caip' +import { sei } from '@shapeshiftoss/chain-adapters' +import { KnownChainIds } from '@shapeshiftoss/types' + +import { getConfig } from '@/config' +import { getAssetService } from '@/lib/asset-service' +import type { Plugins } from '@/plugins/types' + +// eslint-disable-next-line import/no-default-export +export default function register(): Plugins { + return [ + [ + 'seiChainAdapter', + { + name: 'seiChainAdapter', + featureFlag: ['Sei'], + providers: { + chainAdapters: [ + [ + KnownChainIds.SeiMainnet, + () => { + const getKnownTokens = () => { + const assetService = getAssetService() + return assetService.assets + .filter(asset => { + const { chainId, assetNamespace } = fromAssetId(asset.assetId) + return chainId === seiChainId && assetNamespace === 'erc20' + }) + .map(asset => ({ + assetId: asset.assetId, + contractAddress: fromAssetId(asset.assetId).assetReference, + symbol: asset.symbol, + name: asset.name, + precision: asset.precision, + })) + } + + return new sei.ChainAdapter({ + rpcUrl: getConfig().VITE_SEI_NODE_URL, + getKnownTokens, + }) + }, + ], + ], + }, + }, + ], + ] +} diff --git a/src/state/helpers.ts b/src/state/helpers.ts index d1277468e55..247613b9ee9 100644 --- a/src/state/helpers.ts +++ b/src/state/helpers.ts @@ -21,6 +21,7 @@ export const isCrossAccountTradeSupported = (swapperName: SwapperName) => { case SwapperName.Stonfi: case SwapperName.Sunio: case SwapperName.Across: + case SwapperName.Debridge: return true case SwapperName.Zrx: case SwapperName.CowSwap: @@ -55,6 +56,7 @@ export const getEnabledSwappers = ( AvnuSwap, StonfiSwap, AcrossSwap, + DebridgeSwap, }: FeatureFlags, isCrossAccountTrade: boolean, isSolBuyAssetId: boolean, @@ -115,6 +117,8 @@ export const getEnabledSwappers = ( StonfiSwap && (!isCrossAccountTrade || isCrossAccountTradeSupported(SwapperName.Stonfi)), [SwapperName.Across]: AcrossSwap && (!isCrossAccountTrade || isCrossAccountTradeSupported(SwapperName.Across)), + [SwapperName.Debridge]: + DebridgeSwap && (!isCrossAccountTrade || isCrossAccountTradeSupported(SwapperName.Debridge)), [SwapperName.Test]: false, } } diff --git a/src/state/migrations/index.ts b/src/state/migrations/index.ts index ec34ecbea6a..a081bc758cb 100644 --- a/src/state/migrations/index.ts +++ b/src/state/migrations/index.ts @@ -351,6 +351,7 @@ export const clearAssetsMigrations = { 302: clearAssets, 303: clearAssets, 304: clearAssets, + 305: clearAssets, } as unknown as Omit export const clearMarketDataMigrations = { diff --git a/src/state/slices/opportunitiesSlice/mappings.ts b/src/state/slices/opportunitiesSlice/mappings.ts index 360cbb60af5..5b9b69d8849 100644 --- a/src/state/slices/opportunitiesSlice/mappings.ts +++ b/src/state/slices/opportunitiesSlice/mappings.ts @@ -208,6 +208,7 @@ export const CHAIN_ID_TO_SUPPORTED_DEFI_OPPORTUNITIES: Record< [KnownChainIds.BobMainnet]: [], [KnownChainIds.ModeMainnet]: [], [KnownChainIds.SoneiumMainnet]: [], + [KnownChainIds.SeiMainnet]: [], [KnownChainIds.ZcashMainnet]: [], [KnownChainIds.NearMainnet]: [], [KnownChainIds.TonMainnet]: [], diff --git a/src/state/slices/portfolioSlice/utils/index.ts b/src/state/slices/portfolioSlice/utils/index.ts index 3c33e74a33e..e27dff556a5 100644 --- a/src/state/slices/portfolioSlice/utils/index.ts +++ b/src/state/slices/portfolioSlice/utils/index.ts @@ -38,6 +38,7 @@ import { plasmaChainId, polygonChainId, scrollChainId, + seiChainId, solanaChainId, soneiumChainId, sonicChainId, @@ -88,6 +89,7 @@ import { supportsPlasma, supportsPolygon, supportsScroll, + supportsSei, supportsSolana, supportsSoneium, supportsSonic, @@ -150,6 +152,7 @@ export const accountIdToLabel = (accountId: AccountId): string => { case katanaChainId: case storyChainId: case worldChainChainId: + case seiChainId: case scrollChainId: case sonicChainId: case unichainChainId: @@ -586,6 +589,8 @@ export const isAssetSupportedByWallet = (assetId: AssetId, wallet: HDWallet): bo return supportsWorldChain(wallet) case hemiChainId: return supportsHemi(wallet) + case seiChainId: + return supportsSei(wallet) case lineaChainId: return supportsLinea(wallet) case scrollChainId: diff --git a/src/state/slices/preferencesSlice/preferencesSlice.ts b/src/state/slices/preferencesSlice/preferencesSlice.ts index 0dbd27d1c4b..11a27bc910a 100644 --- a/src/state/slices/preferencesSlice/preferencesSlice.ts +++ b/src/state/slices/preferencesSlice/preferencesSlice.ts @@ -50,6 +50,7 @@ export type FeatureFlags = { Blast: boolean WorldChain: boolean Hemi: boolean + Sei: boolean Linea: boolean Scroll: boolean Sonic: boolean @@ -122,6 +123,7 @@ export type FeatureFlags = { AvnuSwap: boolean StonfiSwap: boolean AcrossSwap: boolean + DebridgeSwap: boolean LazyTxHistory: boolean LedgerReadOnly: boolean QuickBuy: boolean @@ -218,6 +220,7 @@ const initialState: Preferences = { Blast: getConfig().VITE_FEATURE_BLAST, WorldChain: getConfig().VITE_FEATURE_WORLDCHAIN, Hemi: getConfig().VITE_FEATURE_HEMI, + Sei: getConfig().VITE_FEATURE_SEI, Linea: getConfig().VITE_FEATURE_LINEA, Scroll: getConfig().VITE_FEATURE_SCROLL, Sonic: getConfig().VITE_FEATURE_SONIC, @@ -290,6 +293,7 @@ const initialState: Preferences = { AvnuSwap: getConfig().VITE_FEATURE_AVNU_SWAP, StonfiSwap: getConfig().VITE_FEATURE_STONFI_SWAP, AcrossSwap: getConfig().VITE_FEATURE_ACROSS_SWAP, + DebridgeSwap: getConfig().VITE_FEATURE_DEBRIDGE_SWAP, LazyTxHistory: getConfig().VITE_FEATURE_TX_HISTORY_BYE_BYE, LedgerReadOnly: getConfig().VITE_FEATURE_LEDGER_READ_ONLY, QuickBuy: getConfig().VITE_FEATURE_QUICK_BUY, diff --git a/src/test/mocks/store.ts b/src/test/mocks/store.ts index 0fe63521dd4..8d0a559a464 100644 --- a/src/test/mocks/store.ts +++ b/src/test/mocks/store.ts @@ -123,6 +123,7 @@ export const mockStore: ReduxState = { Blast: false, WorldChain: false, Hemi: false, + Sei: false, Linea: false, Scroll: false, Sonic: false, @@ -194,6 +195,7 @@ export const mockStore: ReduxState = { AvnuSwap: false, StonfiSwap: false, AcrossSwap: false, + DebridgeSwap: false, LazyTxHistory: false, QuickBuy: false, NewWalletManager: false, diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts index 745a693502a..89ee9eaa92b 100644 --- a/src/vite-env.d.ts +++ b/src/vite-env.d.ts @@ -216,6 +216,8 @@ interface ImportMetaEnv { readonly VITE_FEATURE_WORLDCHAIN: string readonly VITE_BLAST_NODE_URL: string readonly VITE_FEATURE_BLAST: string + readonly VITE_SEI_NODE_URL: string + readonly VITE_FEATURE_SEI: string readonly VITE_FEATURE_NOTIFICATIONS_WEBSERVICES: string // Only present in *some* envs