From 9f0ce71f758a40efc98a2fc59f7c1fba67d5ff1f Mon Sep 17 00:00:00 2001 From: Korbinian Date: Sun, 21 Jan 2024 02:03:36 +0100 Subject: [PATCH 1/4] new canonical check util --- .../bridge-ui-v2/src/libs/error/errors.ts | 4 + .../libs/token/getCanonicalInfoForToken.ts | 150 ++++++++++++++++++ packages/bridge-ui-v2/src/libs/token/types.ts | 6 + packages/bridge-ui-v2/src/stores/canonical.ts | 32 ++++ 4 files changed, 192 insertions(+) create mode 100644 packages/bridge-ui-v2/src/libs/token/getCanonicalInfoForToken.ts create mode 100644 packages/bridge-ui-v2/src/stores/canonical.ts diff --git a/packages/bridge-ui-v2/src/libs/error/errors.ts b/packages/bridge-ui-v2/src/libs/error/errors.ts index 36f695ae926..9d0d3fd41ba 100644 --- a/packages/bridge-ui-v2/src/libs/error/errors.ts +++ b/packages/bridge-ui-v2/src/libs/error/errors.ts @@ -117,3 +117,7 @@ export class BridgePausedError extends Error { export class FilterLogsError extends Error { name = 'FilterLogsError'; } + +export class NoCanonicalInfoFoundError extends Error { + name = 'NoCanonicalInfoFoundError'; +} diff --git a/packages/bridge-ui-v2/src/libs/token/getCanonicalInfoForToken.ts b/packages/bridge-ui-v2/src/libs/token/getCanonicalInfoForToken.ts new file mode 100644 index 00000000000..f27ca2dcbb2 --- /dev/null +++ b/packages/bridge-ui-v2/src/libs/token/getCanonicalInfoForToken.ts @@ -0,0 +1,150 @@ +import { getContract } from '@wagmi/core'; +import { type Abi, type Address, zeroAddress } from 'viem'; + +import { erc20VaultABI, erc721VaultABI, erc1155VaultABI } from '$abi'; +import { routingContractsMap } from '$bridgeConfig'; +import { NoCanonicalInfoFoundError } from '$libs/error'; +import { getLogger } from '$libs/util/logger'; +import { getCanonicalStatusFromStore, setCanonicalTokenInfoStore } from '$stores/canonical'; + +import { type GetTokenInfo, TokenType } from './types'; + +const log = getLogger('token:getCanonicalInfoForToken'); + +type CheckCanonicalStatusArgs = { + address: Address; + srcChainId: number; + destChainId: number; + type: TokenType; +}; + +type CanonicalInfo = { + chainId: number; + address: Address; +}; + +const _getStatus = async ({ address, srcChainId, destChainId, type }: CheckCanonicalStatusArgs) => { + const srcChainTokenAddress = address; + + const vaultABI = + type === TokenType.ERC721 ? erc721VaultABI : type === TokenType.ERC1155 ? erc1155VaultABI : erc20VaultABI; + + const vaultAddressKey = + type === TokenType.ERC721 + ? 'erc721VaultAddress' + : type === TokenType.ERC1155 + ? 'erc1155VaultAddress' + : 'erc20VaultAddress'; + + const srcTokenVaultContract = getContract({ + abi: vaultABI as Abi, + chainId: srcChainId, + address: routingContractsMap[srcChainId][destChainId][vaultAddressKey], + }); + + const destTokenVaultContract = getContract({ + abi: vaultABI as Abi, + chainId: destChainId, + address: routingContractsMap[destChainId][srcChainId][vaultAddressKey], + }); + + let canonicalTokenAddress: Address; + let canonicalChain: number; + + log('checking', srcChainTokenAddress, srcChainId, destChainId); + + const srcCanonicalTokenInfo = (await srcTokenVaultContract.read.bridgedToCanonical([ + srcChainTokenAddress, + ])) as Address; + const srcCanonicalCheck = srcCanonicalTokenInfo[1] as Address; + + const destCanonicalTokenInfo = (await destTokenVaultContract.read.bridgedToCanonical([ + srcChainTokenAddress, + ])) as Address; + + const destCanonicalCheck = destCanonicalTokenInfo[1] as Address; + + if (srcCanonicalCheck === zeroAddress && destCanonicalCheck === zeroAddress) { + // if both are zero we are dealing with a canonical address + canonicalTokenAddress = srcChainTokenAddress; + canonicalChain = srcChainId; + } else if (destCanonicalCheck !== zeroAddress) { + // if the destination is not zero, we found a canonical address there + canonicalTokenAddress = destCanonicalCheck; + canonicalChain = srcChainId; + } else { + // if the source is not zero, we found a canonical address there + canonicalTokenAddress = srcCanonicalCheck; + canonicalChain = destChainId; + } + return { canonicalTokenAddress, canonicalChain }; +}; + +export async function getCanonicalInfoForToken({ + token, + srcChainId, + destChainId, +}: GetTokenInfo): Promise { + if (token.type === TokenType.ETH) return null; // ETH doesn't have an address + log( + `Find canonicalInfo for ${token.type} token ${token.symbol} (${token.name}) from chain ${srcChainId} to chain ${destChainId}`, + token, + ); + + if (token.addresses[srcChainId] && token.addresses[destChainId]) { + // we already have addresses for both, lets find the canonical one + log('addresses for both, fetching canonical one'); + for (const [currentSrcChainId, address] of Object.entries(token.addresses)) { + if (parseInt(currentSrcChainId) === destChainId) continue; + + // check store first + if (getCanonicalStatusFromStore(address)) { + log('found canonical address in store', address); + return { chainId: srcChainId, address }; + } + log('fetching new canonical info'); + + const { canonicalTokenAddress, canonicalChain } = await _getStatus({ + address, + srcChainId: parseInt(currentSrcChainId), + destChainId, + type: token.type, + }); + if (canonicalTokenAddress && canonicalChain) { + log(`Found canonical address ${canonicalTokenAddress} on chain ${canonicalChain}`); + setCanonicalTokenInfoStore(canonicalTokenAddress, true, canonicalChain); + return { chainId: canonicalChain, address: canonicalTokenAddress }; + } + } + } else { + // we take the first address we have and find the canonical one + log('fetching canonical address for first address we have'); + const srcChainTokenAddress = Object.values(token.addresses)[0]; + const srcChainTokenChainId = Object.keys(token.addresses)[0]; + + if (!srcChainTokenAddress || !srcChainTokenChainId) + throw new NoCanonicalInfoFoundError('No canonical info found for token'); + + // check store first + if (getCanonicalStatusFromStore(srcChainTokenAddress)) { + log('found canonical address in store', srcChainTokenAddress); + return { chainId: parseInt(srcChainTokenChainId), address: srcChainTokenAddress }; + } + log('fetching new canonical info'); + + const { canonicalTokenAddress, canonicalChain } = await _getStatus({ + address: srcChainTokenAddress, + srcChainId: parseInt(srcChainTokenChainId), + destChainId, + type: token.type, + }); + + if (canonicalTokenAddress && canonicalChain) { + log(`Found canonical address ${canonicalTokenAddress} on chain ${canonicalChain}`); + setCanonicalTokenInfoStore(canonicalTokenAddress, true, canonicalChain); + return { chainId: canonicalChain, address: canonicalTokenAddress }; + } + } + log('No canonical info found for token', token, srcChainId, destChainId); + throw new NoCanonicalInfoFoundError('No canonical info found for token'); +} diff --git a/packages/bridge-ui-v2/src/libs/token/types.ts b/packages/bridge-ui-v2/src/libs/token/types.ts index 29d251b183b..bf94d506e11 100644 --- a/packages/bridge-ui-v2/src/libs/token/types.ts +++ b/packages/bridge-ui-v2/src/libs/token/types.ts @@ -31,6 +31,12 @@ export type NFT = Token & { metadata?: NFTMetadata; }; +export type GetTokenInfo = { + token: Token; + srcChainId: number; + destChainId: number; +}; + // Based on https://docs.opensea.io/docs/metadata-standards export type NFTMetadata = { description: string; diff --git a/packages/bridge-ui-v2/src/stores/canonical.ts b/packages/bridge-ui-v2/src/stores/canonical.ts new file mode 100644 index 00000000000..00e689ffc87 --- /dev/null +++ b/packages/bridge-ui-v2/src/stores/canonical.ts @@ -0,0 +1,32 @@ +import type { Address } from '@wagmi/core'; +import { get, writable } from 'svelte/store'; + +import { getLogger } from '$libs/util/logger'; + +const log = getLogger('token:canonical'); + +type TokenInfo = { + isCanonical: boolean; + chainId: number; +}; + +type CanonicalTokens = Record; +export const canonicalTokens = writable({}); + +export const setCanonicalTokenInfoStore = (tokenAddress: Address, isCanonical: boolean, chainId: number) => { + canonicalTokens.update((currentTokens) => { + return { ...currentTokens, [tokenAddress]: { isCanonical, chainId } }; + }); +}; + +export const getCanonicalStatusFromStore = (tokenAddress: Address): boolean => { + log('getting canonical token status from store', tokenAddress); + const tokens = get(canonicalTokens); + return tokens[tokenAddress]?.isCanonical ?? false; +}; + +export const getCanonicalTokenInfoStore = (tokenAddress: Address): TokenInfo => { + log('getting canonical token info from store', tokenAddress); + const tokens = get(canonicalTokens); + return tokens[tokenAddress]; +}; From 5a081ba099310800f5d62ba660430de26453ed4b Mon Sep 17 00:00:00 2001 From: Korbinian Date: Sun, 21 Jan 2024 02:04:06 +0100 Subject: [PATCH 2/4] update approval flows --- .../src/components/Bridge/Actions.svelte | 78 ++++++++++++++----- .../Bridge/NFTBridgeSteps/ImportStep.svelte | 4 +- packages/bridge-ui-v2/src/i18n/en.json | 3 +- .../src/libs/bridge/checkBalanceToBridge.ts | 41 +++++++--- .../libs/token/checkTokenApprovalStatus.ts | 19 ++++- 5 files changed, 108 insertions(+), 37 deletions(-) diff --git a/packages/bridge-ui-v2/src/components/Bridge/Actions.svelte b/packages/bridge-ui-v2/src/components/Bridge/Actions.svelte index d6cff7617f5..12c0a3e73e5 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/Actions.svelte +++ b/packages/bridge-ui-v2/src/components/Bridge/Actions.svelte @@ -7,6 +7,7 @@ import { BridgePausedError } from '$libs/error'; import { TokenType } from '$libs/token'; import { checkTokenApprovalStatus } from '$libs/token/checkTokenApprovalStatus'; + import { getCanonicalInfoForToken } from '$libs/token/getCanonicalInfoForToken'; import { account, network } from '$stores'; import { @@ -49,14 +50,19 @@ bridge(); } - onMount(() => { + onMount(async () => { $validatingAmount = true; - checkTokenApprovalStatus($selectedToken); + await checkBridgedStatus(); + if (tokenIsBridged) { + $allApproved = true; + $insufficientAllowance = false; + } else { + checkTokenApprovalStatus($selectedToken); + } isValidTokenBalance(); $validatingAmount = false; }); - //TODO: does this check entered balance?! const isValidTokenBalance = () => { if ($tokenBalance && typeof $tokenBalance !== 'bigint') { if (isETH) { @@ -76,9 +82,33 @@ } }; + const checkBridgedStatus = async () => { + if ($selectedToken?.type === TokenType.ETH) { + return true; + } + const srcChainId = $network?.id; + const destChainId = $destNetwork?.id; + + if (!srcChainId || !destChainId || !$selectedToken) { + tokenIsBridged = false; + return; + } + + const canonicalInfo = await getCanonicalInfoForToken({ token: $selectedToken, srcChainId, destChainId }); + if (!canonicalInfo || canonicalInfo.chainId === srcChainId) { + tokenIsBridged = false; + return; + } + + // override checks if the token is bridged + $allApproved = true; + $insufficientAllowance = false; + return true; + }; + $: isValidBalance = false; - $: validating = $validatingAmount && $enteredAmount > 0; + // $: validating = $enteredAmount > 0; // Basic conditions so we can even start the bridging process $: hasAddress = $recipientAddress || $account?.address; @@ -88,21 +118,29 @@ $: canDoNothing = !hasAddress || !hasNetworks || !hasBalance || !$selectedToken || disabled; // Conditions for approve/bridge steps - $: if ($enteredAmount) { $validatingAmount = true; - checkTokenApprovalStatus($selectedToken); + checkBridgedStatus().then(() => { + if (!tokenIsBridged) checkTokenApprovalStatus($selectedToken); + }); + isValidTokenBalance(); } + $: if ($selectedToken) checkBridgedStatus(); + + $: tokenIsBridged = false; + // Conditions to disable/enable buttons - $: disableApprove = isERC20 - ? canDoNothing || $insufficientBalance || $validatingAmount || approving || $allApproved || !$enteredAmount - : isERC721 - ? $allApproved || approving - : isERC1155 + $: disableApprove = + !tokenIsBridged && + (isERC20 + ? canDoNothing || $insufficientBalance || $validatingAmount || approving || $allApproved || !$enteredAmount + : isERC721 ? $allApproved || approving - : approving; + : isERC1155 + ? $allApproved || approving + : approving); $: isERC20 = $selectedToken?.type === TokenType.ERC20; $: isERC721 = $selectedToken?.type === TokenType.ERC721; @@ -144,23 +182,23 @@
- {#if $selectedToken && !isETH} + {#if $selectedToken && !isETH && !tokenIsBridged} - {#if validating && !approving} - Checking ... + {#if $validatingAmount && !approving} + {$t('bridge.button.validating')}. {/if} {#if approving} {$t('bridge.button.approving')} - {:else if $allApproved && !validating && $enteredAmount > 0} + {:else if $allApproved && !$validatingAmount && $enteredAmount > 0}
{$t('bridge.button.approved')}
- {:else if !validating} + {:else if !$validatingAmount} {$t('bridge.button.approve')} {/if}
@@ -178,11 +216,11 @@
- {#if $selectedToken && !isETH} + {#if $selectedToken && !isETH && !tokenIsBridged} {#if approving} {$t('bridge.button.approving')} diff --git a/packages/bridge-ui-v2/src/components/Bridge/NFTBridgeSteps/ImportStep.svelte b/packages/bridge-ui-v2/src/components/Bridge/NFTBridgeSteps/ImportStep.svelte index 29253338079..d6c34b8b65b 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/NFTBridgeSteps/ImportStep.svelte +++ b/packages/bridge-ui-v2/src/components/Bridge/NFTBridgeSteps/ImportStep.svelte @@ -262,8 +262,7 @@ $selectedNFTs && $selectedNFTs.length > 0 && $selectedNFTs[0].type === TokenType.ERC1155 && - enteredIds && - enteredIds.length > 0 && + (importMethod === ImportMethod.MANUAL ? enteredIds && enteredIds.length > 0 : true) && !validating; $: showNFTAmountInput = nftHasAmount && isOwnerOfAllToken; @@ -331,6 +330,7 @@ Manual NFT Input + {#if !scanned || nothingFound}
diff --git a/packages/bridge-ui-v2/src/i18n/en.json b/packages/bridge-ui-v2/src/i18n/en.json index 82ba2be6cee..55e0f2e9055 100644 --- a/packages/bridge-ui-v2/src/i18n/en.json +++ b/packages/bridge-ui-v2/src/i18n/en.json @@ -43,7 +43,8 @@ "bridge": "Bridge", "bridging": "Bridging", "fetch": "Fetch NFT data", - "import": "Import" + "import": "Import", + "validating": "Validating..." }, "description": { "default": "Send your assets across chains.", diff --git a/packages/bridge-ui-v2/src/libs/bridge/checkBalanceToBridge.ts b/packages/bridge-ui-v2/src/libs/bridge/checkBalanceToBridge.ts index ec8e2dae67b..d1492e6c8bf 100644 --- a/packages/bridge-ui-v2/src/libs/bridge/checkBalanceToBridge.ts +++ b/packages/bridge-ui-v2/src/libs/bridge/checkBalanceToBridge.ts @@ -5,18 +5,23 @@ import { routingContractsMap } from '$bridgeConfig'; import { InsufficientAllowanceError, InsufficientBalanceError, + NoCanonicalInfoFoundError, RevertedWithFailedError, RevertedWithoutMessageError, } from '$libs/error'; import { getAddress, type Token, TokenType } from '$libs/token'; +import { getCanonicalInfoForToken } from '$libs/token/getCanonicalInfoForToken'; import { isDeployedCrossChain } from '$libs/token/isDeployedCrossChain'; import { getConnectedWallet } from '$libs/util/getConnectedWallet'; +import { getLogger } from '$libs/util/logger'; import { bridges } from './bridges'; import { ERC20Bridge } from './ERC20Bridge'; import { estimateCostOfBridging } from './estimateCostOfBridging'; import type { ERC20BridgeArgs, ERC1155BridgeArgs, ETHBridgeArgs } from './types'; +const log = getLogger('token:checkBalanceToBridge'); + type CheckBalanceToBridgeCommonArgs = { to: Address; amount: bigint | bigint[]; @@ -96,6 +101,19 @@ async function handleErc1155Bridge(args: CheckBalanceToBridgeTokenArgs) { ) throw new InsufficientBalanceError('you do not have enough balance to bridge'); + const canonicalTokenInfo = await getCanonicalInfoForToken({ + token, + srcChainId, + destChainId, + }); + if (!canonicalTokenInfo) throw new NoCanonicalInfoFoundError(); + const { address: canonicalTokenAddress } = canonicalTokenInfo; + if (canonicalTokenAddress !== tokenAddress) { + // we have a bridged token, no need for allowance check as we will burn the token + log('token is bridged, no need for allowance check'); + return; + } + const isTokenAlreadyDeployed = await isDeployedCrossChain({ token, srcChainId, @@ -142,16 +160,17 @@ async function handleErc20Bridge(args: CheckBalanceToBridgeTokenArgs): Promise): Promi const tokenAddress = get(selectedToken)?.addresses[currentChainId]; log('selectedToken', get(selectedToken)); + const canonicalTokenInfo = await getCanonicalInfoForToken({ + token, + srcChainId: currentChainId, + destChainId: destinationChainId, + }); + if (!canonicalTokenInfo) throw new NoCanonicalInfoFoundError(); + const { address: canonicalTokenAddress } = canonicalTokenInfo; + if (canonicalTokenAddress !== tokenAddress) { + // we have a bridged token, no need for allowance check as we will burn the token + log('token is bridged, no need for allowance check'); + return; + } + if (!ownerAddress || !tokenAddress) { log('no ownerAddress or tokenAddress', ownerAddress, tokenAddress); return; @@ -71,7 +86,7 @@ export const checkTokenApprovalStatus = async (token: Maybe): Promi allApproved.set(false); } } else if (token.type === TokenType.ERC721 || token.type === TokenType.ERC1155) { - log('checking approval status for NFT'); + log('checking approval status for NFT type' + token.type); const nft = token as NFT; const ownerShipChecks = await checkOwnershipOfNFT(token as NFT, ownerAddress, currentChainId); if (!ownerShipChecks.every((item) => item.isOwner === true)) { @@ -99,7 +114,6 @@ export const checkTokenApprovalStatus = async (token: Maybe): Promi }; if (nft.type === TokenType.ERC1155) { - log('checking approval status for ERC1155'); const bridge = bridges[nft.type] as ERC1155Bridge; try { // Let's check if the vault is approved for all ERC1155 @@ -109,7 +123,6 @@ export const checkTokenApprovalStatus = async (token: Maybe): Promi console.error('isApprovedForAll error'); } } else if (nft.type === TokenType.ERC721) { - log('checking approval status for ERC1155'); const bridge = bridges[nft.type] as ERC721Bridge; try { // Let's check if the vault is approved for all ERC721 From e058c6d80463860c8493592f3be9237645428f59 Mon Sep 17 00:00:00 2001 From: Korbinian Date: Mon, 22 Jan 2024 16:45:27 +0100 Subject: [PATCH 3/4] added checks and bypasses for bridged tokens --- .../src/components/Bridge/Actions.svelte | 54 ++++------------- .../src/components/Bridge/Bridge.svelte | 7 ++- .../src/components/Bridge/NFTBridge.svelte | 49 ++++++++++------ .../NFTBridgeSteps/ConfirmationStep.svelte | 4 +- .../Bridge/NFTBridgeSteps/ImportStep.svelte | 12 ++-- .../Bridge/NFTBridgeSteps/ReviewStep.svelte | 20 ++++++- .../src/components/Bridge/state.ts | 1 + .../TokenDropdown/TokenDropdown.svelte | 22 +++++-- .../src/libs/bridge/ERC1155Bridge.ts | 32 ++++++---- .../src/libs/bridge/ERC20Bridge.ts | 33 +++++++---- .../src/libs/bridge/ERC721Bridge.ts | 30 ++++++---- .../src/libs/bridge/checkBalanceToBridge.ts | 2 +- .../bridge-ui-v2/src/libs/error/errors.ts | 12 ++++ ...calInfoForToken.ts => getCanonicalInfo.ts} | 58 ++++++++++++------- ...valStatus.ts => getTokenApprovalStatus.ts} | 56 +++++++++++++----- 15 files changed, 241 insertions(+), 151 deletions(-) rename packages/bridge-ui-v2/src/libs/token/{getCanonicalInfoForToken.ts => getCanonicalInfo.ts} (79%) rename packages/bridge-ui-v2/src/libs/token/{checkTokenApprovalStatus.ts => getTokenApprovalStatus.ts} (73%) diff --git a/packages/bridge-ui-v2/src/components/Bridge/Actions.svelte b/packages/bridge-ui-v2/src/components/Bridge/Actions.svelte index 12c0a3e73e5..dcfacf59005 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/Actions.svelte +++ b/packages/bridge-ui-v2/src/components/Bridge/Actions.svelte @@ -6,8 +6,7 @@ import { Icon } from '$components/Icon'; import { BridgePausedError } from '$libs/error'; import { TokenType } from '$libs/token'; - import { checkTokenApprovalStatus } from '$libs/token/checkTokenApprovalStatus'; - import { getCanonicalInfoForToken } from '$libs/token/getCanonicalInfoForToken'; + import { getTokenApprovalStatus } from '$libs/token/getTokenApprovalStatus'; import { account, network } from '$stores'; import { @@ -20,6 +19,7 @@ insufficientBalance, recipientAddress, selectedToken, + selectedTokenIsBridged, tokenBalance, validatingAmount, } from './state'; @@ -52,12 +52,12 @@ onMount(async () => { $validatingAmount = true; - await checkBridgedStatus(); - if (tokenIsBridged) { + // await checkBridgedStatus(); + if ($selectedTokenIsBridged) { $allApproved = true; $insufficientAllowance = false; } else { - checkTokenApprovalStatus($selectedToken); + getTokenApprovalStatus($selectedToken); } isValidTokenBalance(); $validatingAmount = false; @@ -82,34 +82,8 @@ } }; - const checkBridgedStatus = async () => { - if ($selectedToken?.type === TokenType.ETH) { - return true; - } - const srcChainId = $network?.id; - const destChainId = $destNetwork?.id; - - if (!srcChainId || !destChainId || !$selectedToken) { - tokenIsBridged = false; - return; - } - - const canonicalInfo = await getCanonicalInfoForToken({ token: $selectedToken, srcChainId, destChainId }); - if (!canonicalInfo || canonicalInfo.chainId === srcChainId) { - tokenIsBridged = false; - return; - } - - // override checks if the token is bridged - $allApproved = true; - $insufficientAllowance = false; - return true; - }; - $: isValidBalance = false; - // $: validating = $enteredAmount > 0; - // Basic conditions so we can even start the bridging process $: hasAddress = $recipientAddress || $account?.address; $: hasNetworks = $network?.id && $destNetwork?.id; @@ -120,20 +94,12 @@ // Conditions for approve/bridge steps $: if ($enteredAmount) { $validatingAmount = true; - checkBridgedStatus().then(() => { - if (!tokenIsBridged) checkTokenApprovalStatus($selectedToken); - }); - isValidTokenBalance(); } - $: if ($selectedToken) checkBridgedStatus(); - - $: tokenIsBridged = false; - // Conditions to disable/enable buttons $: disableApprove = - !tokenIsBridged && + !$selectedTokenIsBridged && (isERC20 ? canDoNothing || $insufficientBalance || $validatingAmount || approving || $allApproved || !$enteredAmount : isERC721 @@ -147,8 +113,10 @@ $: isERC1155 = $selectedToken?.type === TokenType.ERC1155; $: isETH = $selectedToken?.type === TokenType.ETH; + $: validApprovalStatus = $selectedTokenIsBridged ? true : $allApproved; + $: commonConditions = - $allApproved && + validApprovalStatus && !bridging && hasAddress && hasNetworks && @@ -182,7 +150,7 @@
- {#if $selectedToken && !isETH && !tokenIsBridged} + {#if !$selectedTokenIsBridged && $selectedToken && !isETH}
- {#if $selectedToken && !isETH && !tokenIsBridged} + {#if $selectedToken && !isETH && !$selectedTokenIsBridged} { if (!$network?.id) throw new Error('network not found'); - const srcChainId = $network?.id; + const srcChainId = $network.id; + const destChainId = $destinationChain?.id; const tokenId = nftIdArray[0]; - if (isAddress(contractAddress) && srcChainId) - await getTokenWithInfoFromAddress({ contractAddress, srcChainId: srcChainId, tokenId, owner: $account?.address }) - .then(async (token) => { - if (!token) throw new Error('no token with info'); - // detectedTokenType = token.type; - // idInputState = IDInputState.VALID; - $selectedToken = token; - await prefetchImage(); - - nextStep(); - }) - .catch((err) => { - console.error(err); - // detectedTokenType = null; - // idInputState = IDInputState.INVALID; - // invalidToken = true; - }); + if (!isAddress(contractAddress) || !srcChainId || !destChainId) { + return; + } + + try { + const token = await getTokenWithInfoFromAddress({ + contractAddress, + srcChainId, + tokenId, + owner: $account?.address, + }); + if (!token) throw new Error('no token with info'); + + $selectedToken = token; + + const [, info] = await Promise.all([ + prefetchImage(), + getCanonicalInfoForToken({ token, srcChainId, destChainId }), + ]); + + if (info) $selectedTokenIsBridged = contractAddress !== info.address; + + nextStep(); + } catch (err) { + console.error(err); + } }; const handleTransactionDetailsClick = () => { diff --git a/packages/bridge-ui-v2/src/components/Bridge/NFTBridgeSteps/ConfirmationStep.svelte b/packages/bridge-ui-v2/src/components/Bridge/NFTBridgeSteps/ConfirmationStep.svelte index 862cfde9924..2ca2b418cdf 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/NFTBridgeSteps/ConfirmationStep.svelte +++ b/packages/bridge-ui-v2/src/components/Bridge/NFTBridgeSteps/ConfirmationStep.svelte @@ -17,8 +17,8 @@ import { BridgePausedError } from '$libs/error'; import { bridgeTxService } from '$libs/storage'; import { TokenType } from '$libs/token'; - import { checkTokenApprovalStatus } from '$libs/token/checkTokenApprovalStatus'; import { getCrossChainAddress } from '$libs/token/getCrossChainAddress'; + import { getTokenApprovalStatus } from '$libs/token/getTokenApprovalStatus'; import { isBridgePaused } from '$libs/util/checkForPausedContracts'; import { getConnectedWallet } from '$libs/util/getConnectedWallet'; import { account } from '$stores/account'; @@ -135,7 +135,7 @@ await pendingTransactions.add(approveTxHash, $network.id); - await checkTokenApprovalStatus($selectedToken); + await getTokenApprovalStatus($selectedToken); successToast({ title: $t('bridge.actions.approve.success.title'), diff --git a/packages/bridge-ui-v2/src/components/Bridge/NFTBridgeSteps/ImportStep.svelte b/packages/bridge-ui-v2/src/components/Bridge/NFTBridgeSteps/ImportStep.svelte index d6c34b8b65b..f5cd11a6392 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/NFTBridgeSteps/ImportStep.svelte +++ b/packages/bridge-ui-v2/src/components/Bridge/NFTBridgeSteps/ImportStep.svelte @@ -32,10 +32,10 @@ import { PUBLIC_SLOW_L1_BRIDGING_WARNING } from '$env/static/public'; import { fetchNFTs } from '$libs/bridge/fetchNFTs'; import { LayerType } from '$libs/chain'; + import { InternalError, InvalidParametersProvidedError, WrongOwnerError } from '$libs/error'; import { detectContractType, type NFT, TokenType } from '$libs/token'; import { checkOwnership } from '$libs/token/checkOwnership'; import { getTokenWithInfoFromAddress } from '$libs/token/getTokenWithInfoFromAddress'; - import { noop } from '$libs/util/noop'; import { account } from '$stores/account'; import { network } from '$stores/network'; @@ -47,8 +47,6 @@ export let validating: boolean = false; export let contractAddress: Address | string = ''; - export const prefetchImage = () => noop(); - let enteredIds: number[] = []; let scanning: boolean; @@ -148,7 +146,7 @@ const tokenId: number = nftIdArray[0]; // Handle multiple tokens if needed if (typeof tokenId !== 'number') { - throw new Error('Token ID is not a number'); + throw new InvalidParametersProvidedError('Token ID is not a number'); } const ownershipResults = await checkOwnership( @@ -166,7 +164,7 @@ isOwnerOfAllToken = ownershipResults.every((value) => value.isOwner === true); if (!isOwnerOfAllToken) { - throw new Error('Not owner of all tokens'); + throw new WrongOwnerError('Not owner of all NFTs'); } const token = await getTokenWithInfoFromAddress({ @@ -179,7 +177,7 @@ }); if (!token) { - throw new Error('No token with info'); + throw new InternalError('unable to get token'); } detectedTokenType = token.type; @@ -188,8 +186,6 @@ idInputState = IDInputState.VALID; $tokenBalance = token.balance; - - await prefetchImage(); } else { idInputState = IDInputState.INVALID; } 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 0521cec0140..e721cc7549d 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/NFTBridgeSteps/ReviewStep.svelte +++ b/packages/bridge-ui-v2/src/components/Bridge/NFTBridgeSteps/ReviewStep.svelte @@ -1,14 +1,20 @@
diff --git a/packages/bridge-ui-v2/src/components/Bridge/state.ts b/packages/bridge-ui-v2/src/components/Bridge/state.ts index a8582deeb18..0f5b019d5b1 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/state.ts +++ b/packages/bridge-ui-v2/src/components/Bridge/state.ts @@ -47,6 +47,7 @@ export const insufficientBalance = writable(false); export const insufficientAllowance = writable(false); export const allApproved = writable(false); +export const selectedTokenIsBridged = writable(false); // Derived state export const bridgeService = derived(selectedToken, (token) => (token ? bridges[token.type] : null)); diff --git a/packages/bridge-ui-v2/src/components/TokenDropdown/TokenDropdown.svelte b/packages/bridge-ui-v2/src/components/TokenDropdown/TokenDropdown.svelte index b22847add21..69bcd7a71af 100644 --- a/packages/bridge-ui-v2/src/components/TokenDropdown/TokenDropdown.svelte +++ b/packages/bridge-ui-v2/src/components/TokenDropdown/TokenDropdown.svelte @@ -10,16 +10,20 @@ import { closeOnEscapeOrOutsideClick } from '$libs/customActions'; import { tokenService } from '$libs/storage/services'; import { ETHToken, type Token } from '$libs/token'; + import { getCanonicalInfoForToken } from '$libs/token/getCanonicalInfo'; import { getCrossChainAddress } from '$libs/token/getCrossChainAddress'; + import { getLogger } from '$libs/util/logger'; import { uid } from '$libs/util/uid'; import { account } from '$stores/account'; import { network } from '$stores/network'; - import { destNetwork } from '../Bridge/state'; + import { destNetwork, selectedTokenIsBridged } from '../Bridge/state'; import DialogView from './DialogView.svelte'; import DropdownView from './DropdownView.svelte'; import { symbolToIconMap } from './symbolToIconMap'; + const log = getLogger('TokenDropdown'); + export let tokens: Token[] = []; export let value: Maybe = null; export let onlyMintable: boolean = false; @@ -61,15 +65,15 @@ warningToast({ title: $t('messages.network.required') }); return; } + if (!destChain || !destChain.id) { + warningToast({ title: $t('messages.network.required_dest') }); + return; + } // if it is an imported Token, chances are we do not yet have the bridged address // for the destination chain, so we need to fetch it if (token.imported) { // ... in the case of imported tokens, we also require the destination chain to be selected. if (!destChain) { - if (!destChain) { - warningToast({ title: $t('messages.network.required_dest') }); - return; - } let bridgedAddress = null; @@ -91,7 +95,13 @@ } } value = token; - + const info = await getCanonicalInfoForToken({ token, srcChainId: srcChain.id, destChainId: destChain.id }); + if (info && value.addresses[srcChain.id] !== info.address) { + log('selected token is not canonical'); + $selectedTokenIsBridged = true; + } else { + $selectedTokenIsBridged = false; + } closeMenu(); }; diff --git a/packages/bridge-ui-v2/src/libs/bridge/ERC1155Bridge.ts b/packages/bridge-ui-v2/src/libs/bridge/ERC1155Bridge.ts index 05d8474b776..304a52f1a62 100644 --- a/packages/bridge-ui-v2/src/libs/bridge/ERC1155Bridge.ts +++ b/packages/bridge-ui-v2/src/libs/bridge/ERC1155Bridge.ts @@ -6,11 +6,13 @@ import { bridgeService } from '$config'; import { ApproveError, NoApprovalRequiredError, + NoCanonicalInfoFoundError, NotApprovedError, ProcessMessageError, SendERC1155Error, } from '$libs/error'; import type { BridgeProver } from '$libs/proof'; +import { getCanonicalInfoForAddress } from '$libs/token/getCanonicalInfo'; import { getLogger } from '$libs/util/logger'; import { Bridge } from './Bridge'; @@ -62,7 +64,7 @@ export class ERC1155Bridge extends Bridge { } async bridge(args: ERC1155BridgeArgs) { - const { token, tokenVaultAddress, tokenIds, wallet } = args; + const { token, tokenVaultAddress, tokenIds, wallet, srcChainId, destChainId } = args; const { tokenVaultContract, sendERC1155Args } = await ERC1155Bridge._prepareTransaction(args); const { fee: value } = sendERC1155Args; @@ -70,16 +72,24 @@ export class ERC1155Bridge extends Bridge { const tokenId = tokenIds[0]; // TODO: support multiple tokenIds - const isApprovedForAll = await this.isApprovedForAll({ - tokenAddress: token, - spenderAddress: tokenVaultAddress, - tokenId: tokenId, - owner: wallet.account.address, - chainId: wallet.chain.id, - }); - - if (!isApprovedForAll) { - throw new NotApprovedError(`Not approved for all for token`); + const info = await getCanonicalInfoForAddress({ address: token, srcChainId, destChainId }); + if (!info) throw new NoCanonicalInfoFoundError('No canonical info found for token'); + const { address: canonicalTokenAddress } = info; + + if (canonicalTokenAddress === token) { + // Token is native, we need to check if we have approval + const isApprovedForAll = await this.isApprovedForAll({ + tokenAddress: token, + spenderAddress: tokenVaultAddress, + tokenId: tokenId, + owner: wallet.account.address, + chainId: wallet.chain.id, + }); + if (!isApprovedForAll) { + throw new NotApprovedError(`Not approved for all for token`); + } + } else { + log('Token is bridged, no need to check for approval'); } try { diff --git a/packages/bridge-ui-v2/src/libs/bridge/ERC20Bridge.ts b/packages/bridge-ui-v2/src/libs/bridge/ERC20Bridge.ts index a3965b7fc82..0af533cad4a 100644 --- a/packages/bridge-ui-v2/src/libs/bridge/ERC20Bridge.ts +++ b/packages/bridge-ui-v2/src/libs/bridge/ERC20Bridge.ts @@ -9,11 +9,13 @@ import { BridgePausedError, InsufficientAllowanceError, NoAllowanceRequiredError, + NoCanonicalInfoFoundError, ProcessMessageError, ReleaseError, SendERC20Error, } from '$libs/error'; import type { BridgeProver } from '$libs/proof'; +import { getCanonicalInfoForAddress } from '$libs/token/getCanonicalInfo'; import { isBridgePaused } from '$libs/util/checkForPausedContracts'; import { getLogger } from '$libs/util/logger'; @@ -160,17 +162,26 @@ export class ERC20Bridge extends Bridge { } async bridge(args: ERC20BridgeArgs) { - const { amount, token, wallet, tokenVaultAddress } = args; - - const requireAllowance = await this.requireAllowance({ - amount, - tokenAddress: token, - ownerAddress: wallet.account.address, - spenderAddress: tokenVaultAddress, - }); - - if (requireAllowance) { - throw new InsufficientAllowanceError(`Insufficient allowance for the amount ${amount}`); + const { amount, token, wallet, tokenVaultAddress, srcChainId, destChainId } = args; + + const info = await getCanonicalInfoForAddress({ address: token, srcChainId, destChainId }); + if (!info) throw new NoCanonicalInfoFoundError('No canonical info found for token'); + const { address: canonicalTokenAddress } = info; + + if (canonicalTokenAddress === token) { + // Token is native, we need to check if we have approval + const requireAllowance = await this.requireAllowance({ + amount, + tokenAddress: token, + ownerAddress: wallet.account.address, + spenderAddress: tokenVaultAddress, + }); + + if (requireAllowance) { + throw new InsufficientAllowanceError(`Insufficient allowance for the amount ${amount}`); + } + } else { + log('Token is bridged, no need to check for approval'); } const { tokenVaultContract, sendERC20Args } = await ERC20Bridge._prepareTransaction(args); diff --git a/packages/bridge-ui-v2/src/libs/bridge/ERC721Bridge.ts b/packages/bridge-ui-v2/src/libs/bridge/ERC721Bridge.ts index 59b54826fff..53002038fcc 100644 --- a/packages/bridge-ui-v2/src/libs/bridge/ERC721Bridge.ts +++ b/packages/bridge-ui-v2/src/libs/bridge/ERC721Bridge.ts @@ -7,11 +7,13 @@ import { ApproveError, BridgePausedError, NoApprovalRequiredError, + NoCanonicalInfoFoundError, NotApprovedError, ProcessMessageError, SendERC721Error, } from '$libs/error'; import type { BridgeProver } from '$libs/proof'; +import { getCanonicalInfoForAddress } from '$libs/token/getCanonicalInfo'; import { isBridgePaused } from '$libs/util/checkForPausedContracts'; import { getLogger } from '$libs/util/logger'; @@ -67,7 +69,7 @@ export class ERC721Bridge extends Bridge { } async bridge(args: ERC721BridgeArgs) { - const { token, tokenVaultAddress, tokenIds, wallet } = args; + const { token, tokenVaultAddress, tokenIds, wallet, srcChainId, destChainId } = args; const { tokenVaultContract, sendERC721Args } = await ERC721Bridge._prepareTransaction(args); const { fee: value } = sendERC721Args; @@ -75,15 +77,23 @@ export class ERC721Bridge extends Bridge { const tokenId = tokenIds[0]; //TODO: handle multiple tokenIds try { - const requireApproval = await this.requiresApproval({ - tokenAddress: token, - spenderAddress: tokenVaultAddress, - tokenId: tokenId, - chainId: wallet.chain.id, - }); - - if (requireApproval) { - throw new NotApprovedError(`The token with id ${tokenId} is not approved for the token vault`); + const info = await getCanonicalInfoForAddress({ address: token, srcChainId, destChainId }); + if (!info) throw new NoCanonicalInfoFoundError('No canonical info found for token'); + const { address: canonicalTokenAddress } = info; + + if (canonicalTokenAddress === token) { + // Token is native, we need to check if we have approval + const requireApproval = await this.requiresApproval({ + tokenAddress: token, + spenderAddress: tokenVaultAddress, + tokenId: tokenId, + chainId: wallet.chain.id, + }); + if (requireApproval) { + throw new NotApprovedError(`The token with id ${tokenId} is not approved for the token vault`); + } + } else { + log('Token is bridged, no need to check for approval'); } } catch (err) { throw new SendERC721Error('failed to bridge ERC721 token', { cause: err }); diff --git a/packages/bridge-ui-v2/src/libs/bridge/checkBalanceToBridge.ts b/packages/bridge-ui-v2/src/libs/bridge/checkBalanceToBridge.ts index d1492e6c8bf..76b3bf6e7b6 100644 --- a/packages/bridge-ui-v2/src/libs/bridge/checkBalanceToBridge.ts +++ b/packages/bridge-ui-v2/src/libs/bridge/checkBalanceToBridge.ts @@ -10,7 +10,7 @@ import { RevertedWithoutMessageError, } from '$libs/error'; import { getAddress, type Token, TokenType } from '$libs/token'; -import { getCanonicalInfoForToken } from '$libs/token/getCanonicalInfoForToken'; +import { getCanonicalInfoForToken } from '$libs/token/getCanonicalInfo'; import { isDeployedCrossChain } from '$libs/token/isDeployedCrossChain'; import { getConnectedWallet } from '$libs/util/getConnectedWallet'; import { getLogger } from '$libs/util/logger'; diff --git a/packages/bridge-ui-v2/src/libs/error/errors.ts b/packages/bridge-ui-v2/src/libs/error/errors.ts index 9d0d3fd41ba..97e520b69fb 100644 --- a/packages/bridge-ui-v2/src/libs/error/errors.ts +++ b/packages/bridge-ui-v2/src/libs/error/errors.ts @@ -30,6 +30,10 @@ export class NoTokenAddressError extends Error { name = 'NoTokenAddressError'; } +export class NoTokenError extends Error { + name = 'NoTokenError'; +} + export class FailedTransactionError extends Error { name = 'FailedTransactionError'; } @@ -121,3 +125,11 @@ export class FilterLogsError extends Error { export class NoCanonicalInfoFoundError extends Error { name = 'NoCanonicalInfoFoundError'; } + +export class InvalidParametersProvidedError extends Error { + name = 'InvalidParametersProvidedError'; +} + +export class InternalError extends Error { + name = 'InternalError'; +} diff --git a/packages/bridge-ui-v2/src/libs/token/getCanonicalInfoForToken.ts b/packages/bridge-ui-v2/src/libs/token/getCanonicalInfo.ts similarity index 79% rename from packages/bridge-ui-v2/src/libs/token/getCanonicalInfoForToken.ts rename to packages/bridge-ui-v2/src/libs/token/getCanonicalInfo.ts index f27ca2dcbb2..302d4a36e33 100644 --- a/packages/bridge-ui-v2/src/libs/token/getCanonicalInfoForToken.ts +++ b/packages/bridge-ui-v2/src/libs/token/getCanonicalInfo.ts @@ -7,6 +7,7 @@ import { NoCanonicalInfoFoundError } from '$libs/error'; import { getLogger } from '$libs/util/logger'; import { getCanonicalStatusFromStore, setCanonicalTokenInfoStore } from '$stores/canonical'; +import { detectContractType } from './detectContractType'; import { type GetTokenInfo, TokenType } from './types'; const log = getLogger('token:getCanonicalInfoForToken'); @@ -117,34 +118,49 @@ export async function getCanonicalInfoForToken({ } } } else { - // we take the first address we have and find the canonical one - log('fetching canonical address for first address we have'); const srcChainTokenAddress = Object.values(token.addresses)[0]; const srcChainTokenChainId = Object.keys(token.addresses)[0]; - - if (!srcChainTokenAddress || !srcChainTokenChainId) - throw new NoCanonicalInfoFoundError('No canonical info found for token'); - - // check store first - if (getCanonicalStatusFromStore(srcChainTokenAddress)) { - log('found canonical address in store', srcChainTokenAddress); - return { chainId: parseInt(srcChainTokenChainId), address: srcChainTokenAddress }; - } - log('fetching new canonical info'); - - const { canonicalTokenAddress, canonicalChain } = await _getStatus({ + return await getCanonicalInfoForAddress({ address: srcChainTokenAddress, srcChainId: parseInt(srcChainTokenChainId), destChainId, - type: token.type, }); - - if (canonicalTokenAddress && canonicalChain) { - log(`Found canonical address ${canonicalTokenAddress} on chain ${canonicalChain}`); - setCanonicalTokenInfoStore(canonicalTokenAddress, true, canonicalChain); - return { chainId: canonicalChain, address: canonicalTokenAddress }; - } } log('No canonical info found for token', token, srcChainId, destChainId); throw new NoCanonicalInfoFoundError('No canonical info found for token'); } + +export const getCanonicalInfoForAddress = async ({ + address, + srcChainId, + destChainId, + type, +}: { + address: Address; + srcChainId: number; + destChainId: number; + type?: TokenType; +}) => { + if (getCanonicalStatusFromStore(address)) { + log('found canonical address in store', address); + return { chainId: parseInt(address), address }; + } + log('fetching new canonical info'); + + if (!type) type = await detectContractType(address); + const { canonicalTokenAddress, canonicalChain } = await _getStatus({ + address, + srcChainId, + destChainId, + type: type, + }); + + if (canonicalTokenAddress && canonicalChain) { + log(`Found canonical address ${canonicalTokenAddress} on chain ${canonicalChain}`); + setCanonicalTokenInfoStore(canonicalTokenAddress, true, canonicalChain); + return { chainId: canonicalChain, address: canonicalTokenAddress }; + } else { + log('No canonical info found for address', address, srcChainId, destChainId); + throw new NoCanonicalInfoFoundError('No canonical info found for address'); + } +}; diff --git a/packages/bridge-ui-v2/src/libs/token/checkTokenApprovalStatus.ts b/packages/bridge-ui-v2/src/libs/token/getTokenApprovalStatus.ts similarity index 73% rename from packages/bridge-ui-v2/src/libs/token/checkTokenApprovalStatus.ts rename to packages/bridge-ui-v2/src/libs/token/getTokenApprovalStatus.ts index a4f48d0dc91..8adb2c622d1 100644 --- a/packages/bridge-ui-v2/src/libs/token/checkTokenApprovalStatus.ts +++ b/packages/bridge-ui-v2/src/libs/token/getTokenApprovalStatus.ts @@ -14,33 +14,46 @@ import type { ERC20Bridge } from '$libs/bridge/ERC20Bridge'; import type { ERC721Bridge } from '$libs/bridge/ERC721Bridge'; import type { ERC1155Bridge } from '$libs/bridge/ERC1155Bridge'; import { getContractAddressByType } from '$libs/bridge/getContractAddressByType'; -import { NoCanonicalInfoFoundError } from '$libs/error'; +import { + InvalidParametersProvidedError, + NoCanonicalInfoFoundError, + NotConnectedError, + NoTokenError, + UnknownTokenTypeError, +} from '$libs/error'; import { getConnectedWallet } from '$libs/util/getConnectedWallet'; import { getLogger } from '$libs/util/logger'; import { account, network } from '$stores'; import { checkOwnershipOfNFT } from './checkOwnership'; -import { getCanonicalInfoForToken } from './getCanonicalInfoForToken'; +import { getCanonicalInfoForToken } from './getCanonicalInfo'; import { type NFT, type Token, TokenType } from './types'; -const log = getLogger('util:token:checkTokenApprovalStatus'); +const log = getLogger('util:token:getTokenApprovalStatus'); + +export enum ApprovalStatus { + ETH_NO_APPROVAL_REQUIRED, + BRIDGED_NO_APPROVAL_REQUIRED, + APPROVAL_REQUIRED, + NO_APPROVAL_REQUIRED, +} -export const checkTokenApprovalStatus = async (token: Maybe): Promise => { - log('checkTokenApprovalStatus called', token); +export const getTokenApprovalStatus = async (token: Maybe): Promise => { + log('getTokenApprovalStatus called', token); if (!token) { allApproved.set(false); - return; + throw new NoTokenError(); } if (token.type === TokenType.ETH) { allApproved.set(true); log('token is ETH'); - return; + return ApprovalStatus.ETH_NO_APPROVAL_REQUIRED; } const currentChainId = get(network)?.id; const destinationChainId = get(destNetwork)?.id; if (!currentChainId || !destinationChainId) { log('no currentChainId or destinationChainId'); - return; + throw new NotConnectedError(); } const ownerAddress = get(account)?.address; @@ -57,18 +70,18 @@ export const checkTokenApprovalStatus = async (token: Maybe): Promi if (canonicalTokenAddress !== tokenAddress) { // we have a bridged token, no need for allowance check as we will burn the token log('token is bridged, no need for allowance check'); - return; + allApproved.set(true); + return ApprovalStatus.BRIDGED_NO_APPROVAL_REQUIRED; } if (!ownerAddress || !tokenAddress) { log('no ownerAddress or tokenAddress', ownerAddress, tokenAddress); - return; + throw new InvalidParametersProvidedError('no ownerAddress or tokenAddress'); } if (token.type === TokenType.ERC20) { log('checking approval status for ERC20'); const tokenVaultAddress = routingContractsMap[currentChainId][destinationChainId].erc20VaultAddress; - const bridge = bridges[TokenType.ERC20] as ERC20Bridge; try { @@ -81,8 +94,12 @@ export const checkTokenApprovalStatus = async (token: Maybe): Promi log('erc20 requiresApproval', requiresApproval); insufficientAllowance.set(requiresApproval); allApproved.set(!requiresApproval); + if (requiresApproval) { + return ApprovalStatus.APPROVAL_REQUIRED; + } + return ApprovalStatus.NO_APPROVAL_REQUIRED; } catch (error) { - console.error('isApprovedForAll error'); + log('erc20 requireAllowance error', error); allApproved.set(false); } } else if (token.type === TokenType.ERC721 || token.type === TokenType.ERC1155) { @@ -90,7 +107,7 @@ export const checkTokenApprovalStatus = async (token: Maybe): Promi const nft = token as NFT; const ownerShipChecks = await checkOwnershipOfNFT(token as NFT, ownerAddress, currentChainId); if (!ownerShipChecks.every((item) => item.isOwner === true)) { - return; + return ApprovalStatus.APPROVAL_REQUIRED; } const wallet = await getConnectedWallet(); @@ -102,7 +119,7 @@ export const checkTokenApprovalStatus = async (token: Maybe): Promi }); if (!spenderAddress) { - throw new Error('No spender address found'); + throw new InvalidParametersProvidedError('no spender address provided'); } const args: RequireApprovalArgs = { @@ -119,6 +136,10 @@ export const checkTokenApprovalStatus = async (token: Maybe): Promi // Let's check if the vault is approved for all ERC1155 const isApprovedForAll = await bridge.isApprovedForAll(args); allApproved.set(isApprovedForAll); + if (isApprovedForAll) { + return ApprovalStatus.NO_APPROVAL_REQUIRED; + } + return ApprovalStatus.APPROVAL_REQUIRED; } catch (error) { console.error('isApprovedForAll error'); } @@ -128,6 +149,10 @@ export const checkTokenApprovalStatus = async (token: Maybe): Promi // Let's check if the vault is approved for all ERC721 const requiresApproval = await bridge.requiresApproval(args); allApproved.set(!requiresApproval); + if (requiresApproval) { + return ApprovalStatus.APPROVAL_REQUIRED; + } + return ApprovalStatus.NO_APPROVAL_REQUIRED; } catch (error) { console.error('isApprovedForAll error'); } finally { @@ -136,6 +161,7 @@ export const checkTokenApprovalStatus = async (token: Maybe): Promi } } else { log('unknown token type:', token); + throw new UnknownTokenTypeError(); } - return; + return ApprovalStatus.APPROVAL_REQUIRED; }; From ee7d8ab709a846a165318bfc3a632cde7429c57a Mon Sep 17 00:00:00 2001 From: Korbinian Date: Mon, 22 Jan 2024 16:47:30 +0100 Subject: [PATCH 4/4] Update packages/bridge-ui-v2/src/components/Bridge/Actions.svelte --- packages/bridge-ui-v2/src/components/Bridge/Actions.svelte | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/bridge-ui-v2/src/components/Bridge/Actions.svelte b/packages/bridge-ui-v2/src/components/Bridge/Actions.svelte index dcfacf59005..3775e550098 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/Actions.svelte +++ b/packages/bridge-ui-v2/src/components/Bridge/Actions.svelte @@ -52,7 +52,6 @@ onMount(async () => { $validatingAmount = true; - // await checkBridgedStatus(); if ($selectedTokenIsBridged) { $allApproved = true; $insufficientAllowance = false;