From 9827ecde21472cadfed5bb3bc18f53d8494c6988 Mon Sep 17 00:00:00 2001 From: chris Date: Mon, 30 Dec 2024 17:08:07 +1000 Subject: [PATCH 01/20] feat: Add api error handler --- .../src/shared/web3/web3.service.ts | 64 +++++++++++++------ 1 file changed, 43 insertions(+), 21 deletions(-) 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 b183ac3a..8b06f1f9 100644 --- a/apps/canonical-bridge-server/src/shared/web3/web3.service.ts +++ b/apps/canonical-bridge-server/src/shared/web3/web3.service.ts @@ -60,34 +60,50 @@ export class Web3Service { } async getTransferConfigsForAll() { - const { data } = await this.httpService.axiosRef.get( - `${CBRIDGE_ENDPOINT}/v2/getTransferConfigsForAll`, - ); - return data; + try { + const { data } = await this.httpService.axiosRef.get( + `${CBRIDGE_ENDPOINT}/v2/getTransferConfigsForAll`, + ); + return data; + } catch (e) { + console.error(`Failed to retrieve cBridge data at ${new Date().getTime()}`, e); + } } async getDebridgeChains() { - const { data } = await this.httpService.axiosRef.get<{ chains: IDebridgeChain[] }>( - `${DEBRIDGE_ENDPOINT}/supported-chains-info`, - ); + try { + const { data } = await this.httpService.axiosRef.get<{ chains: IDebridgeChain[] }>( + `${DEBRIDGE_ENDPOINT}/supported-chains-info`, + ); - return data; + return data; + } catch (e) { + console.error(`Failed to retrieve DeBridge chain data at ${new Date().getTime()}`, e); + } } async getDebridgeChainTokens(chainId: number) { - const { data } = await this.httpService.axiosRef.get<{ - tokens: Record; - }>(`${DEBRIDGE_ENDPOINT}/token-list?chainId=${chainId}`); + try { + const { data } = await this.httpService.axiosRef.get<{ + tokens: Record; + }>(`${DEBRIDGE_ENDPOINT}/token-list?chainId=${chainId}`); - return data; + return data; + } catch (e) { + console.error( + `Failed to retrieve DeBridge token data from ${chainId} at ${new Date().getTime()}`, + e, + ); + return { tokens: {} }; + } } async getStargateConfigs() { - const { data } = await this.httpService.axiosRef.get( - `${STARGATE_ENDPOINT}`, - ); - const processedTokenList = []; try { + const { data } = await this.httpService.axiosRef.get( + `${STARGATE_ENDPOINT}`, + ); + const processedTokenList = []; const v2List = data.v2; v2List.forEach((token) => { const chainInfo = STARGATE_CHAIN_INFO.filter( @@ -97,17 +113,23 @@ export class Web3Service { processedTokenList.push({ ...token, endpointID: chainInfo[0].endpointID }); } }); + return processedTokenList; } catch (e) { console.log(`Failed to retrieve Stargate API data at ${new Date().getTime()}`, e); + return []; } - return processedTokenList; } async getMesonConfigs() { - const { data } = await this.httpService.axiosRef.get<{ result: IMesonChain[] }>( - `${MESON_ENDPOINT}/limits`, - ); - return data; + try { + const { data } = await this.httpService.axiosRef.get<{ result: IMesonChain[] }>( + `${MESON_ENDPOINT}/limits`, + ); + return data; + } catch (e) { + console.log(`Failed to retrieve Meson API data at ${new Date().getTime()}`, e); + return []; + } } async getAssetPlatforms() { From 68de75d428c26737fb85d8065f4fc29847f91aba Mon Sep 17 00:00:00 2001 From: chris Date: Mon, 30 Dec 2024 17:09:15 +1000 Subject: [PATCH 02/20] chore: Add error return value --- apps/canonical-bridge-server/src/shared/web3/web3.service.ts | 2 ++ 1 file changed, 2 insertions(+) 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 8b06f1f9..77d4cd8c 100644 --- a/apps/canonical-bridge-server/src/shared/web3/web3.service.ts +++ b/apps/canonical-bridge-server/src/shared/web3/web3.service.ts @@ -67,6 +67,7 @@ export class Web3Service { return data; } catch (e) { console.error(`Failed to retrieve cBridge data at ${new Date().getTime()}`, e); + return {}; } } @@ -79,6 +80,7 @@ export class Web3Service { return data; } catch (e) { console.error(`Failed to retrieve DeBridge chain data at ${new Date().getTime()}`, e); + return { chains: [] }; } } From 0854dd5fd13681062fa44a4844d2f19209433b25 Mon Sep 17 00:00:00 2001 From: chris Date: Mon, 30 Dec 2024 17:41:40 +1000 Subject: [PATCH 03/20] chore: Display proper error messages --- .../src/shared/web3/web3.service.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 77d4cd8c..35a3a4cb 100644 --- a/apps/canonical-bridge-server/src/shared/web3/web3.service.ts +++ b/apps/canonical-bridge-server/src/shared/web3/web3.service.ts @@ -66,7 +66,7 @@ export class Web3Service { ); return data; } catch (e) { - console.error(`Failed to retrieve cBridge data at ${new Date().getTime()}`, e); + console.error(`Failed to retrieve cBridge data at ${new Date().getTime()}`, e.message); return {}; } } @@ -79,7 +79,7 @@ export class Web3Service { return data; } catch (e) { - console.error(`Failed to retrieve DeBridge chain data at ${new Date().getTime()}`, e); + console.error(`Failed to retrieve DeBridge chain data at ${new Date().getTime()}`, e.message); return { chains: [] }; } } @@ -94,7 +94,7 @@ export class Web3Service { } catch (e) { console.error( `Failed to retrieve DeBridge token data from ${chainId} at ${new Date().getTime()}`, - e, + e.message, ); return { tokens: {} }; } @@ -117,7 +117,7 @@ export class Web3Service { }); return processedTokenList; } catch (e) { - console.log(`Failed to retrieve Stargate API data at ${new Date().getTime()}`, e); + console.error(`Failed to retrieve Stargate API data at ${new Date().getTime()}`, e.message); return []; } } From 003572b346ec0396809c288398b43aa96889a84d Mon Sep 17 00:00:00 2001 From: chris Date: Mon, 30 Dec 2024 18:47:45 +1000 Subject: [PATCH 04/20] chore: Remove error return value --- apps/canonical-bridge-server/src/shared/web3/web3.service.ts | 4 ---- 1 file changed, 4 deletions(-) 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 35a3a4cb..3b7ee373 100644 --- a/apps/canonical-bridge-server/src/shared/web3/web3.service.ts +++ b/apps/canonical-bridge-server/src/shared/web3/web3.service.ts @@ -67,7 +67,6 @@ export class Web3Service { return data; } catch (e) { console.error(`Failed to retrieve cBridge data at ${new Date().getTime()}`, e.message); - return {}; } } @@ -80,7 +79,6 @@ export class Web3Service { return data; } catch (e) { console.error(`Failed to retrieve DeBridge chain data at ${new Date().getTime()}`, e.message); - return { chains: [] }; } } @@ -96,7 +94,6 @@ export class Web3Service { `Failed to retrieve DeBridge token data from ${chainId} at ${new Date().getTime()}`, e.message, ); - return { tokens: {} }; } } @@ -118,7 +115,6 @@ export class Web3Service { return processedTokenList; } catch (e) { console.error(`Failed to retrieve Stargate API data at ${new Date().getTime()}`, e.message); - return []; } } From 5d08223cbc546b4ff8337967e3dfe893840e3b8f Mon Sep 17 00:00:00 2001 From: chris Date: Mon, 30 Dec 2024 19:31:56 +1000 Subject: [PATCH 05/20] fix: Fix stargate fee display text and handle api error --- .release/.changeset/giant-rings-warn.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .release/.changeset/giant-rings-warn.md diff --git a/.release/.changeset/giant-rings-warn.md b/.release/.changeset/giant-rings-warn.md new file mode 100644 index 00000000..cd0e45b9 --- /dev/null +++ b/.release/.changeset/giant-rings-warn.md @@ -0,0 +1,5 @@ +--- +"@bnb-chain/canonical-bridge-widget": patch +--- + +Fix stargate fee display and handle api error From be8248ca7d6736345a128c998fb2a95d6b907e8f Mon Sep 17 00:00:00 2001 From: chris Date: Mon, 30 Dec 2024 22:18:19 +1000 Subject: [PATCH 06/20] chore: Remove unused files --- ....timestamp-1732519098773-fb36ae85ee687.mjs | 44 ------------------- ....timestamp-1734950241948-d28c013da946b.mjs | 44 ------------------- 2 files changed, 88 deletions(-) delete mode 100644 packages/canonical-bridge-sdk/vite.config.ts.timestamp-1732519098773-fb36ae85ee687.mjs delete mode 100644 packages/canonical-bridge-sdk/vite.config.ts.timestamp-1734950241948-d28c013da946b.mjs diff --git a/packages/canonical-bridge-sdk/vite.config.ts.timestamp-1732519098773-fb36ae85ee687.mjs b/packages/canonical-bridge-sdk/vite.config.ts.timestamp-1732519098773-fb36ae85ee687.mjs deleted file mode 100644 index 914888b9..00000000 --- a/packages/canonical-bridge-sdk/vite.config.ts.timestamp-1732519098773-fb36ae85ee687.mjs +++ /dev/null @@ -1,44 +0,0 @@ -// vite.config.ts -import { defineConfig } from "file:///Users/liwen/Documents/bnb-chain/canonical-bridge/common/temp/node_modules/.pnpm/vite@4.5.5_@types+node@22.7.5_terser@5.36.0/node_modules/vite/dist/node/index.js"; -import react from "file:///Users/liwen/Documents/bnb-chain/canonical-bridge/common/temp/node_modules/.pnpm/@vitejs+plugin-react@4.3.3_vite@4.5.5_@types+node@22.7.5_terser@5.36.0_/node_modules/@vitejs/plugin-react/dist/index.mjs"; -import dts from "file:///Users/liwen/Documents/bnb-chain/canonical-bridge/common/temp/node_modules/.pnpm/vite-plugin-dts@3.9.1_@types+node@22.7.5_rollup@3.29.5_typescript@5.5.4_vite@4.5.5_@types+node@22.7.5_terser@5.36.0_/node_modules/vite-plugin-dts/dist/index.mjs"; -import peerDepsExternal from "file:///Users/liwen/Documents/bnb-chain/canonical-bridge/common/temp/node_modules/.pnpm/rollup-plugin-peer-deps-external@2.2.4_rollup@3.29.5/node_modules/rollup-plugin-peer-deps-external/dist/rollup-plugin-peer-deps-external.js"; -import path from "path"; -var __vite_injected_original_dirname = "/Users/liwen/Documents/bnb-chain/canonical-bridge/packages/canonical-bridge-sdk"; -var vite_config_default = defineConfig({ - plugins: [ - react(), - dts({ - include: "src" - }) - ], - resolve: { - alias: { - "@": path.resolve(__vite_injected_original_dirname, "src") - } - }, - build: { - target: "esnext", - minify: false, - lib: { - formats: ["es"], - entry: { - index: "src/index.ts" - } - }, - rollupOptions: { - plugins: [ - peerDepsExternal({ - includeDependencies: true - }) - ], - output: { - chunkFileNames: "common.js" - } - } - } -}); -export { - vite_config_default as default -}; -//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCIvVXNlcnMvbGl3ZW4vRG9jdW1lbnRzL2JuYi1jaGFpbi9jYW5vbmljYWwtYnJpZGdlL3BhY2thZ2VzL2Nhbm9uaWNhbC1icmlkZ2Utc2RrXCI7Y29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2ZpbGVuYW1lID0gXCIvVXNlcnMvbGl3ZW4vRG9jdW1lbnRzL2JuYi1jaGFpbi9jYW5vbmljYWwtYnJpZGdlL3BhY2thZ2VzL2Nhbm9uaWNhbC1icmlkZ2Utc2RrL3ZpdGUuY29uZmlnLnRzXCI7Y29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2ltcG9ydF9tZXRhX3VybCA9IFwiZmlsZTovLy9Vc2Vycy9saXdlbi9Eb2N1bWVudHMvYm5iLWNoYWluL2Nhbm9uaWNhbC1icmlkZ2UvcGFja2FnZXMvY2Fub25pY2FsLWJyaWRnZS1zZGsvdml0ZS5jb25maWcudHNcIjtpbXBvcnQgeyBkZWZpbmVDb25maWcgfSBmcm9tICd2aXRlJztcbmltcG9ydCByZWFjdCBmcm9tICdAdml0ZWpzL3BsdWdpbi1yZWFjdCc7XG5pbXBvcnQgZHRzIGZyb20gJ3ZpdGUtcGx1Z2luLWR0cyc7XG5pbXBvcnQgcGVlckRlcHNFeHRlcm5hbCBmcm9tICdyb2xsdXAtcGx1Z2luLXBlZXItZGVwcy1leHRlcm5hbCc7XG5pbXBvcnQgcGF0aCBmcm9tICdwYXRoJztcblxuLy8gaHR0cHM6Ly92aXRlanMuZGV2L2NvbmZpZy9cbmV4cG9ydCBkZWZhdWx0IGRlZmluZUNvbmZpZyh7XG4gIHBsdWdpbnM6IFtcbiAgICByZWFjdCgpLFxuICAgIGR0cyh7XG4gICAgICBpbmNsdWRlOiAnc3JjJyxcbiAgICB9KSxcbiAgXSxcbiAgcmVzb2x2ZToge1xuICAgIGFsaWFzOiB7XG4gICAgICAnQCc6IHBhdGgucmVzb2x2ZShfX2Rpcm5hbWUsICdzcmMnKSxcbiAgICB9LFxuICB9LFxuICBidWlsZDoge1xuICAgIHRhcmdldDogJ2VzbmV4dCcsXG4gICAgbWluaWZ5OiBmYWxzZSxcbiAgICBsaWI6IHtcbiAgICAgIGZvcm1hdHM6IFsnZXMnXSxcbiAgICAgIGVudHJ5OiB7XG4gICAgICAgIGluZGV4OiAnc3JjL2luZGV4LnRzJyxcbiAgICAgIH0sXG4gICAgfSxcbiAgICByb2xsdXBPcHRpb25zOiB7XG4gICAgICBwbHVnaW5zOiBbXG4gICAgICAgIHBlZXJEZXBzRXh0ZXJuYWwoe1xuICAgICAgICAgIGluY2x1ZGVEZXBlbmRlbmNpZXM6IHRydWUsXG4gICAgICAgIH0pLFxuICAgICAgXSxcbiAgICAgIG91dHB1dDoge1xuICAgICAgICBjaHVua0ZpbGVOYW1lczogJ2NvbW1vbi5qcycsXG4gICAgICB9LFxuICAgIH0sXG4gIH0sXG59KTtcbiJdLAogICJtYXBwaW5ncyI6ICI7QUFBK1osU0FBUyxvQkFBb0I7QUFDNWIsT0FBTyxXQUFXO0FBQ2xCLE9BQU8sU0FBUztBQUNoQixPQUFPLHNCQUFzQjtBQUM3QixPQUFPLFVBQVU7QUFKakIsSUFBTSxtQ0FBbUM7QUFPekMsSUFBTyxzQkFBUSxhQUFhO0FBQUEsRUFDMUIsU0FBUztBQUFBLElBQ1AsTUFBTTtBQUFBLElBQ04sSUFBSTtBQUFBLE1BQ0YsU0FBUztBQUFBLElBQ1gsQ0FBQztBQUFBLEVBQ0g7QUFBQSxFQUNBLFNBQVM7QUFBQSxJQUNQLE9BQU87QUFBQSxNQUNMLEtBQUssS0FBSyxRQUFRLGtDQUFXLEtBQUs7QUFBQSxJQUNwQztBQUFBLEVBQ0Y7QUFBQSxFQUNBLE9BQU87QUFBQSxJQUNMLFFBQVE7QUFBQSxJQUNSLFFBQVE7QUFBQSxJQUNSLEtBQUs7QUFBQSxNQUNILFNBQVMsQ0FBQyxJQUFJO0FBQUEsTUFDZCxPQUFPO0FBQUEsUUFDTCxPQUFPO0FBQUEsTUFDVDtBQUFBLElBQ0Y7QUFBQSxJQUNBLGVBQWU7QUFBQSxNQUNiLFNBQVM7QUFBQSxRQUNQLGlCQUFpQjtBQUFBLFVBQ2YscUJBQXFCO0FBQUEsUUFDdkIsQ0FBQztBQUFBLE1BQ0g7QUFBQSxNQUNBLFFBQVE7QUFBQSxRQUNOLGdCQUFnQjtBQUFBLE1BQ2xCO0FBQUEsSUFDRjtBQUFBLEVBQ0Y7QUFDRixDQUFDOyIsCiAgIm5hbWVzIjogW10KfQo= diff --git a/packages/canonical-bridge-sdk/vite.config.ts.timestamp-1734950241948-d28c013da946b.mjs b/packages/canonical-bridge-sdk/vite.config.ts.timestamp-1734950241948-d28c013da946b.mjs deleted file mode 100644 index 914888b9..00000000 --- a/packages/canonical-bridge-sdk/vite.config.ts.timestamp-1734950241948-d28c013da946b.mjs +++ /dev/null @@ -1,44 +0,0 @@ -// vite.config.ts -import { defineConfig } from "file:///Users/liwen/Documents/bnb-chain/canonical-bridge/common/temp/node_modules/.pnpm/vite@4.5.5_@types+node@22.7.5_terser@5.36.0/node_modules/vite/dist/node/index.js"; -import react from "file:///Users/liwen/Documents/bnb-chain/canonical-bridge/common/temp/node_modules/.pnpm/@vitejs+plugin-react@4.3.3_vite@4.5.5_@types+node@22.7.5_terser@5.36.0_/node_modules/@vitejs/plugin-react/dist/index.mjs"; -import dts from "file:///Users/liwen/Documents/bnb-chain/canonical-bridge/common/temp/node_modules/.pnpm/vite-plugin-dts@3.9.1_@types+node@22.7.5_rollup@3.29.5_typescript@5.5.4_vite@4.5.5_@types+node@22.7.5_terser@5.36.0_/node_modules/vite-plugin-dts/dist/index.mjs"; -import peerDepsExternal from "file:///Users/liwen/Documents/bnb-chain/canonical-bridge/common/temp/node_modules/.pnpm/rollup-plugin-peer-deps-external@2.2.4_rollup@3.29.5/node_modules/rollup-plugin-peer-deps-external/dist/rollup-plugin-peer-deps-external.js"; -import path from "path"; -var __vite_injected_original_dirname = "/Users/liwen/Documents/bnb-chain/canonical-bridge/packages/canonical-bridge-sdk"; -var vite_config_default = defineConfig({ - plugins: [ - react(), - dts({ - include: "src" - }) - ], - resolve: { - alias: { - "@": path.resolve(__vite_injected_original_dirname, "src") - } - }, - build: { - target: "esnext", - minify: false, - lib: { - formats: ["es"], - entry: { - index: "src/index.ts" - } - }, - rollupOptions: { - plugins: [ - peerDepsExternal({ - includeDependencies: true - }) - ], - output: { - chunkFileNames: "common.js" - } - } - } -}); -export { - vite_config_default as default -}; -//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCIvVXNlcnMvbGl3ZW4vRG9jdW1lbnRzL2JuYi1jaGFpbi9jYW5vbmljYWwtYnJpZGdlL3BhY2thZ2VzL2Nhbm9uaWNhbC1icmlkZ2Utc2RrXCI7Y29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2ZpbGVuYW1lID0gXCIvVXNlcnMvbGl3ZW4vRG9jdW1lbnRzL2JuYi1jaGFpbi9jYW5vbmljYWwtYnJpZGdlL3BhY2thZ2VzL2Nhbm9uaWNhbC1icmlkZ2Utc2RrL3ZpdGUuY29uZmlnLnRzXCI7Y29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2ltcG9ydF9tZXRhX3VybCA9IFwiZmlsZTovLy9Vc2Vycy9saXdlbi9Eb2N1bWVudHMvYm5iLWNoYWluL2Nhbm9uaWNhbC1icmlkZ2UvcGFja2FnZXMvY2Fub25pY2FsLWJyaWRnZS1zZGsvdml0ZS5jb25maWcudHNcIjtpbXBvcnQgeyBkZWZpbmVDb25maWcgfSBmcm9tICd2aXRlJztcbmltcG9ydCByZWFjdCBmcm9tICdAdml0ZWpzL3BsdWdpbi1yZWFjdCc7XG5pbXBvcnQgZHRzIGZyb20gJ3ZpdGUtcGx1Z2luLWR0cyc7XG5pbXBvcnQgcGVlckRlcHNFeHRlcm5hbCBmcm9tICdyb2xsdXAtcGx1Z2luLXBlZXItZGVwcy1leHRlcm5hbCc7XG5pbXBvcnQgcGF0aCBmcm9tICdwYXRoJztcblxuLy8gaHR0cHM6Ly92aXRlanMuZGV2L2NvbmZpZy9cbmV4cG9ydCBkZWZhdWx0IGRlZmluZUNvbmZpZyh7XG4gIHBsdWdpbnM6IFtcbiAgICByZWFjdCgpLFxuICAgIGR0cyh7XG4gICAgICBpbmNsdWRlOiAnc3JjJyxcbiAgICB9KSxcbiAgXSxcbiAgcmVzb2x2ZToge1xuICAgIGFsaWFzOiB7XG4gICAgICAnQCc6IHBhdGgucmVzb2x2ZShfX2Rpcm5hbWUsICdzcmMnKSxcbiAgICB9LFxuICB9LFxuICBidWlsZDoge1xuICAgIHRhcmdldDogJ2VzbmV4dCcsXG4gICAgbWluaWZ5OiBmYWxzZSxcbiAgICBsaWI6IHtcbiAgICAgIGZvcm1hdHM6IFsnZXMnXSxcbiAgICAgIGVudHJ5OiB7XG4gICAgICAgIGluZGV4OiAnc3JjL2luZGV4LnRzJyxcbiAgICAgIH0sXG4gICAgfSxcbiAgICByb2xsdXBPcHRpb25zOiB7XG4gICAgICBwbHVnaW5zOiBbXG4gICAgICAgIHBlZXJEZXBzRXh0ZXJuYWwoe1xuICAgICAgICAgIGluY2x1ZGVEZXBlbmRlbmNpZXM6IHRydWUsXG4gICAgICAgIH0pLFxuICAgICAgXSxcbiAgICAgIG91dHB1dDoge1xuICAgICAgICBjaHVua0ZpbGVOYW1lczogJ2NvbW1vbi5qcycsXG4gICAgICB9LFxuICAgIH0sXG4gIH0sXG59KTtcbiJdLAogICJtYXBwaW5ncyI6ICI7QUFBK1osU0FBUyxvQkFBb0I7QUFDNWIsT0FBTyxXQUFXO0FBQ2xCLE9BQU8sU0FBUztBQUNoQixPQUFPLHNCQUFzQjtBQUM3QixPQUFPLFVBQVU7QUFKakIsSUFBTSxtQ0FBbUM7QUFPekMsSUFBTyxzQkFBUSxhQUFhO0FBQUEsRUFDMUIsU0FBUztBQUFBLElBQ1AsTUFBTTtBQUFBLElBQ04sSUFBSTtBQUFBLE1BQ0YsU0FBUztBQUFBLElBQ1gsQ0FBQztBQUFBLEVBQ0g7QUFBQSxFQUNBLFNBQVM7QUFBQSxJQUNQLE9BQU87QUFBQSxNQUNMLEtBQUssS0FBSyxRQUFRLGtDQUFXLEtBQUs7QUFBQSxJQUNwQztBQUFBLEVBQ0Y7QUFBQSxFQUNBLE9BQU87QUFBQSxJQUNMLFFBQVE7QUFBQSxJQUNSLFFBQVE7QUFBQSxJQUNSLEtBQUs7QUFBQSxNQUNILFNBQVMsQ0FBQyxJQUFJO0FBQUEsTUFDZCxPQUFPO0FBQUEsUUFDTCxPQUFPO0FBQUEsTUFDVDtBQUFBLElBQ0Y7QUFBQSxJQUNBLGVBQWU7QUFBQSxNQUNiLFNBQVM7QUFBQSxRQUNQLGlCQUFpQjtBQUFBLFVBQ2YscUJBQXFCO0FBQUEsUUFDdkIsQ0FBQztBQUFBLE1BQ0g7QUFBQSxNQUNBLFFBQVE7QUFBQSxRQUNOLGdCQUFnQjtBQUFBLE1BQ2xCO0FBQUEsSUFDRjtBQUFBLEVBQ0Y7QUFDRixDQUFDOyIsCiAgIm5hbWVzIjogW10KfQo= From c0fa2ece47e350d9a996435b0efdb17b2ae22c80 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 30 Dec 2024 12:36:22 +0000 Subject: [PATCH 07/20] chore: Update versions --- .release/.changeset/bright-impalas-sniff.md | 6 ------ .release/.changeset/dull-moose-deliver.md | 6 ------ .release/.changeset/giant-rings-warn.md | 5 ----- .release/.changeset/hungry-doors-lick.md | 6 ------ .release/.changeset/itchy-dots-enjoy.md | 6 ------ .release/.changeset/poor-kings-compete.md | 5 ----- .release/.changeset/pre.json | 16 ---------------- .release/.changeset/serious-cars-worry.md | 5 ----- packages/canonical-bridge-sdk/CHANGELOG.md | 9 +++++++++ packages/canonical-bridge-sdk/package.json | 2 +- packages/canonical-bridge-widget/CHANGELOG.md | 12 ++++++++++++ packages/canonical-bridge-widget/package.json | 2 +- 12 files changed, 23 insertions(+), 57 deletions(-) delete mode 100644 .release/.changeset/bright-impalas-sniff.md delete mode 100644 .release/.changeset/dull-moose-deliver.md delete mode 100644 .release/.changeset/giant-rings-warn.md delete mode 100644 .release/.changeset/hungry-doors-lick.md delete mode 100644 .release/.changeset/itchy-dots-enjoy.md delete mode 100644 .release/.changeset/poor-kings-compete.md delete mode 100644 .release/.changeset/pre.json delete mode 100644 .release/.changeset/serious-cars-worry.md diff --git a/.release/.changeset/bright-impalas-sniff.md b/.release/.changeset/bright-impalas-sniff.md deleted file mode 100644 index 11a379bb..00000000 --- a/.release/.changeset/bright-impalas-sniff.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@bnb-chain/canonical-bridge-widget": patch -"@bnb-chain/canonical-bridge-sdk": patch ---- - -Use stargate & meson api to fetch chain & token config diff --git a/.release/.changeset/dull-moose-deliver.md b/.release/.changeset/dull-moose-deliver.md deleted file mode 100644 index 7a08558c..00000000 --- a/.release/.changeset/dull-moose-deliver.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@bnb-chain/canonical-bridge-widget": patch -"@bnb-chain/canonical-bridge-sdk": patch ---- - -Remove duplicated tokens for stargate diff --git a/.release/.changeset/giant-rings-warn.md b/.release/.changeset/giant-rings-warn.md deleted file mode 100644 index cd0e45b9..00000000 --- a/.release/.changeset/giant-rings-warn.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bnb-chain/canonical-bridge-widget": patch ---- - -Fix stargate fee display and handle api error diff --git a/.release/.changeset/hungry-doors-lick.md b/.release/.changeset/hungry-doors-lick.md deleted file mode 100644 index aa293e89..00000000 --- a/.release/.changeset/hungry-doors-lick.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@bnb-chain/canonical-bridge-widget": patch -"@bnb-chain/canonical-bridge-sdk": patch ---- - -Update token element's info diff --git a/.release/.changeset/itchy-dots-enjoy.md b/.release/.changeset/itchy-dots-enjoy.md deleted file mode 100644 index b39fd553..00000000 --- a/.release/.changeset/itchy-dots-enjoy.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@bnb-chain/canonical-bridge-widget": patch -"@bnb-chain/canonical-bridge-sdk": patch ---- - -Fix meson token display symbol issue diff --git a/.release/.changeset/poor-kings-compete.md b/.release/.changeset/poor-kings-compete.md deleted file mode 100644 index 5095b9dd..00000000 --- a/.release/.changeset/poor-kings-compete.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bnb-chain/canonical-bridge-widget": patch ---- - -Fix tron does not display due to its id diff --git a/.release/.changeset/pre.json b/.release/.changeset/pre.json deleted file mode 100644 index ae3bf878..00000000 --- a/.release/.changeset/pre.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "mode": "pre", - "tag": "alpha", - "initialVersions": { - "@bnb-chain/canonical-bridge-sdk": "0.4.5", - "@bnb-chain/canonical-bridge-widget": "0.5.15" - }, - "changesets": [ - "bright-impalas-sniff", - "dull-moose-deliver", - "hungry-doors-lick", - "itchy-dots-enjoy", - "poor-kings-compete", - "serious-cars-worry" - ] -} diff --git a/.release/.changeset/serious-cars-worry.md b/.release/.changeset/serious-cars-worry.md deleted file mode 100644 index 910f2f9c..00000000 --- a/.release/.changeset/serious-cars-worry.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bnb-chain/canonical-bridge-widget": patch ---- - -Add more token info to token element diff --git a/packages/canonical-bridge-sdk/CHANGELOG.md b/packages/canonical-bridge-sdk/CHANGELOG.md index 06ae4a88..32972b04 100644 --- a/packages/canonical-bridge-sdk/CHANGELOG.md +++ b/packages/canonical-bridge-sdk/CHANGELOG.md @@ -1,5 +1,14 @@ # @bnb-chain/canonical-bridge-sdk +## 0.4.6 + +### Patch Changes + +- Use stargate & meson api to fetch chain & token config +- 2538636: Remove duplicated tokens for stargate +- c4f3ee7: Update token element's info +- 7caf17e: Fix meson token display symbol issue + ## 0.4.6-alpha.3 ### Patch Changes diff --git a/packages/canonical-bridge-sdk/package.json b/packages/canonical-bridge-sdk/package.json index 7275b044..434eef71 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.6-alpha.3", + "version": "0.4.6", "description": "canonical bridge sdk", "author": "bnb-chain", "private": false, diff --git a/packages/canonical-bridge-widget/CHANGELOG.md b/packages/canonical-bridge-widget/CHANGELOG.md index 69e9c0b7..a425fd23 100644 --- a/packages/canonical-bridge-widget/CHANGELOG.md +++ b/packages/canonical-bridge-widget/CHANGELOG.md @@ -1,5 +1,17 @@ # @bnb-chain/canonical-bridge-widget +## 0.5.16 + +### Patch Changes + +- Use stargate & meson api to fetch chain & token config +- 2538636: Remove duplicated tokens for stargate +- 5d08223: Fix stargate fee display and handle api error +- c4f3ee7: Update token element's info +- 7caf17e: Fix meson token display symbol issue +- e19a76c: Fix tron does not display due to its id +- b930ad4: Add more token info to token element + ## 0.5.16-alpha.5 ### Patch Changes diff --git a/packages/canonical-bridge-widget/package.json b/packages/canonical-bridge-widget/package.json index 8a7d7a57..78bb0e38 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.16-alpha.5", + "version": "0.5.16", "description": "canonical bridge widget", "author": "bnb-chain", "private": false, From 87f16921a8dfee8fe264f2bd3dfcd47fe7fb2313 Mon Sep 17 00:00:00 2001 From: chris Date: Tue, 31 Dec 2024 16:03:10 +1000 Subject: [PATCH 08/20] chore: Remove comma from value amount --- .../adapters/layerZero/components/LayerZeroOption.tsx | 1 + .../src/modules/transfer/components/ReceiveInfo/index.tsx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/layerZero/components/LayerZeroOption.tsx b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/layerZero/components/LayerZeroOption.tsx index 301610bc..61c3cdd3 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/layerZero/components/LayerZeroOption.tsx +++ b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/layerZero/components/LayerZeroOption.tsx @@ -33,6 +33,7 @@ export const LayerZeroOption = () => { ? `${formatNumber( Number(formatUnits(BigInt(estimatedAmount?.['layerZero']), getToDecimals()['layerZero'])), 8, + false, )}` : '--'; }, [estimatedAmount, toTokenInfo, sendValue, getToDecimals]); diff --git a/packages/canonical-bridge-widget/src/modules/transfer/components/ReceiveInfo/index.tsx b/packages/canonical-bridge-widget/src/modules/transfer/components/ReceiveInfo/index.tsx index b29f63ab..76046266 100644 --- a/packages/canonical-bridge-widget/src/modules/transfer/components/ReceiveInfo/index.tsx +++ b/packages/canonical-bridge-widget/src/modules/transfer/components/ReceiveInfo/index.tsx @@ -206,7 +206,7 @@ export const ReceiveInfo = ({ onOpen }: ReceiveInfoProps) => { {bridgeType && ( Date: Tue, 31 Dec 2024 16:49:30 +1000 Subject: [PATCH 09/20] feat: Solana insufficient SOL message refactory --- .../src/core/components/icons/InfoIcon.tsx | 11 ++++-- .../src/modules/transfer/BridgeTransfer.tsx | 2 + .../TransferWarningMessage/index.tsx | 38 +++++++++++++++++++ .../transfer/hooks/useInputValidation.ts | 20 +++------- 4 files changed, 53 insertions(+), 18 deletions(-) create mode 100644 packages/canonical-bridge-widget/src/modules/transfer/components/TransferWarningMessage/index.tsx diff --git a/packages/canonical-bridge-widget/src/core/components/icons/InfoIcon.tsx b/packages/canonical-bridge-widget/src/core/components/icons/InfoIcon.tsx index 52658ebd..d82aac34 100644 --- a/packages/canonical-bridge-widget/src/core/components/icons/InfoIcon.tsx +++ b/packages/canonical-bridge-widget/src/core/components/icons/InfoIcon.tsx @@ -1,6 +1,11 @@ import { Icon, IconProps } from '@bnb-chain/space'; -export function InfoIcon(props: IconProps) { +interface InfoIconProps extends IconProps { + iconColor?: string; + iconBgColor?: string; +} + +export function InfoIcon(props: InfoIconProps) { return ( ); diff --git a/packages/canonical-bridge-widget/src/modules/transfer/BridgeTransfer.tsx b/packages/canonical-bridge-widget/src/modules/transfer/BridgeTransfer.tsx index 62f5f6f9..d38d81c6 100644 --- a/packages/canonical-bridge-widget/src/modules/transfer/BridgeTransfer.tsx +++ b/packages/canonical-bridge-widget/src/modules/transfer/BridgeTransfer.tsx @@ -10,6 +10,7 @@ import { ToAccount } from '@/modules/transfer/components/ToAccount'; import { SvgDefs } from '@/core/components/icons/defs.tsx'; import { useAppDispatch } from '@/modules/store/StoreProvider'; import { setIsRoutesModalOpen } from '@/modules/transfer/action'; +import { TransferWarningMessage } from '@/modules/transfer/components/TransferWarningMessage'; export function BridgeTransfer() { const { colorMode } = useColorMode(); @@ -57,6 +58,7 @@ export function BridgeTransfer() { dispatch(setIsRoutesModalOpen(true))} /> + {routeContentBottom && ( {routeContentBottom} diff --git a/packages/canonical-bridge-widget/src/modules/transfer/components/TransferWarningMessage/index.tsx b/packages/canonical-bridge-widget/src/modules/transfer/components/TransferWarningMessage/index.tsx new file mode 100644 index 00000000..8c845c48 --- /dev/null +++ b/packages/canonical-bridge-widget/src/modules/transfer/components/TransferWarningMessage/index.tsx @@ -0,0 +1,38 @@ +import { Flex, theme, useColorMode } from '@bnb-chain/space'; +import { rgba } from 'polished'; + +import { MIN_SOL_TO_ENABLED_TX } from '@/core/constants'; +import { InfoIcon } from '@/core/components/icons/InfoIcon'; +import { useAppSelector } from '@/modules/store/StoreProvider'; +import { useSolanaBalance } from '@/modules/wallet/hooks/useSolanaBalance'; + +export const TransferWarningMessage = () => { + const { colorMode } = useColorMode(); + const { data } = useSolanaBalance(); + const solBalance = Number(data?.formatted); + const fromChain = useAppSelector((state) => state.transfer.fromChain); + + if (fromChain?.chainType === 'solana' && solBalance < MIN_SOL_TO_ENABLED_TX) { + return ( + + + {`At least ${MIN_SOL_TO_ENABLED_TX} SOL is required to proceed with this transaction.`} + + ); + } + return null; +}; 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 27044a96..11e56f89 100644 --- a/packages/canonical-bridge-widget/src/modules/transfer/hooks/useInputValidation.ts +++ b/packages/canonical-bridge-widget/src/modules/transfer/hooks/useInputValidation.ts @@ -3,16 +3,10 @@ import { BridgeType } from '@bnb-chain/canonical-bridge-sdk'; import { useCallback } from 'react'; import { formatNumber } from '@/core/utils/number'; -import { useAppSelector } from '@/modules/store/StoreProvider'; -import { useSolanaBalance } from '@/modules/wallet/hooks/useSolanaBalance'; -import { MIN_SOL_TO_ENABLED_TX } from '@/core/constants'; import { useIsWalletCompatible } from '@/modules/wallet/hooks/useIsWalletCompatible'; export const useInputValidation = () => { - const { data } = useSolanaBalance(); const isWalletCompatible = useIsWalletCompatible(); - const solBalance = Number(data?.formatted); - const fromChain = useAppSelector((state) => state.transfer.fromChain); const validateInput = useCallback( ({ balance, @@ -32,15 +26,18 @@ export const useInputValidation = () => { if (!decimal || !value) { return null; } + // check if send amount is smaller than lowest possible token amount if (Number(value) < Math.pow(10, -decimal)) { return { text: `The amount is too small. Please enter a valid amount to transfer.`, isError: true, }; } + // check if send amount is greater than token balance if (!!balance && value > balance) { return { text: `You have insufficient balance`, isError: true }; } + // check Stargate max amount if (estimatedAmount?.stargate && bridgeType === 'stargate' && value) { const stargateMax = formatUnits(estimatedAmount.stargate[0].maxAmountLD, decimal); if (value > Number(stargateMax)) { @@ -52,14 +49,7 @@ export const useInputValidation = () => { } if (!!balance) { - if (fromChain?.chainType === 'solana' && solBalance < MIN_SOL_TO_ENABLED_TX) { - return { - text: `You should have at least ${MIN_SOL_TO_ENABLED_TX} SOL in your balance to perform this trade.`, - isError: true, - }; - } else { - return { text: `${formatNumber(balance)}`, isError: false }; - } + return { text: `${formatNumber(balance)}`, isError: false }; } else if (isWalletCompatible) { return { isError: true, text: 'You have insufficient balance' }; } @@ -68,7 +58,7 @@ export const useInputValidation = () => { console.log(e); } }, - [fromChain?.chainType, solBalance, isWalletCompatible], + [isWalletCompatible], ); return { From 619bce98fbc127f58e39e7bd20302e6625cf48f7 Mon Sep 17 00:00:00 2001 From: chris Date: Thu, 2 Jan 2025 14:49:32 +1000 Subject: [PATCH 10/20] fix: Disable button when SOL is not enough --- .../transfer/hooks/useInputValidation.ts | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) 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 11e56f89..e8313224 100644 --- a/packages/canonical-bridge-widget/src/modules/transfer/hooks/useInputValidation.ts +++ b/packages/canonical-bridge-widget/src/modules/transfer/hooks/useInputValidation.ts @@ -3,10 +3,16 @@ import { BridgeType } from '@bnb-chain/canonical-bridge-sdk'; import { useCallback } from 'react'; import { formatNumber } from '@/core/utils/number'; +import { useAppSelector } from '@/modules/store/StoreProvider'; +import { useSolanaBalance } from '@/modules/wallet/hooks/useSolanaBalance'; +import { MIN_SOL_TO_ENABLED_TX } from '@/core/constants'; import { useIsWalletCompatible } from '@/modules/wallet/hooks/useIsWalletCompatible'; export const useInputValidation = () => { + const { data } = useSolanaBalance(); const isWalletCompatible = useIsWalletCompatible(); + const solBalance = Number(data?.formatted); + const fromChain = useAppSelector((state) => state.transfer.fromChain); const validateInput = useCallback( ({ balance, @@ -49,7 +55,14 @@ export const useInputValidation = () => { } if (!!balance) { - return { text: `${formatNumber(balance)}`, isError: false }; + if (fromChain?.chainType === 'solana' && solBalance < MIN_SOL_TO_ENABLED_TX) { + return { + text: ``, // Error message has been moved to send button section + isError: true, + }; + } else { + return { text: `${formatNumber(balance)}`, isError: false }; + } } else if (isWalletCompatible) { return { isError: true, text: 'You have insufficient balance' }; } @@ -58,7 +71,7 @@ export const useInputValidation = () => { console.log(e); } }, - [isWalletCompatible], + [fromChain?.chainType, solBalance, isWalletCompatible], ); return { From cc5c7edaa7ae6256786eb4d4270f146608eee024 Mon Sep 17 00:00:00 2001 From: chris Date: Thu, 2 Jan 2025 14:51:18 +1000 Subject: [PATCH 11/20] chore: Add sol warning message into multi-language settings --- packages/canonical-bridge-widget/src/core/locales/en.ts | 5 +++++ .../transfer/components/TransferWarningMessage/index.tsx | 6 ++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/canonical-bridge-widget/src/core/locales/en.ts b/packages/canonical-bridge-widget/src/core/locales/en.ts index ccda9559..d3abeb27 100644 --- a/packages/canonical-bridge-widget/src/core/locales/en.ts +++ b/packages/canonical-bridge-widget/src/core/locales/en.ts @@ -63,6 +63,9 @@ export const en = { 'transfer.button.wallet-connect': 'Connect Wallet', 'transfer.button.switch-wallet': 'Switch Wallet', + 'transfer.warning.sol.balance': + 'At least {min} SOL is required to proceed with this transaction.', + 'modal.approve.title': 'Approve Token', 'modal.approve.desc.1': 'Please approve at least ', 'modal.approve.desc.2': ' and initiate the transfer', @@ -81,6 +84,8 @@ export const en = { 'modal.confirm.title': 'Waiting for Confirmation', 'modal.confirm.desc': 'Confirm this transaction in your wallet', + 'modal.summary.title': 'Confirm Transaction', + 'select-modal.tag.incompatible': 'Incompatible', 'select-modal.search.no-result.title': 'No result found', 'select-modal.search.no-result.warning': diff --git a/packages/canonical-bridge-widget/src/modules/transfer/components/TransferWarningMessage/index.tsx b/packages/canonical-bridge-widget/src/modules/transfer/components/TransferWarningMessage/index.tsx index 8c845c48..fa894b50 100644 --- a/packages/canonical-bridge-widget/src/modules/transfer/components/TransferWarningMessage/index.tsx +++ b/packages/canonical-bridge-widget/src/modules/transfer/components/TransferWarningMessage/index.tsx @@ -1,4 +1,4 @@ -import { Flex, theme, useColorMode } from '@bnb-chain/space'; +import { Flex, theme, useColorMode, useIntl } from '@bnb-chain/space'; import { rgba } from 'polished'; import { MIN_SOL_TO_ENABLED_TX } from '@/core/constants'; @@ -12,6 +12,8 @@ export const TransferWarningMessage = () => { const solBalance = Number(data?.formatted); const fromChain = useAppSelector((state) => state.transfer.fromChain); + const { formatMessage } = useIntl(); + if (fromChain?.chainType === 'solana' && solBalance < MIN_SOL_TO_ENABLED_TX) { return ( { fontSize={'12px'} fontWeight={400} /> - {`At least ${MIN_SOL_TO_ENABLED_TX} SOL is required to proceed with this transaction.`} + {formatMessage({ id: 'transfer.warning.sol.balance' }, { min: MIN_SOL_TO_ENABLED_TX })} ); } From c8d2da84428cb0f8d1a35676b998a42f7a424bb7 Mon Sep 17 00:00:00 2001 From: chris Date: Thu, 2 Jan 2025 18:09:44 +1000 Subject: [PATCH 12/20] refactor: Refactor route fee components --- .../cBridge/components/CBridgeOption.tsx | 8 +--- .../deBridge/components/DeBridgeOption.tsx | 8 +--- .../layerZero/components/LayerZeroOption.tsx | 8 +--- .../adapters/meson/components/MesonOption.tsx | 8 +--- .../stargate/components/StarGateOption.tsx | 8 +--- .../transfer/components/ReceiveInfo/index.tsx | 6 +-- .../TransferOverview/RouteInfo/FeesInfo.tsx | 37 +++++++++++++++---- 7 files changed, 36 insertions(+), 47 deletions(-) diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/cBridge/components/CBridgeOption.tsx b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/cBridge/components/CBridgeOption.tsx index 05755e7b..7914ddba 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/cBridge/components/CBridgeOption.tsx +++ b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/cBridge/components/CBridgeOption.tsx @@ -25,7 +25,6 @@ export const CBridgeOption = () => { const estimatedAmount = useAppSelector((state) => state.transfer.estimatedAmount); const sendValue = useAppSelector((state) => state.transfer.sendValue); const routeError = useAppSelector((state) => state.transfer.routeError); - const routeFees = useAppSelector((state) => state.transfer.routeFees); const receiveAmt = useMemo(() => { return estimatedAmount && @@ -89,12 +88,7 @@ export const CBridgeOption = () => { toTokenInfo={toTokenInfo?.['cBridge']} /> - + { const transferActionInfo = useAppSelector((state) => state.transfer.transferActionInfo); const estimatedAmount = useAppSelector((state) => state.transfer.estimatedAmount); const routeError = useAppSelector((state) => state.transfer.routeError); - const routeFees = useAppSelector((state) => state.transfer.routeFees); const receiveAmt = useMemo(() => { return estimatedAmount?.['deBridge'] && @@ -79,12 +78,7 @@ export const DeBridgeOption = ({}: DeBridgeOptionProps) => { toTokenInfo={toTokenInfo?.['deBridge']} /> - + ); diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/layerZero/components/LayerZeroOption.tsx b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/layerZero/components/LayerZeroOption.tsx index 61c3cdd3..bdf8638e 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/layerZero/components/LayerZeroOption.tsx +++ b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/layerZero/components/LayerZeroOption.tsx @@ -22,7 +22,6 @@ export const LayerZeroOption = () => { const transferActionInfo = useAppSelector((state) => state.transfer.transferActionInfo); const estimatedAmount = useAppSelector((state) => state.transfer.estimatedAmount); const routeError = useAppSelector((state) => state.transfer.routeError); - const routeFees = useAppSelector((state) => state.transfer.routeFees); const receiveAmt = useMemo(() => { return estimatedAmount && @@ -71,12 +70,7 @@ export const LayerZeroOption = () => { toTokenInfo={toTokenInfo?.['layerZero']} /> - + ); diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/meson/components/MesonOption.tsx b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/meson/components/MesonOption.tsx index c2607ad4..f4e5e692 100644 --- a/packages/canonical-bridge-widget/src/modules/aggregator/adapters/meson/components/MesonOption.tsx +++ b/packages/canonical-bridge-widget/src/modules/aggregator/adapters/meson/components/MesonOption.tsx @@ -19,7 +19,6 @@ export const MesonOption = () => { const transferActionInfo = useAppSelector((state) => state.transfer.transferActionInfo); const estimatedAmount = useAppSelector((state) => state.transfer.estimatedAmount); const routeError = useAppSelector((state) => state.transfer.routeError); - const routeFees = useAppSelector((state) => state.transfer.routeFees); const fromChain = useAppSelector((state) => state.transfer.fromChain); const receiveAmt = useMemo(() => { @@ -55,12 +54,7 @@ export const MesonOption = () => { - + {/* { const transferActionInfo = useAppSelector((state) => state.transfer.transferActionInfo); const estimatedAmount = useAppSelector((state) => state.transfer.estimatedAmount); const routeError = useAppSelector((state) => state.transfer.routeError); - const routeFees = useAppSelector((state) => state.transfer.routeFees); const receiveAmt = useMemo(() => { return estimatedAmount && @@ -79,12 +78,7 @@ export const StarGateOption = () => { toTokenInfo={toTokenInfo?.['stargate']} /> - + { )} - + { +export const FeesInfo = ({ bridgeType, isError }: FeesInfoProps) => { const theme = useTheme(); const { colorMode } = useColorMode(); const { formatMessage } = useIntl(); + + const routeFees = useAppSelector((state) => state.transfer.routeFees); + + const feeDetails = useMemo(() => { + let feeContent = ''; + const feeBreakdown = []; + if (bridgeType === 'cBridge' && routeFees?.['cBridge']) { + feeContent = routeFees?.['cBridge'].summary; + feeBreakdown.push(...routeFees?.['cBridge'].breakdown); + } else if (bridgeType === 'deBridge' && routeFees?.['deBridge']) { + feeContent = routeFees?.['deBridge'].summary; + feeBreakdown.push(...routeFees?.['deBridge'].breakdown); + } else if (bridgeType === 'stargate' && routeFees?.['stargate']) { + feeContent = routeFees?.['stargate'].summary; + feeBreakdown.push(...routeFees?.['stargate'].breakdown); + } else if (bridgeType === 'layerZero' && routeFees?.['layerZero']) { + feeContent = routeFees?.['layerZero'].summary; + feeBreakdown.push(...routeFees?.['layerZero'].breakdown); + } else if (bridgeType === 'meson' && routeFees?.['meson']) { + feeContent = routeFees?.['meson'].summary; + feeBreakdown.push(...routeFees?.['meson'].breakdown); + } + return { summary: feeContent ? feeContent : '--', breakdown: feeBreakdown }; + }, [bridgeType, routeFees]); return ( - {summary} + {feeDetails.summary} 0 - ? breakdown.map((fee, index) => { + feeDetails.breakdown && feeDetails.breakdown?.length > 0 + ? feeDetails.breakdown.map((fee, index) => { return fee.value !== '0' && fee.value !== null ? ( Date: Thu, 2 Jan 2025 20:33:36 +1000 Subject: [PATCH 13/20] chore: Update warning message components --- .../src/core/locales/en.ts | 2 ++ .../src/modules/transfer/BridgeTransfer.tsx | 2 -- .../transfer/components/ReceiveInfo/index.tsx | 23 ------------ .../TransferWarningMessage/WarningMessage.tsx | 33 +++++++++++++++++ .../TransferWarningMessage/index.tsx | 35 +++++-------------- 5 files changed, 43 insertions(+), 52 deletions(-) create mode 100644 packages/canonical-bridge-widget/src/modules/transfer/components/TransferWarningMessage/WarningMessage.tsx diff --git a/packages/canonical-bridge-widget/src/core/locales/en.ts b/packages/canonical-bridge-widget/src/core/locales/en.ts index d3abeb27..8395915d 100644 --- a/packages/canonical-bridge-widget/src/core/locales/en.ts +++ b/packages/canonical-bridge-widget/src/core/locales/en.ts @@ -62,7 +62,9 @@ export const en = { 'transfer.button.switch-network': 'Switch Network in Wallet', 'transfer.button.wallet-connect': 'Connect Wallet', 'transfer.button.switch-wallet': 'Switch Wallet', + 'transfer.button.confirm-summary': 'Confirm Transfer', + 'transfer.warning.confirm.to.address': 'Please double check the received token address:', 'transfer.warning.sol.balance': 'At least {min} SOL is required to proceed with this transaction.', diff --git a/packages/canonical-bridge-widget/src/modules/transfer/BridgeTransfer.tsx b/packages/canonical-bridge-widget/src/modules/transfer/BridgeTransfer.tsx index d38d81c6..62f5f6f9 100644 --- a/packages/canonical-bridge-widget/src/modules/transfer/BridgeTransfer.tsx +++ b/packages/canonical-bridge-widget/src/modules/transfer/BridgeTransfer.tsx @@ -10,7 +10,6 @@ import { ToAccount } from '@/modules/transfer/components/ToAccount'; import { SvgDefs } from '@/core/components/icons/defs.tsx'; import { useAppDispatch } from '@/modules/store/StoreProvider'; import { setIsRoutesModalOpen } from '@/modules/transfer/action'; -import { TransferWarningMessage } from '@/modules/transfer/components/TransferWarningMessage'; export function BridgeTransfer() { const { colorMode } = useColorMode(); @@ -58,7 +57,6 @@ export function BridgeTransfer() { dispatch(setIsRoutesModalOpen(true))} /> - {routeContentBottom && ( {routeContentBottom} diff --git a/packages/canonical-bridge-widget/src/modules/transfer/components/ReceiveInfo/index.tsx b/packages/canonical-bridge-widget/src/modules/transfer/components/ReceiveInfo/index.tsx index e17de947..1890ae79 100644 --- a/packages/canonical-bridge-widget/src/modules/transfer/components/ReceiveInfo/index.tsx +++ b/packages/canonical-bridge-widget/src/modules/transfer/components/ReceiveInfo/index.tsx @@ -52,7 +52,6 @@ export const ReceiveInfo = ({ onOpen }: ReceiveInfoProps) => { const isGlobalFeeLoading = useAppSelector((state) => state.transfer.isGlobalFeeLoading); const sendValue = useAppSelector((state) => state.transfer.sendValue); const selectedToken = useAppSelector((state) => state.transfer.selectedToken); - const routeFees = useAppSelector((state) => state.transfer.routeFees); const estimatedAmount = useAppSelector((state) => state.transfer.estimatedAmount); const isBase = useBreakpointValue({ base: true, lg: false }) ?? false; @@ -73,28 +72,6 @@ export const ReceiveInfo = ({ onOpen }: ReceiveInfoProps) => { const { allowedSendAmount: STAllowedSendAmount, isAllowSendError: STIsAllowSendError } = useGetStargateFees(); - const feeDetails = useMemo(() => { - let feeContent = ''; - const feeBreakdown = []; - if (bridgeType === 'cBridge' && routeFees?.['cBridge']) { - feeContent = routeFees?.['cBridge'].summary; - feeBreakdown.push(...routeFees?.['cBridge'].breakdown); - } else if (bridgeType === 'deBridge' && routeFees?.['deBridge']) { - feeContent = routeFees?.['deBridge'].summary; - feeBreakdown.push(...routeFees?.['deBridge'].breakdown); - } else if (bridgeType === 'stargate' && routeFees?.['stargate']) { - feeContent = routeFees?.['stargate'].summary; - feeBreakdown.push(...routeFees?.['stargate'].breakdown); - } else if (bridgeType === 'layerZero' && routeFees?.['layerZero']) { - feeContent = routeFees?.['layerZero'].summary; - feeBreakdown.push(...routeFees?.['layerZero'].breakdown); - } else if (bridgeType === 'meson' && routeFees?.['meson']) { - feeContent = routeFees?.['meson'].summary; - feeBreakdown.push(...routeFees?.['meson'].breakdown); - } - return { summary: feeContent ? feeContent : '--', breakdown: feeBreakdown }; - }, [bridgeType, routeFees]); - const allowedAmtContent = useMemo(() => { if (cBridgeAllowedAmt && transferActionInfo?.bridgeType === 'cBridge') { return cBridgeAllowedAmt; diff --git a/packages/canonical-bridge-widget/src/modules/transfer/components/TransferWarningMessage/WarningMessage.tsx b/packages/canonical-bridge-widget/src/modules/transfer/components/TransferWarningMessage/WarningMessage.tsx new file mode 100644 index 00000000..dbfd888f --- /dev/null +++ b/packages/canonical-bridge-widget/src/modules/transfer/components/TransferWarningMessage/WarningMessage.tsx @@ -0,0 +1,33 @@ +import { Flex, useColorMode, useTheme } from '@bnb-chain/space'; +import { rgba } from 'polished'; + +import { InfoIcon } from '@/core/components/icons/InfoIcon'; + +export const WarningMessage = ({ text, ...restProps }: { text: React.ReactNode }) => { + const { colorMode } = useColorMode(); + const theme = useTheme(); + return ( + + + {text} + + ); +}; diff --git a/packages/canonical-bridge-widget/src/modules/transfer/components/TransferWarningMessage/index.tsx b/packages/canonical-bridge-widget/src/modules/transfer/components/TransferWarningMessage/index.tsx index fa894b50..15c32f46 100644 --- a/packages/canonical-bridge-widget/src/modules/transfer/components/TransferWarningMessage/index.tsx +++ b/packages/canonical-bridge-widget/src/modules/transfer/components/TransferWarningMessage/index.tsx @@ -1,40 +1,21 @@ -import { Flex, theme, useColorMode, useIntl } from '@bnb-chain/space'; -import { rgba } from 'polished'; +import React from 'react'; import { MIN_SOL_TO_ENABLED_TX } from '@/core/constants'; -import { InfoIcon } from '@/core/components/icons/InfoIcon'; import { useAppSelector } from '@/modules/store/StoreProvider'; import { useSolanaBalance } from '@/modules/wallet/hooks/useSolanaBalance'; +import { WarningMessage } from '@/modules/transfer/components/TransferWarningMessage/WarningMessage'; -export const TransferWarningMessage = () => { - const { colorMode } = useColorMode(); +interface ITransferWarningMessageProps { + text: React.ReactNode; +} + +export const TransferWarningMessage = ({ text, ...restProps }: ITransferWarningMessageProps) => { const { data } = useSolanaBalance(); const solBalance = Number(data?.formatted); const fromChain = useAppSelector((state) => state.transfer.fromChain); - const { formatMessage } = useIntl(); - if (fromChain?.chainType === 'solana' && solBalance < MIN_SOL_TO_ENABLED_TX) { - return ( - - - {formatMessage({ id: 'transfer.warning.sol.balance' }, { min: MIN_SOL_TO_ENABLED_TX })} - - ); + return ; } return null; }; From 5f5b7ecddba57fe9d26761432229058c959a88b0 Mon Sep 17 00:00:00 2001 From: chris Date: Fri, 3 Jan 2025 07:20:56 +1000 Subject: [PATCH 14/20] feat: Transfer summary moal --- .../aggregator/hooks/useHandleTxFailure.ts | 41 ++ .../components/Button/TransferButton.tsx | 467 +------------- .../Button/TransferConfirmButton.tsx | 569 ++++++++++++++++++ .../TransactionSummaryModal/FeeSummary.tsx | 21 + .../TransactionSummaryModal/TokenInfo.tsx | 49 ++ .../TransferSummary.tsx | 83 +++ .../Modal/TransactionSummaryModal/index.tsx | 104 ++++ .../components/TransferButtonGroup/index.tsx | 32 +- 8 files changed, 911 insertions(+), 455 deletions(-) create mode 100644 packages/canonical-bridge-widget/src/modules/aggregator/hooks/useHandleTxFailure.ts create mode 100644 packages/canonical-bridge-widget/src/modules/transfer/components/Button/TransferConfirmButton.tsx create mode 100644 packages/canonical-bridge-widget/src/modules/transfer/components/Modal/TransactionSummaryModal/FeeSummary.tsx create mode 100644 packages/canonical-bridge-widget/src/modules/transfer/components/Modal/TransactionSummaryModal/TokenInfo.tsx create mode 100644 packages/canonical-bridge-widget/src/modules/transfer/components/Modal/TransactionSummaryModal/TransferSummary.tsx create mode 100644 packages/canonical-bridge-widget/src/modules/transfer/components/Modal/TransactionSummaryModal/index.tsx diff --git a/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useHandleTxFailure.ts b/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useHandleTxFailure.ts new file mode 100644 index 00000000..89655e2e --- /dev/null +++ b/packages/canonical-bridge-widget/src/modules/aggregator/hooks/useHandleTxFailure.ts @@ -0,0 +1,41 @@ +import { useCallback } from 'react'; + +import { reportEvent } from '@/core/utils/gtm'; +import { useAppSelector } from '@/modules/store/StoreProvider'; + +export const useHandleTxFailure = ({ onOpenFailedModal }: { onOpenFailedModal: () => void }) => { + const fromChain = useAppSelector((state) => state.transfer.fromChain); + const toChain = useAppSelector((state) => state.transfer.toChain); + const selectedToken = useAppSelector((state) => state.transfer.selectedToken); + const sendValue = useAppSelector((state) => state.transfer.sendValue); + const transferActionInfo = useAppSelector((state) => state.transfer.transferActionInfo); + + const handleFailure = useCallback( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (e: any) => { + reportEvent({ + id: 'transaction_bridge_fail', + params: { + item_category: fromChain?.name, + item_category2: toChain?.name, + token: selectedToken?.displaySymbol, + value: sendValue, + item_variant: transferActionInfo?.bridgeType, + message: JSON.stringify(e.message || e), + page_location: JSON.stringify(e.message || e), + }, + }); + onOpenFailedModal(); + }, + [ + fromChain?.name, + onOpenFailedModal, + selectedToken?.displaySymbol, + sendValue, + toChain?.name, + transferActionInfo?.bridgeType, + ], + ); + + return { handleFailure }; +}; diff --git a/packages/canonical-bridge-widget/src/modules/transfer/components/Button/TransferButton.tsx b/packages/canonical-bridge-widget/src/modules/transfer/components/Button/TransferButton.tsx index c7273459..6b51a0a0 100644 --- a/packages/canonical-bridge-widget/src/modules/transfer/components/Button/TransferButton.tsx +++ b/packages/canonical-bridge-widget/src/modules/transfer/components/Button/TransferButton.tsx @@ -1,71 +1,50 @@ /* eslint-disable no-console */ import { Button, Flex, useColorMode, useIntl, useTheme } from '@bnb-chain/space'; import { useCallback, useState } from 'react'; -import { useAccount, useBytecode, usePublicClient, useSignMessage, useWalletClient } from 'wagmi'; -import { formatUnits, parseUnits } from 'viem'; +import { useAccount, useBytecode, usePublicClient, useWalletClient } from 'wagmi'; +import { formatUnits } from 'viem'; import { useWallet as useTronWallet } from '@tronweb3/tronwallet-adapter-react-hooks'; -import { useConnection } from '@solana/wallet-adapter-react'; -import { useWallet as useSolanaWallet } from '@solana/wallet-adapter-react'; -import { VersionedTransaction } from '@solana/web3.js'; import { useAppSelector } from '@/modules/store/StoreProvider'; import { useGetAllowance } from '@/core/contract/hooks/useGetAllowance'; -import { useCBridgeTransferParams } from '@/modules/aggregator/adapters/cBridge/hooks/useCBridgeTransferParams'; -import { useBridgeSDK } from '@/core/hooks/useBridgeSDK'; import { reportEvent } from '@/core/utils/gtm'; import { useGetTronAllowance } from '@/modules/aggregator/adapters/meson/hooks/useGetTronAllowance'; import { useTronTransferInfo } from '@/modules/transfer/hooks/tron/useTronTransferInfo'; -import { utf8ToHex } from '@/core/utils/string'; import { useTronContract } from '@/modules/aggregator/adapters/meson/hooks/useTronContract'; import { useSolanaTransferInfo } from '@/modules/transfer/hooks/solana/useSolanaTransferInfo'; import { useTronAccount } from '@/modules/wallet/hooks/useTronAccount'; -import { useWaitForTxReceipt } from '@/core/hooks/useWaitForTxReceipt'; -import { - CBRIDGE_ENDPOINT, - DEBRIDGE_ENDPOINT, - MESON_ENDPOINT, - STARGATE_ENDPOINT, -} from '@/core/constants'; +import { useHandleTxFailure } from '@/modules/aggregator/hooks/useHandleTxFailure'; export function TransferButton({ - onOpenSubmittedModal, onOpenFailedModal, onOpenApproveModal, - onOpenConfirmingModal, + onOpenSummaryModal, onCloseConfirmingModal, - setHash, setChosenBridge, }: { - onOpenSubmittedModal: () => void; onOpenFailedModal: () => void; onOpenApproveModal: () => void; - onOpenConfirmingModal: () => void; + onOpenSummaryModal: () => void; onCloseConfirmingModal: () => void; - setHash: (hash: string | null) => void; + setChosenBridge: (bridge: string | null) => void; }) { const { data: walletClient } = useWalletClient(); - const { args: cBridgeArgs } = useCBridgeTransferParams(); - const bridgeSDK = useBridgeSDK(); const { formatMessage } = useIntl(); const theme = useTheme(); const { colorMode } = useColorMode(); const { address } = useAccount(); - const { address: tronAddress, signTransaction } = useTronWallet(); + const { address: tronAddress } = useTronWallet(); const { isTronAvailableToAccount, isTronTransfer } = useTronTransferInfo(); - const { signMessageAsync } = useSignMessage(); const { isSolanaTransfer, isSolanaAvailableToAccount } = useSolanaTransferInfo(); - const { connection } = useConnection(); - const { sendTransaction: sendSolanaTransaction } = useSolanaWallet(); const sendValue = useAppSelector((state) => state.transfer.sendValue); const transferActionInfo = useAppSelector((state) => state.transfer.transferActionInfo); const selectedToken = useAppSelector((state) => state.transfer.selectedToken); const isGlobalFeeLoading = useAppSelector((state) => state.transfer.isGlobalFeeLoading); const isTransferable = useAppSelector((state) => state.transfer.isTransferable); - const toToken = useAppSelector((state) => state.transfer.toToken); const fromChain = useAppSelector((state) => state.transfer.fromChain); const toChain = useAppSelector((state) => state.transfer.toChain); const toAccount = useAppSelector((state) => state.transfer.toAccount); @@ -73,7 +52,6 @@ export function TransferButton({ // eslint-disable-next-line @typescript-eslint/no-explicit-any const publicClient = usePublicClient({ chainId: fromChain?.id }) as any; - const toPublicClient = usePublicClient({ chainId: toChain?.id }) as any; const [isLoading, setIsLoading] = useState(false); const { allowance } = useGetAllowance({ @@ -86,10 +64,11 @@ export function TransferButton({ chainId: toChain?.id, }); + const { handleFailure } = useHandleTxFailure({ onOpenFailedModal }); + const tronAllowance = useGetTronAllowance(); const { isConnected: isEvmConnected } = useAccount(); const { isConnected: isTronConnected } = useTronAccount(); - const { waitForTxReceipt } = useWaitForTxReceipt(); const isApproveNeeded = (fromChain?.chainType === 'evm' && @@ -104,7 +83,7 @@ export function TransferButton({ Number(formatUnits(tronAllowance, selectedToken?.meson?.raw?.decimals || 6))) || (fromChain?.chainType === 'solana' && false); - const sendTx = useCallback(async () => { + const onConfirmSummary = useCallback(async () => { if ( !selectedToken || !transferActionInfo?.bridgeType || @@ -122,24 +101,7 @@ export function TransferButton({ ) { return; } - const handleFailure = (e: any) => { - reportEvent({ - id: 'transaction_bridge_fail', - params: { - item_category: fromChain?.name, - item_category2: toChain?.name, - token: selectedToken.displaySymbol, - value: sendValue, - item_variant: transferActionInfo?.bridgeType, - message: JSON.stringify(e.message || e), - page_location: JSON.stringify(e.message || e), - }, - }); - onOpenFailedModal(); - }; - try { - setHash(null); setChosenBridge(''); setIsLoading(true); if ( @@ -167,379 +129,8 @@ export function TransferButton({ return; } - onOpenConfirmingModal(); - - reportEvent({ - id: 'click_bridge_goal', - params: { - item_name: 'Send', - }, - }); - - if (transferActionInfo.bridgeType === 'cBridge' && cBridgeArgs && fromChain && address) { - try { - const isValidToken = await bridgeSDK.cBridge.validateCBridgeToken({ - isPegged: selectedToken.isPegged, - fromChainId: fromChain.id, - fromTokenAddress: selectedToken?.cBridge?.raw?.token.address as `0x${string}`, - fromTokenSymbol: selectedToken?.cBridge?.raw?.token?.symbol as string, - fromTokenDecimals: selectedToken.cBridge?.raw?.token.decimal as number, - bridgeAddress: transferActionInfo.bridgeAddress as `0x${string}`, - toChainId: toChain?.id, - toTokenAddress: toToken?.cBridge?.raw?.token.address as `0x${string}`, - toTokenSymbol: toToken?.cBridge?.raw?.token.symbol, - toTokenDecimals: toToken?.cBridge?.raw?.token.decimal as number, - amount: Number(sendValue), - cBridgeEndpoint: `${CBRIDGE_ENDPOINT}/getTransferConfigsForAll`, - }); - - if (!isValidToken) { - handleFailure({ - fromTokenAddress: selectedToken.address as `0x${string}`, - bridgeAddress: transferActionInfo.bridgeAddress as `0x${string}`, - fromChainId: fromChain.id, - isPegged: selectedToken.isPegged, - fromTokenSymbol: selectedToken.symbol, - toChainId: toChain?.id, - toTokenAddress: toToken?.cBridge?.raw?.token.address as `0x${string}`, - toTokenSymbol: toToken?.cBridge?.raw?.token.symbol, - decimals: selectedToken.decimals, - amount: Number(sendValue), - message: `(Token Validation Failed) - Invalid cBridge token!!`, - }); - return; - } - const cBridgeHash = await bridgeSDK.cBridge.sendToken({ - // eslint-disable-next-line @typescript-eslint/no-explicit-any - walletClient: walletClient as any, - publicClient, - bridgeAddress: transferActionInfo.bridgeAddress as string, - fromChainId: fromChain?.id, - isPegged: selectedToken.isPegged, - address, - peggedConfig: selectedToken?.cBridge?.peggedConfig, - args: cBridgeArgs.args, - }); - await waitForTxReceipt({ - publicClient, - hash: cBridgeHash, - }); - if (cBridgeHash) { - reportEvent({ - id: 'transaction_bridge_success', - params: { - item_category: fromChain?.name, - item_category2: toChain?.name, - token: selectedToken.displaySymbol, - value: sendValue, - item_variant: 'cBridge', - }, - }); - onCloseConfirmingModal(); - setHash(cBridgeHash); - setChosenBridge('cBridge'); - onOpenSubmittedModal(); - } - // eslint-disable-next-line no-console - console.log('cBridge tx', cBridgeHash); - } catch (e) { - // eslint-disable-next-line no-console - console.log(e); - handleFailure(e); - } - } else if (transferActionInfo.bridgeType === 'deBridge') { - try { - let deBridgeHash: string | undefined; - const isValidToken = await bridgeSDK.deBridge.validateDeBridgeToken({ - fromChainId: fromChain?.id, - toChainId: toChain?.id, - fromTokenSymbol: selectedToken.symbol, - fromTokenAddress: selectedToken.deBridge?.raw?.address as `0x${string}`, - fromTokenDecimals: selectedToken.deBridge?.raw?.decimals as number, - toTokenSymbol: toToken?.deBridge?.raw?.symbol, - toTokenAddress: toToken?.deBridge?.raw?.address as `0x${string}`, - toTokenDecimals: toToken?.deBridge?.raw?.decimals as number, - amount: Number(sendValue), - fromChainType: fromChain?.chainType, - fromBridgeAddress: transferActionInfo.bridgeAddress as `0x${string}`, - toChainType: toChain?.chainType, - deBridgeEndpoint: DEBRIDGE_ENDPOINT, - }); - if (!isValidToken) { - handleFailure({ - message: '(Token Validation Failed) - Invalid deBridge token!!', - fromChainId: fromChain?.id, - tokenSymbol: selectedToken.symbol, - tokenAddress: selectedToken.address as `0x${string}`, - }); - return; - } - if (fromChain?.chainType === 'evm' && transferActionInfo.value && address) { - deBridgeHash = await bridgeSDK.deBridge.sendToken({ - // eslint-disable-next-line @typescript-eslint/no-explicit-any - walletClient: walletClient as any, - bridgeAddress: transferActionInfo.bridgeAddress as string, - data: transferActionInfo.data as `0x${string}`, - amount: BigInt(transferActionInfo.value), - address, - }); - await waitForTxReceipt({ - publicClient, - hash: deBridgeHash, - }); - } - - if (fromChain?.chainType === 'solana') { - const { blockhash } = await connection.getLatestBlockhash(); - const data = (transferActionInfo.data as string)?.slice(2); - const tx = VersionedTransaction.deserialize(Buffer.from(data, 'hex')); - - tx.message.recentBlockhash = blockhash; - deBridgeHash = await sendSolanaTransaction(tx, connection); - - console.log('---solana---'); - console.log('blockhash: ', blockhash); - console.log('data:', data); - console.log('tx:', tx); - console.log('hash:', deBridgeHash); - } - - if (deBridgeHash) { - reportEvent({ - id: 'transaction_bridge_success', - params: { - item_category: fromChain?.name, - item_category2: toChain?.name, - token: selectedToken.displaySymbol, - value: sendValue, - item_variant: 'deBridge', - }, - }); - onCloseConfirmingModal(); - setChosenBridge('deBridge'); - setHash(deBridgeHash); - onOpenSubmittedModal(); - } - } catch (e) { - // eslint-disable-next-line no-console - console.log(e); - handleFailure(e); - } - } else if (transferActionInfo.bridgeType === 'stargate' && address) { - const isValidToken = await bridgeSDK.stargate.validateStargateToken({ - fromBridgeAddress: transferActionInfo.bridgeAddress as `0x${string}`, - toBridgeAddress: toToken?.stargate?.raw?.address as `0x${string}`, - fromTokenAddress: selectedToken?.stargate?.raw?.token?.address as `0x${string}`, - fromTokenSymbol: selectedToken?.stargate?.raw?.token?.symbol as string, - fromTokenDecimals: selectedToken?.stargate?.raw?.token?.decimals as number, - fromChainId: fromChain?.id, - toTokenAddress: toToken?.stargate?.raw?.token?.address as `0x${string}`, - toTokenSymbol: toToken?.stargate?.raw?.token?.symbol as string, - toTokenDecimals: toToken?.stargate?.raw?.token?.decimals as number, - toChainId: toChain?.id, - amount: Number(sendValue), - dstEndpointId: toToken?.stargate?.raw?.endpointID as number, - toPublicClient, - fromPublicClient: publicClient, - stargateEndpoint: STARGATE_ENDPOINT, - }); - if (!isValidToken) { - handleFailure({ - messages: '(Token Validation Failed) - Invalid Stargate token!!', - fromChainId: fromChain?.id, - tokenAddress: selectedToken.address as `0x${string}`, - bridgeAddress: transferActionInfo.bridgeAddress as `0x${string}`, - tokenSymbol: selectedToken.symbol, - }); - return; - } - const stargateHash = await bridgeSDK.stargate.sendToken({ - // eslint-disable-next-line @typescript-eslint/no-explicit-any - walletClient: walletClient as any, - publicClient, - bridgeAddress: transferActionInfo.bridgeAddress as `0x${string}`, - tokenAddress: selectedToken.address as `0x${string}`, - endPointId: toToken?.stargate?.raw?.endpointID as number, - receiver: address, - amount: parseUnits(sendValue, selectedToken.decimals), - }); - if (stargateHash) { - reportEvent({ - id: 'transaction_bridge_success', - params: { - item_category: fromChain?.name, - item_category2: toChain?.name, - token: selectedToken.displaySymbol, - value: sendValue, - item_variant: 'stargate', - }, - }); - onCloseConfirmingModal(); - setChosenBridge('stargate'); - setHash(stargateHash); - onOpenSubmittedModal(); - } - } else if (transferActionInfo.bridgeType === 'layerZero' && address) { - // check layerZero token address - const isValidToken = await bridgeSDK.layerZero.validateLayerZeroToken({ - fromPublicClient: publicClient, - toPublicClient, - bridgeAddress: transferActionInfo.bridgeAddress as `0x${string}`, - fromTokenAddress: selectedToken.layerZero?.raw?.address as `0x${string}`, - fromTokenSymbol: selectedToken.layerZero?.raw?.symbol as string, - fromTokenDecimals: selectedToken.layerZero?.raw?.decimals as number, - toTokenAddress: toToken?.layerZero?.raw?.address as `0x${string}`, - toTokenDecimals: toToken?.layerZero?.raw?.decimals as number, - toTokenSymbol: toToken?.layerZero?.raw?.symbol as string, - toBridgeAddress: toToken?.layerZero?.raw?.bridgeAddress as `0x${string}`, - dstEndpoint: toToken?.layerZero?.raw?.endpointID as number, - amount: Number(sendValue), - }); - if (!isValidToken) { - handleFailure({ - messages: '(Token Validation Failed) - Invalid LayerZero token!!', - fromChainId: fromChain?.id, - tokenAddress: selectedToken.layerZero?.raw?.address as `0x${string}`, - bridgeAddress: transferActionInfo.bridgeAddress as `0x${string}`, - tokenSymbol: selectedToken.symbol, - }); - return; - } - const layerZeroHash = await bridgeSDK.layerZero.sendToken({ - bridgeAddress: transferActionInfo.bridgeAddress as `0x${string}`, - dstEndpoint: toToken?.layerZero?.raw?.endpointID as number, - userAddress: address, - amount: parseUnits(sendValue, selectedToken.decimals), - // eslint-disable-next-line @typescript-eslint/no-explicit-any - walletClient: walletClient as any, - publicClient, - }); - if (layerZeroHash) { - reportEvent({ - id: 'transaction_bridge_success', - params: { - item_category: fromChain?.name, - item_category2: toChain?.name, - token: selectedToken.displaySymbol, - value: sendValue, - item_variant: 'layerZero', - }, - }); - onCloseConfirmingModal(); - setChosenBridge('layerZero'); - setHash(layerZeroHash); - onOpenSubmittedModal(); - } - } else if (transferActionInfo.bridgeType === 'meson') { - const isValidToken = await bridgeSDK.meson.validateMesonToken({ - fromChainId: fromChain?.id, - toChainId: toChain?.id, - fromTokenAddress: - selectedToken.meson?.raw?.addr ?? '0x0000000000000000000000000000000000000000', - fromTokenSymbol: selectedToken.meson?.raw?.id as string, - fromTokenDecimals: selectedToken.meson?.raw?.decimals as number, - fromChainType: fromChain?.chainType, - toChainType: toChain?.chainType, - toTokenAddress: toToken?.meson?.raw?.addr ?? '0x0000000000000000000000000000000000000000', - toTokenSymbol: toToken?.meson?.raw?.id, - toTokenDecimals: toToken?.meson?.raw?.decimals, - amount: Number(sendValue), - mesonEndpoint: MESON_ENDPOINT, - }); - if (!isValidToken) { - handleFailure({ - message: '(Token Validation Failed) Invalid Meson token!!', - fromChainId: fromChain?.id, - tokenAddress: selectedToken.address as `0x${string}`, - tokenSymbol: selectedToken.symbol, - }); - return; - } - let fromAddress = ''; - let toAddress = ''; - let msg = ''; - let signature = ''; - - if (fromChain?.chainType === 'tron' && tronAddress) { - fromAddress = tronAddress; - } else if (fromChain?.chainType !== 'tron' && address) { - fromAddress = address; - } - - if (isTronTransfer && isTronAvailableToAccount && toAccount?.address) { - toAddress = toAccount.address; - } else if (address) { - toAddress = address; - } - - // get unsigned message - const unsignedMessage = await bridgeSDK.meson.getUnsignedMessage({ - fromToken: `${fromChain?.meson?.raw?.id}:${selectedToken?.meson?.raw?.id}`, - toToken: `${toChain?.meson?.raw?.id}:${toToken?.meson?.raw?.id}`, - amount: sendValue, - fromAddress: fromAddress, - recipient: toAddress, - }); - - if (unsignedMessage?.result) { - const result = unsignedMessage.result; - const encodedData = result.encoded; - const message = result.signingRequest.message; - - if (fromChain?.chainType === 'tron') { - const hexTronHeader = utf8ToHex('\x19TRON Signed Message:\n32'); - msg = message.replace(hexTronHeader, ''); - } else { - const hexEthHeader = utf8ToHex('\x19Ethereum Signed Message:\n52'); - msg = message.replace(hexEthHeader, ''); - } - - if (fromChain?.chainType != 'tron') { - signature = await signMessageAsync({ - account: address, - message: { - raw: msg as `0x${string}`, - }, - }); - } else { - // TODO - signature = String(await signTransaction(msg as any)); - } - - const swapId = await bridgeSDK.meson.sendToken({ - fromAddress: fromAddress, - recipient: toAddress, - signature: signature, - encodedData: encodedData, - }); - - // eslint-disable-next-line no-console - console.log('Meson swap id', swapId); - if (swapId?.result?.swapId) { - setChosenBridge('meson'); - setHash(swapId?.result?.swapId); - } - if (swapId?.error) { - throw new Error(swapId?.error.message); - } - - reportEvent({ - id: 'transaction_bridge_success', - params: { - item_category: fromChain?.name, - item_category2: toChain?.name, - token: selectedToken.displaySymbol, - value: sendValue, - item_variant: 'meson', - }, - }); - - onCloseConfirmingModal(); - onOpenSubmittedModal(); - } else { - throw new Error(unsignedMessage?.error.message); - } - } + onOpenSummaryModal(); + // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (e: any) { // eslint-disable-next-line no-console console.error(e, e.message); @@ -552,49 +143,23 @@ export function TransferButton({ selectedToken, transferActionInfo?.bridgeType, transferActionInfo?.bridgeAddress, - transferActionInfo?.value, - transferActionInfo?.data, fromChain, walletClient, publicClient, - toPublicClient, address, allowance, isEvmConnected, isTronConnected, tronAddress, tronAllowance, - toChain?.name, - toChain?.meson?.raw?.id, sendValue, - onOpenFailedModal, - setHash, + setChosenBridge, isApproveNeeded, - onOpenConfirmingModal, - cBridgeArgs, + onOpenSummaryModal, onOpenApproveModal, - bridgeSDK.cBridge, - bridgeSDK.deBridge, - bridgeSDK.stargate, - bridgeSDK.layerZero, - bridgeSDK.meson, onCloseConfirmingModal, - onOpenSubmittedModal, - connection, - sendSolanaTransaction, - toToken?.stargate?.raw, - toToken?.layerZero?.raw, - toToken?.meson?.raw, - toToken?.cBridge?.raw, - toToken?.deBridge?.raw, - toChain?.id, - toChain?.chainType, - isTronTransfer, - isTronAvailableToAccount, - toAccount.address, - signMessageAsync, - signTransaction, + handleFailure, ]); const isDisabled = @@ -627,7 +192,7 @@ export function TransferButton({ bg: theme.colors[colorMode].button.brand.hover, _disabled: { bg: theme.colors[colorMode].button.disabled }, }} - onClick={sendTx} + onClick={onConfirmSummary} isDisabled={isDisabled} > {isApproveNeeded 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 new file mode 100644 index 00000000..b82bff77 --- /dev/null +++ b/packages/canonical-bridge-widget/src/modules/transfer/components/Button/TransferConfirmButton.tsx @@ -0,0 +1,569 @@ +/* eslint-disable no-console */ +import { Button, Flex, useColorMode, useIntl, useTheme } from '@bnb-chain/space'; +import { useCallback, useState } from 'react'; +import { useAccount, usePublicClient, useSignMessage, useWalletClient } from 'wagmi'; +import { parseUnits } from 'viem'; +import { useWallet as useTronWallet } from '@tronweb3/tronwallet-adapter-react-hooks'; +import { useConnection } from '@solana/wallet-adapter-react'; +import { useWallet as useSolanaWallet } from '@solana/wallet-adapter-react'; +import { VersionedTransaction } from '@solana/web3.js'; + +import { useAppSelector } from '@/modules/store/StoreProvider'; +import { useGetAllowance } from '@/core/contract/hooks/useGetAllowance'; +import { useCBridgeTransferParams } from '@/modules/aggregator/adapters/cBridge/hooks/useCBridgeTransferParams'; +import { useBridgeSDK } from '@/core/hooks/useBridgeSDK'; +import { reportEvent } from '@/core/utils/gtm'; +import { useGetTronAllowance } from '@/modules/aggregator/adapters/meson/hooks/useGetTronAllowance'; +import { useTronTransferInfo } from '@/modules/transfer/hooks/tron/useTronTransferInfo'; +import { utf8ToHex } from '@/core/utils/string'; +import { useTronAccount } from '@/modules/wallet/hooks/useTronAccount'; +import { useWaitForTxReceipt } from '@/core/hooks/useWaitForTxReceipt'; +import { + CBRIDGE_ENDPOINT, + DEBRIDGE_ENDPOINT, + MESON_ENDPOINT, + STARGATE_ENDPOINT, +} from '@/core/constants'; +import { useHandleTxFailure } from '@/modules/aggregator/hooks/useHandleTxFailure'; + +export const TransferConfirmButton = ({ + onClose, + onOpenSubmittedModal, + onOpenFailedModal, + onOpenConfirmingModal, + onCloseConfirmingModal, + setHash, + setChosenBridge, +}: { + onClose: () => void; + onOpenSubmittedModal: () => void; + onOpenFailedModal: () => void; + onOpenConfirmingModal: () => void; + onCloseConfirmingModal: () => void; + setHash: (hash: string | null) => void; + setChosenBridge: (bridge: string | null) => void; +}) => { + const { data: walletClient } = useWalletClient(); + const { args: cBridgeArgs } = useCBridgeTransferParams(); + const bridgeSDK = useBridgeSDK(); + const { formatMessage } = useIntl(); + const theme = useTheme(); + const { colorMode } = useColorMode(); + + const { address } = useAccount(); + const { address: tronAddress, signTransaction } = useTronWallet(); + const { isTronAvailableToAccount, isTronTransfer } = useTronTransferInfo(); + const { signMessageAsync } = useSignMessage(); + const { handleFailure } = useHandleTxFailure({ onOpenFailedModal }); + + const { connection } = useConnection(); + const { sendTransaction: sendSolanaTransaction } = useSolanaWallet(); + + const sendValue = useAppSelector((state) => state.transfer.sendValue); + const transferActionInfo = useAppSelector((state) => state.transfer.transferActionInfo); + const selectedToken = useAppSelector((state) => state.transfer.selectedToken); + const isGlobalFeeLoading = useAppSelector((state) => state.transfer.isGlobalFeeLoading); + const isTransferable = useAppSelector((state) => state.transfer.isTransferable); + const toToken = useAppSelector((state) => state.transfer.toToken); + const fromChain = useAppSelector((state) => state.transfer.fromChain); + const toChain = useAppSelector((state) => state.transfer.toChain); + const toAccount = useAppSelector((state) => state.transfer.toAccount); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const publicClient = usePublicClient({ chainId: fromChain?.id }) as any; + const toPublicClient = usePublicClient({ chainId: toChain?.id }) as any; + const [isLoading, setIsLoading] = useState(false); + + const { allowance } = useGetAllowance({ + tokenAddress: selectedToken?.address as `0x${string}`, + sender: transferActionInfo?.bridgeAddress as `0x${string}`, + }); + + const tronAllowance = useGetTronAllowance(); + const { isConnected: isEvmConnected } = useAccount(); + const { isConnected: isTronConnected } = useTronAccount(); + const { waitForTxReceipt } = useWaitForTxReceipt(); + + const sendTx = useCallback(async () => { + if ( + !selectedToken || + !transferActionInfo?.bridgeType || + (!transferActionInfo?.bridgeAddress && fromChain?.chainType !== 'solana') || + ((!walletClient || + !publicClient || + !address || + (allowance === null && + selectedToken?.address !== '0x0000000000000000000000000000000000000000') || + !isEvmConnected) && + fromChain?.chainType !== 'tron' && + fromChain?.chainType !== 'solana') || + ((!isTronConnected || !tronAddress || tronAllowance === null) && + fromChain?.chainType === 'tron') + ) { + return; + } + + try { + setHash(null); + setChosenBridge(''); + setIsLoading(true); + + onClose(); // Close summary modal + onOpenConfirmingModal(); + + reportEvent({ + id: 'click_bridge_goal', + params: { + item_name: 'Send', + }, + }); + + if (transferActionInfo.bridgeType === 'cBridge' && cBridgeArgs && fromChain && address) { + try { + const isValidToken = await bridgeSDK.cBridge.validateCBridgeToken({ + isPegged: selectedToken.isPegged, + fromChainId: fromChain.id, + fromTokenAddress: selectedToken?.cBridge?.raw?.token.address as `0x${string}`, + fromTokenSymbol: selectedToken?.cBridge?.raw?.token?.symbol as string, + fromTokenDecimals: selectedToken.cBridge?.raw?.token.decimal as number, + bridgeAddress: transferActionInfo.bridgeAddress as `0x${string}`, + toChainId: toChain?.id, + toTokenAddress: toToken?.cBridge?.raw?.token.address as `0x${string}`, + toTokenSymbol: toToken?.cBridge?.raw?.token.symbol, + toTokenDecimals: toToken?.cBridge?.raw?.token.decimal as number, + amount: Number(sendValue), + cBridgeEndpoint: `${CBRIDGE_ENDPOINT}/getTransferConfigsForAll`, + }); + + if (!isValidToken) { + handleFailure({ + fromTokenAddress: selectedToken.address as `0x${string}`, + bridgeAddress: transferActionInfo.bridgeAddress as `0x${string}`, + fromChainId: fromChain.id, + isPegged: selectedToken.isPegged, + fromTokenSymbol: selectedToken.symbol, + toChainId: toChain?.id, + toTokenAddress: toToken?.cBridge?.raw?.token.address as `0x${string}`, + toTokenSymbol: toToken?.cBridge?.raw?.token.symbol, + decimals: selectedToken.decimals, + amount: Number(sendValue), + message: `(Token Validation Failed) - Invalid cBridge token!!`, + }); + return; + } + const cBridgeHash = await bridgeSDK.cBridge.sendToken({ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + walletClient: walletClient as any, + publicClient, + bridgeAddress: transferActionInfo.bridgeAddress as string, + fromChainId: fromChain?.id, + isPegged: selectedToken.isPegged, + address, + peggedConfig: selectedToken?.cBridge?.peggedConfig, + args: cBridgeArgs.args, + }); + await waitForTxReceipt({ + publicClient, + hash: cBridgeHash, + }); + if (cBridgeHash) { + reportEvent({ + id: 'transaction_bridge_success', + params: { + item_category: fromChain?.name, + item_category2: toChain?.name, + token: selectedToken.displaySymbol, + value: sendValue, + item_variant: 'cBridge', + }, + }); + onCloseConfirmingModal(); + setHash(cBridgeHash); + setChosenBridge('cBridge'); + onOpenSubmittedModal(); + } + // eslint-disable-next-line no-console + console.log('cBridge tx', cBridgeHash); + } catch (e) { + // eslint-disable-next-line no-console + console.log(e); + handleFailure(e); + } + } else if (transferActionInfo.bridgeType === 'deBridge') { + try { + let deBridgeHash: string | undefined; + const isValidToken = await bridgeSDK.deBridge.validateDeBridgeToken({ + fromChainId: fromChain?.id, + toChainId: toChain?.id, + fromTokenSymbol: selectedToken.symbol, + fromTokenAddress: selectedToken.deBridge?.raw?.address as `0x${string}`, + fromTokenDecimals: selectedToken.deBridge?.raw?.decimals as number, + toTokenSymbol: toToken?.deBridge?.raw?.symbol, + toTokenAddress: toToken?.deBridge?.raw?.address as `0x${string}`, + toTokenDecimals: toToken?.deBridge?.raw?.decimals as number, + amount: Number(sendValue), + fromChainType: fromChain?.chainType, + fromBridgeAddress: transferActionInfo.bridgeAddress as `0x${string}`, + toChainType: toChain?.chainType, + deBridgeEndpoint: DEBRIDGE_ENDPOINT, + }); + if (!isValidToken) { + handleFailure({ + message: '(Token Validation Failed) - Invalid deBridge token!!', + fromChainId: fromChain?.id, + tokenSymbol: selectedToken.symbol, + tokenAddress: selectedToken.address as `0x${string}`, + }); + return; + } + if (fromChain?.chainType === 'evm' && transferActionInfo.value && address) { + deBridgeHash = await bridgeSDK.deBridge.sendToken({ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + walletClient: walletClient as any, + bridgeAddress: transferActionInfo.bridgeAddress as string, + data: transferActionInfo.data as `0x${string}`, + amount: BigInt(transferActionInfo.value), + address, + }); + await waitForTxReceipt({ + publicClient, + hash: deBridgeHash, + }); + } + + if (fromChain?.chainType === 'solana') { + const { blockhash } = await connection.getLatestBlockhash(); + const data = (transferActionInfo.data as string)?.slice(2); + const tx = VersionedTransaction.deserialize(Buffer.from(data, 'hex')); + + tx.message.recentBlockhash = blockhash; + deBridgeHash = await sendSolanaTransaction(tx, connection); + + console.log('---solana---'); + console.log('blockhash: ', blockhash); + console.log('data:', data); + console.log('tx:', tx); + console.log('hash:', deBridgeHash); + } + + if (deBridgeHash) { + reportEvent({ + id: 'transaction_bridge_success', + params: { + item_category: fromChain?.name, + item_category2: toChain?.name, + token: selectedToken.displaySymbol, + value: sendValue, + item_variant: 'deBridge', + }, + }); + onCloseConfirmingModal(); + setChosenBridge('deBridge'); + setHash(deBridgeHash); + onOpenSubmittedModal(); + } + } catch (e) { + // eslint-disable-next-line no-console + console.log(e); + handleFailure(e); + } + } else if (transferActionInfo.bridgeType === 'stargate' && address) { + const isValidToken = await bridgeSDK.stargate.validateStargateToken({ + fromBridgeAddress: transferActionInfo.bridgeAddress as `0x${string}`, + toBridgeAddress: toToken?.stargate?.raw?.address as `0x${string}`, + fromTokenAddress: selectedToken?.stargate?.raw?.token?.address as `0x${string}`, + fromTokenSymbol: selectedToken?.stargate?.raw?.token?.symbol as string, + fromTokenDecimals: selectedToken?.stargate?.raw?.token?.decimals as number, + fromChainId: fromChain?.id, + toTokenAddress: toToken?.stargate?.raw?.token?.address as `0x${string}`, + toTokenSymbol: toToken?.stargate?.raw?.token?.symbol as string, + toTokenDecimals: toToken?.stargate?.raw?.token?.decimals as number, + toChainId: toChain?.id, + amount: Number(sendValue), + dstEndpointId: toToken?.stargate?.raw?.endpointID as number, + toPublicClient, + fromPublicClient: publicClient, + stargateEndpoint: STARGATE_ENDPOINT, + }); + if (!isValidToken) { + handleFailure({ + messages: '(Token Validation Failed) - Invalid Stargate token!!', + fromChainId: fromChain?.id, + tokenAddress: selectedToken.address as `0x${string}`, + bridgeAddress: transferActionInfo.bridgeAddress as `0x${string}`, + tokenSymbol: selectedToken.symbol, + }); + return; + } + const stargateHash = await bridgeSDK.stargate.sendToken({ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + walletClient: walletClient as any, + publicClient, + bridgeAddress: transferActionInfo.bridgeAddress as `0x${string}`, + tokenAddress: selectedToken.address as `0x${string}`, + endPointId: toToken?.stargate?.raw?.endpointID as number, + receiver: address, + amount: parseUnits(sendValue, selectedToken.decimals), + }); + if (stargateHash) { + reportEvent({ + id: 'transaction_bridge_success', + params: { + item_category: fromChain?.name, + item_category2: toChain?.name, + token: selectedToken.displaySymbol, + value: sendValue, + item_variant: 'stargate', + }, + }); + onCloseConfirmingModal(); + setChosenBridge('stargate'); + setHash(stargateHash); + onOpenSubmittedModal(); + } + } else if (transferActionInfo.bridgeType === 'layerZero' && address) { + // check layerZero token address + const isValidToken = await bridgeSDK.layerZero.validateLayerZeroToken({ + fromPublicClient: publicClient, + toPublicClient, + bridgeAddress: transferActionInfo.bridgeAddress as `0x${string}`, + fromTokenAddress: selectedToken.layerZero?.raw?.address as `0x${string}`, + fromTokenSymbol: selectedToken.layerZero?.raw?.symbol as string, + fromTokenDecimals: selectedToken.layerZero?.raw?.decimals as number, + toTokenAddress: toToken?.layerZero?.raw?.address as `0x${string}`, + toTokenDecimals: toToken?.layerZero?.raw?.decimals as number, + toTokenSymbol: toToken?.layerZero?.raw?.symbol as string, + toBridgeAddress: toToken?.layerZero?.raw?.bridgeAddress as `0x${string}`, + dstEndpoint: toToken?.layerZero?.raw?.endpointID as number, + amount: Number(sendValue), + }); + if (!isValidToken) { + handleFailure({ + messages: '(Token Validation Failed) - Invalid LayerZero token!!', + fromChainId: fromChain?.id, + tokenAddress: selectedToken.layerZero?.raw?.address as `0x${string}`, + bridgeAddress: transferActionInfo.bridgeAddress as `0x${string}`, + tokenSymbol: selectedToken.symbol, + }); + return; + } + const layerZeroHash = await bridgeSDK.layerZero.sendToken({ + bridgeAddress: transferActionInfo.bridgeAddress as `0x${string}`, + dstEndpoint: toToken?.layerZero?.raw?.endpointID as number, + userAddress: address, + amount: parseUnits(sendValue, selectedToken.decimals), + // eslint-disable-next-line @typescript-eslint/no-explicit-any + walletClient: walletClient as any, + publicClient, + }); + if (layerZeroHash) { + reportEvent({ + id: 'transaction_bridge_success', + params: { + item_category: fromChain?.name, + item_category2: toChain?.name, + token: selectedToken.displaySymbol, + value: sendValue, + item_variant: 'layerZero', + }, + }); + onCloseConfirmingModal(); + setChosenBridge('layerZero'); + setHash(layerZeroHash); + onOpenSubmittedModal(); + } + } else if (transferActionInfo.bridgeType === 'meson') { + const isValidToken = await bridgeSDK.meson.validateMesonToken({ + fromChainId: fromChain?.id, + toChainId: toChain?.id, + fromTokenAddress: + selectedToken.meson?.raw?.addr ?? '0x0000000000000000000000000000000000000000', + fromTokenSymbol: selectedToken.meson?.raw?.id as string, + fromTokenDecimals: selectedToken.meson?.raw?.decimals as number, + fromChainType: fromChain?.chainType, + toChainType: toChain?.chainType, + toTokenAddress: toToken?.meson?.raw?.addr ?? '0x0000000000000000000000000000000000000000', + toTokenSymbol: toToken?.meson?.raw?.id, + toTokenDecimals: toToken?.meson?.raw?.decimals, + amount: Number(sendValue), + mesonEndpoint: MESON_ENDPOINT, + }); + if (!isValidToken) { + handleFailure({ + message: '(Token Validation Failed) Invalid Meson token!!', + fromChainId: fromChain?.id, + tokenAddress: selectedToken.address as `0x${string}`, + tokenSymbol: selectedToken.symbol, + }); + return; + } + let fromAddress = ''; + let toAddress = ''; + let msg = ''; + let signature = ''; + + if (fromChain?.chainType === 'tron' && tronAddress) { + fromAddress = tronAddress; + } else if (fromChain?.chainType !== 'tron' && address) { + fromAddress = address; + } + + if (isTronTransfer && isTronAvailableToAccount && toAccount?.address) { + toAddress = toAccount.address; + } else if (address) { + toAddress = address; + } + + // get unsigned message + const unsignedMessage = await bridgeSDK.meson.getUnsignedMessage({ + fromToken: `${fromChain?.meson?.raw?.id}:${selectedToken?.meson?.raw?.id}`, + toToken: `${toChain?.meson?.raw?.id}:${toToken?.meson?.raw?.id}`, + amount: sendValue, + fromAddress: fromAddress, + recipient: toAddress, + }); + + if (unsignedMessage?.result) { + const result = unsignedMessage.result; + const encodedData = result.encoded; + const message = result.signingRequest.message; + + if (fromChain?.chainType === 'tron') { + const hexTronHeader = utf8ToHex('\x19TRON Signed Message:\n32'); + msg = message.replace(hexTronHeader, ''); + } else { + const hexEthHeader = utf8ToHex('\x19Ethereum Signed Message:\n52'); + msg = message.replace(hexEthHeader, ''); + } + + if (fromChain?.chainType != 'tron') { + signature = await signMessageAsync({ + account: address, + message: { + raw: msg as `0x${string}`, + }, + }); + } else { + // TODO + signature = String(await signTransaction(msg as any)); + } + + const swapId = await bridgeSDK.meson.sendToken({ + fromAddress: fromAddress, + recipient: toAddress, + signature: signature, + encodedData: encodedData, + }); + + // eslint-disable-next-line no-console + console.log('Meson swap id', swapId); + if (swapId?.result?.swapId) { + setChosenBridge('meson'); + setHash(swapId?.result?.swapId); + } + if (swapId?.error) { + throw new Error(swapId?.error.message); + } + + reportEvent({ + id: 'transaction_bridge_success', + params: { + item_category: fromChain?.name, + item_category2: toChain?.name, + token: selectedToken.displaySymbol, + value: sendValue, + item_variant: 'meson', + }, + }); + + onCloseConfirmingModal(); + onOpenSubmittedModal(); + } else { + throw new Error(unsignedMessage?.error.message); + } + } + } catch (e: any) { + // eslint-disable-next-line no-console + console.error(e, e.message); + handleFailure(e); + } finally { + onCloseConfirmingModal(); + setIsLoading(false); + } + }, [ + onClose, + selectedToken, + transferActionInfo?.bridgeType, + transferActionInfo?.bridgeAddress, + transferActionInfo?.value, + transferActionInfo?.data, + fromChain, + walletClient, + publicClient, + toPublicClient, + address, + allowance, + isEvmConnected, + isTronConnected, + tronAddress, + tronAllowance, + toChain?.name, + toChain?.meson?.raw?.id, + sendValue, + onOpenFailedModal, + setHash, + setChosenBridge, + onOpenConfirmingModal, + cBridgeArgs, + bridgeSDK.cBridge, + bridgeSDK.deBridge, + bridgeSDK.stargate, + bridgeSDK.layerZero, + bridgeSDK.meson, + onCloseConfirmingModal, + onOpenSubmittedModal, + connection, + sendSolanaTransaction, + toToken?.stargate?.raw, + toToken?.layerZero?.raw, + toToken?.meson?.raw, + toToken?.cBridge?.raw, + toToken?.deBridge?.raw, + toChain?.id, + toChain?.chainType, + isTronTransfer, + isTronAvailableToAccount, + toAccount.address, + signMessageAsync, + signTransaction, + handleFailure, + ]); + + const isDisabled = + isLoading || + isGlobalFeeLoading || + !sendValue || + !Number(sendValue) || + !transferActionInfo || + !isTransferable; + + return ( + + + + ); +}; diff --git a/packages/canonical-bridge-widget/src/modules/transfer/components/Modal/TransactionSummaryModal/FeeSummary.tsx b/packages/canonical-bridge-widget/src/modules/transfer/components/Modal/TransactionSummaryModal/FeeSummary.tsx new file mode 100644 index 00000000..336e4505 --- /dev/null +++ b/packages/canonical-bridge-widget/src/modules/transfer/components/Modal/TransactionSummaryModal/FeeSummary.tsx @@ -0,0 +1,21 @@ +import { Box, useColorMode, useTheme } from '@bnb-chain/space'; +import { useMemo } from 'react'; + +import { FeesInfo } from '@/modules/transfer/components/TransferOverview/RouteInfo/FeesInfo'; +import { useAppSelector } from '@/modules/store/StoreProvider'; +import { EstimatedArrivalTime } from '@/modules/transfer/components/TransferOverview/RouteInfo/EstimatedArrivalTime'; + +export const FeeSummary = () => { + const theme = useTheme(); + const { colorMode } = useColorMode(); + const transferActionInfo = useAppSelector((state) => state.transfer.transferActionInfo); + + const bridgeType = useMemo(() => transferActionInfo?.bridgeType, [transferActionInfo]); + + return ( + + + + + ); +}; diff --git a/packages/canonical-bridge-widget/src/modules/transfer/components/Modal/TransactionSummaryModal/TokenInfo.tsx b/packages/canonical-bridge-widget/src/modules/transfer/components/Modal/TransactionSummaryModal/TokenInfo.tsx new file mode 100644 index 00000000..d6709dc1 --- /dev/null +++ b/packages/canonical-bridge-widget/src/modules/transfer/components/Modal/TransactionSummaryModal/TokenInfo.tsx @@ -0,0 +1,49 @@ +import { Box, Flex, theme, useColorMode } from '@bnb-chain/space'; + +import { IconImage } from '@/core/components/IconImage'; + +export const TokenInfo = ({ + chainIconUrl, + tokenIconUrl, + chainName, + amount, + tokenSymbol, +}: { + chainIconUrl?: string; + tokenIconUrl?: string; + chainName?: string; + amount?: string; + tokenSymbol?: string; +}) => { + const { colorMode } = useColorMode(); + + return ( + + + + + + + + {chainName} + + + + {amount ?? '--'} {tokenSymbol} + + + ); +}; diff --git a/packages/canonical-bridge-widget/src/modules/transfer/components/Modal/TransactionSummaryModal/TransferSummary.tsx b/packages/canonical-bridge-widget/src/modules/transfer/components/Modal/TransactionSummaryModal/TransferSummary.tsx new file mode 100644 index 00000000..c53b4ca8 --- /dev/null +++ b/packages/canonical-bridge-widget/src/modules/transfer/components/Modal/TransactionSummaryModal/TransferSummary.tsx @@ -0,0 +1,83 @@ +import { Flex, Link, useBreakpointValue, useColorMode, useIntl, useTheme } from '@bnb-chain/space'; +import { useMemo } from 'react'; + +import { useAppSelector } from '@/modules/store/StoreProvider'; +import { useGetReceiveAmount } from '@/modules/transfer/hooks/useGetReceiveAmount'; +import { useToTokenInfo } from '@/modules/transfer/hooks/useToTokenInfo'; +import { TransferToIcon } from '@/core/components/icons/TransferToIcon'; +import { TokenInfo } from '@/modules/transfer/components/Modal/TransactionSummaryModal/TokenInfo'; +import { formatTokenUrl } from '@/core/utils/string'; +import { WarningMessage } from '@/modules/transfer/components/TransferWarningMessage/WarningMessage'; +import { formatAppAddress } from '@/core/utils/address'; + +export const TransferSummary = () => { + const { colorMode } = useColorMode(); + const theme = useTheme(); + const { getSortedReceiveAmount } = useGetReceiveAmount(); + const { formatMessage } = useIntl(); + const isBase = useBreakpointValue({ base: true, md: false }) ?? false; + + const fromChain = useAppSelector((state) => state.transfer.fromChain); + const toChain = useAppSelector((state) => state.transfer.toChain); + const selectedToken = useAppSelector((state) => state.transfer.selectedToken); + const sendValue = useAppSelector((state) => state.transfer.sendValue); + const transferActionInfo = useAppSelector((state) => state.transfer.transferActionInfo); + const { toTokenInfo } = useToTokenInfo(); + + const receiveAmt = useMemo(() => { + if (!Number(sendValue)) return null; + if (transferActionInfo && transferActionInfo.bridgeType) { + const bridgeType = transferActionInfo.bridgeType; + const receiveValue = getSortedReceiveAmount(); + return Number(receiveValue[bridgeType].value); + } + return null; + }, [getSortedReceiveAmount, transferActionInfo, sendValue]); + + return ( + + + + + + {formatMessage({ id: 'transfer.warning.confirm.to.address' })} + + {isBase + ? formatAppAddress({ address: toTokenInfo?.address, isTruncated: true }) + : toTokenInfo?.address} + + + } + /> + + ); +}; diff --git a/packages/canonical-bridge-widget/src/modules/transfer/components/Modal/TransactionSummaryModal/index.tsx b/packages/canonical-bridge-widget/src/modules/transfer/components/Modal/TransactionSummaryModal/index.tsx new file mode 100644 index 00000000..f53af6fa --- /dev/null +++ b/packages/canonical-bridge-widget/src/modules/transfer/components/Modal/TransactionSummaryModal/index.tsx @@ -0,0 +1,104 @@ +import { CloseIcon } from '@bnb-chain/icons'; +import { + Flex, + Modal, + ModalContent, + ModalOverlay, + useColorMode, + useIntl, + useTheme, +} from '@bnb-chain/space'; + +import { TransferSummary } from '@/modules/transfer/components/Modal/TransactionSummaryModal/TransferSummary'; +import { TransferConfirmButton } from '@/modules/transfer/components/Button/TransferConfirmButton'; +import { FeeSummary } from '@/modules/transfer/components/Modal/TransactionSummaryModal/FeeSummary'; + +interface ITransactionSummaryModalProps { + isOpen: boolean; + onClose: () => void; + onOpenSubmittedModal: () => void; + onOpenFailedModal: () => void; + onOpenApproveModal: () => void; + onOpenConfirmingModal: () => void; + onCloseConfirmingModal: () => void; + setHash: (hash: string | null) => void; + setChosenBridge: (bridge: string | null) => void; +} + +export function TransactionSummaryModal(props: ITransactionSummaryModalProps) { + const { + isOpen, + onClose, + onOpenSubmittedModal, + onOpenFailedModal, + onOpenConfirmingModal, + onCloseConfirmingModal, + setHash, + setChosenBridge, + } = props; + + const theme = useTheme(); + const { colorMode } = useColorMode(); + const { formatMessage } = useIntl(); + + return ( + + + + + + {formatMessage({ id: 'modal.summary.title' })} + + + + + + + + + + + + ); +} diff --git a/packages/canonical-bridge-widget/src/modules/transfer/components/TransferButtonGroup/index.tsx b/packages/canonical-bridge-widget/src/modules/transfer/components/TransferButtonGroup/index.tsx index e0ce85ce..5f1e06db 100644 --- a/packages/canonical-bridge-widget/src/modules/transfer/components/TransferButtonGroup/index.tsx +++ b/packages/canonical-bridge-widget/src/modules/transfer/components/TransferButtonGroup/index.tsx @@ -1,4 +1,4 @@ -import { Flex, useDisclosure } from '@bnb-chain/space'; +import { Flex, useDisclosure, useIntl } from '@bnb-chain/space'; import { useState } from 'react'; import { TransferButton } from '@/modules/transfer/components/Button/TransferButton'; @@ -7,10 +7,14 @@ import { TransactionFailedModal } from '@/modules/transfer/components/Modal/Tran import { TransactionApproveModal } from '@/modules/transfer/components/Modal/TransactionApproveModal'; import { TransactionConfirmingModal } from '@/modules/transfer/components/Modal/TransactionConfirmingModal'; import { WalletButtonWrapper } from '@/modules/transfer/components/Button/WalletButtonWrapper'; +import { TransactionSummaryModal } from '@/modules/transfer/components/Modal/TransactionSummaryModal'; +import { TransferWarningMessage } from '@/modules/transfer/components/TransferWarningMessage'; +import { MIN_SOL_TO_ENABLED_TX } from '@/core/constants'; export const TransferButtonGroup = () => { const [hash, setHash] = useState(null); const [chosenBridge, setChosenBridge] = useState(null); + const { formatMessage } = useIntl(); const { isOpen: isSubmittedModalOpen, @@ -32,6 +36,11 @@ export const TransferButtonGroup = () => { onOpen: onOpenConfirmingModal, onClose: onCloseConfirmingModal, } = useDisclosure(); + const { + isOpen: isSummaryModalOpen, + onOpen: onOpenSummaryModal, + onClose: onCloseSummaryModal, + } = useDisclosure(); return ( <> @@ -42,15 +51,19 @@ export const TransferButtonGroup = () => { > + { onCloseConfirmingModal={onCloseConfirmingModal} /> + ); }; From 51546a5e9fa79085e689dd5f54d54cc6dc52189c Mon Sep 17 00:00:00 2001 From: chris Date: Fri, 3 Jan 2025 11:33:27 +1000 Subject: [PATCH 15/20] fix: Icon style --- .../src/core/components/icons/TransferToIcon.tsx | 8 ++++++-- .../Modal/TransactionSummaryModal/TransferSummary.tsx | 8 +++++++- .../components/Modal/TransactionSummaryModal/index.tsx | 6 +++++- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/packages/canonical-bridge-widget/src/core/components/icons/TransferToIcon.tsx b/packages/canonical-bridge-widget/src/core/components/icons/TransferToIcon.tsx index 387276b0..4449aefc 100644 --- a/packages/canonical-bridge-widget/src/core/components/icons/TransferToIcon.tsx +++ b/packages/canonical-bridge-widget/src/core/components/icons/TransferToIcon.tsx @@ -1,6 +1,10 @@ import { Icon, IconProps } from '@bnb-chain/space'; -export function TransferToIcon(props: IconProps) { +interface TransferToIconProps extends IconProps { + iconOpacity?: string; +} + +export function TransferToIcon(props: TransferToIconProps) { return ( ); diff --git a/packages/canonical-bridge-widget/src/modules/transfer/components/Modal/TransactionSummaryModal/TransferSummary.tsx b/packages/canonical-bridge-widget/src/modules/transfer/components/Modal/TransactionSummaryModal/TransferSummary.tsx index c53b4ca8..1c45ea4b 100644 --- a/packages/canonical-bridge-widget/src/modules/transfer/components/Modal/TransactionSummaryModal/TransferSummary.tsx +++ b/packages/canonical-bridge-widget/src/modules/transfer/components/Modal/TransactionSummaryModal/TransferSummary.tsx @@ -50,7 +50,13 @@ export const TransferSummary = () => { amount={!!sendValue ? `-${sendValue}` : ''} tokenSymbol={selectedToken?.symbol ?? ''} /> - + - + Date: Fri, 3 Jan 2025 15:24:15 +1000 Subject: [PATCH 16/20] chore: Popup overlay style --- .../core/components/ThemeProvider/index.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/canonical-bridge-ui/core/components/ThemeProvider/index.tsx b/apps/canonical-bridge-ui/core/components/ThemeProvider/index.tsx index 5e900bfb..5f1d2a65 100644 --- a/apps/canonical-bridge-ui/core/components/ThemeProvider/index.tsx +++ b/apps/canonical-bridge-ui/core/components/ThemeProvider/index.tsx @@ -24,6 +24,9 @@ export const ThemeProvider = ({ children }: ThemeProviderProps) => { global: ({ colorMode }: { colorMode: ColorMode }) => ({ body: { bg: theme.colors[colorMode].background[3], + '.bccb-widget-transaction-summary-modal-overlap': { + opacity: '0.88 !important', + }, }, }), }, From a4d77f42ee93788d0ecbd1aeac3c86e26d480c5f Mon Sep 17 00:00:00 2001 From: chris Date: Fri, 3 Jan 2025 15:41:45 +1000 Subject: [PATCH 17/20] chore: Add skeleton --- .../src/core/locales/en.ts | 1 + .../Button/TransferConfirmButton.tsx | 14 ++++------ .../TransactionSummaryModal/FeeSummary.tsx | 17 +++++++++--- .../TransactionSummaryModal/TokenInfo.tsx | 26 ++++++++++++------- 4 files changed, 35 insertions(+), 23 deletions(-) diff --git a/packages/canonical-bridge-widget/src/core/locales/en.ts b/packages/canonical-bridge-widget/src/core/locales/en.ts index 8395915d..03fc6bf3 100644 --- a/packages/canonical-bridge-widget/src/core/locales/en.ts +++ b/packages/canonical-bridge-widget/src/core/locales/en.ts @@ -63,6 +63,7 @@ export const en = { 'transfer.button.wallet-connect': 'Connect Wallet', 'transfer.button.switch-wallet': 'Switch Wallet', 'transfer.button.confirm-summary': 'Confirm Transfer', + 'transfer.button.confirm-loading': 'Reloading Quotation', 'transfer.warning.confirm.to.address': 'Please double check the received token address:', 'transfer.warning.sol.balance': 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 b82bff77..53c6a717 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 @@ -539,13 +539,7 @@ export const TransferConfirmButton = ({ handleFailure, ]); - const isDisabled = - isLoading || - isGlobalFeeLoading || - !sendValue || - !Number(sendValue) || - !transferActionInfo || - !isTransferable; + const isFeeLoading = isLoading || isGlobalFeeLoading || !transferActionInfo || !isTransferable; return ( @@ -560,9 +554,11 @@ export const TransferConfirmButton = ({ _disabled: { bg: theme.colors[colorMode].button.disabled }, }} onClick={sendTx} - isDisabled={isDisabled} + isDisabled={isFeeLoading} > - {formatMessage({ id: 'transfer.button.confirm-summary' })} + {formatMessage({ + id: isFeeLoading ? 'transfer.button.confirm-loading' : 'transfer.button.confirm-summary', + })} ); diff --git a/packages/canonical-bridge-widget/src/modules/transfer/components/Modal/TransactionSummaryModal/FeeSummary.tsx b/packages/canonical-bridge-widget/src/modules/transfer/components/Modal/TransactionSummaryModal/FeeSummary.tsx index 336e4505..361279e0 100644 --- a/packages/canonical-bridge-widget/src/modules/transfer/components/Modal/TransactionSummaryModal/FeeSummary.tsx +++ b/packages/canonical-bridge-widget/src/modules/transfer/components/Modal/TransactionSummaryModal/FeeSummary.tsx @@ -1,4 +1,4 @@ -import { Box, useColorMode, useTheme } from '@bnb-chain/space'; +import { Box, Flex, Skeleton, useColorMode, useTheme } from '@bnb-chain/space'; import { useMemo } from 'react'; import { FeesInfo } from '@/modules/transfer/components/TransferOverview/RouteInfo/FeesInfo'; @@ -9,13 +9,22 @@ export const FeeSummary = () => { const theme = useTheme(); const { colorMode } = useColorMode(); const transferActionInfo = useAppSelector((state) => state.transfer.transferActionInfo); - + const isGlobalFeeLoading = useAppSelector((state) => state.transfer.isGlobalFeeLoading); const bridgeType = useMemo(() => transferActionInfo?.bridgeType, [transferActionInfo]); return ( - - + {isGlobalFeeLoading ? ( + + + + + ) : ( + <> + + + + )} ); }; diff --git a/packages/canonical-bridge-widget/src/modules/transfer/components/Modal/TransactionSummaryModal/TokenInfo.tsx b/packages/canonical-bridge-widget/src/modules/transfer/components/Modal/TransactionSummaryModal/TokenInfo.tsx index d6709dc1..4ce37773 100644 --- a/packages/canonical-bridge-widget/src/modules/transfer/components/Modal/TransactionSummaryModal/TokenInfo.tsx +++ b/packages/canonical-bridge-widget/src/modules/transfer/components/Modal/TransactionSummaryModal/TokenInfo.tsx @@ -1,6 +1,7 @@ -import { Box, Flex, theme, useColorMode } from '@bnb-chain/space'; +import { Box, Flex, Skeleton, theme, useColorMode } from '@bnb-chain/space'; import { IconImage } from '@/core/components/IconImage'; +import { useAppSelector } from '@/modules/store/StoreProvider'; export const TokenInfo = ({ chainIconUrl, @@ -16,6 +17,7 @@ export const TokenInfo = ({ tokenSymbol?: string; }) => { const { colorMode } = useColorMode(); + const isGlobalFeeLoading = useAppSelector((state) => state.transfer.isGlobalFeeLoading); return ( @@ -35,15 +37,19 @@ export const TokenInfo = ({ {chainName} - - {amount ?? '--'} {tokenSymbol} - + {isGlobalFeeLoading ? ( + + ) : ( + + {amount ?? '--'} {tokenSymbol} + + )} ); }; From 1944ec0be297221b3fbec0632d321c84f682236c Mon Sep 17 00:00:00 2001 From: chris Date: Fri, 3 Jan 2025 17:35:23 +1000 Subject: [PATCH 18/20] chore: Remove props error --- .../src/core/components/icons/InfoIcon.tsx | 8 ++++---- .../src/core/components/icons/TransferToIcon.tsx | 4 ++-- .../Modal/TransactionSummaryModal/TransferSummary.tsx | 2 +- .../components/TransferWarningMessage/WarningMessage.tsx | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/canonical-bridge-widget/src/core/components/icons/InfoIcon.tsx b/packages/canonical-bridge-widget/src/core/components/icons/InfoIcon.tsx index d82aac34..8f6e1b59 100644 --- a/packages/canonical-bridge-widget/src/core/components/icons/InfoIcon.tsx +++ b/packages/canonical-bridge-widget/src/core/components/icons/InfoIcon.tsx @@ -1,8 +1,8 @@ import { Icon, IconProps } from '@bnb-chain/space'; interface InfoIconProps extends IconProps { - iconColor?: string; - iconBgColor?: string; + iconcolor?: string; + iconbgcolor?: string; } export function InfoIcon(props: InfoIconProps) { @@ -17,11 +17,11 @@ export function InfoIcon(props: InfoIconProps) { > ); diff --git a/packages/canonical-bridge-widget/src/core/components/icons/TransferToIcon.tsx b/packages/canonical-bridge-widget/src/core/components/icons/TransferToIcon.tsx index 4449aefc..a1db9df8 100644 --- a/packages/canonical-bridge-widget/src/core/components/icons/TransferToIcon.tsx +++ b/packages/canonical-bridge-widget/src/core/components/icons/TransferToIcon.tsx @@ -1,7 +1,7 @@ import { Icon, IconProps } from '@bnb-chain/space'; interface TransferToIconProps extends IconProps { - iconOpacity?: string; + iconopacity?: string; } export function TransferToIcon(props: TransferToIconProps) { @@ -17,7 +17,7 @@ export function TransferToIcon(props: TransferToIconProps) { ); diff --git a/packages/canonical-bridge-widget/src/modules/transfer/components/Modal/TransactionSummaryModal/TransferSummary.tsx b/packages/canonical-bridge-widget/src/modules/transfer/components/Modal/TransactionSummaryModal/TransferSummary.tsx index 1c45ea4b..fe3d1e39 100644 --- a/packages/canonical-bridge-widget/src/modules/transfer/components/Modal/TransactionSummaryModal/TransferSummary.tsx +++ b/packages/canonical-bridge-widget/src/modules/transfer/components/Modal/TransactionSummaryModal/TransferSummary.tsx @@ -55,7 +55,7 @@ export const TransferSummary = () => { h={'24px'} mb={{ base: '-8px' }} transform={'rotate(90deg)'} - iconOpacity="1" + iconopacity="1" /> {text} From ba3726a13ac47e234afff31163554cc7fbedcd4c Mon Sep 17 00:00:00 2001 From: chris Date: Fri, 3 Jan 2025 17:54:40 +1000 Subject: [PATCH 19/20] fix: Avoid loading fee twice due to refresh button re-rendering twice issue --- .../src/modules/transfer/BridgeRoutes.tsx | 61 ++++++++++++++++- .../src/modules/transfer/action.ts | 4 ++ .../components/Button/RefreshingButton.tsx | 65 ++----------------- .../src/modules/transfer/reducer.ts | 6 ++ 4 files changed, 75 insertions(+), 61 deletions(-) diff --git a/packages/canonical-bridge-widget/src/modules/transfer/BridgeRoutes.tsx b/packages/canonical-bridge-widget/src/modules/transfer/BridgeRoutes.tsx index 14571fc6..538bdfce 100644 --- a/packages/canonical-bridge-widget/src/modules/transfer/BridgeRoutes.tsx +++ b/packages/canonical-bridge-widget/src/modules/transfer/BridgeRoutes.tsx @@ -1,10 +1,17 @@ import { useBreakpointValue, useIntl } from '@bnb-chain/space'; +import { useEffect } from 'react'; import { TransferOverview } from '@/modules/transfer/components/TransferOverview'; import { RoutesModal } from '@/modules/transfer/components/TransferOverview/modal/RoutesModal'; import { useBridgeConfig } from '@/CanonicalBridgeProvider'; import { useAppDispatch, useAppSelector } from '@/modules/store/StoreProvider'; -import { setIsRoutesModalOpen } from '@/modules/transfer/action'; +import { + setIsGlobalFeeLoading, + setIsManuallyReload, + setIsRefreshing, + setIsRoutesModalOpen, +} from '@/modules/transfer/action'; +import { TriggerType, useLoadingBridgeFees } from '@/modules/transfer/hooks/useLoadingBridgeFees'; export function BridgeRoutes() { const { formatMessage } = useIntl(); @@ -12,7 +19,59 @@ export function BridgeRoutes() { const isBase = useBreakpointValue({ base: true, lg: false }) ?? false; const { routeContentBottom } = useBridgeConfig(); + const { loadingBridgeFees } = useLoadingBridgeFees(); + const bridgeConfig = useBridgeConfig(); const isRoutesModalOpen = useAppSelector((state) => state.transfer.isRoutesModalOpen); + const transferActionInfo = useAppSelector((state) => state.transfer.transferActionInfo); + const isManuallyReload = useAppSelector((state) => state.transfer.isManuallyReload); + + // Load estimated bridge fees every 30 seconds when there is bridge route available + useEffect(() => { + let mount = true; + if (!mount) return; + if (transferActionInfo) { + const params = { + triggerType: 'refresh' as TriggerType, + }; + + let interval = setInterval(() => { + dispatch(setIsGlobalFeeLoading(true)); + loadingBridgeFees(params); + }, bridgeConfig.http.refetchingInterval ?? 30000); + + // Stop and restart fee loading + if (isManuallyReload === true) { + dispatch(setIsManuallyReload(false)); + dispatch(setIsRefreshing(true)); + if (interval) { + clearInterval(interval); + dispatch(setIsGlobalFeeLoading(true)); + loadingBridgeFees(params); + dispatch(setIsRefreshing(false)); + interval = setInterval(() => { + dispatch(setIsGlobalFeeLoading(true)); + loadingBridgeFees(params); + }, bridgeConfig.http?.refetchingInterval ?? 30000); + } + } + + return () => { + mount = false; + interval && clearInterval(interval); + dispatch(setIsManuallyReload(false)); + }; + } else { + dispatch(setIsManuallyReload(false)); + mount = false; + dispatch(setIsManuallyReload(false)); + } + }, [ + transferActionInfo, + loadingBridgeFees, + dispatch, + bridgeConfig.http.refetchingInterval, + isManuallyReload, + ]); if (isBase) { return ( diff --git a/packages/canonical-bridge-widget/src/modules/transfer/action.ts b/packages/canonical-bridge-widget/src/modules/transfer/action.ts index 27b696a9..8209f0fd 100644 --- a/packages/canonical-bridge-widget/src/modules/transfer/action.ts +++ b/packages/canonical-bridge-widget/src/modules/transfer/action.ts @@ -47,3 +47,7 @@ export const setIsToAddressChecked = createAction( 'transfer/setIsRoutesModalOpen', ); + +export const setIsManuallyReload = createAction( + 'transfer/setIsManuallyReload', +); diff --git a/packages/canonical-bridge-widget/src/modules/transfer/components/Button/RefreshingButton.tsx b/packages/canonical-bridge-widget/src/modules/transfer/components/Button/RefreshingButton.tsx index 1c740f5f..a7a5cc82 100644 --- a/packages/canonical-bridge-widget/src/modules/transfer/components/Button/RefreshingButton.tsx +++ b/packages/canonical-bridge-widget/src/modules/transfer/components/Button/RefreshingButton.tsx @@ -1,72 +1,17 @@ -import { useEffect, useState } from 'react'; import { Box, BoxProps, useColorMode, useTheme } from '@bnb-chain/space'; import { useAppDispatch, useAppSelector } from '@/modules/store/StoreProvider'; -import { setIsGlobalFeeLoading, setIsRefreshing } from '@/modules/transfer/action'; -import { TriggerType, useLoadingBridgeFees } from '@/modules/transfer/hooks/useLoadingBridgeFees'; +import { setIsManuallyReload, setIsRefreshing } from '@/modules/transfer/action'; import { RefreshingIcon } from '@/modules/transfer/components/LoadingImg/RefreshingIcon'; import { useBridgeConfig } from '@/index'; export const RefreshingButton = (props: BoxProps) => { const { colorMode } = useColorMode(); - const isGlobalFeeLoading = useAppSelector((state) => state.transfer.isGlobalFeeLoading); - const isRefreshing = useAppSelector((state) => state.transfer.isRefreshing); - const transferActionInfo = useAppSelector((state) => state.transfer.transferActionInfo); const theme = useTheme(); const dispatch = useAppDispatch(); - const [isButtonPressed, setIsButtonPressed] = useState(false); - - const { loadingBridgeFees } = useLoadingBridgeFees(); - const bridgeConfig = useBridgeConfig(); - - // Load estimated bridge fees every 30 seconds when there is bridge route available - useEffect(() => { - let mount = true; - if (!mount) return; - if (transferActionInfo) { - const params = { - triggerType: 'refresh' as TriggerType, - }; - - let interval = setInterval(() => { - dispatch(setIsGlobalFeeLoading(true)); - loadingBridgeFees(params); - }, bridgeConfig.http.refetchingInterval ?? 30000); - - // Stop and restart fee loading - if (isButtonPressed === true) { - dispatch(setIsRefreshing(true)); - if (interval) { - clearInterval(interval); - dispatch(setIsGlobalFeeLoading(true)); - loadingBridgeFees(params); - dispatch(setIsRefreshing(false)); - interval = setInterval(() => { - dispatch(setIsGlobalFeeLoading(true)); - loadingBridgeFees(params); - }, bridgeConfig.http?.refetchingInterval ?? 30000); - } - setIsButtonPressed(false); - } - - return () => { - mount = false; - interval && clearInterval(interval); - setIsButtonPressed(false); - }; - } else { - return () => { - mount = false; - setIsButtonPressed(false); - }; - } - }, [ - transferActionInfo, - loadingBridgeFees, - dispatch, - isButtonPressed, - bridgeConfig.http.refetchingInterval, - ]); + const isGlobalFeeLoading = useAppSelector((state) => state.transfer.isGlobalFeeLoading); + const transferActionInfo = useAppSelector((state) => state.transfer.transferActionInfo); + const isRefreshing = useAppSelector((state) => state.transfer.isRefreshing); const { refreshingIcon } = useBridgeConfig(); @@ -82,7 +27,7 @@ export const RefreshingButton = (props: BoxProps) => { : theme.colors[colorMode].button.refresh.text, }} onClick={() => { - setIsButtonPressed(true); + dispatch(setIsManuallyReload(true)); dispatch(setIsRefreshing(true)); }} {...props} diff --git a/packages/canonical-bridge-widget/src/modules/transfer/reducer.ts b/packages/canonical-bridge-widget/src/modules/transfer/reducer.ts index 39b63aa9..65f4aeaf 100644 --- a/packages/canonical-bridge-widget/src/modules/transfer/reducer.ts +++ b/packages/canonical-bridge-widget/src/modules/transfer/reducer.ts @@ -24,6 +24,7 @@ export interface ITransferState { estimatedAmount?: IEstimatedAmount; routeFees?: IRouteFees; isToAddressChecked?: boolean; + isManuallyReload: boolean; toAccount: { address?: string; }; @@ -52,6 +53,7 @@ const initStates: ITransferState = { address: '', }, isRoutesModalOpen: false, + isManuallyReload: false, }; export default createReducer(initStates, (builder) => { @@ -137,4 +139,8 @@ export default createReducer(initStates, (builder) => { ...state, isRoutesModalOpen: payload, })); + builder.addCase(actions.setIsManuallyReload, (state, { payload }) => ({ + ...state, + isManuallyReload: payload, + })); }); From 42daa5331c4b25ed098c9a1686507adceb858055 Mon Sep 17 00:00:00 2001 From: chris Date: Fri, 3 Jan 2025 18:13:35 +1000 Subject: [PATCH 20/20] chore: Refresh button in confirm modal --- .../src/core/locales/en.ts | 5 +++++ .../components/Button/RefreshingButton.tsx | 14 ++++++++++---- .../components/LoadingImg/RefreshingIcon.tsx | 5 +++-- .../Modal/FailedToGetQuoteModal/index.tsx | 19 +++++++++++++++++++ .../Modal/TransactionSummaryModal/index.tsx | 17 ++++++++++++++++- .../transfer/components/ReceiveInfo/index.tsx | 8 +++++++- 6 files changed, 60 insertions(+), 8 deletions(-) create mode 100644 packages/canonical-bridge-widget/src/modules/transfer/components/Modal/FailedToGetQuoteModal/index.tsx diff --git a/packages/canonical-bridge-widget/src/core/locales/en.ts b/packages/canonical-bridge-widget/src/core/locales/en.ts index 03fc6bf3..06225390 100644 --- a/packages/canonical-bridge-widget/src/core/locales/en.ts +++ b/packages/canonical-bridge-widget/src/core/locales/en.ts @@ -89,6 +89,11 @@ export const en = { 'modal.summary.title': 'Confirm Transaction', + 'modal.quote.error.title': 'Failed to Get the Quotation', + 'modal.quote.error.desc': + 'We’ve encountered an unknown issue on this route. Please try again later.', + 'modal.quote.error.button.close': 'OK', + 'select-modal.tag.incompatible': 'Incompatible', 'select-modal.search.no-result.title': 'No result found', 'select-modal.search.no-result.warning': diff --git a/packages/canonical-bridge-widget/src/modules/transfer/components/Button/RefreshingButton.tsx b/packages/canonical-bridge-widget/src/modules/transfer/components/Button/RefreshingButton.tsx index a7a5cc82..ffa9c408 100644 --- a/packages/canonical-bridge-widget/src/modules/transfer/components/Button/RefreshingButton.tsx +++ b/packages/canonical-bridge-widget/src/modules/transfer/components/Button/RefreshingButton.tsx @@ -1,11 +1,17 @@ -import { Box, BoxProps, useColorMode, useTheme } from '@bnb-chain/space'; +import { Box, BoxProps, IconProps, useColorMode, useTheme } from '@bnb-chain/space'; import { useAppDispatch, useAppSelector } from '@/modules/store/StoreProvider'; import { setIsManuallyReload, setIsRefreshing } from '@/modules/transfer/action'; import { RefreshingIcon } from '@/modules/transfer/components/LoadingImg/RefreshingIcon'; import { useBridgeConfig } from '@/index'; -export const RefreshingButton = (props: BoxProps) => { +export const RefreshingButton = ({ + iconProps, + boxProps, +}: { + iconProps?: IconProps; + boxProps?: BoxProps; +}) => { const { colorMode } = useColorMode(); const theme = useTheme(); const dispatch = useAppDispatch(); @@ -30,9 +36,9 @@ export const RefreshingButton = (props: BoxProps) => { dispatch(setIsManuallyReload(true)); dispatch(setIsRefreshing(true)); }} - {...props} + {...boxProps} > - {refreshingIcon ?? } + {refreshingIcon ?? } ) : null; }; diff --git a/packages/canonical-bridge-widget/src/modules/transfer/components/LoadingImg/RefreshingIcon.tsx b/packages/canonical-bridge-widget/src/modules/transfer/components/LoadingImg/RefreshingIcon.tsx index f6ed536e..6783e8c6 100644 --- a/packages/canonical-bridge-widget/src/modules/transfer/components/LoadingImg/RefreshingIcon.tsx +++ b/packages/canonical-bridge-widget/src/modules/transfer/components/LoadingImg/RefreshingIcon.tsx @@ -6,6 +6,7 @@ import { useAppSelector } from '@/modules/store/StoreProvider'; export const RefreshingIcon = (props: IconProps) => { const isGlobalFeeLoading = useAppSelector((state) => state.transfer.isGlobalFeeLoading); const isRefreshing = useAppSelector((state) => state.transfer.isRefreshing); + const randomStr = Math.random().toString(36).substring(7); return ( { strokeDashoffset={128.76} /> { > - + ) => { + const { ...restProps } = props; + const { formatMessage } = useIntl(); + + return ( + + ); +}; diff --git a/packages/canonical-bridge-widget/src/modules/transfer/components/Modal/TransactionSummaryModal/index.tsx b/packages/canonical-bridge-widget/src/modules/transfer/components/Modal/TransactionSummaryModal/index.tsx index b34feade..b85ded40 100644 --- a/packages/canonical-bridge-widget/src/modules/transfer/components/Modal/TransactionSummaryModal/index.tsx +++ b/packages/canonical-bridge-widget/src/modules/transfer/components/Modal/TransactionSummaryModal/index.tsx @@ -4,6 +4,7 @@ import { Modal, ModalContent, ModalOverlay, + SkeletonCircle, useColorMode, useIntl, useTheme, @@ -12,6 +13,8 @@ import { import { TransferSummary } from '@/modules/transfer/components/Modal/TransactionSummaryModal/TransferSummary'; import { TransferConfirmButton } from '@/modules/transfer/components/Button/TransferConfirmButton'; import { FeeSummary } from '@/modules/transfer/components/Modal/TransactionSummaryModal/FeeSummary'; +import { RefreshingButton } from '@/modules/transfer/components/Button/RefreshingButton'; +import { useAppSelector } from '@/modules/store/StoreProvider'; interface ITransactionSummaryModalProps { isOpen: boolean; @@ -40,6 +43,7 @@ export function TransactionSummaryModal(props: ITransactionSummaryModalProps) { const theme = useTheme(); const { colorMode } = useColorMode(); const { formatMessage } = useIntl(); + const isGlobalLoading = useAppSelector((state) => state.transfer.isGlobalFeeLoading); return ( @@ -70,7 +74,18 @@ export function TransactionSummaryModal(props: ITransactionSummaryModalProps) { borderBottom={`1px solid ${theme.colors[colorMode].border['3']}`} flexShrink={0} > - + + {isGlobalLoading ? ( + + ) : ( + + )} + {formatMessage({ id: 'modal.summary.title' })} { }, }} > - + } {bridgeType && (