From 3ae25973f7d1789cba880a2d423c1d1f3b4e049a Mon Sep 17 00:00:00 2001 From: Kenk Date: Tue, 18 Jul 2023 20:46:31 +0200 Subject: [PATCH 1/7] feat(website): update blogs (#14201) --- .../website/components/Home/BlogSection.tsx | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/packages/website/components/Home/BlogSection.tsx b/packages/website/components/Home/BlogSection.tsx index 0a8f78d0587..1b71bcf9302 100644 --- a/packages/website/components/Home/BlogSection.tsx +++ b/packages/website/components/Home/BlogSection.tsx @@ -1,44 +1,44 @@ const posts = [ { - title: "Cross-chain communication exploration – rollups’ vision", - href: "https://taiko.mirror.xyz/ryYEi4gAeOWwyERqYTs7CPbNEOYXaEeiMEui6gdlnyg", + title: "Eldfell L3 (alpha-4) is live!", + href: "https://taiko.mirror.xyz/HJCWBluTwmNyWRkhzIXXr0k5xAaalRNtmlyDMJTu_ws", description: - "TLDR: This article explores the approaches of different L2s to cross-chain messaging from rollups’ perspective, focusing more on trustless cross-chain communication.", - date: "Jul 12, 2023", - datetime: "2023-07-12", + "Taiko’s fourth testnet has arrived! Eldfell L3 (alpha-4) is our first experiment with inception layers and a new staking based proving design.", + date: "Jul 18, 2023", + datetime: "2023-07-18", imageUrl: - "https://mirror-media.imgix.net/publication-images/HQGT0nBEq8AzLQxQFqqpI.png?height=512&width=1024&h=512&w=1024&auto=compress", - readingTime: "14 min", + "https://mirror-media.imgix.net/publication-images/ew9PQRsaJTLtGd8ShGiyR.jpeg?height=600&width=1200&h=600&w=1200&auto=compress", + readingTime: "3 min", author: { - name: "Lisa A.", - imageUrl: "https://avatars.githubusercontent.com/u/106527861?v=4", + name: "d1onys1us", + imageUrl: "https://avatars.githubusercontent.com/u/13951458?v=4", }, }, { - title: "Alpha-3 testnet update: stats & future plans", - href: "https://taiko.mirror.xyz/LBukIvc5nRfNdbzk3_bDZNkBRSmavFp_D90IkBYCuFk", + title: "Announcing our first community grant program", + href: "https://taiko.mirror.xyz/G7dmuoR42S4D55vT8bs_lAxPZP63kAgRu2IfqkJdf6U", description: - "Alpha-3 has been drumming along for one month now! Let's take a look at what’s happened.", - date: "Jul 07, 2023", - datetime: "2023-07-07", + "We are excited to launch our first community grant program to discover & support builders to enhance our ecosystem with funds and dev resources.", + date: "Jul 14, 2023", + datetime: "2023-07-14", imageUrl: - "https://mirror-media.imgix.net/publication-images/4vqDxKbexLf3eX3d4GtRB.jpeg?height=512&width=1024&h=512&w=1024&auto=compress", - readingTime: "2 min", + "https://mirror-media.imgix.net/publication-images/wmQORiddLvZqlHJi8ma1k.jpeg?height=960&width=1920&h=960&w=1920&auto=compress", + readingTime: "10 min", author: { name: "d1onys1us", imageUrl: "https://avatars.githubusercontent.com/u/13951458?v=4", }, }, { - title: "ZK-Roller-Coaster #8", - href: "https://taiko.mirror.xyz/tOUCZgLRV9bKH24bxhahISpdhkQmqVyM-ZX-wMWtqkI", + title: "Cross-chain communication exploration – rollups’ vision", + href: "https://taiko.mirror.xyz/ryYEi4gAeOWwyERqYTs7CPbNEOYXaEeiMEui6gdlnyg", description: - "This is the 8th edition of ZK-Roller-Coaster where we track and investigate the most exciting, meaningful, and crazy ZK-stuff of the prior two weeks.", - date: "Jul 04, 2023", - datetime: "2023-07-04", + "TLDR: This article explores the approaches of different L2s to cross-chain messaging from rollups’ perspective, focusing more on trustless cross-chain communication.", + date: "Jul 12, 2023", + datetime: "2023-07-12", imageUrl: - "https://mirror-media.imgix.net/publication-images/3cfm8O9yVJ8aszk8bQ700.png?height=1536&width=3072&h=1536&w=3072&auto=compress", - readingTime: "4 min", + "https://mirror-media.imgix.net/publication-images/HQGT0nBEq8AzLQxQFqqpI.png?height=512&width=1024&h=512&w=1024&auto=compress", + readingTime: "14 min", author: { name: "Lisa A.", imageUrl: "https://avatars.githubusercontent.com/u/106527861?v=4", From 64a8766963088158c884289a494c0bb4f4c1e12a Mon Sep 17 00:00:00 2001 From: dave | d1onys1us <13951458+d1onys1us@users.noreply.github.com> Date: Wed, 19 Jul 2023 04:01:53 -0400 Subject: [PATCH 2/7] fix(website): fix typo in banner (#14204) --- packages/website/theme.config.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/website/theme.config.tsx b/packages/website/theme.config.tsx index 0931908ef07..b4694f23f69 100644 --- a/packages/website/theme.config.tsx +++ b/packages/website/theme.config.tsx @@ -3,13 +3,14 @@ import { ThemedImage } from "./components/ThemedImage"; import { useConfig } from "nextra-theme-docs"; import { useRouter } from "next/router"; import { ThemeToggle } from "./components/ThemeToggle"; +import { ELDFELL_CONFIG } from "./domain/chain"; export default { banner: { key: "banner", text: ( - 📌 Eldell L3 is here! Get started → + 📌 {ELDFELL_CONFIG.names.shortishName} is here! Get started → ), }, From a7fd219aa7ef320ae4ab2e737e71a48527c6b386 Mon Sep 17 00:00:00 2001 From: Oleh Korchynskyy <93517196+korchinskiiy@users.noreply.github.com> Date: Wed, 19 Jul 2023 11:35:00 +0300 Subject: [PATCH 3/7] fix(docs): fix links (#14205) Co-authored-by: dave | d1onys1us <13951458+d1onys1us@users.noreply.github.com> --- .../pages/docs/guides/build-on-taiko/receive-tokens.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/website/pages/docs/guides/build-on-taiko/receive-tokens.mdx b/packages/website/pages/docs/guides/build-on-taiko/receive-tokens.mdx index 9d72f962b4a..1ebd65029f7 100644 --- a/packages/website/pages/docs/guides/build-on-taiko/receive-tokens.mdx +++ b/packages/website/pages/docs/guides/build-on-taiko/receive-tokens.mdx @@ -2,7 +2,7 @@ import { Callout, Steps } from "nextra-theme-docs"; # Receive tokens -This guide will help you receive testnet tokens. You can see all the deployed tokens [here](/docs/reference/contract-addresses). Keep in mind HORSE and BLL faucets only distribute to Grimsvotn L2. To get HORSE and BLL on Eldfell L3, you will need to [use the bridge](/docs/guides/bridge-tokens). +This guide will help you receive testnet tokens. You can see all the deployed tokens [here](/docs/reference/contract-addresses). Keep in mind HORSE and BLL faucets only distribute to Grimsvotn L2. To get HORSE and BLL on Eldfell L3, you will need to [use the bridge](/docs/guides/build-on-taiko/bridge-tokens). ## Prerequisites @@ -23,7 +23,7 @@ This guide will help you receive testnet tokens. You can see all the deployed to ### Receive HORSE Navigate to the [Sepolia L1 ↔ Grimsvotn L2 bridge](https://bridge.test.taiko.xyz), select HORSE from the dropdown, and click "Faucet". - To receive HORSE on Eldfell L3, you will need to [use the bridge](/docs/guides/bridge-tokens). + To receive HORSE on Eldfell L3, you will need to [use the bridge](/docs/guides/build-on-taiko/bridge-tokens). ![receive horse](/images/guides/receive-horse.png) From 57ef8f271b5b248ea8492525731bcaf68367aed4 Mon Sep 17 00:00:00 2001 From: Kenk Date: Wed, 19 Jul 2023 18:11:02 +0200 Subject: [PATCH 4/7] feat(website): update wallet setup docs (#14209) --- .../components/Button/AddTokenButton.tsx | 46 ++++++++++++++++-- .../build-on-taiko/setup-your-wallet.mdx | 48 +++++++++++++++++-- 2 files changed, 87 insertions(+), 7 deletions(-) diff --git a/packages/website/components/Button/AddTokenButton.tsx b/packages/website/components/Button/AddTokenButton.tsx index 85af5bbde41..6ba56d8222e 100644 --- a/packages/website/components/Button/AddTokenButton.tsx +++ b/packages/website/components/Button/AddTokenButton.tsx @@ -1,15 +1,52 @@ +import { + ELDFELL_ADD_ETHEREUM_CHAIN, + ELDFELL_CONFIG, + GRIMSVOTN_ADD_ETHEREUM_CHAIN, + GRIMSVOTN_CONFIG, + SEPOLIA_ADD_ETHEREUM_CHAIN, + SEPOLIA_CONFIG, +} from "../../domain/chain"; + import { ethereumRequest } from "../../utils/ethereumRequest"; +type ConnectButtonProps = { + network: + | typeof SEPOLIA_CONFIG.names.shortName + | typeof ELDFELL_CONFIG.names.shortName + | typeof GRIMSVOTN_CONFIG.names.shortName; +}; + +const chainMap = { + Eldfell: ELDFELL_CONFIG.chainId.hex, // 167005 + Grimsvotn: GRIMSVOTN_CONFIG.chainId.hex, // 167005 + Sepolia: SEPOLIA_CONFIG.chainId.hex, // 11155111 +}; + +const configMap = { + Eldfell: ELDFELL_ADD_ETHEREUM_CHAIN, + Grimsvotn: GRIMSVOTN_ADD_ETHEREUM_CHAIN, + Sepolia: SEPOLIA_ADD_ETHEREUM_CHAIN, +}; + interface AddTokenButtonProps { address: string; symbol: string; decimals: number; image: string; + network: ConnectButtonProps["network"]; } const addTokenToWallet = async (token: AddTokenButtonProps) => { - const options = { ...token, type: "ERC20" }; - await ethereumRequest("wallet_watchAsset", options); + const { ethereum } = window as any; + + if (ethereum.chainId != chainMap[token.network]) { + await ethereumRequest("wallet_addEthereumChain", [configMap[token.network]]); + } + + const params = { options: { address: token.address, symbol: token.symbol, decimals: token.decimals, image: token.image }, type: "ERC20" }; + + await ethereumRequest("wallet_watchAsset", params); + }; export function AddTokenButton({ @@ -17,13 +54,14 @@ export function AddTokenButton({ symbol, decimals, image, + network, }: AddTokenButtonProps) { return (
addTokenToWallet({ address, symbol, decimals, image })} + onClick={() => addTokenToWallet({ address, symbol, decimals, image, network })} className="hover:cursor-pointer text-neutral-100 bg-[#E81899] hover:bg-[#d1168a] border-solid border-neutral-200 focus:ring-4 focus:outline-none focus:ring-neutral-100 font-medium rounded-lg text-sm px-3 py-2 text-center inline-flex items-center" > - Add {symbol} to wallet + Add {symbol} ({network})
); } diff --git a/packages/website/pages/docs/guides/build-on-taiko/setup-your-wallet.mdx b/packages/website/pages/docs/guides/build-on-taiko/setup-your-wallet.mdx index 330d18b4bb8..4b4a65fdac1 100644 --- a/packages/website/pages/docs/guides/build-on-taiko/setup-your-wallet.mdx +++ b/packages/website/pages/docs/guides/build-on-taiko/setup-your-wallet.mdx @@ -1,4 +1,12 @@ import { Steps } from "nextra-theme-docs"; +import { AddTokenButton } from "components/Button/AddTokenButton"; +import { ConnectToMetamaskButton } from "components/Button/ConnectToMetamaskButton"; + +import { + ELDFELL_CONFIG, + GRIMSVOTN_CONFIG, + SEPOLIA_CONFIG, +} from "domain/chain"; # Setup your wallet @@ -11,11 +19,45 @@ This guide helps you connect your wallet to Taiko's networks. ## Steps - ### Visit RPC configuration - Visit the [RPC configuration page](/docs/reference/rpc-configuration) and click the buttons to add the networks. Or, add them manually in your wallet with the provided configuration. + ### Add chains to your wallet + +
+ +
+ ### Add tokens to your wallet - Use your wallet (e.g., Metamask) to [import the tokens](https://support.ledger.com/hc/en-us/articles/6375103346077-Add-custom-tokens-to-MetaMask?docs=true). You can find all deployed tokens contract addresses [here](/docs/reference/contract-addresses). + + + +
+ + +
From 4b639d7a5315c20a6766fb2b59d0ce5d3b973453 Mon Sep 17 00:00:00 2001 From: Francisco Ramos Date: Thu, 20 Jul 2023 08:22:05 +0200 Subject: [PATCH 5/7] feat(bridge-ui-v2): amount input validation (#14213) --- packages/bridge-ui-v2/src/app.config.ts | 5 + .../Bridge/AmountInput/AmountInput.svelte | 136 +++++++++++++++++- .../Bridge/AmountInput/Balance.svelte | 11 +- .../Bridge/ProcessingFee/NoneOption.svelte | 2 +- .../Bridge/ProcessingFee/ProcessingFee.svelte | 20 +-- .../src/components/Bridge/state.ts | 7 +- .../ChainSelector/ChainSelector.svelte | 6 +- .../src/components/Faucet/Faucet.svelte | 33 ++--- .../src/components/InputBox/InputBox.svelte | 14 +- packages/bridge-ui-v2/src/i18n/en.json | 6 +- .../src/libs/bridge/ERC1155Bridge.ts | 7 + .../src/libs/bridge/ERC20Bridge.ts | 67 +++++++++ .../src/libs/bridge/ERC721Bridge.ts | 7 + .../bridge-ui-v2/src/libs/bridge/ETHBridge.ts | 69 +++++++++ .../bridge-ui-v2/src/libs/bridge/bridges.ts | 12 ++ .../src/libs/bridge/estimateCostOfBridging.ts | 12 ++ .../src/libs/bridge/getMaxToBridge.ts | 60 ++++++++ .../bridge-ui-v2/src/libs/bridge/index.ts | 3 + .../bridge-ui-v2/src/libs/bridge/types.ts | 97 +++++++++++++ .../bridge-ui-v2/src/libs/chain/chains.ts | 3 +- .../src/libs/token/checkMintable.test.ts | 21 +-- .../src/libs/token/checkMintable.ts | 25 ++-- .../src/libs/token/getBalance.test.ts | 1 - .../bridge-ui-v2/src/libs/token/getBalance.ts | 6 +- .../bridge-ui-v2/src/libs/token/mint.test.ts | 2 +- packages/bridge-ui-v2/src/libs/token/mint.ts | 7 +- packages/bridge-ui-v2/src/libs/token/types.ts | 3 +- .../bridge-ui-v2/src/libs/util/debounce.ts | 14 ++ .../bridge-ui-v2/src/libs/util/getWallet.ts | 11 ++ .../src/libs/util/truncateDecimal.ts | 4 + .../bridge-ui-v2/src/styles/components.css | 6 +- 31 files changed, 584 insertions(+), 93 deletions(-) create mode 100644 packages/bridge-ui-v2/src/libs/bridge/ERC1155Bridge.ts create mode 100644 packages/bridge-ui-v2/src/libs/bridge/ERC20Bridge.ts create mode 100644 packages/bridge-ui-v2/src/libs/bridge/ERC721Bridge.ts create mode 100644 packages/bridge-ui-v2/src/libs/bridge/ETHBridge.ts create mode 100644 packages/bridge-ui-v2/src/libs/bridge/bridges.ts create mode 100644 packages/bridge-ui-v2/src/libs/bridge/estimateCostOfBridging.ts create mode 100644 packages/bridge-ui-v2/src/libs/bridge/getMaxToBridge.ts create mode 100644 packages/bridge-ui-v2/src/libs/bridge/index.ts create mode 100644 packages/bridge-ui-v2/src/libs/bridge/types.ts create mode 100644 packages/bridge-ui-v2/src/libs/util/debounce.ts create mode 100644 packages/bridge-ui-v2/src/libs/util/getWallet.ts create mode 100644 packages/bridge-ui-v2/src/libs/util/truncateDecimal.ts diff --git a/packages/bridge-ui-v2/src/app.config.ts b/packages/bridge-ui-v2/src/app.config.ts index 5584560c844..0c49606a95c 100644 --- a/packages/bridge-ui-v2/src/app.config.ts +++ b/packages/bridge-ui-v2/src/app.config.ts @@ -8,3 +8,8 @@ export const processingFeeComponent = { closingDelayOptionClick: 300, intervalComputeRecommendedFee: 20000, }; + +export const bridge = { + noOwnerGasLimit: BigInt(140000), + noTokenDeployedGasLimit: BigInt(3000000), +}; diff --git a/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte b/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte index d2cd50a02cc..02e08753648 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte +++ b/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte @@ -1,26 +1,158 @@
- +
+
- +
+ + {#if errorAmount} + +
+ +
+ {$t('amount_input.error.insufficient_balance')} +
+
+ {/if}
diff --git a/packages/bridge-ui-v2/src/components/Bridge/AmountInput/Balance.svelte b/packages/bridge-ui-v2/src/components/Bridge/AmountInput/Balance.svelte index 18000188fa6..a82bc0d8fa2 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/AmountInput/Balance.svelte +++ b/packages/bridge-ui-v2/src/components/Bridge/AmountInput/Balance.svelte @@ -10,7 +10,8 @@ import { destNetwork, selectedToken } from '../state'; - let tokenBalance: Maybe; + export let value: Maybe; + let computingTokenBalance = false; let errorComputingTokenBalance = false; @@ -21,14 +22,14 @@ errorComputingTokenBalance = false; try { - tokenBalance = await getTokenBalance({ + value = await getTokenBalance({ token, destChainId, userAddress: account.address, chainId: srcChainId, }); - } catch (error) { - console.error(error); + } catch (err) { + console.error(err); errorComputingTokenBalance = true; } finally { computingTokenBalance = false; @@ -50,7 +51,7 @@ {:else} - {renderTokenBalance(tokenBalance)} + {renderTokenBalance(value)} {/if} diff --git a/packages/bridge-ui-v2/src/components/Bridge/ProcessingFee/NoneOption.svelte b/packages/bridge-ui-v2/src/components/Bridge/ProcessingFee/NoneOption.svelte index fb5dbd614f8..99af71c167a 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/ProcessingFee/NoneOption.svelte +++ b/packages/bridge-ui-v2/src/components/Bridge/ProcessingFee/NoneOption.svelte @@ -1,5 +1,5 @@ - + diff --git a/packages/bridge-ui-v2/src/i18n/en.json b/packages/bridge-ui-v2/src/i18n/en.json index cc9e3ca9999..56ab85e6681 100644 --- a/packages/bridge-ui-v2/src/i18n/en.json +++ b/packages/bridge-ui-v2/src/i18n/en.json @@ -84,7 +84,11 @@ "amount_input": { "label": "Amount", "balance": "Balance", - "button.max": "Max" + "button": { + "max": "Max", + "failed_max": "Coult not estimate max amount to bridge." + }, + "error.insufficient_balance": "Insufficient balance" }, "chain_selector": { diff --git a/packages/bridge-ui-v2/src/libs/bridge/ERC1155Bridge.ts b/packages/bridge-ui-v2/src/libs/bridge/ERC1155Bridge.ts new file mode 100644 index 00000000000..ddad81ecab1 --- /dev/null +++ b/packages/bridge-ui-v2/src/libs/bridge/ERC1155Bridge.ts @@ -0,0 +1,7 @@ +import type { Bridge } from './types'; + +export class ERC1155Bridge implements Bridge { + async estimateGas(): Promise { + return Promise.resolve(BigInt(0)); + } +} diff --git a/packages/bridge-ui-v2/src/libs/bridge/ERC20Bridge.ts b/packages/bridge-ui-v2/src/libs/bridge/ERC20Bridge.ts new file mode 100644 index 00000000000..074bc062a27 --- /dev/null +++ b/packages/bridge-ui-v2/src/libs/bridge/ERC20Bridge.ts @@ -0,0 +1,67 @@ +import { getContract } from '@wagmi/core'; + +import { tokenVaultABI } from '$abi'; +import { bridge } from '$config'; +import { getConnectedWallet } from '$libs/util/getWallet'; +import { getLogger } from '$libs/util/logger'; + +import type { Bridge, ERC20BridgeArgs, SendERC20Args } from './types'; + +const log = getLogger('ERC20Bridge'); + +export class ERC20Bridge implements Bridge { + private static async _prepareTransaction(args: ERC20BridgeArgs) { + const walletClient = await getConnectedWallet(); + + const { + to, + memo = '', + amount, + destChainId, + tokenAddress, + processingFee, + tokenVaultAddress, + isTokenAlreadyDeployed, + } = args; + + const tokenVaultContract = getContract({ + walletClient, + abi: tokenVaultABI, + address: tokenVaultAddress, + }); + + const refundAddress = walletClient.account.address; + + const gasLimit = !isTokenAlreadyDeployed + ? BigInt(bridge.noTokenDeployedGasLimit) + : processingFee > 0 + ? bridge.noOwnerGasLimit + : BigInt(0); + + const sendERC20Args: SendERC20Args = [ + BigInt(destChainId), + to, + tokenAddress, + amount, + gasLimit, + processingFee, + refundAddress, + memo, + ]; + + log('Preparing transaction with args', sendERC20Args); + + return { tokenVaultContract, sendERC20Args }; + } + + async estimateGas(args: ERC20BridgeArgs) { + const { tokenVaultContract, sendERC20Args } = await ERC20Bridge._prepareTransaction(args); + const [, , , , , processingFee] = sendERC20Args; + + const value = processingFee; + + log('Estimating gas for sendERC20 call. Sending value', value); + + return tokenVaultContract.estimateGas.sendERC20([...sendERC20Args], { value }); + } +} diff --git a/packages/bridge-ui-v2/src/libs/bridge/ERC721Bridge.ts b/packages/bridge-ui-v2/src/libs/bridge/ERC721Bridge.ts new file mode 100644 index 00000000000..a9d03418e55 --- /dev/null +++ b/packages/bridge-ui-v2/src/libs/bridge/ERC721Bridge.ts @@ -0,0 +1,7 @@ +import type { Bridge } from './types'; + +export class ERC721Bridge implements Bridge { + async estimateGas(): Promise { + return Promise.resolve(BigInt(0)); + } +} diff --git a/packages/bridge-ui-v2/src/libs/bridge/ETHBridge.ts b/packages/bridge-ui-v2/src/libs/bridge/ETHBridge.ts new file mode 100644 index 00000000000..45c54f813a9 --- /dev/null +++ b/packages/bridge-ui-v2/src/libs/bridge/ETHBridge.ts @@ -0,0 +1,69 @@ +import { getContract } from '@wagmi/core'; + +import { bridgeABI } from '$abi'; +import { bridge } from '$config'; +import { getConnectedWallet } from '$libs/util/getWallet'; +import { getLogger } from '$libs/util/logger'; + +import type { Bridge, ETHBridgeArgs, Message } from './types'; + +const log = getLogger('ETHBridge'); + +export class ETHBridge implements Bridge { + private static async _prepareTransaction(args: ETHBridgeArgs) { + const walletClient = await getConnectedWallet(); + + const { to, memo = '', amount, srcChainId, destChainId, bridgeAddress, processingFee } = args; + + const bridgeContract = getContract({ + walletClient, + abi: bridgeABI, + address: bridgeAddress, + }); + + const owner = walletClient.account.address; + + // TODO: contract actually supports bridging to ourselves as well as + // to another address at the same time + const [depositValue, callValue] = + to.toLowerCase() === owner.toLowerCase() ? [amount, BigInt(0)] : [BigInt(0), amount]; + + // If there is a processing fee, use the specified message gas limit + // if not called by the owner + const gasLimit = processingFee > 0 ? bridge.noOwnerGasLimit : BigInt(0); + + const message: Message = { + to, + owner, + sender: owner, + refundAddress: owner, + + srcChainId: BigInt(srcChainId), + destChainId: BigInt(destChainId), + + gasLimit, + callValue, + depositValue, + processingFee, + + memo, + data: '0x', + id: BigInt(0), // will be set in contract + }; + + log('Preparing transaction with message', message); + + return { bridgeContract, message }; + } + + async estimateGas(args: ETHBridgeArgs) { + const { bridgeContract, message } = await ETHBridge._prepareTransaction(args); + const { depositValue, callValue, processingFee } = message; + + const value = depositValue + callValue + processingFee; + + log('Estimating gas for sendMessage call. Sending value', value); + + return bridgeContract.estimateGas.sendMessage([message], { value }); + } +} diff --git a/packages/bridge-ui-v2/src/libs/bridge/bridges.ts b/packages/bridge-ui-v2/src/libs/bridge/bridges.ts new file mode 100644 index 00000000000..8eefde0a6dd --- /dev/null +++ b/packages/bridge-ui-v2/src/libs/bridge/bridges.ts @@ -0,0 +1,12 @@ +import { ERC20Bridge } from './ERC20Bridge'; +import { ERC721Bridge } from './ERC721Bridge'; +import { ERC1155Bridge } from './ERC1155Bridge'; +import { ETHBridge } from './ETHBridge'; +import type { Bridge, BridgeType } from './types'; + +export const bridges: Record = { + ETH: new ETHBridge(), + ERC20: new ERC20Bridge(), + ERC721: new ERC721Bridge(), + ERC1155: new ERC1155Bridge(), +}; diff --git a/packages/bridge-ui-v2/src/libs/bridge/estimateCostOfBridging.ts b/packages/bridge-ui-v2/src/libs/bridge/estimateCostOfBridging.ts new file mode 100644 index 00000000000..adcb8c81d74 --- /dev/null +++ b/packages/bridge-ui-v2/src/libs/bridge/estimateCostOfBridging.ts @@ -0,0 +1,12 @@ +import { getPublicClient } from '@wagmi/core'; + +import type { Bridge, BridgeArgs } from './types'; + +export async function estimateCostOfBridging(bridge: Bridge, bridgeArgs: BridgeArgs) { + const publicClient = getPublicClient(); + + // Calculate the estimated cost of bridging + const estimatedGas = await bridge.estimateGas(bridgeArgs); + const gasPrice = await publicClient.getGasPrice(); + return estimatedGas * gasPrice; +} diff --git a/packages/bridge-ui-v2/src/libs/bridge/getMaxToBridge.ts b/packages/bridge-ui-v2/src/libs/bridge/getMaxToBridge.ts new file mode 100644 index 00000000000..041567139a4 --- /dev/null +++ b/packages/bridge-ui-v2/src/libs/bridge/getMaxToBridge.ts @@ -0,0 +1,60 @@ +import type { Address } from 'viem'; + +import { chainContractsMap, chains } from '$libs/chain'; +import { isETH, type Token } from '$libs/token'; +import { getLogger } from '$libs/util/logger'; + +import { bridges } from './bridges'; +import { estimateCostOfBridging } from './estimateCostOfBridging'; +import type { ETHBridgeArgs } from './types'; + +type GetMaxToBridgeArgs = { + token: Token; + balance: bigint; + srcChainId: number; + userAddress: Address; + processingFee: bigint; + destChainId?: number; + amount?: bigint; +}; + +const log = getLogger('getMaxToBridge'); + +export async function getMaxToBridge({ + token, + balance, + srcChainId, + userAddress, + processingFee, + destChainId, + amount, +}: GetMaxToBridgeArgs) { + if (isETH(token)) { + const to = userAddress; + const { bridgeAddress } = chainContractsMap[srcChainId.toString()]; + + const bridgeArgs = { + to, + srcChainId, + bridgeAddress, + processingFee, + + // If no amount passed in, use whatever just to get an estimation + amount: amount ?? BigInt(1), + + // If no destination chain is selected, find another chain to estimate + // TODO: we might want to really find a compatible chain to bridge to + // if we have multiple layers + destChainId: destChainId ?? chains.find((chain) => chain.id !== srcChainId)?.id, + } as ETHBridgeArgs; + + const estimatedCost = await estimateCostOfBridging(bridges.ETH, bridgeArgs); + + log('Estimated cost of bridging', estimatedCost, 'with argument', bridgeArgs); + + return balance - processingFee - estimatedCost; + } + + // For ERC20 tokens, we can bridge the whole balance + return balance; +} diff --git a/packages/bridge-ui-v2/src/libs/bridge/index.ts b/packages/bridge-ui-v2/src/libs/bridge/index.ts new file mode 100644 index 00000000000..5187917dc4b --- /dev/null +++ b/packages/bridge-ui-v2/src/libs/bridge/index.ts @@ -0,0 +1,3 @@ +export { bridges } from './bridges'; +export { estimateCostOfBridging } from './estimateCostOfBridging'; +export * from './types'; diff --git a/packages/bridge-ui-v2/src/libs/bridge/types.ts b/packages/bridge-ui-v2/src/libs/bridge/types.ts new file mode 100644 index 00000000000..7dd0f52172c --- /dev/null +++ b/packages/bridge-ui-v2/src/libs/bridge/types.ts @@ -0,0 +1,97 @@ +import type { Address, Hex } from 'viem'; + +export enum BridgeType { + ETH = 'ETH', + + // https://ethereum.org/en/developers/docs/standards/tokens/erc-20/ + ERC20 = 'ERC20', + + // https://ethereum.org/en/developers/docs/standards/tokens/erc-721/ + ERC721 = 'ERC721', + + // https://ethereum.org/en/developers/docs/standards/tokens/erc-1155/ + ERC1155 = 'ERC1155', +} + +// Bridge sendMessage(message: Message) +export type Message = { + // Message ID. Will be set in contract + id: bigint; + // Message sender address (auto filled) + sender: Address; + // Source chain ID (auto filled) + srcChainId: bigint; + // Destination chain ID where the `to` address lives (auto filled) + destChainId: bigint; + // Owner address of the bridged asset. + owner: Address; + // Destination owner address + to: Address; + // Alternate address to send any refund. If blank, defaults to owner. + refundAddress: Address; + // Deposited Ether minus the processingFee. + depositValue: bigint; + // callValue to invoke on the destination chain, for ERC20 transfers. + callValue: bigint; + // Processing fee for the relayer. Zero if owner will process themself. + processingFee: bigint; + // gasLimit to invoke on the destination chain, for ERC20 transfers. + gasLimit: bigint; + // callData to invoke on the destination chain, for ERC20 transfers. + data: Hex; + // Optional memo. + memo: string; +}; + +// TokenVault sendERC20(...args) +export type SendERC20Args = [ + bigint, // destChainId + Address, // to + Address, // token + bigint, // amount + bigint, // gasLimit + bigint, // processingFee + Address, // refundAddress + string, // memo +]; + +// TODO: future sendToken(op: BridgeTransferOp) +export type BridgeTransferOp = { + destChainId: bigint; + to: Address; + token: Address; + amount: bigint; + gasLimit: bigint; + processingFee: bigint; + refundAddress: Address; + memo: string; +}; + +export type ApproveArgs = { + amount: bigint; + tokenAddress: Address; + spenderAddress: Address; +}; + +export type BridgeArgs = { + to: Address; + srcChainId: number; + destChainId: number; + amount: bigint; + processingFee: bigint; + memo?: string; +}; + +export type ETHBridgeArgs = BridgeArgs & { + bridgeAddress: Address; +}; + +export type ERC20BridgeArgs = BridgeArgs & { + tokenAddress: Address; + tokenVaultAddress: Address; + isTokenAlreadyDeployed?: boolean; +}; + +export interface Bridge { + estimateGas(args: BridgeArgs): Promise; +} diff --git a/packages/bridge-ui-v2/src/libs/chain/chains.ts b/packages/bridge-ui-v2/src/libs/chain/chains.ts index 2779bff7880..496137b14ba 100644 --- a/packages/bridge-ui-v2/src/libs/chain/chains.ts +++ b/packages/bridge-ui-v2/src/libs/chain/chains.ts @@ -1,5 +1,4 @@ -import type { Chain } from '@wagmi/core'; -import type { Address } from 'abitype'; +import type { Address, Chain } from '@wagmi/core'; import { PUBLIC_L1_BRIDGE_ADDRESS, diff --git a/packages/bridge-ui-v2/src/libs/token/checkMintable.test.ts b/packages/bridge-ui-v2/src/libs/token/checkMintable.test.ts index 3066604fa6c..503542c036e 100644 --- a/packages/bridge-ui-v2/src/libs/token/checkMintable.test.ts +++ b/packages/bridge-ui-v2/src/libs/token/checkMintable.test.ts @@ -45,24 +45,15 @@ describe('checkMintable', () => { vi.mocked(getPublicClient).mockReturnValue(mockPublicClient); }); - it('should throw when wallet is not connected', async () => { - vi.mocked(getWalletClient).mockResolvedValueOnce(null); - - try { - await checkMintable(BLLToken, mainnetChain); - expect.fail('should have thrown'); - } catch (error) { - const { cause } = error as Error; - expect(cause).toBe(MintableError.NOT_CONNECTED); - expect(getWalletClient).toHaveBeenCalledWith({ chainId: mainnetChain.id }); - } + beforeEach(() => { + vi.clearAllMocks(); }); it('should throw when user has already minted', async () => { vi.mocked(mockTokenContract.read.minters).mockResolvedValueOnce(true); try { - await checkMintable(BLLToken, mainnetChain); + await checkMintable(BLLToken, mainnetChain.id); expect.fail('should have thrown'); } catch (error) { const { cause } = error as Error; @@ -91,12 +82,12 @@ describe('checkMintable', () => { vi.mocked(mockPublicClient.getBalance).mockResolvedValueOnce(BigInt(100)); try { - await checkMintable(BLLToken, mainnetChain); + await checkMintable(BLLToken, mainnetChain.id); expect.fail('should have thrown'); } catch (error) { const { cause } = error as Error; expect(cause).toBe(MintableError.INSUFFICIENT_BALANCE); - expect(getPublicClient).toHaveBeenCalledWith({ chainId: mainnetChain.id }); + expect(getPublicClient).toHaveBeenCalled(); expect(mockTokenContract.estimateGas.mint).toHaveBeenCalledWith([mockWalletClient.account.address]); expect(mockPublicClient.getBalance).toHaveBeenCalledWith({ address: mockWalletClient.account.address }); } @@ -117,7 +108,7 @@ describe('checkMintable', () => { vi.mocked(mockPublicClient.getBalance).mockResolvedValueOnce(BigInt(300)); try { - await checkMintable(BLLToken, mainnetChain); + await checkMintable(BLLToken, mainnetChain.id); } catch (error) { expect.fail('should not have thrown'); } diff --git a/packages/bridge-ui-v2/src/libs/token/checkMintable.ts b/packages/bridge-ui-v2/src/libs/token/checkMintable.ts index 848de485316..2c91d47a4eb 100644 --- a/packages/bridge-ui-v2/src/libs/token/checkMintable.ts +++ b/packages/bridge-ui-v2/src/libs/token/checkMintable.ts @@ -1,21 +1,15 @@ -import { type Chain, getContract, getPublicClient, getWalletClient } from '@wagmi/core'; -import { formatEther } from 'viem'; +import { getContract, getPublicClient } from '@wagmi/core'; import { freeMintErc20ABI } from '$abi'; +import { getConnectedWallet } from '$libs/util/getWallet'; import { MintableError, type Token } from './types'; // Throws an error if: -// 1. User is not connected to the network -// 2. User has already minted this token -// 3. User has insufficient balance to mint this token -export async function checkMintable(token: Token, network: Chain) { - const chainId = network.id; - const walletClient = await getWalletClient({ chainId }); - - if (!walletClient) { - throw Error(`user is not connected to ${network.name}`, { cause: MintableError.NOT_CONNECTED }); - } +// 1. User has already minted this token +// 2. User has insufficient balance to mint this token +export async function checkMintable(token: Token, chainId: number) { + const walletClient = await getConnectedWallet(); const tokenContract = getContract({ walletClient, @@ -28,12 +22,13 @@ export async function checkMintable(token: Token, network: Chain) { const hasMinted = await tokenContract.read.minters([userAddress]); if (hasMinted) { - throw Error(`token ${token.symbol} has already been minted`, { cause: MintableError.TOKEN_MINTED }); + throw Error(`token already minted`, { cause: MintableError.TOKEN_MINTED }); } // Check whether the user has enough balance to mint. // Compute the cost of the transaction: - const publicClient = getPublicClient({ chainId }); + const publicClient = getPublicClient(); + const estimatedGas = await tokenContract.estimateGas.mint([userAddress]); const gasPrice = await publicClient.getGasPrice(); const estimatedCost = estimatedGas * gasPrice; @@ -41,7 +36,7 @@ export async function checkMintable(token: Token, network: Chain) { const userBalance = await publicClient.getBalance({ address: userAddress }); if (estimatedCost > userBalance) { - throw Error(`user has insufficient balance to mint ${token.symbol}: ${formatEther(userBalance)}`, { + throw Error('user has insufficient balance', { cause: MintableError.INSUFFICIENT_BALANCE, }); } diff --git a/packages/bridge-ui-v2/src/libs/token/getBalance.test.ts b/packages/bridge-ui-v2/src/libs/token/getBalance.test.ts index a6846bb2be6..7b9d00d2e70 100644 --- a/packages/bridge-ui-v2/src/libs/token/getBalance.test.ts +++ b/packages/bridge-ui-v2/src/libs/token/getBalance.test.ts @@ -68,7 +68,6 @@ describe('getBalance', () => { }); expect(fetchBalance).toHaveBeenCalledWith({ address: mockWalletClient.account.address, - chainId: Number(PUBLIC_L1_CHAIN_ID), token: BLLToken.addresses[PUBLIC_L1_CHAIN_ID], }); }); diff --git a/packages/bridge-ui-v2/src/libs/token/getBalance.ts b/packages/bridge-ui-v2/src/libs/token/getBalance.ts index ebb974455e1..f4715362bd5 100644 --- a/packages/bridge-ui-v2/src/libs/token/getBalance.ts +++ b/packages/bridge-ui-v2/src/libs/token/getBalance.ts @@ -1,6 +1,5 @@ import { fetchBalance, type FetchBalanceResult } from '@wagmi/core'; -import type { Address } from 'abitype'; -import { zeroAddress } from 'viem'; +import { type Address, zeroAddress } from 'viem'; import { getLogger } from '$libs/util/logger'; @@ -21,7 +20,7 @@ export async function getBalance({ token, userAddress, chainId, destChainId }: G let tokenBalance: FetchBalanceResult | null = null; if (isETH(token)) { - tokenBalance = await fetchBalance({ address: userAddress, chainId }); + tokenBalance = await fetchBalance({ address: userAddress }); } else { // We are dealing with an ERC20 token. We need to first find out its address // on the current chain in order to fetch the balance. @@ -32,7 +31,6 @@ export async function getBalance({ token, userAddress, chainId, destChainId }: G // Wagmi is such an amazing library. We had to do this // more manually before. tokenBalance = await fetchBalance({ - chainId, address: userAddress, token: tokenAddress, }); diff --git a/packages/bridge-ui-v2/src/libs/token/mint.test.ts b/packages/bridge-ui-v2/src/libs/token/mint.test.ts index 379c5798d88..2be21d8334d 100644 --- a/packages/bridge-ui-v2/src/libs/token/mint.test.ts +++ b/packages/bridge-ui-v2/src/libs/token/mint.test.ts @@ -28,7 +28,7 @@ describe('mint', () => { vi.mocked(getContract).mockReturnValue(mockTokenContract); vi.mocked(mockTokenContract.write.mint).mockResolvedValue('0x123456'); - await expect(mint(BLLToken, mockWalletClient)).resolves.toEqual('0x123456'); + await expect(mint(BLLToken)).resolves.toEqual('0x123456'); expect(mockTokenContract.write.mint).toHaveBeenCalledWith([mockWalletClient.account.address]); }); }); diff --git a/packages/bridge-ui-v2/src/libs/token/mint.ts b/packages/bridge-ui-v2/src/libs/token/mint.ts index cfd35b3b331..c9585583711 100644 --- a/packages/bridge-ui-v2/src/libs/token/mint.ts +++ b/packages/bridge-ui-v2/src/libs/token/mint.ts @@ -1,13 +1,16 @@ -import { getContract, type WalletClient } from '@wagmi/core'; +import { getContract } from '@wagmi/core'; import { freeMintErc20ABI } from '$abi'; +import { getConnectedWallet } from '$libs/util/getWallet'; import { getLogger } from '../util/logger'; import type { Token } from './types'; const log = getLogger('token:mint'); -export async function mint(token: Token, walletClient: WalletClient) { +export async function mint(token: Token) { + const walletClient = await getConnectedWallet(); + const tokenSymbol = token.symbol; const userAddress = walletClient.account.address; const chainId = walletClient.chain.id; diff --git a/packages/bridge-ui-v2/src/libs/token/types.ts b/packages/bridge-ui-v2/src/libs/token/types.ts index c47b27f7fa3..230e5814d1d 100644 --- a/packages/bridge-ui-v2/src/libs/token/types.ts +++ b/packages/bridge-ui-v2/src/libs/token/types.ts @@ -1,4 +1,4 @@ -import type { Address } from 'abitype'; +import type { Address } from 'viem'; export type Token = { name: string; @@ -14,7 +14,6 @@ export type TokenEnv = { }; export enum MintableError { - NOT_CONNECTED = 'NOT_CONNECTED', TOKEN_MINTED = 'TOKEN_MINTED', INSUFFICIENT_BALANCE = 'INSUFFICIENT_BALANCE', } diff --git a/packages/bridge-ui-v2/src/libs/util/debounce.ts b/packages/bridge-ui-v2/src/libs/util/debounce.ts new file mode 100644 index 00000000000..75cdc862732 --- /dev/null +++ b/packages/bridge-ui-v2/src/libs/util/debounce.ts @@ -0,0 +1,14 @@ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function debounce ReturnType>( + callback: T, + timeout: number, +): (...args: Parameters) => void { + let timer: ReturnType; + + return (...args: Parameters) => { + clearTimeout(timer); + timer = setTimeout(() => { + callback(...args); + }, timeout); + }; +} diff --git a/packages/bridge-ui-v2/src/libs/util/getWallet.ts b/packages/bridge-ui-v2/src/libs/util/getWallet.ts new file mode 100644 index 00000000000..d609ef62344 --- /dev/null +++ b/packages/bridge-ui-v2/src/libs/util/getWallet.ts @@ -0,0 +1,11 @@ +import { getWalletClient } from '@wagmi/core'; + +export async function getConnectedWallet() { + const walletClient = await getWalletClient(); + + if (!walletClient) { + throw Error('wallet is not connected'); + } + + return walletClient; +} diff --git a/packages/bridge-ui-v2/src/libs/util/truncateDecimal.ts b/packages/bridge-ui-v2/src/libs/util/truncateDecimal.ts new file mode 100644 index 00000000000..bd3391864cb --- /dev/null +++ b/packages/bridge-ui-v2/src/libs/util/truncateDecimal.ts @@ -0,0 +1,4 @@ +export function truncateDecimal(num: number, decimalPlaces: number) { + const factor = 10 ** decimalPlaces; + return Math.floor(num * factor) / factor; +} diff --git a/packages/bridge-ui-v2/src/styles/components.css b/packages/bridge-ui-v2/src/styles/components.css index 7b82bbfa2c6..839066bb4d9 100644 --- a/packages/bridge-ui-v2/src/styles/components.css +++ b/packages/bridge-ui-v2/src/styles/components.css @@ -83,7 +83,11 @@ /* focus:border-[3px] */ /* focus:border-primary-border-accent */ - focus:shadow-[0_0_0_3px_#E81899]; + focus:!shadow-[0_0_0_3px_#E81899]; + } + + .input-box.error { + @apply !shadow-[0_0_0_3px_#F15C5D]; } /* Separatos */ From 61138a88b529d70df0e81468052971a4fc4fde16 Mon Sep 17 00:00:00 2001 From: Francisco Ramos Date: Thu, 20 Jul 2023 23:19:27 +0200 Subject: [PATCH 6/7] fix(bridge-ui-v2): processing fee and amount input validation (#14220) --- .../Amount.svelte} | 104 +++++++++++------- .../{AmountInput => Amount}/Balance.svelte | 4 +- .../src/components/Bridge/Amount/index.ts | 1 + .../components/Bridge/AmountInput/index.ts | 1 - .../src/components/Bridge/Bridge.svelte | 10 +- .../Bridge/ProcessingFee/NoneOption.svelte | 3 +- .../Bridge/ProcessingFee/ProcessingFee.svelte | 36 +++++- .../ProcessingFee/RecommendedFee.svelte | 3 +- .../Recipient.svelte} | 0 .../src/components/Bridge/Recipient/index.ts | 1 + .../components/Bridge/RecipientInput/index.ts | 1 - .../Bridge/SwitchChainsButton.svelte | 13 ++- .../src/components/Button/Button.svelte | 2 +- .../src/components/Faucet/Faucet.svelte | 28 +++-- .../NotificationToast/ItemToast.svelte | 2 +- packages/bridge-ui-v2/src/i18n/en.json | 14 ++- .../src/libs/bridge/ERC20Bridge.ts | 2 +- .../bridge-ui-v2/src/libs/bridge/ETHBridge.ts | 4 +- .../src/libs/bridge/checkBalanceToBridge.ts | 87 +++++++++++++++ ...MaxToBridge.ts => getMaxAmountToBridge.ts} | 47 ++++---- .../bridge-ui-v2/src/libs/bridge/index.ts | 2 + .../bridge-ui-v2/src/libs/error/errors.ts | 3 + packages/bridge-ui-v2/src/libs/error/index.ts | 1 + .../src/libs/fee/recommendProcessingFee.ts | 10 +- .../src/libs/token/checkMintable.test.ts | 8 +- .../src/libs/token/checkMintable.ts | 11 +- .../src/libs/token/getAddress.test.ts | 20 ++-- .../bridge-ui-v2/src/libs/token/getAddress.ts | 38 +++---- .../src/libs/token/getBalance.test.ts | 27 +++-- .../bridge-ui-v2/src/libs/token/getBalance.ts | 18 +-- .../libs/token/getCrossChainAddress.test.ts | 5 + .../src/libs/token/getCrossChainAddress.ts | 33 ++++++ packages/bridge-ui-v2/src/libs/token/index.ts | 1 + .../src/libs/token/isDeployedCrossChain.ts | 24 ++++ .../bridge-ui-v2/src/libs/token/mint.test.ts | 8 +- packages/bridge-ui-v2/src/libs/token/mint.ts | 9 +- packages/bridge-ui-v2/src/libs/token/types.ts | 5 - .../src/libs/util/getConnectedWallet.test.ts | 5 + .../{getWallet.ts => getConnectedWallet.ts} | 4 +- .../src/libs/util/truncateDecimal.test.ts | 9 ++ .../src/libs/util/truncateString.test.ts | 9 ++ 41 files changed, 435 insertions(+), 178 deletions(-) rename packages/bridge-ui-v2/src/components/Bridge/{AmountInput/AmountInput.svelte => Amount/Amount.svelte} (58%) rename packages/bridge-ui-v2/src/components/Bridge/{AmountInput => Amount}/Balance.svelte (95%) create mode 100644 packages/bridge-ui-v2/src/components/Bridge/Amount/index.ts delete mode 100644 packages/bridge-ui-v2/src/components/Bridge/AmountInput/index.ts rename packages/bridge-ui-v2/src/components/Bridge/{RecipientInput/RecipientInput.svelte => Recipient/Recipient.svelte} (100%) create mode 100644 packages/bridge-ui-v2/src/components/Bridge/Recipient/index.ts delete mode 100644 packages/bridge-ui-v2/src/components/Bridge/RecipientInput/index.ts create mode 100644 packages/bridge-ui-v2/src/libs/bridge/checkBalanceToBridge.ts rename packages/bridge-ui-v2/src/libs/bridge/{getMaxToBridge.ts => getMaxAmountToBridge.ts} (52%) create mode 100644 packages/bridge-ui-v2/src/libs/error/errors.ts create mode 100644 packages/bridge-ui-v2/src/libs/error/index.ts create mode 100644 packages/bridge-ui-v2/src/libs/token/getCrossChainAddress.test.ts create mode 100644 packages/bridge-ui-v2/src/libs/token/getCrossChainAddress.ts create mode 100644 packages/bridge-ui-v2/src/libs/token/isDeployedCrossChain.ts create mode 100644 packages/bridge-ui-v2/src/libs/util/getConnectedWallet.test.ts rename packages/bridge-ui-v2/src/libs/util/{getWallet.ts => getConnectedWallet.ts} (54%) create mode 100644 packages/bridge-ui-v2/src/libs/util/truncateDecimal.test.ts create mode 100644 packages/bridge-ui-v2/src/libs/util/truncateString.test.ts diff --git a/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte b/packages/bridge-ui-v2/src/components/Bridge/Amount/Amount.svelte similarity index 58% rename from packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte rename to packages/bridge-ui-v2/src/components/Bridge/Amount/Amount.svelte index 02e08753648..9775ac28559 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte +++ b/packages/bridge-ui-v2/src/components/Bridge/Amount/Amount.svelte @@ -1,12 +1,13 @@ diff --git a/packages/bridge-ui-v2/src/components/Button/Button.svelte b/packages/bridge-ui-v2/src/components/Button/Button.svelte index 0bcab9c66ff..b93c011bb4e 100644 --- a/packages/bridge-ui-v2/src/components/Button/Button.svelte +++ b/packages/bridge-ui-v2/src/components/Button/Button.svelte @@ -45,7 +45,7 @@ }; $: classes = classNames( - 'btn w-full h-auto min-h-fit border-0', + 'btn h-auto min-h-fit border-0', type === 'primary' ? 'body-bold' : 'body-regular', diff --git a/packages/bridge-ui-v2/src/components/Faucet/Faucet.svelte b/packages/bridge-ui-v2/src/components/Faucet/Faucet.svelte index 2b7e621a568..95216792d9d 100644 --- a/packages/bridge-ui-v2/src/components/Faucet/Faucet.svelte +++ b/packages/bridge-ui-v2/src/components/Faucet/Faucet.svelte @@ -11,7 +11,8 @@ import { successToast, warningToast } from '$components/NotificationToast'; import { TokenDropdown } from '$components/TokenDropdown'; import { PUBLIC_L1_CHAIN_ID, PUBLIC_L1_CHAIN_NAME, PUBLIC_L1_EXPLORER_URL } from '$env/static/public'; - import { MintableError, testERC20Tokens, type Token } from '$libs/token'; + import { InsufficientBalanceError, TokenMintedError } from '$libs/error'; + import { testERC20Tokens, type Token } from '$libs/token'; import { checkMintable, mint } from '$libs/token'; import { account } from '$stores/account'; import { network } from '$stores/network'; @@ -46,14 +47,14 @@ // During loading state we make sure the user cannot use this function if (checkingMintable || minting) return; - // A token and a source chain must be selected in order to be able to mint + // Token and source chain are needed to mint if (!selectedToken || !$network) return; // Let's begin the minting process minting = true; try { - const txHash = await mint(selectedToken); + const txHash = await mint(selectedToken, $network.id); successToast( $t('faucet.minting_tx', { @@ -69,7 +70,10 @@ } catch (err) { console.error(err); - // const { cause } = error as Error; + const { cause } = err as Error; + if (cause instanceof UserRejectedRequestError) { + warningToast($t('messages.mint.rejected')); + } } finally { minting = false; } @@ -99,13 +103,11 @@ } catch (err) { console.error(err); - const { cause } = err as Error; - - switch (cause) { - case MintableError.INSUFFICIENT_BALANCE: + switch (true) { + case err instanceof InsufficientBalanceError: reasonNotMintable = $t('faucet.warning.insufficient_balance'); break; - case MintableError.TOKEN_MINTED: + case err instanceof TokenMintedError: reasonNotMintable = $t('faucet.warning.token_minted'); break; default: @@ -145,7 +147,11 @@ {#if connected && wrongChain} -