From 3a3a67f5569cc4c416ba529b0e509207ba3667b2 Mon Sep 17 00:00:00 2001 From: wenty22 Date: Wed, 15 Jan 2025 23:06:15 +0800 Subject: [PATCH 1/6] feat: Add toToken info to element --- .release/.changeset/pre.json | 3 ++- .release/.changeset/thick-donkeys-kneel.md | 6 +++++ .../token-config/mainnet/chains.ts | 26 +++++++++---------- common/config/rush/pnpm-lock.yaml | 3 ++- packages/canonical-bridge-sdk/CHANGELOG.md | 6 +++++ packages/canonical-bridge-sdk/package.json | 4 +-- packages/canonical-bridge-widget/CHANGELOG.md | 9 +++++++ packages/canonical-bridge-widget/package.json | 7 ++--- .../src/core/locales/index.ts | 5 ++++ .../canonical-bridge-widget/src/index.tsx | 2 +- .../components/ToTokenSection/index.tsx | 8 +++--- 11 files changed, 54 insertions(+), 25 deletions(-) create mode 100644 .release/.changeset/thick-donkeys-kneel.md create mode 100644 packages/canonical-bridge-widget/src/core/locales/index.ts diff --git a/.release/.changeset/pre.json b/.release/.changeset/pre.json index dcc9414e..9b910a01 100644 --- a/.release/.changeset/pre.json +++ b/.release/.changeset/pre.json @@ -6,6 +6,7 @@ "@bnb-chain/canonical-bridge-widget": "0.5.18" }, "changesets": [ - "blue-goats-shave" + "blue-goats-shave", + "thick-donkeys-kneel" ] } diff --git a/.release/.changeset/thick-donkeys-kneel.md b/.release/.changeset/thick-donkeys-kneel.md new file mode 100644 index 00000000..32311c7f --- /dev/null +++ b/.release/.changeset/thick-donkeys-kneel.md @@ -0,0 +1,6 @@ +--- +"@bnb-chain/canonical-bridge-widget": patch +"@bnb-chain/canonical-bridge-sdk": patch +--- + +Remove unused code diff --git a/apps/canonical-bridge-ui/token-config/mainnet/chains.ts b/apps/canonical-bridge-ui/token-config/mainnet/chains.ts index c73a6b9a..4e182c8e 100644 --- a/apps/canonical-bridge-ui/token-config/mainnet/chains.ts +++ b/apps/canonical-bridge-ui/token-config/mainnet/chains.ts @@ -841,19 +841,19 @@ export const chains: IChainConfig[] = [ default: { name: 'Blastscan', url: 'https://blastscan.io' }, }, }, - { - chainType: 'evm', - id: 112358, - name: 'Metachain One', - nativeCurrency: { name: 'Metao', symbol: 'METAO', decimals: 18 }, - rpcUrls: { default: { http: ['https://rpc.metachain.one'] } }, - blockExplorers: { - default: { - name: 'blockscout', - url: 'https://explorer.metachain.one', - }, - }, - }, + // { + // chainType: 'evm', + // id: 112358, + // name: 'Metachain One', + // nativeCurrency: { name: 'Metao', symbol: 'METAO', decimals: 18 }, + // rpcUrls: { default: { http: ['https://rpc.metachain.one'] } }, + // blockExplorers: { + // default: { + // name: 'blockscout', + // url: 'https://explorer.metachain.one', + // }, + // }, + // }, { chainType: 'evm', id: 167000, diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index cb2ee9c3..69a8779b 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -286,10 +286,11 @@ importers: version: 3.9.1(@types/node@22.7.5)(rollup@4.29.2)(typescript@5.5.4)(vite@6.0.7(@types/node@22.7.5)(terser@5.37.0)(tsx@4.15.9)(yaml@2.7.0)) ../../packages/canonical-bridge-widget: - devDependencies: + dependencies: '@bnb-chain/canonical-bridge-sdk': specifier: workspace:* version: link:../canonical-bridge-sdk + devDependencies: '@bnb-chain/eslint-config': specifier: ^1 version: 1.0.2(@babel/plugin-syntax-flow@7.26.0(@babel/core@7.26.0))(@babel/plugin-transform-react-jsx@7.25.9(@babel/core@7.26.0))(@typescript-eslint/parser@8.19.0(eslint@8.57.1)(typescript@5.5.4))(eslint@8.57.1)(typescript@5.5.4) diff --git a/packages/canonical-bridge-sdk/CHANGELOG.md b/packages/canonical-bridge-sdk/CHANGELOG.md index 91195084..86a60610 100644 --- a/packages/canonical-bridge-sdk/CHANGELOG.md +++ b/packages/canonical-bridge-sdk/CHANGELOG.md @@ -1,5 +1,11 @@ # @bnb-chain/canonical-bridge-sdk +## 0.5.0-alpha.8 + +### Patch Changes + +- Remove unused code + ## 0.4.7 ### Patch Changes diff --git a/packages/canonical-bridge-sdk/package.json b/packages/canonical-bridge-sdk/package.json index 81af1241..25e7db88 100644 --- a/packages/canonical-bridge-sdk/package.json +++ b/packages/canonical-bridge-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@bnb-chain/canonical-bridge-sdk", - "version": "0.4.7", + "version": "0.5.0-alpha.8", "description": "canonical bridge sdk", "author": "bnb-chain", "private": false, @@ -13,7 +13,6 @@ "keywords": [ "bridge" ], - "type": "module", "files": [ "dist" ], @@ -21,6 +20,7 @@ "access": "public" }, "sideEffects": false, + "type": "module", "main": "./dist/index.js", "types": "./dist/index.d.ts", "scripts": { diff --git a/packages/canonical-bridge-widget/CHANGELOG.md b/packages/canonical-bridge-widget/CHANGELOG.md index 8cb8038f..0d6a33b0 100644 --- a/packages/canonical-bridge-widget/CHANGELOG.md +++ b/packages/canonical-bridge-widget/CHANGELOG.md @@ -1,5 +1,14 @@ # @bnb-chain/canonical-bridge-widget +## 0.6.0-alpha.13 + +### Patch Changes + +- d5065a6: chore: Update confirmation popup amount styling +- Remove unused code +- Updated dependencies + - @bnb-chain/canonical-bridge-sdk@0.5.0-alpha.8 + ## 0.5.19-alpha.0 ### Patch Changes diff --git a/packages/canonical-bridge-widget/package.json b/packages/canonical-bridge-widget/package.json index 5c8ce318..f1f6a6cf 100644 --- a/packages/canonical-bridge-widget/package.json +++ b/packages/canonical-bridge-widget/package.json @@ -1,6 +1,6 @@ { "name": "@bnb-chain/canonical-bridge-widget", - "version": "0.5.19-alpha.0", + "version": "0.6.0-alpha.13", "description": "canonical bridge widget", "author": "bnb-chain", "private": false, @@ -13,7 +13,6 @@ "keywords": [ "bridge" ], - "type": "module", "files": [ "dist" ], @@ -21,6 +20,7 @@ "access": "public" }, "sideEffects": false, + "type": "module", "main": "./dist/index.js", "types": "./dist/index.d.ts", "scripts": { @@ -40,7 +40,8 @@ "@solana/wallet-adapter-react": "^0", "@solana/web3.js": "^1", "tronweb": "^6", - "@tronweb3/tronwallet-adapter-react-hooks": "^1" + "@tronweb3/tronwallet-adapter-react-hooks": "^1", + "@bnb-chain/canonical-bridge-sdk": "^0.5.0-alpha.8" }, "devDependencies": { "@bnb-chain/canonical-bridge-sdk": "workspace:*", diff --git a/packages/canonical-bridge-widget/src/core/locales/index.ts b/packages/canonical-bridge-widget/src/core/locales/index.ts new file mode 100644 index 00000000..5d61ca6c --- /dev/null +++ b/packages/canonical-bridge-widget/src/core/locales/index.ts @@ -0,0 +1,5 @@ +import { en } from '@/core/locales/en'; + +export const locales = { + en, +}; diff --git a/packages/canonical-bridge-widget/src/index.tsx b/packages/canonical-bridge-widget/src/index.tsx index cf3da94c..6d341348 100644 --- a/packages/canonical-bridge-widget/src/index.tsx +++ b/packages/canonical-bridge-widget/src/index.tsx @@ -17,7 +17,7 @@ export * from '@/core/utils/gtm'; export * from '@/core/constants/error'; // locales -export * from '@/core/locales/en'; +export * from '@/core/locales'; // wallet export * from '@/modules/wallet/components/ConnectButton'; diff --git a/packages/canonical-bridge-widget/src/modules/transfer/components/ToTokenSection/index.tsx b/packages/canonical-bridge-widget/src/modules/transfer/components/ToTokenSection/index.tsx index cd768ba2..22b06f79 100644 --- a/packages/canonical-bridge-widget/src/modules/transfer/components/ToTokenSection/index.tsx +++ b/packages/canonical-bridge-widget/src/modules/transfer/components/ToTokenSection/index.tsx @@ -75,10 +75,10 @@ function ToTokenItem({ token, isSelected }: { token: IBridgeToken; isSelected: b Date: Tue, 21 Jan 2025 12:14:13 +0800 Subject: [PATCH 2/6] feat: Use `chainId` + `tokenAddress` to get token price --- apps/canonical-bridge-server/package.json | 1 + .../src/common/constants/chains.ts | 1123 +++++++++++++++++ .../src/common/constants/index.ts | 23 +- .../src/module/bridge/bridge.processor.ts | 120 +- .../src/module/bridge/bridge.service.ts | 89 +- .../src/module/token/token.controller.ts | 24 +- .../src/module/token/token.module.ts | 3 +- .../src/module/token/token.processor.ts | 106 +- .../src/module/token/token.schedule.ts | 32 +- .../src/module/token/token.service.ts | 136 +- .../src/shared/database/database.service.ts | 86 +- .../src/shared/web3/web3.interface.ts | 95 +- .../src/shared/web3/web3.service.ts | 30 +- common/config/rush/pnpm-lock.yaml | 26 +- packages/canonical-bridge-sdk/package.json | 9 +- .../src/adapters/stargate/types.ts | 1 + packages/canonical-bridge-sdk/vite.config.ts | 2 +- packages/canonical-bridge-widget/package.json | 9 +- .../canonical-bridge-widget/vite.config.ts | 2 +- 19 files changed, 1684 insertions(+), 233 deletions(-) create mode 100644 apps/canonical-bridge-server/src/common/constants/chains.ts diff --git a/apps/canonical-bridge-server/package.json b/apps/canonical-bridge-server/package.json index d125de91..95fa02e2 100644 --- a/apps/canonical-bridge-server/package.json +++ b/apps/canonical-bridge-server/package.json @@ -20,6 +20,7 @@ "prisma:migrate:div": "npx dotenv -e .env -- npx prisma migrate dev" }, "dependencies": { + "@bnb-chain/canonical-bridge-sdk": "workspace:*", "@nestjs/axios": "~3.0.3", "@nestjs/bullmq": "~10.2.1", "@nestjs/cache-manager": "~2.2.2", diff --git a/apps/canonical-bridge-server/src/common/constants/chains.ts b/apps/canonical-bridge-server/src/common/constants/chains.ts new file mode 100644 index 00000000..6df4b40f --- /dev/null +++ b/apps/canonical-bridge-server/src/common/constants/chains.ts @@ -0,0 +1,1123 @@ +import { IChainConfig } from '@bnb-chain/canonical-bridge-sdk'; + +export interface IServerChainConfig extends IChainConfig { + extra?: { + cmcPlatform?: string; // cmc + llamaPlatform?: string; // llama + }; +} + +export const chains: IServerChainConfig[] = [ + { + chainType: 'evm', + id: 56, + name: 'BNB Smart Chain', + nativeCurrency: { + name: 'BNB Chain Native Token', + symbol: 'BNB', + decimals: 18, + }, + rpcUrls: { default: { http: ['https://bsc-dataseed.bnbchain.org'] } }, + blockExplorers: { + default: { name: 'bscscan', url: 'https://bscscan.com' }, + }, + extra: { + cmcPlatform: 'bnb', + }, + }, + { + chainType: 'evm', + id: 1, + name: 'Ethereum', + nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 }, + rpcUrls: { + default: { http: ['https://ethereum-rpc.publicnode.com/'] }, + }, + blockExplorers: { + default: { name: 'Etherscan', url: 'https://etherscan.io' }, + }, + extra: { + cmcPlatform: 'ethereum', + }, + }, + { + chainType: 'evm', + id: 10, + name: 'Optimism', + nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 }, + rpcUrls: { default: { http: ['https://mainnet.optimism.io'] } }, + blockExplorers: { + default: { + name: 'OP Mainnet Explorer', + url: 'https://optimistic.etherscan.io', + }, + }, + extra: { + cmcPlatform: 'optimism-ethereum', + }, + }, + { + chainType: 'evm', + id: 14, + name: 'Flare', + nativeCurrency: { name: 'FLR', symbol: 'FLR', decimals: 18 }, + rpcUrls: { + default: { http: ['https://flare-api.flare.network/ext/bc/C/rpc'] }, + }, + blockExplorers: { + default: { name: 'Flare Scan', url: 'https://flarescan.com/' }, + }, + extra: { + cmcPlatform: 'flare', + }, + }, + { + chainType: 'evm', + id: 25, + name: 'Cronos', + nativeCurrency: { name: 'CRO', symbol: 'CRO', decimals: 18 }, + rpcUrls: { default: { http: ['https://evm.cronos.org'] } }, + blockExplorers: { + default: { name: 'Crono Scan', url: 'https://cronoscan.com/' }, + }, + extra: { + cmcPlatform: 'cronos', + }, + }, + { + chainType: 'evm', + id: 44, + name: 'Crab Network', + nativeCurrency: { + name: 'Crab Network Native Token', + symbol: 'CRAB', + decimals: 18, + }, + rpcUrls: { default: { http: ['https://crab-rpc.darwinia.network'] } }, + blockExplorers: { + default: { + name: 'Crab explorer', + url: 'https://crab-scan.darwinia.network', + }, + }, + extra: { + cmcPlatform: 'darwinia-crab-network', + }, + }, + { + chainType: 'evm', + id: 57, + name: 'Syscoin', + nativeCurrency: { name: 'Syscoin', symbol: 'SYS', decimals: 18 }, + rpcUrls: { default: { http: ['https://rpc.syscoin.org'] } }, + blockExplorers: { + default: { + name: 'Syscoin Block Explorer', + url: 'https://explorer.syscoin.org', + }, + }, + extra: { + cmcPlatform: 'syscoin', + }, + }, + { + chainType: 'evm', + id: 58, + name: 'Ontology', + nativeCurrency: { name: 'ONG', symbol: 'ONG', decimals: 18 }, + rpcUrls: { default: { http: ['https://dappnode1.ont.io:10339'] } }, + blockExplorers: { + default: { name: 'explorer', url: 'https://explorer.ont.io' }, + }, + extra: { + cmcPlatform: 'ontology-gas', + }, + }, + { + chainType: 'evm', + id: 66, + name: 'OKXChain', + nativeCurrency: { + name: 'OKXChain Global Utility Token', + symbol: 'OKT', + decimals: 18, + }, + rpcUrls: { default: { http: ['https://exchainrpc.okex.org'] } }, + blockExplorers: { + default: { name: 'oklink', url: 'https://www.oklink.com/oktc' }, + }, + }, + { + chainType: 'evm', + id: 73, + name: 'FNCY', + nativeCurrency: { name: 'FNCY', symbol: 'FNCY', decimals: 18 }, + rpcUrls: { default: { http: ['https://fncy-seed1.fncy.world'] } }, + blockExplorers: { + default: { name: 'fncy scan', url: 'https://fncyscan.fncy.world' }, + }, + }, + { + chainType: 'evm', + id: 100, + name: 'Gnosis', + nativeCurrency: { name: 'xDAI', symbol: 'XDAI', decimals: 18 }, + rpcUrls: { default: { http: ['https://rpc.gnosischain.com'] } }, + blockExplorers: { + default: { name: 'gnosisscan', url: 'https://gnosisscan.io' }, + }, + }, + { + chainType: 'evm', + id: 128, + name: 'Huobi ECO Chain', + nativeCurrency: { + name: 'Huobi ECO Chain Native Token', + symbol: 'HT', + decimals: 18, + }, + rpcUrls: { + default: { http: ['https://http-mainnet.hecochain.com'] }, + }, + blockExplorers: { + default: { + name: 'hecoinfo', + url: 'https://hecoinfo.com', + tokenUrlPattern: 'https://hecoscan.io/#/token20/{0}', + }, + }, + }, + { + chainType: 'evm', + id: 137, + name: 'Polygon', + nativeCurrency: { name: 'MATIC', symbol: 'MATIC', decimals: 18 }, + rpcUrls: { default: { http: ['https://polygon-rpc.com'] } }, + blockExplorers: { + default: { name: 'polygonscan', url: 'https://polygonscan.com' }, + }, + extra: { + cmcPlatform: 'polygon', + }, + }, + { + chainType: 'evm', + id: 169, + name: 'Manta Pacific', + nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 }, + rpcUrls: { + default: { http: ['https://pacific-rpc.manta.network/http'] }, + }, + blockExplorers: { + default: { + name: 'manta-pacific Explorer', + url: 'https://pacific-explorer.manta.network', + }, + }, + }, + { + chainType: 'evm', + id: 196, + name: 'X Layer', + nativeCurrency: { + name: 'X Layer Global Utility Token', + symbol: 'OKB', + decimals: 18, + }, + rpcUrls: { default: { http: ['https://rpc.xlayer.tech'] } }, + blockExplorers: { + default: { name: 'OKLink', url: 'https://www.oklink.com/xlayer' }, + }, + }, + { + chainType: 'evm', + id: 204, + name: 'opBNB', + nativeCurrency: { + name: 'BNB Chain Native Token', + symbol: 'BNB', + decimals: 18, + }, + rpcUrls: { + default: { http: ['https://opbnb-mainnet-rpc.bnbchain.org'] }, + }, + blockExplorers: { + default: { name: 'opbnbscan', url: 'https://mainnet.opbnbscan.com' }, + }, + }, + { + chainType: 'evm', + id: 223, + name: 'b2', + nativeCurrency: { name: 'BTC', symbol: 'BTC', decimals: 18 }, + rpcUrls: { default: { http: ['https://rpc.bsquared.network'] } }, + blockExplorers: { + default: { + name: 'B2 Network Explorer', + url: 'https://explorer.bsquared.network', + }, + }, + }, + { + chainType: 'evm', + id: 248, + name: 'Oasys', + nativeCurrency: { name: 'OAS', symbol: 'OAS', decimals: 18 }, + rpcUrls: { default: { http: ['https://rpc.mainnet.oasys.games'] } }, + blockExplorers: { + default: { + name: 'Oasys-Mainnet explorer', + url: 'https://explorer.oasys.games', + }, + }, + }, + { + chainType: 'evm', + id: 250, + name: 'Fantom Opera', + nativeCurrency: { name: 'Fantom', symbol: 'FTM', decimals: 18 }, + rpcUrls: { default: { http: ['https://rpcapi.fantom.network'] } }, + blockExplorers: { + default: { name: 'ftmscan', url: 'https://ftmscan.com' }, + }, + extra: { + cmcPlatform: 'fantom', + }, + }, + { + chainType: 'evm', + id: 255, + name: 'Kroma', + nativeCurrency: { name: 'ETH', symbol: 'ETH', decimals: 18 }, + rpcUrls: { default: { http: ['https://api.kroma.network'] } }, + blockExplorers: { + default: { name: 'Kroma Scan', url: 'https://kromascan.com' }, + }, + }, + { + chainType: 'evm', + id: 288, + name: 'Boba Network', + nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 }, + rpcUrls: { default: { http: ['https://mainnet.boba.network'] } }, + blockExplorers: { + default: { name: 'Bobascan', url: 'https://bobascan.com' }, + }, + }, + { + chainType: 'evm', + id: 314, + name: 'Filecoin', + nativeCurrency: { name: 'filecoin', symbol: 'FIL', decimals: 18 }, + rpcUrls: { default: { http: ['https://api.node.glif.io'] } }, + blockExplorers: { + default: { name: 'Filfox', url: 'https://filfox.info/en' }, + }, + }, + { + chainType: 'evm', + id: 324, + name: 'zkSync', + nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 }, + rpcUrls: { default: { http: ['https://mainnet.era.zksync.io'] } }, + blockExplorers: { + default: { + name: 'zkSync Era Block Explorer', + url: 'https://explorer.zksync.io', + tokenUrlPattern: 'https://explorer.zksync.io/address/{0}', + }, + }, + extra: { + cmcPlatform: 'zksync', + }, + }, + { + chainType: 'evm', + id: 336, + name: 'Shiden', + nativeCurrency: { name: 'Shiden', symbol: 'SDN', decimals: 18 }, + rpcUrls: { default: { http: ['https://shiden.public.blastapi.io'] } }, + blockExplorers: { + default: { name: 'subscan', url: 'https://shiden.subscan.io' }, + }, + }, + { + chainType: 'evm', + id: 416, + name: 'SX Network', + nativeCurrency: { name: 'SX Network', symbol: 'SX', decimals: 18 }, + rpcUrls: { default: { http: ['https://rpc.sx.technology'] } }, + blockExplorers: { + default: { + name: 'SX Network Explorer', + url: 'https://explorer.sx.technology', + }, + }, + }, + { + chainType: 'evm', + id: 592, + name: 'Astar', + nativeCurrency: { name: 'Astar', symbol: 'ASTR', decimals: 18 }, + rpcUrls: { default: { http: ['https://evm.astar.network'] } }, + blockExplorers: { + default: { name: 'subscan', url: 'https://astar.subscan.io' }, + }, + }, + { + chainType: 'evm', + id: 1024, + name: 'CLV Parachain', + nativeCurrency: { name: 'CLV', symbol: 'CLV', decimals: 18 }, + rpcUrls: { default: { http: ['https://api-para.clover.finance'] } }, + blockExplorers: { + default: { + name: 'CLV Blockchain Explore', + url: 'https://clvscan.com/', + }, + }, + }, + { + chainType: 'evm', + id: 1030, + name: 'Conflux eSpace', + nativeCurrency: { name: 'CFX', symbol: 'CFX', decimals: 18 }, + rpcUrls: { default: { http: ['https://evm.confluxrpc.com'] } }, + blockExplorers: { + default: { + name: 'Conflux Scan', + url: 'https://evm.confluxscan.net', + }, + }, + }, + { + chainType: 'evm', + id: 1088, + name: 'Metis Andromeda', + nativeCurrency: { name: 'Metis', symbol: 'METIS', decimals: 18 }, + rpcUrls: { + default: { http: ['https://andromeda.metis.io/?owner=1088'] }, + }, + blockExplorers: { + default: { + name: 'Metis Andromeda explorer', + url: 'https://andromeda-explorer.metis.io', + }, + }, + }, + { + chainType: 'evm', + id: 1101, + name: 'Polygon zkEVM', + nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 }, + rpcUrls: { default: { http: ['https://zkevm-rpc.com'] } }, + blockExplorers: { + default: { + name: 'PolygonScan', + url: 'https://zkevm.polygonscan.com', + }, + }, + }, + { + chainType: 'evm', + id: 1116, + name: 'Core', + nativeCurrency: { name: 'CORE', symbol: 'CORE', decimals: 18 }, + rpcUrls: { default: { http: ['https://rpc.coredao.org'] } }, + blockExplorers: { + default: { name: 'Core Explorer', url: 'https://scan.coredao.org' }, + }, + }, + { + chainType: 'evm', + id: 1284, + name: 'Moonbeam', + nativeCurrency: { name: 'Glimmer', symbol: 'GLMR', decimals: 18 }, + rpcUrls: { default: { http: ['https://rpc.api.moonbeam.network'] } }, + blockExplorers: { + default: { name: 'moonscan', url: 'https://moonbeam.moonscan.io' }, + }, + extra: { + cmcPlatform: 'moonbeam', + }, + }, + { + chainType: 'evm', + id: 1285, + name: 'Moonriver', + nativeCurrency: { name: 'Moonriver', symbol: 'MOVR', decimals: 18 }, + rpcUrls: { + default: { http: ['https://rpc.api.moonriver.moonbeam.network'] }, + }, + blockExplorers: { + default: { name: 'moonscan', url: 'https://moonriver.moonscan.io' }, + }, + extra: { + cmcPlatform: 'moonriver', + }, + }, + { + chainType: 'evm', + id: 1329, + name: 'Sei Network', + nativeCurrency: { name: 'SEI', symbol: 'SEI', decimals: 18 }, + rpcUrls: { default: { http: ['https://evm-rpc.sei-apis.com'] } }, + blockExplorers: { + default: { name: 'Sei Scan', url: 'https://www.seiscan.app/' }, + }, + }, + { + chainType: 'evm', + id: 1625, + name: 'Gravity Alpha', + nativeCurrency: { name: 'Gravity', symbol: 'G.', decimals: 18 }, + rpcUrls: { default: { http: ['https://rpc.gravity.xyz'] } }, + blockExplorers: { + default: { + name: 'Gravity Alpha Mainnet Explorer', + url: 'https://explorer.gravity.xyz', + }, + }, + }, + { + chainType: 'evm', + id: 2001, + name: 'Milkomeda C1', + nativeCurrency: { name: 'milkAda', symbol: 'mADA', decimals: 18 }, + rpcUrls: { + default: { + http: ['https://rpc-mainnet-cardano-evm.c1.milkomeda.com'], + }, + }, + blockExplorers: { + default: { + name: 'Blockscout', + url: 'https://explorer-mainnet-cardano-evm.c1.milkomeda.com', + }, + }, + }, + { + chainType: 'evm', + id: 2002, + name: 'Milkomeda A1', + nativeCurrency: { name: 'milkALGO', symbol: 'mALGO', decimals: 18 }, + rpcUrls: { + default: { + http: ['https://rpc-mainnet-algorand-rollup.a1.milkomeda.com'], + }, + }, + blockExplorers: { default: { name: '', url: '' } }, + }, + { + chainType: 'evm', + id: 2222, + name: 'Kava', + nativeCurrency: { name: 'Kava', symbol: 'KAVA', decimals: 18 }, + rpcUrls: { default: { http: ['https://evm.kava.io'] } }, + blockExplorers: { + default: { name: 'Kava EVM Explorer', url: 'https://kavascan.com' }, + }, + }, + { + chainType: 'evm', + id: 2525, + name: 'inEVM', + nativeCurrency: { name: 'INJ', symbol: 'INJ', decimals: 18 }, + rpcUrls: { + default: { http: ['https://mainnet.rpc.inevm.com/http'] }, + }, + blockExplorers: { + default: { + name: 'inEVM Explorer', + url: 'https://explorer.inevm.com', + }, + }, + }, + { + chainType: 'evm', + id: 2649, + name: 'AILayer', + nativeCurrency: { name: 'BTC', symbol: 'BTC', decimals: 18 }, + rpcUrls: { default: { http: ['https://mainnet-rpc.ailayer.xyz'] } }, + blockExplorers: { + default: { + name: 'AI Layer Explorer', + url: 'https://mainnet-explorer.ailayer.xyz', + }, + }, + }, + { + chainType: 'evm', + id: 4200, + name: 'Merlin', + nativeCurrency: { name: 'BTC', symbol: 'BTC', decimals: 18 }, + rpcUrls: { default: { http: ['https://rpc.merlinchain.io'] } }, + blockExplorers: { + default: { name: 'Merlin Scan', url: 'https://scan.merlinchain.io' }, + }, + }, + { + chainType: 'evm', + id: 5000, + name: 'Mantle', + nativeCurrency: { name: 'Mantle', symbol: 'MNT', decimals: 18 }, + rpcUrls: { default: { http: ['https://rpc.mantle.xyz/'] } }, + blockExplorers: { + default: { + name: 'Mantle Mainnet Explorer', + url: 'https://explorer.mantle.xyz/', + }, + }, + }, + { + chainType: 'evm', + id: 6001, + name: 'BB', + nativeCurrency: { name: 'BB', symbol: 'BB', decimals: 18 }, + rpcUrls: { + default: { http: ['https://fullnode-mainnet.bouncebitapi.com'] }, + }, + blockExplorers: { + default: { name: 'BB Scan', url: 'https://bbscan.io' }, + }, + }, + { + chainType: 'evm', + id: 7000, + name: 'ZetaChain', + nativeCurrency: { name: 'ZETA', symbol: 'ZETA', decimals: 18 }, + rpcUrls: { + default: { + http: ['https://zetachain-evm.blockpi.network:443/v1/rpc/public'], + }, + }, + blockExplorers: { + default: { + name: 'Zeta Chain Explorer', + url: 'https://explorer.zetachain.com/', + }, + }, + }, + { + chainType: 'evm', + id: 7700, + name: 'Canto', + nativeCurrency: { name: 'Canto', symbol: 'CANTO', decimals: 18 }, + rpcUrls: { default: { http: ['https://canto.gravitychain.io'] } }, + blockExplorers: { + default: { + name: 'Canto Explorer (OKLink)', + url: 'https://www.oklink.com/canto', + }, + }, + }, + { + chainType: 'evm', + id: 8217, + name: 'Kaia', + nativeCurrency: { + name: 'KAIA', + symbol: 'KAIA', + decimals: 18, + }, + rpcUrls: { default: { http: ['https://public-en.node.kaia.io'] } }, + blockExplorers: { + default: { + name: 'Kaiascope', + url: 'https://kaiascope.com', + }, + }, + extra: { + cmcPlatform: 'kaia', + }, + }, + { + chainType: 'evm', + id: 8453, + name: 'Base', + nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 }, + rpcUrls: { default: { http: ['https://mainnet.base.org'] } }, + blockExplorers: { + default: { name: 'basescan', url: 'https://basescan.org' }, + }, + extra: { + cmcPlatform: 'base', + }, + }, + { + chainType: 'evm', + id: 8822, + name: 'IOTA EVM', + nativeCurrency: { + name: 'IOTA Token', + symbol: 'IOTA', + decimals: 18, + }, + rpcUrls: { + default: { http: ['https://json-rpc.evm.iotaledger.net'] }, + }, + blockExplorers: { + default: { + name: 'IOTA EVM explorer', + url: 'https://explorer.evm.iota.org', + }, + }, + }, + { + chainType: 'evm', + id: 9001, + name: 'Evmos', + nativeCurrency: { name: 'Evmos', symbol: 'EVMOS', decimals: 18 }, + rpcUrls: { + default: { http: ['https://evmos-mainnet.public.blastapi.io'] }, + }, + blockExplorers: { + default: { + name: 'Evmos Explorer (Escan)', + url: 'https://www.mintscan.io/evmos', + tokenUrlPattern: 'https://www.mintscan.io/evmos/address/{0}', + }, + }, + }, + { + chainType: 'evm', + id: 11501, + name: 'BEVM', + nativeCurrency: { name: 'BTC', symbol: 'BTC', decimals: 18 }, + rpcUrls: { default: { http: ['https://rpc-mainnet-1.bevm.io'] } }, + blockExplorers: { + default: { + name: 'BEVM Explorer', + url: 'https://scan-mainnet.bevm.io', + }, + }, + }, + { + chainType: 'evm', + id: 13000, + name: 'SPS', + nativeCurrency: { name: 'ECG', symbol: 'ECG', decimals: 18 }, + rpcUrls: { default: { http: ['https://rpc.ssquad.games'] } }, + blockExplorers: { + default: { + name: 'SPS Explorer', + url: 'http://spsscan.ssquad.games', + }, + }, + }, + { + chainType: 'evm', + id: 17777, + name: 'EOS EVM', + nativeCurrency: { name: 'EOS', symbol: 'EOS', decimals: 18 }, + rpcUrls: { default: { http: ['https://api.evm.eosnetwork.com'] } }, + blockExplorers: { + default: { + name: 'EOS EVM Explorer', + url: 'https://explorer.evm.eosnetwork.com', + }, + }, + extra: { + cmcPlatform: 'eos', + }, + }, + { + chainType: 'evm', + id: 22776, + name: 'MAP Protocol', + nativeCurrency: { name: 'MAPO', symbol: 'MAPO', decimals: 18 }, + rpcUrls: { default: { http: ['https://rpc.maplabs.io'] } }, + blockExplorers: { + default: { name: 'MAPO Scan', url: 'https://maposcan.io' }, + }, + }, + { + chainType: 'evm', + id: 23294, + name: 'Oasis Sapphire', + nativeCurrency: { + name: 'Sapphire Rose', + symbol: 'ROSE', + decimals: 18, + }, + rpcUrls: { default: { http: ['https://sapphire.oasis.io'] } }, + blockExplorers: { + default: { + name: 'Oasis Sapphire Explorer', + url: 'https://explorer.oasis.io/mainnet/sapphire', + }, + }, + }, + { + chainType: 'evm', + id: 34443, + name: 'Mode', + nativeCurrency: { name: 'ETH', symbol: 'ETH', decimals: 18 }, + rpcUrls: { default: { http: ['https://mainnet.mode.network'] } }, + blockExplorers: { + default: { + name: 'Mode Explorer', + url: 'https://explorer.mode.network', + }, + }, + }, + { + chainType: 'evm', + id: 42161, + name: 'Arbitrum One', + nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 }, + rpcUrls: { default: { http: ['https://arb1.arbitrum.io/rpc'] } }, + blockExplorers: { + default: { name: 'Arbiscan', url: 'https://arbiscan.io' }, + }, + extra: { + cmcPlatform: 'arbitrum', + }, + }, + { + chainType: 'evm', + id: 42170, + name: 'Arbitrum Nova', + nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 }, + rpcUrls: { default: { http: ['https://nova.arbitrum.io/rpc'] } }, + blockExplorers: { + default: { + name: 'Arbitrum Nova Chain Explorer', + url: 'https://nova-explorer.arbitrum.io', + }, + }, + extra: { + cmcPlatform: 'arbitrum-nova', + }, + }, + { + chainType: 'evm', + id: 42220, + name: 'Celo', + nativeCurrency: { name: 'CELO', symbol: 'CELO', decimals: 18 }, + rpcUrls: { default: { http: ['https://forno.celo.org'] } }, + blockExplorers: { + default: { name: 'Celoscan', url: 'https://celoscan.io' }, + }, + extra: { + cmcPlatform: 'celo', + }, + }, + { + chainType: 'evm', + id: 42262, + name: 'Oasis Emerald', + nativeCurrency: { + name: 'Emerald Rose', + symbol: 'ROSE', + decimals: 18, + }, + rpcUrls: { default: { http: ['https://emerald.oasis.io'] } }, + blockExplorers: { + default: { + name: 'Oasis Emerald Explorer', + url: 'https://explorer.oasis.io/mainnet/emerald', + }, + }, + }, + { + chainType: 'evm', + id: 42766, + name: 'ZKFair', + nativeCurrency: { name: 'USDC', symbol: 'USDC', decimals: 18 }, + rpcUrls: { default: { http: ['https://rpc.zkfair.io'] } }, + blockExplorers: { + default: { name: 'Zkfair Scan', url: 'https://scan.zkfair.io' }, + }, + }, + { + chainType: 'evm', + id: 43114, + name: 'Avalanche', + nativeCurrency: { name: 'Avalanche', symbol: 'AVAX', decimals: 18 }, + rpcUrls: { + default: { http: ['https://api.avax.network/ext/bc/C/rpc'] }, + }, + blockExplorers: { + default: { name: 'snowtrace', url: 'https://snowtrace.io' }, + }, + extra: { + cmcPlatform: 'avalanche', + }, + }, + { + chainType: 'evm', + id: 47805, + name: 'REI Network', + nativeCurrency: { name: 'REI', symbol: 'REI', decimals: 18 }, + rpcUrls: { default: { http: ['https://rpc.rei.network'] } }, + blockExplorers: { + default: { name: 'rei-scan', url: 'https://scan.rei.network' }, + }, + }, + { + chainType: 'evm', + id: 48900, + name: 'Zircuit', + nativeCurrency: { name: 'ETH', symbol: 'ETH', decimals: 18 }, + rpcUrls: { default: { http: ['https://zircuit-mainnet.drpc.org'] } }, + blockExplorers: { + default: { + name: 'Zircuit Explorer', + url: 'https://explorer.zircuit.com', + }, + }, + }, + { + chainType: 'evm', + id: 59144, + name: 'Linea', + nativeCurrency: { + name: 'Linea Ether', + symbol: 'ETH', + decimals: 18, + }, + rpcUrls: { default: { http: ['https://rpc.linea.build'] } }, + blockExplorers: { + default: { name: 'Etherscan', url: 'https://lineascan.build' }, + }, + }, + { + chainType: 'evm', + id: 71402, + name: 'Godwoken', + nativeCurrency: { name: 'pCKB', symbol: 'pCKB', decimals: 18 }, + rpcUrls: { + default: { http: ['https://v1.mainnet.godwoken.io/rpc'] }, + }, + blockExplorers: { + default: { + name: 'GWScan Block Explorer', + url: 'https://v1.gwscan.com', + tokenUrlPattern: 'https://v1.gwscan.com/account/{0}', + }, + }, + }, + { + chainType: 'evm', + id: 81457, + name: 'Blast', + nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 }, + rpcUrls: { default: { http: ['https://rpc.blast.io'] } }, + blockExplorers: { + default: { name: 'Blastscan', url: 'https://blastscan.io' }, + }, + extra: { + cmcPlatform: 'blast', + }, + }, + { + chainType: 'evm', + id: 167000, + name: 'Taiko', + nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 }, + rpcUrls: { default: { http: ['https://rpc.mainnet.taiko.xyz'] } }, + blockExplorers: { + default: { name: 'Taiko Scan', url: 'https://taikoscan.io' }, + }, + }, + { + chainType: 'evm', + id: 200901, + name: 'Bitlayer Mainnet', + nativeCurrency: { name: 'BTC', symbol: 'BTC', decimals: 18 }, + rpcUrls: { default: { http: ['https://rpc.bitlayer.org'] } }, + blockExplorers: { + default: { name: 'Bitlayer Scan', url: 'https://www.btrscan.com' }, + }, + }, + { + chainType: 'evm', + id: 210425, + name: 'PlatON', + nativeCurrency: { name: 'LAT', symbol: 'lat', decimals: 18 }, + rpcUrls: { + default: { http: ['https://openapi2.platon.network/rpc'] }, + }, + blockExplorers: { + default: { + name: 'PlatON explorer', + url: 'https://scan.platon.network', + tokenUrlPattern: 'https://scan.platon.network/tokens-detail?type=erc20&address={0}', + }, + }, + }, + { + chainType: 'evm', + id: 534352, + name: 'Scroll', + nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 }, + rpcUrls: { default: { http: ['https://rpc.scroll.io'] } }, + blockExplorers: { + default: { name: 'Scrollscan', url: 'https://scrollscan.com' }, + }, + }, + { + chainType: 'evm', + id: 60808, + name: 'BOB', + nativeCurrency: { name: 'ETH', symbol: 'ETH', decimals: 18 }, + rpcUrls: { default: { http: ['https://rpc.gobob.xyz'] } }, + blockExplorers: { + default: { name: 'BOB Explorer', url: 'https://explorer.gobob.xyz' }, + }, + }, + { + chainType: 'evm', + id: 810180, + name: 'zkLink', + nativeCurrency: { name: 'ETH', symbol: 'ETH', decimals: 18 }, + rpcUrls: { default: { http: ['https://rpc.zklink.io'] } }, + blockExplorers: { + default: { + name: 'ZKLink Explorer', + url: 'https://explorer.zklink.io', + }, + }, + }, + { + chainType: 'evm', + id: 888888888, + name: 'Ancient8', + nativeCurrency: { name: 'ETH', symbol: 'ETH', decimals: 18 }, + rpcUrls: { default: { http: ['https://rpc.ancient8.gg'] } }, + blockExplorers: { + default: { name: 'Ancient8 Scan', url: 'https://scan.ancient8.gg' }, + }, + }, + { + chainType: 'evm', + id: 1313161554, + name: 'Aurora', + nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 }, + rpcUrls: { default: { http: ['https://mainnet.aurora.dev'] } }, + blockExplorers: { + default: { name: 'aurorascan.dev', url: 'https://aurorascan.dev' }, + }, + }, + { + chainType: 'evm', + id: 1380012617, + name: 'RARI Chain', + nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 }, + rpcUrls: { + default: { http: ['https://mainnet.rpc.rarichain.org/http'] }, + }, + blockExplorers: { + default: { + name: 'Rari Mainnet Explorer', + url: 'https://mainnet.explorer.rarichain.org/', + }, + }, + }, + { + chainType: 'evm', + id: 1482601649, + name: 'SKALE Nebula Hub', + nativeCurrency: { name: 'sFUEL', symbol: 'sFUEL', decimals: 18 }, + rpcUrls: { + default: { + http: ['https://mainnet.skalenodes.com/v1/green-giddy-denebola'], + }, + }, + blockExplorers: { + default: { + name: 'SKALE Nebula Explorer', + url: 'https://green-giddy-denebola.explorer.mainnet.skalenodes.com', + }, + }, + }, + { + chainType: 'evm', + id: 1564830818, + name: 'SKALE Calypso', + nativeCurrency: { name: 'sFUEL', symbol: 'sFUEL', decimals: 18 }, + rpcUrls: { + default: { + http: ['https://mainnet.skalenodes.com/v1/honorable-steel-rasalhague'], + }, + }, + blockExplorers: { + default: { + name: 'SKALE Calypso Hub Explorer', + url: 'https://honorable-steel-rasalhague.explorer.mainnet.skalenodes.com/', + }, + }, + }, + { + chainType: 'evm', + id: 1666600000, + name: 'Harmony One', + nativeCurrency: { name: 'ONE', symbol: 'ONE', decimals: 18 }, + rpcUrls: { default: { http: ['https://api.harmony.one'] } }, + blockExplorers: { + default: { + name: 'Harmony Block Explorer', + url: 'https://explorer.harmony.one', + }, + }, + extra: { + cmcPlatform: 'harmony', + }, + }, + { + chainType: 'evm', + id: 2046399126, + name: 'SKALE Europa Hub', + nativeCurrency: { name: 'sFUEL', symbol: 'sFUEL', decimals: 18 }, + rpcUrls: { + default: { + http: ['https://mainnet.skalenodes.com/v1/elated-tan-skat'], + }, + }, + blockExplorers: { + default: { + name: 'SKALE Europa Hub Explorer', + url: 'https://elated-tan-skat.explorer.mainnet.skalenodes.com', + }, + }, + }, + { + chainType: 'tron', + id: 728126428, + name: 'Tron', + nativeCurrency: { name: 'TRX', symbol: 'TRX', decimals: 6 }, + rpcUrls: { default: { http: ['https://api.trongrid.io'] } }, + blockExplorers: { + default: { + name: 'Tron Scan', + url: 'https://tronscan.io/', + tokenUrlPattern: 'https://tronscan.io/#/token20/{0}', + }, + }, + extra: { + cmcPlatform: 'tron', + llamaPlatform: 'tron', + }, + }, + { + chainType: 'solana', + id: 7565164, + name: 'Solana', + nativeCurrency: { name: 'SOL', symbol: 'SOL', decimals: 9 }, + rpcUrls: { + default: { http: ['https://solana-rpc.debridge.finance'] }, + }, + blockExplorers: { + default: { + name: 'Solana explorer', + url: 'https://explorer.solana.com', + tokenUrlPattern: 'https://explorer.solana.com/address/{0}', + }, + }, + extra: { + cmcPlatform: 'solana', + llamaPlatform: 'solana', + }, + }, +]; diff --git a/apps/canonical-bridge-server/src/common/constants/index.ts b/apps/canonical-bridge-server/src/common/constants/index.ts index ee521bbd..cc5aca3c 100644 --- a/apps/canonical-bridge-server/src/common/constants/index.ts +++ b/apps/canonical-bridge-server/src/common/constants/index.ts @@ -25,16 +25,21 @@ export enum Queues { } export enum Tasks { - fetchToken = 'fetchToken', - fetchCoingeckoToken = 'fetchCoingeckoToken', - fetchPrice = 'fetchPrice', + fetchCmcTokens = 'fetchTokens', + fetchCoingeckoTokens = 'fetchCoingeckoTokens', + + fetchCmcPrice = 'fetchCmcPrice', fetchLlamaPrice = 'fetchLlamaPrice', + + cacheCmcConfig = 'cacheCmcConfig', + cacheLlamaConfig = 'cacheLlamaConfig', + cacheCmcConfigV2 = 'cacheCmcConfigV2', + cacheLlamaConfigV2 = 'cacheLlamaConfigV2', + fetchCbridge = 'fetchCbridge', fetchDebridge = 'fetchDebridge', fetchStargate = 'fetchStargate', fetchMeson = 'fetchMeson', - cacheCmcConfig = 'cacheCmcConfig', - cacheLlamaConfig = 'cacheLlamaConfig', filterCBridge = 'filterCBridge', filterDeBridge = 'filterDeBridge', @@ -42,8 +47,9 @@ export enum Tasks { filterMeson = 'filterMeson', } -export const TOKEN_REQUEST_LIMIT = 1000; -export const PRICE_REQUEST_LIMIT = 200; +export const CMC_TOKEN_REQUEST_LIMIT = 2000; +export const CMC_PRICE_REQUEST_LIMIT = 1000; +export const LLAMA_PRICE_REQUEST_LIMIT = 100; export const CACHE_KEY = { TOKEN_STARTED: 'token:started', @@ -59,6 +65,9 @@ export const CACHE_KEY = { LLAMA_CONFIG: 'llama:config', PLATFORM_MAPPING: 'llama:platform', + CMC_CONFIG_V2: 'cmc:config_v2', + LLAMA_CONFIG_V2: 'llama:config_v2', + FIELDED_CBRIDGE_CONFIG: 'bridge:filtered:cbridge', FIELDED_DEBRIDGE_CONFIG: 'bridge:filtered:debridge', FIELDED_STARGATE_CONFIG: 'bridge:filtered:stargate', diff --git a/apps/canonical-bridge-server/src/module/bridge/bridge.processor.ts b/apps/canonical-bridge-server/src/module/bridge/bridge.processor.ts index 7cafbd58..c7730a55 100644 --- a/apps/canonical-bridge-server/src/module/bridge/bridge.processor.ts +++ b/apps/canonical-bridge-server/src/module/bridge/bridge.processor.ts @@ -4,16 +4,21 @@ import { Inject, Logger } from '@nestjs/common'; import { Job } from 'bullmq'; import { Web3Service } from '@/shared/web3/web3.service'; import { Cache, CACHE_MANAGER } from '@nestjs/cache-manager'; -import { - IDebridgeConfig, - IDebridgeToken, - IMesonChain, - IStargateBridgeTokenInfo, - ITransferConfigsForAll, - ITransferToken, -} from '@/shared/web3/web3.interface'; import { ITokenPriceRecord } from '@/module/token/token.interface'; import { isEmpty } from 'lodash'; +import { + IMesonChain, + IDeBridgeToken, + IDeBridgeTransferConfig, + ICBridgeTransferConfig, + ICBridgeToken, + IStargateTransferConfig, + IMesonTransferConfig, + TRON_CHAIN_ID, + isNativeToken, + EVM_NATIVE_TOKEN_ADDRESS, +} from '@bnb-chain/canonical-bridge-sdk'; +import { chains } from '@/common/constants/chains'; @Processor(Queues.SyncBridge) export class BridgeProcessor extends WorkerHost { @@ -54,7 +59,7 @@ export class BridgeProcessor extends WorkerHost { if (!config) return; - const tokenMap: Record = {}; + const tokenMap: Record = {}; for (const chain of config.chains) { const data = await this.web3Service.getDebridgeChainTokens(chain.chainId); @@ -84,7 +89,7 @@ export class BridgeProcessor extends WorkerHost { await this.cache.set(`${CACHE_KEY.MESON_CONFIG}`, config, TIME.DAY); } - private updateDeBridgeConfigManually(config?: IDebridgeConfig) { + private updateDeBridgeConfigManually(config?: IDeBridgeTransferConfig) { if (!config) return config; const finalConfig = { @@ -148,75 +153,79 @@ export class BridgeProcessor extends WorkerHost { public async getPriceConfig() { const [cmcRes, llamaRes] = await Promise.allSettled([ - this.cache.get(CACHE_KEY.CMC_CONFIG), - this.cache.get(CACHE_KEY.LLAMA_CONFIG), + this.cache.get(CACHE_KEY.CMC_CONFIG_V2), + this.cache.get(CACHE_KEY.LLAMA_CONFIG_V2), ]); + return { - cmc: cmcRes.status === 'fulfilled' ? cmcRes.value : {}, - llama: llamaRes.status === 'fulfilled' ? llamaRes.value : {}, + cmcPrices: cmcRes.status === 'fulfilled' ? cmcRes.value : {}, + llamaPrices: llamaRes.status === 'fulfilled' ? llamaRes.value : {}, }; } - public hasTokenPrice(params: { - prices: { cmc?: ITokenPriceRecord; llama?: ITokenPriceRecord }; - tokenSymbol: string; + public hasTokenPrice({ + cmcPrices = {}, + llamaPrices = {}, + chainId, + tokenAddress, + }: { + cmcPrices: ITokenPriceRecord; + llamaPrices: ITokenPriceRecord; + chainId: number; tokenAddress: string; }) { - if (isEmpty(params.prices.cmc) && isEmpty(params.prices.llama)) { + if (isEmpty(cmcPrices) && isEmpty(llamaPrices)) { return true; } - const key1 = `${params.tokenSymbol?.toLowerCase()}:${params.tokenAddress?.toLowerCase()}`; - const key3 = params.tokenSymbol?.toLowerCase(); - const key2 = `ethereum:${key3}`; - - const price = - params.prices.cmc?.[key1]?.price ?? - params.prices.llama?.[key1]?.price ?? - params.prices.cmc?.[key2]?.price ?? - params.prices.llama?.[key2]?.price ?? - params.prices.cmc?.[key3]?.price ?? - params.prices.llama?.[key3]?.price; + const chainInfo = chains.find((e) => e.id === chainId); + if (!chainInfo) { + return false; + } + + const isNative = isNativeToken(tokenAddress, chainInfo.chainType); + const key = isNative ? `${chainId}` : `${chainId}:${tokenAddress}`; + const price = cmcPrices[key] ?? llamaPrices[key]; return !!price; } async filterCBridge() { - const config = await this.cache.get(CACHE_KEY.CBRIDGE_CONFIG); + const config = await this.cache.get(CACHE_KEY.CBRIDGE_CONFIG); if (!config) return; - const prices = await this.getPriceConfig(); + const priceConfig = await this.getPriceConfig(); - const chainToken: Record = {}; + const chainToken: Record = {}; Object.entries(config.chain_token).forEach(([key, { token }]) => { const chainId = Number(key); chainToken[chainId] = { token: [] }; chainToken[chainId].token = token.filter((e) => { return this.hasTokenPrice({ - prices, + ...priceConfig, tokenAddress: e.token.address, - tokenSymbol: e.token.symbol, + chainId, }); }); }); - const peggedPairConfigs: ITransferConfigsForAll['pegged_pair_configs'] = + const peggedPairConfigs: ICBridgeTransferConfig['pegged_pair_configs'] = config.pegged_pair_configs.filter((e) => { const orgHasPrice = this.hasTokenPrice({ - prices, - tokenSymbol: e.org_token.token.symbol, + ...priceConfig, tokenAddress: e.org_token.token.address, + chainId: e.org_chain_id, }); const peggedHasPrice = this.hasTokenPrice({ - prices, - tokenSymbol: e.pegged_token.token.symbol, + ...priceConfig, tokenAddress: e.pegged_token.token.address, + chainId: e.pegged_chain_id, }); return orgHasPrice && peggedHasPrice; }); - const finalConfig: ITransferConfigsForAll = { + const finalConfig: ICBridgeTransferConfig = { ...config, chain_token: chainToken, pegged_pair_configs: peggedPairConfigs, @@ -226,25 +235,25 @@ export class BridgeProcessor extends WorkerHost { } async filterDeBridge() { - const _config = await this.cache.get(CACHE_KEY.DEBRIDGE_CONFIG); + const _config = await this.cache.get(CACHE_KEY.DEBRIDGE_CONFIG); const config = this.updateDeBridgeConfigManually(_config); if (!config) return config; - const prices = await this.getPriceConfig(); - const chainTokens: Record = {}; + const priceConfig = await this.getPriceConfig(); + const chainTokens: Record = {}; Object.entries(config.tokens).forEach(([key, tokens]) => { const chainId = Number(key); chainTokens[chainId] = tokens.filter((e) => { return this.hasTokenPrice({ - prices, + ...priceConfig, tokenAddress: e.address, - tokenSymbol: e.symbol, + chainId, }); }); }); - const finalConfig: IDebridgeConfig = { + const finalConfig: IDeBridgeTransferConfig = { ...config, tokens: chainTokens, }; @@ -253,16 +262,16 @@ export class BridgeProcessor extends WorkerHost { } async filterStargate() { - const config = await this.cache.get(CACHE_KEY.STARGATE_CONFIG); + const config = await this.cache.get(CACHE_KEY.STARGATE_CONFIG); if (!config) return config; - const prices = await this.getPriceConfig(); + const priceConfig = await this.getPriceConfig(); const finalConfig = config.filter((e) => { return this.hasTokenPrice({ - prices, + ...priceConfig, tokenAddress: e.token.address, - tokenSymbol: e.token.symbol, + chainId: e.chainId, }); }); @@ -270,20 +279,21 @@ export class BridgeProcessor extends WorkerHost { } async filterMeson() { - const config = await this.cache.get(CACHE_KEY.MESON_CONFIG); + const config = await this.cache.get(CACHE_KEY.MESON_CONFIG); if (!config) return config; - const prices = await this.getPriceConfig(); + const priceConfig = await this.getPriceConfig(); const finalConfig: IMesonChain[] = []; config.forEach((chain) => { const tokens = chain.tokens.filter((e) => { return this.hasTokenPrice({ - prices, - tokenAddress: e.addr, - tokenSymbol: e.symbol, + ...priceConfig, + tokenAddress: e.addr ?? EVM_NATIVE_TOKEN_ADDRESS, + chainId: chain.chainId === 'tron' ? TRON_CHAIN_ID : Number(chain.chainId), }); }); + if (tokens?.length) { finalConfig.push({ ...chain, diff --git a/apps/canonical-bridge-server/src/module/bridge/bridge.service.ts b/apps/canonical-bridge-server/src/module/bridge/bridge.service.ts index 7d1031c3..9a346266 100644 --- a/apps/canonical-bridge-server/src/module/bridge/bridge.service.ts +++ b/apps/canonical-bridge-server/src/module/bridge/bridge.service.ts @@ -1,9 +1,94 @@ -import { Injectable, Logger } from '@nestjs/common'; +import { Inject, Injectable, Logger } from '@nestjs/common'; import { DatabaseService } from '@/shared/database/database.service'; +import { + ICBridgeTransferConfig, + IDeBridgeTransferConfig, + IMesonTransferConfig, + IStargateTransferConfig, +} from '@bnb-chain/canonical-bridge-sdk'; +import { PrismaService } from '@/shared/prisma/prisma.service'; +import { CACHE_MANAGER, Cache } from '@nestjs/cache-manager'; +import { CACHE_KEY, STARGATE_CHAIN_INFO } from '@/common/constants'; @Injectable() export class BridgeService { private logger = new Logger(BridgeService.name); - constructor(private databaseService: DatabaseService) {} + constructor( + private databaseService: DatabaseService, + private prismaService: PrismaService, + @Inject(CACHE_MANAGER) private cache: Cache, + ) {} + + async test() { + const tokens = await this.prismaService.token.findMany(); + const dbMap: Record = {}; + tokens.forEach((e) => { + if (e.address && e.price) { + dbMap[e.address.toLowerCase()] = { + platform: e.platformSlug ?? e.slug, + }; + } + }); + + // const llamaTokens = await this.prismaService.llamaToken.findMany(); + // llamaTokens.forEach((e) => { + // if (e.address && e.price) { + // dbMap[e.address?.toLowerCase()] = { + // platform: + // }; + // } + // }); + + const cBridge = await this.cache.get(CACHE_KEY.CBRIDGE_CONFIG); + const deBridge = await this.cache.get(CACHE_KEY.DEBRIDGE_CONFIG); + const stargate = await this.cache.get(CACHE_KEY.STARGATE_CONFIG); + const meson = await this.cache.get(CACHE_KEY.MESON_CONFIG); + + const bridgeMap: Record = {}; + Object.entries(cBridge.chain_token).forEach(([key, { token }]) => { + token.forEach((e) => { + if (e.token.address) { + bridgeMap[e.token.address.toLowerCase()] = key; + } + }); + }); + + Object.entries(deBridge.tokens).forEach(([key, tokens]) => { + tokens.forEach((e) => { + if (e.address) { + bridgeMap[e.address.toLowerCase()] = key; + } + }); + }); + + stargate.filter((e) => { + const chainId = STARGATE_CHAIN_INFO.find( + (t) => t.chainName.toUpperCase() === e.chainKey.toUpperCase(), + )?.chainId; + if (chainId && e.token.address) { + bridgeMap[e.token.address.toLowerCase()] = chainId; + } + }); + + meson.forEach((chain) => { + const chainId = Number(chain.chainId); + if (chainId) { + chain.tokens.filter((e) => { + if (e.addr) { + bridgeMap[e.addr.toLowerCase()] = chainId; + } + }); + } + }); + + const result: any = {}; + Object.entries(bridgeMap).forEach(([addr, chainId]) => { + if (dbMap[addr]) { + result[dbMap[addr].platform] = chainId; + } + }); + + console.log(`[test]`, Object.keys(dbMap).length, Object.keys(bridgeMap).length, '===', result); + } } diff --git a/apps/canonical-bridge-server/src/module/token/token.controller.ts b/apps/canonical-bridge-server/src/module/token/token.controller.ts index 0ff5107c..e09fad33 100644 --- a/apps/canonical-bridge-server/src/module/token/token.controller.ts +++ b/apps/canonical-bridge-server/src/module/token/token.controller.ts @@ -1,12 +1,16 @@ -import { Controller, Get, Inject, Logger } from '@nestjs/common'; +import { Controller, Get, Inject, Logger, Query } from '@nestjs/common'; import { CACHE_KEY } from '@/common/constants'; import { Cache, CACHE_MANAGER } from '@nestjs/cache-manager'; +import { TokenService } from '@/module/token/token.service'; @Controller('token') export class TokenController { private logger = new Logger(TokenController.name); - constructor(@Inject(CACHE_MANAGER) private cache: Cache) {} + constructor( + @Inject(CACHE_MANAGER) private cache: Cache, + private tokenService: TokenService, + ) {} @Get('/cmc') getCmcConfig() { @@ -17,4 +21,20 @@ export class TokenController { getLlamaConfig() { return this.cache.get(CACHE_KEY.LLAMA_CONFIG); } + + @Get('/v2/price') + getTokenPrice(@Query('chainId') _chainId: string, @Query('tokenAddress') tokenAddress?: string) { + const chainId = Number(_chainId); + return this.tokenService.getTokenPrice(chainId, tokenAddress); + } + + @Get('/v2/cmc') + getCmcConfigV2() { + return this.cache.get(CACHE_KEY.CMC_CONFIG_V2); + } + + @Get('/v2/llama') + getLlamaConfigV2() { + return this.cache.get(CACHE_KEY.LLAMA_CONFIG_V2); + } } diff --git a/apps/canonical-bridge-server/src/module/token/token.module.ts b/apps/canonical-bridge-server/src/module/token/token.module.ts index 057c8807..82b336e2 100644 --- a/apps/canonical-bridge-server/src/module/token/token.module.ts +++ b/apps/canonical-bridge-server/src/module/token/token.module.ts @@ -5,10 +5,11 @@ import { TokenSchedule } from './token.schedule'; import { BullModule } from '@nestjs/bullmq'; import { Queues } from '@/common/constants'; import { TokenProcessor } from '@/module/token/token.processor'; +import { UtilService } from '@/shared/util/util.service'; @Module({ imports: [BullModule.registerQueue({ name: Queues.SyncToken })], controllers: [TokenController], - providers: [TokenProcessor, TokenService, TokenSchedule], + providers: [TokenProcessor, TokenService, TokenSchedule, UtilService], }) export class TokenModule {} diff --git a/apps/canonical-bridge-server/src/module/token/token.processor.ts b/apps/canonical-bridge-server/src/module/token/token.processor.ts index e7d8e96e..c5d340e9 100644 --- a/apps/canonical-bridge-server/src/module/token/token.processor.ts +++ b/apps/canonical-bridge-server/src/module/token/token.processor.ts @@ -1,6 +1,13 @@ import { InjectQueue, Processor, WorkerHost } from '@nestjs/bullmq'; import { Inject, Logger } from '@nestjs/common'; -import { CACHE_KEY, JOB_KEY, Queues, Tasks, TIME, TOKEN_REQUEST_LIMIT } from '@/common/constants'; +import { + CACHE_KEY, + JOB_KEY, + Queues, + Tasks, + TIME, + CMC_TOKEN_REQUEST_LIMIT, +} from '@/common/constants'; import { Job, Queue } from 'bullmq'; import { ITokenJob } from '@/module/token/token.interface'; import { Web3Service } from '@/shared/web3/web3.service'; @@ -22,18 +29,25 @@ export class TokenProcessor extends WorkerHost { async process(job: Job) { switch (job.name) { - case Tasks.fetchCoingeckoToken: - return this.fetchCoingeckoToken(); - case Tasks.fetchToken: - return this.fetchTokens(job); - case Tasks.fetchPrice: - return this.fetchPrice(job); + // case Tasks.fetchCoingeckoTokens: + // return this.fetchCoingeckoTokens(); + case Tasks.fetchCmcTokens: + return this.fetchCmcTokens(job); + + case Tasks.fetchCmcPrice: + return this.fetchCmcPrice(job); case Tasks.fetchLlamaPrice: return this.fetchLlamaPrice(job); + case Tasks.cacheCmcConfig: return this.cacheCmcConfig(); case Tasks.cacheLlamaConfig: return this.cacheLlamaConfig(); + + case Tasks.cacheCmcConfigV2: + return this.cacheCmcConfigV2(); + case Tasks.cacheLlamaConfigV2: + return this.cacheLlamaConfigV2(); default: } } @@ -63,6 +77,7 @@ export class TokenProcessor extends WorkerHost { async cacheCmcConfig() { const tokens = await this.tokenService.getAllTokens(); + const config = tokens .filter((t) => t.price) .reduce((r, c) => { @@ -83,7 +98,68 @@ export class TokenProcessor extends WorkerHost { return config; } - async fetchCoingeckoToken() { + // v2 + async cacheLlamaConfigV2() { + const tokens = await this.tokenService.getAllCoingeckoTokens(); + + const now = Date.now(); + const config = tokens + .map((t) => { + const chainConfig = this.tokenService.getChainConfigByLlamaPlatform(t.platform); + return { + ...t, + chainId: chainConfig?.id ?? t.chainId, + }; + }) + .filter((t) => { + return t.price && t.chainId && now - new Date(t.updateAt).getTime() < TIME.DAY; + }) + .reduce((r, c) => { + const { address, chainId } = c; + + const formattedAddr = this.tokenService.getFormattedAddress(chainId, address); + const key = formattedAddr ? `${chainId}:${formattedAddr}` : chainId; + + r[key] = { price: c.price, decimals: c.decimals }; + return r; + }, {}); + + await this.cache.set(`${CACHE_KEY.LLAMA_CONFIG_V2}`, config, TIME.DAY); + return config; + } + + // v2 + async cacheCmcConfigV2() { + const tokens = await this.tokenService.getAllTokens(); + + const now = Date.now(); + const config = tokens + .map((t) => { + const chainConfig = + this.tokenService.getChainConfigByCmcPlatform(t.platformSlug) || + this.tokenService.getChainConfigByCmcPlatform(t.slug); + + return { + ...t, + chainId: chainConfig?.id, + }; + }) + .filter((t) => t.price && t.chainId && now - new Date(t.updateAt).getTime() < TIME.DAY) + .reduce((r, c) => { + const { address, chainId } = c; + + const formattedAddr = this.tokenService.getFormattedAddress(chainId, address); + const key = formattedAddr ? `${chainId}:${formattedAddr}` : chainId; + + r[key] = { price: c.price, id: c.id }; + return r; + }, {}); + + await this.cache.set(`${CACHE_KEY.CMC_CONFIG_V2}`, config, TIME.DAY); + return config; + } + + async fetchCoingeckoTokens() { const getPlatforms = this.web3Service.getAssetPlatforms(); const getCoins = this.web3Service.getCoinList(); @@ -101,17 +177,17 @@ export class TokenProcessor extends WorkerHost { {} as Record, ); - await this.cache.set(`${CACHE_KEY.PLATFORM_MAPPING}`, mapping, TIME.DAY); + await this.cache.set(`${CACHE_KEY.PLATFORM_MAPPING}`, mapping); this.tokenService.syncCoingeckoTokens(coins, platforms); } - async fetchTokens(job: Job) { + async fetchCmcTokens(job: Job) { const tokens = await this.web3Service.getCryptoCurrencyMap(job.data); - if (tokens.length === TOKEN_REQUEST_LIMIT) { - const start = job.data.start + TOKEN_REQUEST_LIMIT; + if (tokens.length === CMC_TOKEN_REQUEST_LIMIT) { + const start = job.data.start + CMC_TOKEN_REQUEST_LIMIT; await this.syncToken.add( - Tasks.fetchToken, + Tasks.fetchCmcTokens, { start }, { jobId: `${JOB_KEY.CORN_TOKEN_PREFIX}${start}`, removeOnComplete: true }, ); @@ -120,15 +196,13 @@ export class TokenProcessor extends WorkerHost { this.tokenService.syncTokens(tokens); } - async fetchPrice(job: Job) { + async fetchCmcPrice(job: Job) { const tokens = await this.web3Service.getCryptoCurrencyQuotes(job.data.ids); - await this.tokenService.syncTokenPrice(tokens); } async fetchLlamaPrice(job: Job) { const tokens = await this.web3Service.getLlamaTokenPrice(job.data.ids); - await this.tokenService.syncLlamaTokenPrice(tokens.coins, job.data.keyMap); } } diff --git a/apps/canonical-bridge-server/src/module/token/token.schedule.ts b/apps/canonical-bridge-server/src/module/token/token.schedule.ts index 021205e8..70e55a22 100644 --- a/apps/canonical-bridge-server/src/module/token/token.schedule.ts +++ b/apps/canonical-bridge-server/src/module/token/token.schedule.ts @@ -19,7 +19,7 @@ export class TokenSchedule implements OnModuleInit { async syncCmcTokens() { this.logger.log('syncCmcTokens'); await this.syncToken.add( - Tasks.fetchToken, + Tasks.fetchCmcTokens, { start: 1 }, { jobId: `${JOB_KEY.CORN_TOKEN_PREFIX}1`, removeOnComplete: true }, ); @@ -28,8 +28,8 @@ export class TokenSchedule implements OnModuleInit { @Cron(CronExpression.EVERY_DAY_AT_11PM) async syncCoingeckoTokens() { this.logger.log('syncCoingeckoTokens'); - await this.syncToken.add(Tasks.fetchCoingeckoToken, null, { - jobId: Tasks.fetchCoingeckoToken, + await this.syncToken.add(Tasks.fetchCoingeckoTokens, null, { + jobId: Tasks.fetchCoingeckoTokens, removeOnComplete: true, }); } @@ -37,10 +37,11 @@ export class TokenSchedule implements OnModuleInit { @Cron(CronExpression.EVERY_5_MINUTES) async syncCmcTokenPrice() { this.logger.log('syncCmcTokenPrice'); - const ids = await this.tokensService.getJobIds(); - if (!ids) return; + const ids = await this.tokensService.getCmcTokenIdsForPriceJob(); + + if (!ids?.length) return; await this.syncToken.add( - Tasks.fetchPrice, + Tasks.fetchCmcPrice, { ids }, { jobId: `${JOB_KEY.CORN_PRICE_PREFIX}${ids}`, removeOnComplete: true }, ); @@ -49,8 +50,9 @@ export class TokenSchedule implements OnModuleInit { @Cron(CronExpression.EVERY_5_MINUTES) async syncCoingeckoTokenPrice() { this.logger.log('syncCoingeckoTokenPrice'); - const { tokens, keyMap } = await this.tokensService.getLlamaJobIds(); - if (!tokens) return; + const { tokens, keyMap } = await this.tokensService.getLlamaTokenIdsForPriceJob(); + + if (!tokens?.length) return; await this.syncToken.add( Tasks.fetchLlamaPrice, { ids: tokens, keyMap }, @@ -71,10 +73,24 @@ export class TokenSchedule implements OnModuleInit { }); } + @Cron(CronExpression.EVERY_10_MINUTES) + async syncTokenConfigV2() { + this.logger.log('syncTokenConfigV2'); + await this.syncToken.add(Tasks.cacheCmcConfigV2, null, { + jobId: Tasks.cacheCmcConfigV2, + removeOnComplete: true, + }); + await this.syncToken.add(Tasks.cacheLlamaConfigV2, null, { + jobId: Tasks.cacheLlamaConfigV2, + removeOnComplete: true, + }); + } + async onModuleInit() { await this.syncCmcTokens(); await this.syncCoingeckoTokens(); await this.syncTokenConfig(); + await this.syncTokenConfigV2(); const jobs = await this.syncToken.getFailed(); jobs.forEach((job) => job?.retry()); if (!jobs.length) return; diff --git a/apps/canonical-bridge-server/src/module/token/token.service.ts b/apps/canonical-bridge-server/src/module/token/token.service.ts index 223a26df..9b75aa26 100644 --- a/apps/canonical-bridge-server/src/module/token/token.service.ts +++ b/apps/canonical-bridge-server/src/module/token/token.service.ts @@ -7,16 +7,22 @@ import { ICryptoCurrencyMapEntity, ICryptoCurrencyQuoteEntity, } from '@/shared/web3/web3.interface'; -import { CACHE_KEY, PRICE_REQUEST_LIMIT } from '@/common/constants'; +import { CACHE_KEY, CMC_PRICE_REQUEST_LIMIT, LLAMA_PRICE_REQUEST_LIMIT } from '@/common/constants'; import { get, isEmpty } from 'lodash'; import { Prisma } from '@prisma/client'; import { Cache, CACHE_MANAGER } from '@nestjs/cache-manager'; +import { chains } from '@/common/constants/chains'; +import { Web3Service } from '@/shared/web3/web3.service'; @Injectable() export class TokenService { private logger = new Logger(TokenService.name); + private cmcTokenStart = 1; + private llamaTokenStart = 1; + constructor( + private web3Service: Web3Service, private databaseService: DatabaseService, @Inject(CACHE_MANAGER) private cache: Cache, ) {} @@ -32,7 +38,6 @@ export class TokenService { platformName: token.platform?.name, address: token.platform?.token_address, })); - return this.databaseService.createTokens(payload); } @@ -62,32 +67,49 @@ export class TokenService { } async syncTokenPrice(tokens: ICryptoCurrencyQuoteEntity[]) { - const payload = tokens.map((token) => ({ - id: token.id, - price: token.quote.USD?.price, - })); + const payload = tokens + .filter((token) => { + return token.is_active === 1; + }) + .map((token) => ({ + id: token.id, + price: token.quote.USD?.price, + })); return this.databaseService.updateTokens(payload); } - async getJobIds() { - const tokens = await this.databaseService.getTokens(PRICE_REQUEST_LIMIT); - return tokens.map((token) => token.id).join(','); - } - async syncLlamaTokenPrice(tokens: Record, keyMap: Record) { const _tokens: ICoinPrice[] = []; Object.entries(keyMap).forEach(([key, v]) => { const data = tokens[key]; - _tokens.push({ id: v, price: data?.price, decimals: data?.decimals }); + if (data?.price) { + _tokens.push({ id: v, price: data?.price, decimals: data?.decimals }); + } }); return this.databaseService.updateLlamaTokens(_tokens); } - async getLlamaJobIds() { - const tokens = await this.databaseService.getCoingeckoTokens(PRICE_REQUEST_LIMIT / 2); + async getCmcTokenIdsForPriceJob() { + const platforms = this.getChainCmcPlatforms(); + + const tokens = await this.databaseService.getTokens( + this.cmcTokenStart, + CMC_PRICE_REQUEST_LIMIT, + platforms, + ); + + if (tokens.length < CMC_PRICE_REQUEST_LIMIT) { + this.cmcTokenStart = 1; + } else { + this.cmcTokenStart++; + } + return tokens.map((token) => token.id).join(','); + } + + async getLlamaTokenIds(tokens: Prisma.LlamaTokenCreateInput[]) { const keyMap: Record = {}; const platformMapping = await this.cache.get>( `${CACHE_KEY.PLATFORM_MAPPING}`, @@ -108,6 +130,26 @@ export class TokenService { return { tokens: _tokens.join(','), keyMap }; } + async getLlamaTokenIdsForPriceJob() { + const chainIds = this.getChainIds(); + const platforms = this.getChainLlamaPlatforms(); + + const tokens = await this.databaseService.getCoingeckoTokens( + this.llamaTokenStart, + LLAMA_PRICE_REQUEST_LIMIT, + chainIds, + platforms, + ); + + if (tokens.length < LLAMA_PRICE_REQUEST_LIMIT) { + this.llamaTokenStart = 1; + } else { + this.llamaTokenStart++; + } + + return this.getLlamaTokenIds(tokens); + } + async getAllTokens() { return this.databaseService.getAllTokens(); } @@ -115,4 +157,70 @@ export class TokenService { async getAllCoingeckoTokens() { return this.databaseService.getAllCoingeckoTokens(); } + + async getTokenPrice(chainId: number, tokenAddress?: string) { + const cmcPlatform = this.getChainConfigByChainId(chainId)?.extra?.cmcPlatform; + const cmcToken = await this.databaseService.getToken(cmcPlatform, tokenAddress); + + const llamaPlatform = this.getChainConfigByChainId(chainId)?.extra?.llamaPlatform; + const llamaToken = await this.databaseService.getCoingeckoToken( + chainId, + llamaPlatform, + tokenAddress, + ); + + const reqArr: Promise[] = []; + if (cmcToken) { + reqArr.push(this.web3Service.getCryptoCurrencyQuotes(cmcToken.id.toString())); + } else { + reqArr.push(Promise.reject()); + } + + if (llamaToken) { + const { tokens } = await this.getLlamaTokenIds([llamaToken]); + reqArr.push(this.web3Service.getLlamaTokenPrice(tokens)); + } else { + reqArr.push(Promise.reject()); + } + + const [cmcRes, llamaRes] = await Promise.allSettled(reqArr); + if (cmcRes.status === 'fulfilled' && cmcRes.value?.[0]?.quote.USD?.price !== undefined) { + return cmcRes.value?.[0]?.quote.USD?.price; + } + if (llamaRes.status === 'fulfilled' && llamaRes.value.coins) { + return Object.values(llamaRes.value.coins ?? {})?.[0].price; + } + } + + public getFormattedAddress(chainId?: number, address?: string) { + const chainInfo = chains.find((e) => e.id === chainId); + if (chainInfo?.chainType !== 'evm') { + return address; + } + return address?.toLowerCase(); + } + + public getChainConfigByCmcPlatform(platform: string) { + return chains.find((e) => e.extra?.cmcPlatform === platform); + } + + public getChainConfigByLlamaPlatform(platform: string) { + return chains.find((e) => e.extra?.llamaPlatform === platform); + } + + public getChainCmcPlatforms() { + return chains.filter((e) => e.extra?.cmcPlatform).map((e) => e.extra?.cmcPlatform); + } + + public getChainLlamaPlatforms() { + return chains.filter((e) => e.extra?.llamaPlatform).map((e) => e.extra?.llamaPlatform); + } + + public getChainIds() { + return chains.map((e) => e.id); + } + + public getChainConfigByChainId(chainId: number) { + return chains.find((e) => e.id === chainId); + } } diff --git a/apps/canonical-bridge-server/src/shared/database/database.service.ts b/apps/canonical-bridge-server/src/shared/database/database.service.ts index 0041bbd6..b733a9d8 100644 --- a/apps/canonical-bridge-server/src/shared/database/database.service.ts +++ b/apps/canonical-bridge-server/src/shared/database/database.service.ts @@ -38,17 +38,45 @@ export class DatabaseService { ); } - async getTokens(limit: number) { + async getTokens(start: number, limit: number, platforms: string[]) { return this.prismaService.token.findMany({ + skip: (start - 1) * limit, take: limit, - orderBy: { updateAt: 'asc' }, + where: { + OR: [ + { + slug: { + in: platforms, + }, + }, + { + platformSlug: { + in: platforms, + }, + }, + ], + }, }); } - async getCoingeckoTokens(limit: number) { + async getCoingeckoTokens(start: number, limit: number, chainIds: number[], platforms: string[]) { return this.prismaService.llamaToken.findMany({ + skip: (start - 1) * limit, take: limit, - orderBy: { updateAt: 'asc' }, + where: { + OR: [ + { + chainId: { + in: chainIds, + }, + }, + { + platform: { + in: platforms, + }, + }, + ], + }, }); } @@ -59,4 +87,54 @@ export class DatabaseService { async getAllCoingeckoTokens() { return this.prismaService.llamaToken.findMany(); } + + async getToken(platform: string, tokenAddress?: string) { + if (platform && tokenAddress) { + return this.prismaService.token.findFirst({ + where: { + platformSlug: platform, + address: tokenAddress, + }, + }); + } + + if (platform) { + return this.prismaService.token.findFirst({ + where: { + slug: platform, + platformSlug: null, + address: null, + }, + }); + } + } + + async getCoingeckoToken(chainId: number, platform?: string, tokenAddress?: string) { + if (tokenAddress && chainId) { + return this.prismaService.llamaToken.findFirst({ + where: { + chainId, + address: tokenAddress, + }, + }); + } + + if (tokenAddress && platform) { + return this.prismaService.llamaToken.findFirst({ + where: { + platform, + address: tokenAddress, + }, + }); + } + + if (chainId) { + return this.prismaService.llamaToken.findFirst({ + where: { + chainId, + address: null, + }, + }); + } + } } diff --git a/apps/canonical-bridge-server/src/shared/web3/web3.interface.ts b/apps/canonical-bridge-server/src/shared/web3/web3.interface.ts index d0457138..e7e54ae3 100644 --- a/apps/canonical-bridge-server/src/shared/web3/web3.interface.ts +++ b/apps/canonical-bridge-server/src/shared/web3/web3.interface.ts @@ -29,6 +29,7 @@ export interface ICryptoCurrencyMapEntity { export interface ICryptoCurrencyQuoteEntity { id: number; + is_active: number; quote: { USD: { price: number; @@ -82,41 +83,6 @@ export interface ITransferChain { cmc_price: string; } -export interface ITransferConfigsForAll { - chains: ITransferChain[]; - chain_token: Record; - farming_reward_contract_addr: string; - pegged_pair_configs: Array<{ - org_chain_id: number; - org_token: ITransferToken; - pegged_chain_id: number; - pegged_token: ITransferToken; - pegged_deposit_contract_addr: string; - pegged_burn_contract_addr: string; - vault_version: number; - bridge_version: number; - }>; -} - -export interface IDebridgeToken { - address: string; - symbol: string; - decimals: number; - name: string; - cmc_price: string; -} - -export interface IDebridgeChain { - chainId: number; - originalChainId: number; - chainName: string; -} - -export interface IDebridgeConfig { - chains: IDebridgeChain[]; - tokens: Record; -} - export interface IAssetPlatform { id: string; chain_identifier: number; @@ -137,62 +103,3 @@ export interface ICoinPrice { price: number; decimals: number; } - -export interface IStargateBridgeTokenInfo { - stargateType: string; - address: `0x${string}`; - token: { - address: `0x${string}`; - decimals: number; - symbol: string; - }; - lpToken?: { - address: `0x${string}`; - decimals: number; - symbol: string; - }; - farm?: { - stargateStaking: { - address: `0x${string}`; - rewardTokens: [ - { - address: `0x${string}`; - decimals: number; - symbol: string; - }, - { - address: `0x${string}`; - decimals: number; - symbol: string; - }, - ]; - }; - }; - id: string; - assetId: string; - chainKey: string; - chainName: string; - tokenMessaging: `0x${string}`; - sharedDecimals: number; -} -export interface IStargateTokenList { - v1: IStargateBridgeTokenInfo[]; - v2: IStargateBridgeTokenInfo[]; -} - -export interface IMesonToken { - id: string; - symbol: string; - name: string; - decimals: number; - addr?: string; - min: string; - max: string; -} -export interface IMesonChain { - id: string; - name: string; - chainId: string; - address: string; - tokens: IMesonToken[]; -} diff --git a/apps/canonical-bridge-server/src/shared/web3/web3.service.ts b/apps/canonical-bridge-server/src/shared/web3/web3.service.ts index 3b7ee373..17bc103d 100644 --- a/apps/canonical-bridge-server/src/shared/web3/web3.service.ts +++ b/apps/canonical-bridge-server/src/shared/web3/web3.service.ts @@ -7,11 +7,6 @@ import { ICryptoCurrencyMapEntity, ICryptoCurrencyMapPayload, ICryptoCurrencyQuoteEntity, - IDebridgeChain, - IDebridgeToken, - IMesonChain, - IStargateTokenList, - ITransferConfigsForAll, } from '@/shared/web3/web3.interface'; import { CBRIDGE_ENDPOINT, @@ -22,10 +17,17 @@ import { STARGATE_ENDPOINT, MESON_ENDPOINT, LLAMA_COINS_ENDPOINT, - TOKEN_REQUEST_LIMIT, + CMC_TOKEN_REQUEST_LIMIT, STARGATE_CHAIN_INFO, } from '@/common/constants'; import { values } from 'lodash'; +import { + ICBridgeTransferConfig, + IDeBridgeChain, + IDeBridgeToken, + IMesonTransferConfig, + IStargateTokenList, +} from '@bnb-chain/canonical-bridge-sdk'; @Injectable() export class Web3Service { @@ -35,7 +37,7 @@ export class Web3Service { async getCryptoCurrencyMap(payload: Partial) { const query = new URLSearchParams({ - limit: String(TOKEN_REQUEST_LIMIT), + limit: String(CMC_TOKEN_REQUEST_LIMIT), start: String(payload.start), }).toString(); @@ -61,7 +63,7 @@ export class Web3Service { async getTransferConfigsForAll() { try { - const { data } = await this.httpService.axiosRef.get( + const { data } = await this.httpService.axiosRef.get( `${CBRIDGE_ENDPOINT}/v2/getTransferConfigsForAll`, ); return data; @@ -72,7 +74,7 @@ export class Web3Service { async getDebridgeChains() { try { - const { data } = await this.httpService.axiosRef.get<{ chains: IDebridgeChain[] }>( + const { data } = await this.httpService.axiosRef.get<{ chains: IDeBridgeChain[] }>( `${DEBRIDGE_ENDPOINT}/supported-chains-info`, ); @@ -85,7 +87,7 @@ export class Web3Service { async getDebridgeChainTokens(chainId: number) { try { const { data } = await this.httpService.axiosRef.get<{ - tokens: Record; + tokens: Record; }>(`${DEBRIDGE_ENDPOINT}/token-list?chainId=${chainId}`); return data; @@ -109,7 +111,11 @@ export class Web3Service { (chain) => chain.chainName.toUpperCase() === token.chainKey.toUpperCase(), ); if (chainInfo && chainInfo.length > 0) { - processedTokenList.push({ ...token, endpointID: chainInfo[0].endpointID }); + processedTokenList.push({ + ...token, + chainId: chainInfo[0].chainId, + endpointID: chainInfo[0].endpointID, + }); } }); return processedTokenList; @@ -120,7 +126,7 @@ export class Web3Service { async getMesonConfigs() { try { - const { data } = await this.httpService.axiosRef.get<{ result: IMesonChain[] }>( + const { data } = await this.httpService.axiosRef.get<{ result: IMesonTransferConfig }>( `${MESON_ENDPOINT}/limits`, ); return data; diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 69a8779b..ce8a8fb4 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -12,6 +12,9 @@ importers: ../../apps/canonical-bridge-server: dependencies: + '@bnb-chain/canonical-bridge-sdk': + specifier: workspace:* + version: link:../../packages/canonical-bridge-sdk '@nestjs/axios': specifier: ~3.0.3 version: 3.0.3(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(axios@1.7.9)(rxjs@7.8.1) @@ -138,7 +141,7 @@ importers: version: 11.13.5(@emotion/react@11.13.5(@types/react@18.3.18)(react@18.2.0)(supports-color@9.4.0))(@types/react@18.3.18)(react@18.2.0)(supports-color@9.4.0) '@node-real/walletkit': specifier: 2.7.4 - version: 2.7.4(@babel/core@7.26.0(supports-color@9.4.0))(@babel/runtime@7.26.0)(@react-native-async-storage/async-storage@1.24.0)(@tanstack/react-query@5.50.1(react@18.2.0))(bs58@6.0.0)(bufferutil@4.0.9)(encoding@0.1.13)(ioredis@5.4.2(supports-color@9.4.0))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(supports-color@9.4.0)(tslib@2.8.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(viem@2.21.60(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(wagmi@2.14.6(@react-native-async-storage/async-storage@1.24.0)(@tanstack/query-core@5.50.1)(@tanstack/react-query@5.50.1(react@18.2.0))(@types/react@18.3.18)(bufferutil@4.0.9)(encoding@0.1.13)(ioredis@5.4.2(supports-color@9.4.0))(react@18.2.0)(supports-color@9.4.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(viem@2.21.60(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))) + version: 2.7.4(@babel/core@7.26.0(supports-color@9.4.0))(@babel/runtime@7.26.0)(@react-native-async-storage/async-storage@1.24.0)(@tanstack/react-query@5.50.1(react@18.2.0))(bs58@6.0.0)(bufferutil@4.0.9)(encoding@0.1.13)(ioredis@5.4.2(supports-color@9.4.0))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(supports-color@9.4.0)(tslib@2.8.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(viem@2.21.60(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(wagmi@2.14.6(@react-native-async-storage/async-storage@1.24.0)(@tanstack/query-core@5.50.1)(@tanstack/react-query@5.50.1(react@18.2.0))(@types/react@18.3.18)(bufferutil@4.0.9)(encoding@0.1.13)(ioredis@5.4.2(supports-color@9.4.0))(react@18.2.0)(supports-color@9.4.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(viem@2.21.60(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)))(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@solana/spl-token': specifier: ~0.4.9 version: 0.4.9(@solana/web3.js@1.95.8(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.5.4)(utf-8-validate@5.0.10) @@ -286,11 +289,10 @@ importers: version: 3.9.1(@types/node@22.7.5)(rollup@4.29.2)(typescript@5.5.4)(vite@6.0.7(@types/node@22.7.5)(terser@5.37.0)(tsx@4.15.9)(yaml@2.7.0)) ../../packages/canonical-bridge-widget: - dependencies: + devDependencies: '@bnb-chain/canonical-bridge-sdk': specifier: workspace:* version: link:../canonical-bridge-sdk - devDependencies: '@bnb-chain/eslint-config': specifier: ^1 version: 1.0.2(@babel/plugin-syntax-flow@7.26.0(@babel/core@7.26.0))(@babel/plugin-transform-react-jsx@7.25.9(@babel/core@7.26.0))(@typescript-eslint/parser@8.19.0(eslint@8.57.1)(typescript@5.5.4))(eslint@8.57.1)(typescript@5.5.4) @@ -13096,11 +13098,11 @@ snapshots: '@noble/hashes@1.7.0': {} - '@node-real/walletkit@2.7.4(@babel/core@7.26.0(supports-color@9.4.0))(@babel/runtime@7.26.0)(@react-native-async-storage/async-storage@1.24.0)(@tanstack/react-query@5.50.1(react@18.2.0))(bs58@6.0.0)(bufferutil@4.0.9)(encoding@0.1.13)(ioredis@5.4.2(supports-color@9.4.0))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(supports-color@9.4.0)(tslib@2.8.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(viem@2.21.60(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(wagmi@2.14.6(@react-native-async-storage/async-storage@1.24.0)(@tanstack/query-core@5.50.1)(@tanstack/react-query@5.50.1(react@18.2.0))(@types/react@18.3.18)(bufferutil@4.0.9)(encoding@0.1.13)(ioredis@5.4.2(supports-color@9.4.0))(react@18.2.0)(supports-color@9.4.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(viem@2.21.60(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)))': + '@node-real/walletkit@2.7.4(@babel/core@7.26.0(supports-color@9.4.0))(@babel/runtime@7.26.0)(@react-native-async-storage/async-storage@1.24.0)(@tanstack/react-query@5.50.1(react@18.2.0))(bs58@6.0.0)(bufferutil@4.0.9)(encoding@0.1.13)(ioredis@5.4.2(supports-color@9.4.0))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(supports-color@9.4.0)(tslib@2.8.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(viem@2.21.60(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(wagmi@2.14.6(@react-native-async-storage/async-storage@1.24.0)(@tanstack/query-core@5.50.1)(@tanstack/react-query@5.50.1(react@18.2.0))(@types/react@18.3.18)(bufferutil@4.0.9)(encoding@0.1.13)(ioredis@5.4.2(supports-color@9.4.0))(react@18.2.0)(supports-color@9.4.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(viem@2.21.60(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)))(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: '@metamask/jazzicon': 2.0.0 '@solana/wallet-adapter-react': 0.15.35(@solana/web3.js@1.95.8(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bs58@6.0.0)(react@18.2.0) - '@solana/wallet-adapter-wallets': 0.19.32(@babel/core@7.26.0(supports-color@9.4.0))(@babel/runtime@7.26.0)(@react-native-async-storage/async-storage@1.24.0)(@solana/web3.js@1.95.8(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bs58@6.0.0)(bufferutil@4.0.9)(encoding@0.1.13)(ioredis@5.4.2(supports-color@9.4.0))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(supports-color@9.4.0)(tslib@2.8.1)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@solana/wallet-adapter-wallets': 0.19.32(@babel/core@7.26.0(supports-color@9.4.0))(@babel/runtime@7.26.0)(@react-native-async-storage/async-storage@1.24.0)(@solana/web3.js@1.95.8(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bs58@6.0.0)(bufferutil@4.0.9)(encoding@0.1.13)(ioredis@5.4.2(supports-color@9.4.0))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(supports-color@9.4.0)(tslib@2.8.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@solana/web3.js': 1.95.8(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10) '@tanstack/react-query': 5.50.1(react@18.2.0) '@tronweb3/tronwallet-abstract-adapter': 1.1.8 @@ -14120,11 +14122,11 @@ snapshots: - supports-color - utf-8-validate - '@solana/wallet-adapter-trezor@0.1.2(@babel/core@7.26.0(supports-color@9.4.0))(@solana/web3.js@1.95.8(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(encoding@0.1.13)(supports-color@9.4.0)(tslib@2.8.1)(typescript@5.5.4)(utf-8-validate@5.0.10)': + '@solana/wallet-adapter-trezor@0.1.2(@babel/core@7.26.0(supports-color@9.4.0))(@solana/web3.js@1.95.8(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(encoding@0.1.13)(supports-color@9.4.0)(tslib@2.8.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: '@solana/wallet-adapter-base': 0.9.23(@solana/web3.js@1.95.8(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)) '@solana/web3.js': 1.95.8(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10) - '@trezor/connect-web': 9.4.7(@babel/core@7.26.0(supports-color@9.4.0))(bufferutil@4.0.9)(encoding@0.1.13)(supports-color@9.4.0)(tslib@2.8.1)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@trezor/connect-web': 9.4.7(@babel/core@7.26.0(supports-color@9.4.0))(bufferutil@4.0.9)(encoding@0.1.13)(supports-color@9.4.0)(tslib@2.8.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) buffer: 6.0.3 transitivePeerDependencies: - '@babel/core' @@ -14180,7 +14182,7 @@ snapshots: - uploadthing - utf-8-validate - '@solana/wallet-adapter-wallets@0.19.32(@babel/core@7.26.0(supports-color@9.4.0))(@babel/runtime@7.26.0)(@react-native-async-storage/async-storage@1.24.0)(@solana/web3.js@1.95.8(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bs58@6.0.0)(bufferutil@4.0.9)(encoding@0.1.13)(ioredis@5.4.2(supports-color@9.4.0))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(supports-color@9.4.0)(tslib@2.8.1)(typescript@5.5.4)(utf-8-validate@5.0.10)': + '@solana/wallet-adapter-wallets@0.19.32(@babel/core@7.26.0(supports-color@9.4.0))(@babel/runtime@7.26.0)(@react-native-async-storage/async-storage@1.24.0)(@solana/web3.js@1.95.8(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bs58@6.0.0)(bufferutil@4.0.9)(encoding@0.1.13)(ioredis@5.4.2(supports-color@9.4.0))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(supports-color@9.4.0)(tslib@2.8.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: '@solana/wallet-adapter-alpha': 0.1.10(@solana/web3.js@1.95.8(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)) '@solana/wallet-adapter-avana': 0.1.13(@solana/web3.js@1.95.8(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)) @@ -14213,7 +14215,7 @@ snapshots: '@solana/wallet-adapter-tokenary': 0.1.12(@solana/web3.js@1.95.8(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)) '@solana/wallet-adapter-tokenpocket': 0.4.19(@solana/web3.js@1.95.8(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)) '@solana/wallet-adapter-torus': 0.11.28(@babel/runtime@7.26.0)(@solana/web3.js@1.95.8(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(encoding@0.1.13)(supports-color@9.4.0)(utf-8-validate@5.0.10) - '@solana/wallet-adapter-trezor': 0.1.2(@babel/core@7.26.0(supports-color@9.4.0))(@solana/web3.js@1.95.8(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(encoding@0.1.13)(supports-color@9.4.0)(tslib@2.8.1)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@solana/wallet-adapter-trezor': 0.1.2(@babel/core@7.26.0(supports-color@9.4.0))(@solana/web3.js@1.95.8(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(encoding@0.1.13)(supports-color@9.4.0)(tslib@2.8.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@solana/wallet-adapter-trust': 0.1.13(@solana/web3.js@1.95.8(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)) '@solana/wallet-adapter-unsafe-burner': 0.1.7(@solana/web3.js@1.95.8(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)) '@solana/wallet-adapter-walletconnect': 0.1.16(@react-native-async-storage/async-storage@1.24.0)(@solana/web3.js@1.95.8(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(ioredis@5.4.2(supports-color@9.4.0))(utf-8-validate@5.0.10) @@ -14702,9 +14704,9 @@ snapshots: - expo-localization - react-native - '@trezor/connect-web@9.4.7(@babel/core@7.26.0(supports-color@9.4.0))(bufferutil@4.0.9)(encoding@0.1.13)(supports-color@9.4.0)(tslib@2.8.1)(typescript@5.5.4)(utf-8-validate@5.0.10)': + '@trezor/connect-web@9.4.7(@babel/core@7.26.0(supports-color@9.4.0))(bufferutil@4.0.9)(encoding@0.1.13)(supports-color@9.4.0)(tslib@2.8.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: - '@trezor/connect': 9.4.7(@babel/core@7.26.0(supports-color@9.4.0))(bufferutil@4.0.9)(encoding@0.1.13)(supports-color@9.4.0)(tslib@2.8.1)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@trezor/connect': 9.4.7(@babel/core@7.26.0(supports-color@9.4.0))(bufferutil@4.0.9)(encoding@0.1.13)(supports-color@9.4.0)(tslib@2.8.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@trezor/connect-common': 0.2.7(tslib@2.8.1) '@trezor/utils': 9.2.6(tslib@2.8.1) tslib: 2.8.1 @@ -14721,7 +14723,7 @@ snapshots: - utf-8-validate - ws - '@trezor/connect@9.4.7(@babel/core@7.26.0(supports-color@9.4.0))(bufferutil@4.0.9)(encoding@0.1.13)(supports-color@9.4.0)(tslib@2.8.1)(typescript@5.5.4)(utf-8-validate@5.0.10)': + '@trezor/connect@9.4.7(@babel/core@7.26.0(supports-color@9.4.0))(bufferutil@4.0.9)(encoding@0.1.13)(supports-color@9.4.0)(tslib@2.8.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: '@babel/preset-typescript': 7.26.0(@babel/core@7.26.0(supports-color@9.4.0))(supports-color@9.4.0) '@ethereumjs/common': 4.4.0 diff --git a/packages/canonical-bridge-sdk/package.json b/packages/canonical-bridge-sdk/package.json index 25e7db88..37000a53 100644 --- a/packages/canonical-bridge-sdk/package.json +++ b/packages/canonical-bridge-sdk/package.json @@ -20,9 +20,14 @@ "access": "public" }, "sideEffects": false, - "type": "module", - "main": "./dist/index.js", + "main": "./dist/index.mjs", "types": "./dist/index.d.ts", + "exports": { + "types": "./dist/index.d.ts", + "import": "./dist/index.mjs", + "require": "./dist/index.js", + "default": "./dist/index.mjs" + }, "scripts": { "dev": "vite __dev__ --config vite.config.ts --port 3333 --host 0.0.0.0 --open", "watch": "vite build --watch --emptyOutDir=false", diff --git a/packages/canonical-bridge-sdk/src/adapters/stargate/types.ts b/packages/canonical-bridge-sdk/src/adapters/stargate/types.ts index 1b8b84a4..19eae647 100644 --- a/packages/canonical-bridge-sdk/src/adapters/stargate/types.ts +++ b/packages/canonical-bridge-sdk/src/adapters/stargate/types.ts @@ -106,6 +106,7 @@ export interface IStargateBridgeTokenInfo { assetId: string; chainKey: string; chainName: string; + chainId: number; tokenMessaging: `0x${string}`; sharedDecimals: number; } diff --git a/packages/canonical-bridge-sdk/vite.config.ts b/packages/canonical-bridge-sdk/vite.config.ts index 76ada4a3..611e0c3f 100644 --- a/packages/canonical-bridge-sdk/vite.config.ts +++ b/packages/canonical-bridge-sdk/vite.config.ts @@ -24,7 +24,7 @@ export default defineConfig({ target: 'esnext', minify: false, lib: { - formats: ['es'], + formats: ['es', 'cjs'], entry: { index: 'src/index.ts', }, diff --git a/packages/canonical-bridge-widget/package.json b/packages/canonical-bridge-widget/package.json index f1f6a6cf..db780732 100644 --- a/packages/canonical-bridge-widget/package.json +++ b/packages/canonical-bridge-widget/package.json @@ -20,9 +20,14 @@ "access": "public" }, "sideEffects": false, - "type": "module", - "main": "./dist/index.js", + "main": "./dist/index.mjs", "types": "./dist/index.d.ts", + "exports": { + "types": "./dist/index.d.ts", + "import": "./dist/index.mjs", + "require": "./dist/index.js", + "default": "./dist/index.mjs" + }, "scripts": { "build": "vite build", "watch": "vite build --watch --emptyOutDir=false", diff --git a/packages/canonical-bridge-widget/vite.config.ts b/packages/canonical-bridge-widget/vite.config.ts index 8efc68c7..fdeb916a 100644 --- a/packages/canonical-bridge-widget/vite.config.ts +++ b/packages/canonical-bridge-widget/vite.config.ts @@ -30,7 +30,7 @@ export default defineConfig({ build: { target: 'esnext', lib: { - formats: ['es'], + formats: ['es', 'cjs'], entry: { index: 'src/index.tsx', }, From d46a0770edeefbe5b5ef013caa50d739438b7ad3 Mon Sep 17 00:00:00 2001 From: wenty22 Date: Tue, 21 Jan 2025 13:08:47 +0800 Subject: [PATCH 3/6] feat: Use real-time api to fetch token price --- .../SelectModal/hooks/useTokenList.ts | 16 +++- .../modules/aggregator/hooks/useSelection.ts | 9 ++- .../aggregator/hooks/useTokenUpperLimit.ts | 38 +++++++--- .../providers/TokenPricesProvider.tsx | 76 ++++++++++++------- .../Button/TransferConfirmButton.tsx | 6 +- .../transfer/components/SendInput/MaxLink.tsx | 6 +- .../transfer/hooks/useInputValidation.ts | 2 +- .../transfer/hooks/usePriceValidation.ts | 37 +++++---- 8 files changed, 127 insertions(+), 63 deletions(-) diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/components/SelectModal/hooks/useTokenList.ts b/packages/canonical-bridge-widget/src/modules/aggregator/components/SelectModal/hooks/useTokenList.ts index 9dbf4a0f..29237d98 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/components/SelectModal/hooks/useTokenList.ts +++ b/packages/canonical-bridge-widget/src/modules/aggregator/components/SelectModal/hooks/useTokenList.ts @@ -11,6 +11,7 @@ import { useTokenBalance } from '@/modules/aggregator/providers/TokenBalancesPro import { useTokenPrice } from '@/modules/aggregator/providers/TokenPricesProvider'; export function useTokenList(tokens: IBridgeToken[] = [], keyword?: string) { + const fromChain = useAppSelector((state) => state.transfer.fromChain); const selectedToken = useAppSelector((state) => state.transfer.selectedToken); const isLoadingTokenBalances = useAppSelector((state) => state.aggregator.isLoadingTokenBalances); const isLoadingTokenPrices = useAppSelector((state) => state.aggregator.isLoadingTokenPrices); @@ -21,7 +22,11 @@ export function useTokenList(tokens: IBridgeToken[] = [], keyword?: string) { const sortedTokens = useMemo(() => { const tmpTokens = tokens.map((item) => { const balance = getTokenBalance(item); - const price = getTokenPrice(item); + const price = getTokenPrice({ + chainId: fromChain?.id, + chainType: fromChain?.chainType, + tokenAddress: selectedToken?.address, + }); let value: number | undefined; if (balance !== undefined && price !== undefined) { @@ -42,7 +47,14 @@ export function useTokenList(tokens: IBridgeToken[] = [], keyword?: string) { }); return sortedTokens; - }, [tokens, getTokenBalance, getTokenPrice, selectedToken?.address]); + }, [ + tokens, + getTokenBalance, + getTokenPrice, + fromChain?.id, + fromChain?.chainType, + selectedToken?.address, + ]); return { data: sortedTokens, isLoading: isLoadingTokenBalances || isLoadingTokenPrices }; } diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useSelection.ts b/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useSelection.ts index 12339dcb..6395b850 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useSelection.ts +++ b/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useSelection.ts @@ -20,6 +20,7 @@ import { useBridgeConfig } from '@/index'; import { setFromChain, setSelectedToken, + setSendValue, setToChain, setToToken, setToTokens, @@ -202,6 +203,8 @@ export function useSelection() { }; const exchange = async () => { + dispatch(setSendValue('')); + const fromChainId = toChain!.id; const toChainId = fromChain!.id; @@ -303,7 +306,11 @@ function useSortedTokens() { const tmpTokens = tokens.map((item) => { const balance = balances[item.address?.toLowerCase()]; - const price = getTokenPrice(item); + const price = getTokenPrice({ + chainId: fromChainId, + chainType, + tokenAddress: item?.address, + }); let value: number | undefined; if (balance !== undefined && price !== undefined) { diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useTokenUpperLimit.ts b/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useTokenUpperLimit.ts index 3f018190..836433ae 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useTokenUpperLimit.ts +++ b/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useTokenUpperLimit.ts @@ -1,31 +1,49 @@ import { useMemo } from 'react'; -import { IBridgeToken } from '@bnb-chain/canonical-bridge-sdk'; import { parseUnits } from 'viem'; +import { useQuery } from '@tanstack/react-query'; import { useTokenPrice } from '@/modules/aggregator/providers/TokenPricesProvider'; import { useBridgeConfig } from '@/index'; +import { useAppSelector } from '@/modules/store/StoreProvider'; +import { TIME } from '@/core/constants'; -export function useTokenUpperLimit(token?: IBridgeToken) { - const { getTokenPrice } = useTokenPrice(); +export function useTokenUpperLimit() { + const { fetchTokenPrice } = useTokenPrice(); const bridgeConfig = useBridgeConfig(); + const fromChain = useAppSelector((state) => state.transfer.fromChain); + const selectedToken = useAppSelector((state) => state.transfer.selectedToken); - const result = useMemo(() => { - if (!token) return; + const { data: price } = useQuery({ + staleTime: TIME.MINUTE * 5, + refetchInterval: TIME.MINUTE * 5, + queryKey: ['tokenPrice', fromChain?.id, fromChain?.chainType, selectedToken?.address], + queryFn: async () => { + if (fromChain && selectedToken) { + return ( + await fetchTokenPrice({ + chainId: fromChain?.id, + chainType: fromChain?.chainType, + tokenAddress: selectedToken?.address, + }) + ).data; + } + }, + }); - const price = getTokenPrice(token); - if (price) { + const result = useMemo(() => { + if (price !== undefined && selectedToken) { const upperLimit = bridgeConfig.transfer.dollarUpperLimit / price; - const value = parseUnits(String(upperLimit), token.decimals); + const value = parseUnits(String(upperLimit), selectedToken.decimals); return { upperLimit, value, price, - decimals: token.decimals, + decimals: selectedToken.decimals, }; } - }, [bridgeConfig.transfer.dollarUpperLimit, getTokenPrice, token]); + }, [bridgeConfig.transfer.dollarUpperLimit, price, selectedToken]); return result; } diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/providers/TokenPricesProvider.tsx b/packages/canonical-bridge-widget/src/modules/aggregator/providers/TokenPricesProvider.tsx index 476a8e96..86038490 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/providers/TokenPricesProvider.tsx +++ b/packages/canonical-bridge-widget/src/modules/aggregator/providers/TokenPricesProvider.tsx @@ -1,9 +1,9 @@ import axios from 'axios'; import { useQuery } from '@tanstack/react-query'; import { useCallback, useEffect } from 'react'; -import { IBridgeToken } from '@bnb-chain/canonical-bridge-sdk'; +import { ChainType, isNativeToken } from '@bnb-chain/canonical-bridge-sdk'; -import { IBridgeConfig, useBridgeConfig } from '@/CanonicalBridgeProvider'; +import { useBridgeConfig } from '@/CanonicalBridgeProvider'; import { TIME } from '@/core/constants'; import { useAppDispatch, useAppSelector } from '@/modules/store/StoreProvider'; import { setIsLoadingTokenPrices, setTokenPrices } from '@/modules/aggregator/action'; @@ -19,7 +19,6 @@ interface ITokenPricesResponse { } export function TokenPricesProvider() { - const bridgeConfig = useBridgeConfig(); const dispatch = useAppDispatch(); const { fetchApiTokenPrices } = useTokenPrice(); @@ -27,7 +26,7 @@ export function TokenPricesProvider() { staleTime: TIME.MINUTE * 5, refetchInterval: TIME.MINUTE * 5, queryKey: ['tokenPrices'], - queryFn: async () => fetchApiTokenPrices(bridgeConfig), + queryFn: async () => fetchApiTokenPrices(), }); useEffect(() => { @@ -40,27 +39,32 @@ export function TokenPricesProvider() { export function useTokenPrice() { const tokenPrices = useAppSelector((state) => state.aggregator.tokenPrices); + const bridgeConfig = useBridgeConfig(); - const getTokenPrice = useCallback( - (params: IBridgeToken | { symbol?: string; address?: string } = {}) => { - const tokenSymbol = (params as IBridgeToken).displaySymbol; - const tokenAddress = params.address; + const { serverEndpoint } = bridgeConfig.http; - if (tokenSymbol && tokenAddress) { + const getTokenPrice = useCallback( + ({ + chainId, + chainType, + tokenAddress, + }: { chainId?: number; chainType?: ChainType; tokenAddress?: string } = {}) => { + if (chainId && chainType && tokenAddress) { const { cmcPrices, llamaPrices } = tokenPrices; - const key1 = `${tokenSymbol?.toLowerCase()}:${tokenAddress?.toLowerCase()}`; - const key3 = tokenSymbol?.toLowerCase(); - const key2 = `ethereum:${key3}`; - - let price = - cmcPrices?.[key1]?.price ?? - llamaPrices?.[key1]?.price ?? - cmcPrices?.[key2]?.price ?? - llamaPrices?.[key2]?.price ?? - cmcPrices?.[key3]?.price ?? - llamaPrices?.[key3]?.price; + let key = ''; + const isNative = isNativeToken(tokenAddress, chainType); + if (isNative) { + key = `${chainId}`; + } else { + if (chainType === 'evm') { + key = `${chainId}:${tokenAddress.toLowerCase()}`; + } else { + key = `${chainId}:${tokenAddress}`; + } + } + let price = cmcPrices?.[key]?.price ?? llamaPrices[key]?.price; if (price !== undefined) { price = Number(price); } @@ -71,12 +75,10 @@ export function useTokenPrice() { [tokenPrices], ); - const fetchApiTokenPrices = useCallback(async (bridgeConfig: IBridgeConfig) => { - const { serverEndpoint } = bridgeConfig.http; - + const fetchApiTokenPrices = useCallback(async () => { const [cmcRes, llamaRes] = await Promise.allSettled([ - axios.get(`${serverEndpoint}/api/token/cmc`), - axios.get(`${serverEndpoint}/api/token/llama`), + axios.get(`${serverEndpoint}/api/token/v2/cmc`), + axios.get(`${serverEndpoint}/api/token/v2/llama`), ]); const cmcPrices = cmcRes.status === 'fulfilled' ? cmcRes.value.data.data : {}; @@ -86,10 +88,32 @@ export function useTokenPrice() { cmcPrices, llamaPrices, }; - }, []); + }, [serverEndpoint]); + + const fetchTokenPrice = useCallback( + async ({ + chainId, + chainType, + tokenAddress, + }: { + chainId: number; + chainType: ChainType; + tokenAddress: string; + }) => { + const { data } = await axios.get<{ data: number }>(`${serverEndpoint}/api/token/v2/price`, { + params: { + chainId, + tokenAddress: isNativeToken(tokenAddress, chainType) ? undefined : tokenAddress, + }, + }); + return data; + }, + [serverEndpoint], + ); return { getTokenPrice, + fetchTokenPrice, fetchApiTokenPrices, }; } diff --git a/packages/canonical-bridge-widget/src/modules/transfer/components/Button/TransferConfirmButton.tsx b/packages/canonical-bridge-widget/src/modules/transfer/components/Button/TransferConfirmButton.tsx index 425fa01e..813154aa 100644 --- a/packages/canonical-bridge-widget/src/modules/transfer/components/Button/TransferConfirmButton.tsx +++ b/packages/canonical-bridge-widget/src/modules/transfer/components/Button/TransferConfirmButton.tsx @@ -88,6 +88,7 @@ export const TransferConfirmButton = ({ const sendTx = useCallback(async () => { if ( + !fromChain || !selectedToken || !transferActionInfo?.bridgeType || (!transferActionInfo?.bridgeAddress && fromChain?.chainType !== 'solana') || @@ -108,10 +109,11 @@ export const TransferConfirmButton = ({ try { // Check whether token price exists const result = await validateTokenPrice({ - tokenSymbol: selectedToken.symbol, + chainId: fromChain.id, + chainType: fromChain.chainType, tokenAddress: selectedToken.address, }); - if (!result) { + if (result === undefined) { throw new Error( `Can not get token price from API server: ${sendValue} ${selectedToken.symbol}`, ); diff --git a/packages/canonical-bridge-widget/src/modules/transfer/components/SendInput/MaxLink.tsx b/packages/canonical-bridge-widget/src/modules/transfer/components/SendInput/MaxLink.tsx index b2932ca1..8b232f77 100644 --- a/packages/canonical-bridge-widget/src/modules/transfer/components/SendInput/MaxLink.tsx +++ b/packages/canonical-bridge-widget/src/modules/transfer/components/SendInput/MaxLink.tsx @@ -20,7 +20,11 @@ export const MaxLink: React.FC = () => { const { getTokenPrice } = useTokenPrice(); const balance = getTokenBalance(selectedToken); - const tokenPrice = getTokenPrice(selectedToken); + const tokenPrice = getTokenPrice({ + chainId: fromChain?.id, + chainType: fromChain?.chainType, + tokenAddress: selectedToken?.address, + }); const setMaxAmount = () => { if (!!balance && selectedToken) { diff --git a/packages/canonical-bridge-widget/src/modules/transfer/hooks/useInputValidation.ts b/packages/canonical-bridge-widget/src/modules/transfer/hooks/useInputValidation.ts index d8caf368..adb26659 100644 --- a/packages/canonical-bridge-widget/src/modules/transfer/hooks/useInputValidation.ts +++ b/packages/canonical-bridge-widget/src/modules/transfer/hooks/useInputValidation.ts @@ -20,7 +20,7 @@ export const useInputValidation = () => { const fromChain = useAppSelector((state) => state.transfer.fromChain); const selectedToken = useAppSelector((state) => state.transfer.selectedToken); - const priceInfo = useTokenUpperLimit(selectedToken); + const priceInfo = useTokenUpperLimit(); const validateInput = useCallback( ({ balance, diff --git a/packages/canonical-bridge-widget/src/modules/transfer/hooks/usePriceValidation.ts b/packages/canonical-bridge-widget/src/modules/transfer/hooks/usePriceValidation.ts index 11509f1e..2e21c6b2 100644 --- a/packages/canonical-bridge-widget/src/modules/transfer/hooks/usePriceValidation.ts +++ b/packages/canonical-bridge-widget/src/modules/transfer/hooks/usePriceValidation.ts @@ -1,32 +1,29 @@ import { useCallback } from 'react'; +import { ChainType } from '@bnb-chain/canonical-bridge-sdk'; -import { useBridgeConfig } from '@/index'; import { useTokenPrice } from '@/modules/aggregator/providers/TokenPricesProvider'; export const usePriceValidation = () => { - const { fetchApiTokenPrices } = useTokenPrice(); - const bridgeConfig = useBridgeConfig(); + const { fetchTokenPrice } = useTokenPrice(); const validateTokenPrice = useCallback( - async ({ tokenSymbol, tokenAddress }: { tokenSymbol: string; tokenAddress: string }) => { - const { cmcPrices, llamaPrices } = await fetchApiTokenPrices(bridgeConfig); - - const key1 = `${tokenSymbol?.toLowerCase()}:${tokenAddress?.toLowerCase()}`; - const key3 = tokenSymbol?.toLowerCase(); - const key2 = `ethereum:${key3}`; - let price = - cmcPrices?.[key1]?.price ?? - llamaPrices?.[key1]?.price ?? - cmcPrices?.[key2]?.price ?? - llamaPrices?.[key2]?.price ?? - cmcPrices?.[key3]?.price ?? - llamaPrices?.[key3]?.price; - if (price !== undefined) { - price = Number(price); - } + async ({ + chainId, + chainType, + tokenAddress, + }: { + chainId: number; + chainType: ChainType; + tokenAddress: string; + }) => { + const { data: price } = await fetchTokenPrice({ + chainId, + chainType, + tokenAddress, + }); return price; }, - [bridgeConfig, fetchApiTokenPrices], + [fetchTokenPrice], ); return { validateTokenPrice }; From 6afdf2d1aad7eace481ec284ad137c4d2b66f1c6 Mon Sep 17 00:00:00 2001 From: wenty22 Date: Tue, 21 Jan 2025 15:38:07 +0800 Subject: [PATCH 4/6] feat: Remove refetch logic for token upper limit --- .../src/modules/aggregator/hooks/useTokenUpperLimit.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useTokenUpperLimit.ts b/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useTokenUpperLimit.ts index 836433ae..66699622 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useTokenUpperLimit.ts +++ b/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useTokenUpperLimit.ts @@ -16,7 +16,6 @@ export function useTokenUpperLimit() { const { data: price } = useQuery({ staleTime: TIME.MINUTE * 5, - refetchInterval: TIME.MINUTE * 5, queryKey: ['tokenPrice', fromChain?.id, fromChain?.chainType, selectedToken?.address], queryFn: async () => { if (fromChain && selectedToken) { From 8242e537ca518ccba8ec5aa89d1c7eca974c705c Mon Sep 17 00:00:00 2001 From: wenty22 Date: Tue, 21 Jan 2025 18:35:11 +0800 Subject: [PATCH 5/6] fix: Update token select button styles --- .../src/core/components/IconImage/index.tsx | 2 +- .../aggregator/hooks/useTokenUpperLimit.ts | 24 +++++++++++++++---- .../SelectButton/TokenSelectButton.tsx | 7 +----- .../transfer/hooks/useInputValidation.ts | 8 +++++++ 4 files changed, 29 insertions(+), 12 deletions(-) diff --git a/packages/canonical-bridge-widget/src/core/components/IconImage/index.tsx b/packages/canonical-bridge-widget/src/core/components/IconImage/index.tsx index ddab849b..3b79ea20 100644 --- a/packages/canonical-bridge-widget/src/core/components/IconImage/index.tsx +++ b/packages/canonical-bridge-widget/src/core/components/IconImage/index.tsx @@ -22,7 +22,7 @@ export function IconImage(props: IconImageProps) { className="default-icon" size={boxSize} {...restProps} - bg={fallbackBgColor ?? theme.colors[colorMode].support.primary[4]} + bg={fallbackBgColor ?? theme.colors[colorMode].support.primary[3]} /> ) : ( state.transfer.fromChain); const selectedToken = useAppSelector((state) => state.transfer.selectedToken); + const sendValue = useAppSelector((state) => state.transfer.sendValue); - const { data: price } = useQuery({ + const { data: price, isError } = useQuery({ staleTime: TIME.MINUTE * 5, - queryKey: ['tokenPrice', fromChain?.id, fromChain?.chainType, selectedToken?.address], + queryKey: [ + 'tokenPrice', + fromChain?.id, + fromChain?.chainType, + selectedToken?.address, + !!sendValue, + ], queryFn: async () => { - if (fromChain && selectedToken) { + if (fromChain && selectedToken && sendValue) { return ( await fetchTokenPrice({ chainId: fromChain?.id, @@ -31,18 +38,25 @@ export function useTokenUpperLimit() { }); const result = useMemo(() => { - if (price !== undefined && selectedToken) { + if (!selectedToken) return; + + if (isError) { + return { + isError: true, + }; + } else if (price !== undefined) { const upperLimit = bridgeConfig.transfer.dollarUpperLimit / price; const value = parseUnits(String(upperLimit), selectedToken.decimals); return { + isError: false, upperLimit, value, price, decimals: selectedToken.decimals, }; } - }, [bridgeConfig.transfer.dollarUpperLimit, price, selectedToken]); + }, [bridgeConfig.transfer.dollarUpperLimit, isError, price, selectedToken]); return result; } diff --git a/packages/canonical-bridge-widget/src/modules/transfer/components/SelectButton/TokenSelectButton.tsx b/packages/canonical-bridge-widget/src/modules/transfer/components/SelectButton/TokenSelectButton.tsx index bb2dade3..5fae16d8 100644 --- a/packages/canonical-bridge-widget/src/modules/transfer/components/SelectButton/TokenSelectButton.tsx +++ b/packages/canonical-bridge-widget/src/modules/transfer/components/SelectButton/TokenSelectButton.tsx @@ -52,12 +52,7 @@ export function TokenSelectButton(props: SelectButtonProps) { data-display-symbol={token?.displaySymbol} > - + { isError: true, }; } + // Check upper limit + if (priceInfo?.isError) { + return { + text: `This token is not available at the moment. Please try again later.`, + isError: true, + }; + } + if (priceInfo?.upperLimit && Number(value) >= Number(priceInfo?.upperLimit)) { return { text: `Transfer value over $${formatNumber(dollarUpperLimit)} (${formatNumber( From 4faf7039e893ca374c38a64535a1996118bd0240 Mon Sep 17 00:00:00 2001 From: wenty22 Date: Wed, 22 Jan 2025 10:37:39 +0800 Subject: [PATCH 6/6] feat: Use logger to output log --- .../src/module/token/token.schedule.ts | 2 +- .../src/shared/web3/web3.service.ts | 19 ++++++++++++------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/apps/canonical-bridge-server/src/module/token/token.schedule.ts b/apps/canonical-bridge-server/src/module/token/token.schedule.ts index 70e55a22..95504e04 100644 --- a/apps/canonical-bridge-server/src/module/token/token.schedule.ts +++ b/apps/canonical-bridge-server/src/module/token/token.schedule.ts @@ -47,7 +47,7 @@ export class TokenSchedule implements OnModuleInit { ); } - @Cron(CronExpression.EVERY_5_MINUTES) + @Cron(CronExpression.EVERY_MINUTE) async syncCoingeckoTokenPrice() { this.logger.log('syncCoingeckoTokenPrice'); const { tokens, keyMap } = await this.tokensService.getLlamaTokenIdsForPriceJob(); diff --git a/apps/canonical-bridge-server/src/shared/web3/web3.service.ts b/apps/canonical-bridge-server/src/shared/web3/web3.service.ts index 17bc103d..b9fc3be5 100644 --- a/apps/canonical-bridge-server/src/shared/web3/web3.service.ts +++ b/apps/canonical-bridge-server/src/shared/web3/web3.service.ts @@ -68,7 +68,7 @@ export class Web3Service { ); return data; } catch (e) { - console.error(`Failed to retrieve cBridge data at ${new Date().getTime()}`, e.message); + this.logger.log(`Failed to retrieve cBridge data at ${new Date().getTime()}, ${e.message}`); } } @@ -80,7 +80,10 @@ export class Web3Service { return data; } catch (e) { - console.error(`Failed to retrieve DeBridge chain data at ${new Date().getTime()}`, e.message); + this.logger.log( + `Failed to retrieve DeBridge chain data at ${new Date().getTime()}, + ${e.message}`, + ); } } @@ -92,9 +95,9 @@ export class Web3Service { return data; } catch (e) { - console.error( - `Failed to retrieve DeBridge token data from ${chainId} at ${new Date().getTime()}`, - e.message, + this.logger.log( + `Failed to retrieve DeBridge token data from ${chainId} at ${new Date().getTime()}, + ${e.message}`, ); } } @@ -120,7 +123,9 @@ export class Web3Service { }); return processedTokenList; } catch (e) { - console.error(`Failed to retrieve Stargate API data at ${new Date().getTime()}`, e.message); + this.logger.log( + `Failed to retrieve Stargate API data at ${new Date().getTime()}, ${e.message}`, + ); } } @@ -131,7 +136,7 @@ export class Web3Service { ); return data; } catch (e) { - console.log(`Failed to retrieve Meson API data at ${new Date().getTime()}`, e); + this.logger.log(`Failed to retrieve Meson API data at ${new Date().getTime()}, ${e}`); return []; } }