diff --git a/packages/bridge-ui-v2/README.md b/packages/bridge-ui-v2/README.md index 83ddab8ce38..f134747b621 100644 --- a/packages/bridge-ui-v2/README.md +++ b/packages/bridge-ui-v2/README.md @@ -48,7 +48,7 @@ These are the additional configuration files that have to be filled in: | **/config/configuredBridges.json** | Defines the chains that are connected via taiko bridges and lists the contract addresses | | **/config/configuredChains.json** | Defines some metadata for the chains, such as name, icons, explorer URL, etc. | | **/config/configuredRelayer.json** | If chains have a relayer, the URL and the chain IDs it covers are entered here | -| **/config/configuredCustomTokens.json** | Defines a list of tokens that should be available in the token dropdowns | +| **/config/configuredCustomTokens.json** | Defines a list of tokens that should be available in the token dropdowns | --- diff --git a/packages/bridge-ui-v2/src/components/Bridge/Amount.svelte b/packages/bridge-ui-v2/src/components/Bridge/Amount.svelte index aedfe96c918..a916991301d 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/Amount.svelte +++ b/packages/bridge-ui-v2/src/components/Bridge/Amount.svelte @@ -59,6 +59,7 @@ } export async function validateAmount(token = $selectedToken, fee = $processingFee) { + if (!$network?.id) return; $validatingAmount = true; // During validation, we disable all the actions $insufficientBalance = false; $insufficientAllowance = false; diff --git a/packages/bridge-ui-v2/src/components/Bridge/NFTBridge.svelte b/packages/bridge-ui-v2/src/components/Bridge/NFTBridge.svelte index c8fbe1ce3e2..ec150ac1dcf 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/NFTBridge.svelte +++ b/packages/bridge-ui-v2/src/components/Bridge/NFTBridge.svelte @@ -24,7 +24,13 @@ import RecipientStep from './NFTBridgeSteps/RecipientStep.svelte'; import ReviewStep from './NFTBridgeSteps/ReviewStep.svelte'; import type { ProcessingFee } from './ProcessingFee'; - import { activeBridge, destNetwork as destinationChain, selectedNFTs, selectedToken } from './state'; + import { + activeBridge, + destNetwork as destinationChain, + recipientAddress, + selectedNFTs, + selectedToken, + } from './state'; import { NFTSteps } from './types'; let amountComponent: Amount; @@ -35,6 +41,8 @@ let contractAddress: Address | string = ''; let bridgingStatus: 'pending' | 'done' = 'pending'; + let hasEnoughEth: boolean = false; + function onNetworkChange(newNetwork: Network, oldNetwork: Network) { updateForm(); activeStep = NFTSteps.CONFIRM; @@ -89,12 +97,13 @@ if (amountComponent) amountComponent.clearAmount(); if (processingFeeComponent) processingFeeComponent.resetProcessingFee(); if (addressInputComponent) addressInputComponent.clearAddress(); - if (recipientStepComponent) recipientStepComponent.reset(); // Update balance after bridging if (amountComponent) amountComponent.updateBalance(); if (nftIdInputComponent) nftIdInputComponent.clearIds(); + $recipientAddress = $account?.address || null; + // $processingFee = 0n; $selectedToken = ETHToken; importMethod === null; scanned = false; @@ -198,6 +207,8 @@ onDestroy(() => { resetForm(); }); + + $: activeStep === NFTSteps.IMPORT && resetForm();
@@ -224,10 +235,10 @@ bind:validating={validatingImport} /> {:else if activeStep === NFTSteps.REVIEW} - + {:else if activeStep === NFTSteps.RECIPIENT} - + {:else if activeStep === NFTSteps.CONFIRM} diff --git a/packages/bridge-ui-v2/src/components/Bridge/NFTBridgeSteps/RecipientStep.svelte b/packages/bridge-ui-v2/src/components/Bridge/NFTBridgeSteps/RecipientStep.svelte index 5c5f36b2206..c1bdd11be78 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/NFTBridgeSteps/RecipientStep.svelte +++ b/packages/bridge-ui-v2/src/components/Bridge/NFTBridgeSteps/RecipientStep.svelte @@ -7,8 +7,8 @@ export let hasEnoughEth: boolean = false; export const reset = () => { - recipientComponent.clearRecipient(); - processingFeeComponent.resetProcessingFee(); + recipientComponent?.clearRecipient(); + processingFeeComponent?.resetProcessingFee(); }; diff --git a/packages/bridge-ui-v2/src/components/Bridge/NFTBridgeSteps/ReviewStep.svelte b/packages/bridge-ui-v2/src/components/Bridge/NFTBridgeSteps/ReviewStep.svelte index 0360ed51a7a..9cf7b17b9cf 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/NFTBridgeSteps/ReviewStep.svelte +++ b/packages/bridge-ui-v2/src/components/Bridge/NFTBridgeSteps/ReviewStep.svelte @@ -14,7 +14,7 @@ let recipientComponent: Recipient; let processingFeeComponent: ProcessingFee; - let hasEnoughEth: boolean; + export let hasEnoughEth: boolean; const dispatch = createEventDispatcher(); diff --git a/packages/bridge-ui-v2/src/components/Icon/Icon.svelte b/packages/bridge-ui-v2/src/components/Icon/Icon.svelte index ae20a854b8f..8276772077c 100644 --- a/packages/bridge-ui-v2/src/components/Icon/Icon.svelte +++ b/packages/bridge-ui-v2/src/components/Icon/Icon.svelte @@ -153,7 +153,7 @@ class={fillClass} fill-rule="evenodd" clip-rule="evenodd" - d="M14.0003 0.916748C6.7746 0.916748 0.916992 6.77436 0.916992 14.0001C0.916992 21.2258 6.7746 27.0834 14.0003 27.0834C21.2261 27.0834 27.0837 21.2258 27.0837 14.0001C27.0837 6.77436 21.2261 0.916748 14.0003 0.916748ZM15.0067 9.97444C15.0067 9.41862 14.5562 8.96803 14.0003 8.96803C13.4445 8.96803 12.9939 9.41862 12.9939 9.97444V12.9937H9.97468C9.41886 12.9937 8.96828 13.4443 8.96828 14.0001C8.96828 14.5559 9.41886 15.0065 9.97468 15.0065H12.9939V18.0257C12.9939 18.5815 13.4445 19.0321 14.0003 19.0321C14.5562 19.0321 15.0067 18.5815 15.0067 18.0257V15.0065H18.026C18.5818 15.0065 19.0324 14.5559 19.0324 14.0001C19.0324 13.4443 18.5818 12.9937 18.026 12.9937H15.0067V9.97444Z" /> + d="m10,1.98C5.57,1.98,1.98,5.57,1.98,10s3.59,8.02,8.02,8.02,8.02-3.59,8.02-8.02S14.43,1.98,10,1.98Zm.62,5.55c0-.34-.28-.62-.62-.62s-.62.28-.62.62v1.85h-1.85c-.34,0-.62.28-.62.62s.28.62.62.62h1.85v1.85c0,.34.28.62.62.62s.62-.28.62-.62v-1.85h1.85c.34,0,.62-.28.62-.62s-.28-.62-.62-.62h-1.85v-1.85Z" /> {:else if type === 'circle'} {:else if type === 'arrow-right'} diff --git a/packages/bridge-ui-v2/src/components/NFTs/NFTCards/NFTCardGrid.svelte b/packages/bridge-ui-v2/src/components/NFTs/NFTCards/NFTCardGrid.svelte index f868b56e7de..ff5ce8885f1 100644 --- a/packages/bridge-ui-v2/src/components/NFTs/NFTCards/NFTCardGrid.svelte +++ b/packages/bridge-ui-v2/src/components/NFTs/NFTCards/NFTCardGrid.svelte @@ -40,7 +40,7 @@ {nftsGroup[0].type}
-
+
{#each nftsGroup as nft} {@const collectionAddress = nft.addresses[chainId]} {#if collectionAddress === undefined} diff --git a/packages/bridge-ui-v2/src/libs/bridge/checkBalanceToBridge.ts b/packages/bridge-ui-v2/src/libs/bridge/checkBalanceToBridge.ts index 0356106b31d..f5efd98ef73 100644 --- a/packages/bridge-ui-v2/src/libs/bridge/checkBalanceToBridge.ts +++ b/packages/bridge-ui-v2/src/libs/bridge/checkBalanceToBridge.ts @@ -10,200 +10,169 @@ import { getConnectedWallet } from '$libs/util/getConnectedWallet'; import { bridges } from './bridges'; import { ERC20Bridge } from './ERC20Bridge'; import { estimateCostOfBridging } from './estimateCostOfBridging'; -import type { BridgeArgs, ERC20BridgeArgs, ERC1155BridgeArgs, ETHBridgeArgs } from './types'; +import type { ERC20BridgeArgs, ERC1155BridgeArgs, ETHBridgeArgs } from './types'; -type HasEnoughBalanceToBridgeArgs = { +type CheckBalanceToBridgeCommonArgs = { to: Address; - token: Token; amount: bigint | bigint[]; - balance: bigint; srcChainId: number; destChainId: number; fee?: bigint; +}; + +type CheckBalanceToBridgeTokenArgs = CheckBalanceToBridgeCommonArgs & { + token: Token; + balance: bigint; tokenIds?: bigint[]; }; -export async function checkBalanceToBridge({ - to, - token, - amount, - balance, - srcChainId, - destChainId, - fee, - tokenIds, -}: HasEnoughBalanceToBridgeArgs) { - const wallet = await getConnectedWallet(); - let estimatedCost = BigInt(0); +export async function checkBalanceToBridge(args: CheckBalanceToBridgeTokenArgs) { + switch (args.token.type) { + case TokenType.ETH: + return handleEthBridge({ ...args }); + case TokenType.ERC1155: + return handleErc1155Bridge({ ...args }); + case TokenType.ERC20: + return handleErc20Bridge({ ...args }); + default: + throw new Error('Unsupported token type'); + } +} - if (token.type === TokenType.ETH) { - const { bridgeAddress } = routingContractsMap[srcChainId][destChainId]; - const bridgeArgs = { - to, - amount, - wallet, - srcChainId, - destChainId, - fee, - } as BridgeArgs; - - const _amount = amount as bigint; - - try { - estimatedCost = await estimateCostOfBridging(bridges.ETH, { - ...bridgeArgs, - bridgeAddress, - } as ETHBridgeArgs); - } catch (err) { - console.error(err); - - // TODO: rely on error code, or instance, instead of string matching - if (`${err}`.includes('transaction exceeds the balance')) { - throw new InsufficientBalanceError('you do not have enough balance to bridge ETH', { cause: err }); - } - - if (`${err}`.includes('reverted with the following reason: Failed')) { - throw new RevertedWithFailedError('BLL token doing its thing', { cause: err }); - } - } - if (estimatedCost > balance - _amount) { - throw new InsufficientBalanceError('you do not have enough balance to bridge'); - } - } else if (token.type === TokenType.ERC1155) { - const bridgeArgs = { - to, - amounts: [amount], +async function handleEthBridge(args: CheckBalanceToBridgeCommonArgs): Promise { + const { bridgeAddress } = routingContractsMap[args.srcChainId][args.destChainId]; + const _amount = args.amount as bigint; + + const wallet = await getConnectedWallet(); + let estimatedCost; + try { + estimatedCost = await estimateCostOfBridging(bridges.ETH, { + ...args, wallet, - srcChainId, - destChainId, - fee, - tokenIds, - } as ERC1155BridgeArgs; - - const { erc1155VaultAddress } = routingContractsMap[srcChainId][destChainId]; - const tokenAddress = await getAddress({ token, srcChainId, destChainId }); - - // since we are briding a token, we need the ETH balance of the wallet - balance = await getPublicClient().getBalance(wallet.account); - const tokenBalance = token.balance; - const _amount = [amount] as bigint[]; - - if ( - !tokenAddress || - !tokenBalance || - tokenAddress === zeroAddress || - balance === BigInt(0) || - tokenBalance < _amount[0] //TODO: only single token for now - ) - throw new InsufficientBalanceError('you do not have enough balance to bridge'); - - // const bridge = bridges[token.type]; - - // if (bridge instanceof ERC1155Bridge) { - // // Let's check if the vault is approved for all ERC1155 - // const isApprovedForAll = await bridge.isApprovedForAll({ - // tokenAddress, - // owner: wallet.account.address, - // spenderAddress: erc1155VaultAddress, - // tokenId: 0n, - // chainId: srcChainId, - // }); - - // if (!isApprovedForAll) { - // throw new NotApprovedError(`Not approved for all for token`); - // } - // } - - const isTokenAlreadyDeployed = await isDeployedCrossChain({ - token, - srcChainId, - destChainId, - }); + bridgeAddress, + } as ETHBridgeArgs); + } catch (err) { + console.error(err); - try { - estimatedCost = await estimateCostOfBridging(bridges.ERC1155, { - ...bridgeArgs, - token: tokenAddress, - tokenVaultAddress: erc1155VaultAddress, - isTokenAlreadyDeployed, - } as BridgeArgs); - } catch (err) { - console.error(err); - // if (err instanceof ContractFunctionExecutionError) { - // throw err; - // } - // TODO: catch/rethrow other errors + if (`${err}`.includes('transaction exceeds the balance')) { + throw new InsufficientBalanceError('you do not have enough balance to bridge ETH', { cause: err }); } - // no need to deduct the amount we want to bridge from the balance as we pay in ETH - if (estimatedCost > balance) { - throw new InsufficientBalanceError('you do not have enough balance to bridge'); + + if (`${err}`.includes('reverted with the following reason: Failed')) { + throw new RevertedWithFailedError('BLL token doing its thing', { cause: err }); } - } else { - const { erc20VaultAddress } = routingContractsMap[srcChainId][destChainId]; - const tokenAddress = await getAddress({ token, srcChainId, destChainId }); - const _amount = amount as bigint; + } + if (!estimatedCost) throw new Error('estimated cost is undefined'); + const balance = await fetchBalance({ address: wallet.account.address, chainId: args.srcChainId }); - // since we are briding a token, we need the ETH balance of the wallet - balance = await getPublicClient().getBalance(wallet.account); + if (estimatedCost > balance.value - _amount) { + throw new InsufficientBalanceError('you do not have enough balance to bridge'); + } +} - const tokenBalance = await fetchBalance({ - address: wallet.account.address, - token: tokenAddress, - chainId: srcChainId, - }); +async function handleErc1155Bridge(args: CheckBalanceToBridgeTokenArgs) { + const { token, srcChainId, destChainId, amount } = args; - if (!tokenAddress || tokenAddress === zeroAddress || balance === BigInt(0) || tokenBalance.value < _amount) - throw new InsufficientBalanceError('you do not have enough balance to bridge'); + const { erc1155VaultAddress } = routingContractsMap[srcChainId][destChainId]; + const tokenAddress = await getAddress({ token, srcChainId, destChainId }); + const wallet = await getConnectedWallet(); + const balance = await getPublicClient().getBalance(wallet.account); + const tokenBalance = token.balance; + const _amount = [amount] as bigint[]; + + if ( + !tokenAddress || + !tokenBalance || + tokenAddress === zeroAddress || + balance === BigInt(0) || + tokenBalance < _amount[0] //TODO: only single token for now + ) + throw new InsufficientBalanceError('you do not have enough balance to bridge'); + + const isTokenAlreadyDeployed = await isDeployedCrossChain({ + token, + srcChainId, + destChainId, + }); + + let estimatedCost; + try { + estimatedCost = await estimateCostOfBridging(bridges.ERC1155, { + ...args, + wallet, + amounts: _amount, + token: tokenAddress, + tokenVaultAddress: erc1155VaultAddress, + isTokenAlreadyDeployed, + } as ERC1155BridgeArgs); + } catch (err) { + console.error(err); + // TODO: catch/rethrow other errors + } - const bridge = bridges[token.type]; + if (!estimatedCost) throw new Error('estimated cost is undefined'); + if (estimatedCost && estimatedCost > balance) { + throw new InsufficientBalanceError('you do not have enough balance to bridge'); + } +} - if (bridge instanceof ERC20Bridge) { - // Let's check the allowance to actually bridge the ERC20 token - const allowance = await bridge.requireAllowance({ - amount: _amount, - tokenAddress, - ownerAddress: wallet.account.address, - spenderAddress: erc20VaultAddress, - }); +async function handleErc20Bridge(args: CheckBalanceToBridgeTokenArgs): Promise { + const { token, srcChainId, destChainId, amount, balance } = args; + const wallet = await getConnectedWallet(); + const { erc20VaultAddress } = routingContractsMap[srcChainId][destChainId]; + const tokenAddress = await getAddress({ token, srcChainId, destChainId }); + const _amount = amount as bigint; + + const tokenBalance = await fetchBalance({ + address: wallet.account.address, + token: tokenAddress, + chainId: args.srcChainId, + }); + + if (!tokenAddress || tokenAddress === zeroAddress || args.balance === BigInt(0) || tokenBalance.value < _amount) + throw new InsufficientBalanceError('you do not have enough balance to bridge'); + + const bridge = bridges[args.token.type]; + + if (bridge instanceof ERC20Bridge) { + // Let's check the allowance to actually bridge the ERC20 token + const allowance = await bridge.requireAllowance({ + amount: _amount, + tokenAddress, + ownerAddress: wallet.account.address, + spenderAddress: erc20VaultAddress, + }); - if (allowance) { - throw new InsufficientAllowanceError(`insufficient allowance for the amount ${_amount}`); - } + if (allowance) { + throw new InsufficientAllowanceError(`insufficient allowance for the amount ${_amount}`); } + } - const isTokenAlreadyDeployed = await isDeployedCrossChain({ - token, - srcChainId, - destChainId, - }); + const isTokenAlreadyDeployed = await isDeployedCrossChain({ + token, + srcChainId, + destChainId, + }); - try { - const bridgeArgs = { - to, - amount, - wallet, - srcChainId, - destChainId, - fee, - } as BridgeArgs; - - estimatedCost = await estimateCostOfBridging(bridges.ERC20, { - ...bridgeArgs, - token: tokenAddress, - tokenVaultAddress: erc20VaultAddress, - isTokenAlreadyDeployed, - } as ERC20BridgeArgs); - } catch (err) { - console.error(err); - - // TODO: same here. Error code or instance would be better - if (`${err}`.includes('insufficient allowance')) { - throw new InsufficientAllowanceError(`insufficient allowance for the amount ${amount}`, { cause: err }); - } - } - // no need to deduct the amount we want to bridge from the balance as we pay in ETH - if (estimatedCost > balance) { - throw new InsufficientBalanceError('you do not have enough balance to bridge'); + let estimatedCost; + try { + estimatedCost = await estimateCostOfBridging(bridges.ERC20, { + ...args, + wallet, + token: tokenAddress, + tokenVaultAddress: erc20VaultAddress, + isTokenAlreadyDeployed, + } as ERC20BridgeArgs); + } catch (err) { + console.error(err); + + // TODO: same here. Error code or instance would be better + if (`${err}`.includes('insufficient allowance')) { + throw new InsufficientAllowanceError(`insufficient allowance for the amount ${_amount}`, { cause: err }); } } + if (!estimatedCost) throw new Error('estimated cost is undefined'); + if (estimatedCost > balance) { + throw new InsufficientBalanceError('you do not have enough balance to bridge'); + } }