Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(bridge-ui-v2): Bridged tokens do not require approval #15545

Merged
merged 5 commits into from
Jan 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 28 additions & 23 deletions packages/bridge-ui-v2/src/components/Bridge/Actions.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import { Icon } from '$components/Icon';
import { BridgePausedError } from '$libs/error';
import { TokenType } from '$libs/token';
import { checkTokenApprovalStatus } from '$libs/token/checkTokenApprovalStatus';
import { getTokenApprovalStatus } from '$libs/token/getTokenApprovalStatus';
import { account, network } from '$stores';

import {
Expand All @@ -19,6 +19,7 @@
insufficientBalance,
recipientAddress,
selectedToken,
selectedTokenIsBridged,
tokenBalance,
validatingAmount,
} from './state';
Expand Down Expand Up @@ -49,14 +50,18 @@
bridge();
}

onMount(() => {
onMount(async () => {
$validatingAmount = true;
checkTokenApprovalStatus($selectedToken);
if ($selectedTokenIsBridged) {
$allApproved = true;
$insufficientAllowance = false;
} else {
getTokenApprovalStatus($selectedToken);
}
isValidTokenBalance();
$validatingAmount = false;
});

//TODO: does this check entered balance?!
const isValidTokenBalance = () => {
if ($tokenBalance && typeof $tokenBalance !== 'bigint') {
if (isETH) {
Expand All @@ -78,8 +83,6 @@

$: isValidBalance = false;

$: validating = $validatingAmount && $enteredAmount > 0;

// Basic conditions so we can even start the bridging process
$: hasAddress = $recipientAddress || $account?.address;
$: hasNetworks = $network?.id && $destNetwork?.id;
Expand All @@ -88,29 +91,31 @@
$: canDoNothing = !hasAddress || !hasNetworks || !hasBalance || !$selectedToken || disabled;

// Conditions for approve/bridge steps

$: if ($enteredAmount) {
$validatingAmount = true;
checkTokenApprovalStatus($selectedToken);
isValidTokenBalance();
}

// Conditions to disable/enable buttons
$: disableApprove = isERC20
? canDoNothing || $insufficientBalance || $validatingAmount || approving || $allApproved || !$enteredAmount
: isERC721
? $allApproved || approving
: isERC1155
$: disableApprove =
!$selectedTokenIsBridged &&
(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;
$: isERC1155 = $selectedToken?.type === TokenType.ERC1155;
$: isETH = $selectedToken?.type === TokenType.ETH;

$: validApprovalStatus = $selectedTokenIsBridged ? true : $allApproved;

$: commonConditions =
$allApproved &&
validApprovalStatus &&
!bridging &&
hasAddress &&
hasNetworks &&
Expand Down Expand Up @@ -144,23 +149,23 @@
<!-- TODO: temporary enable two styles, remove for UI v2.1 -->

<div class="f-between-center w-full gap-4">
{#if $selectedToken && !isETH}
{#if !$selectedTokenIsBridged && $selectedToken && !isETH}
<ActionButton
priority="primary"
disabled={disableApprove}
loading={approving || validating}
loading={approving || $validatingAmount}
on:click={onApproveClick}>
{#if validating && !approving}
<span class="body-bold">Checking ...</span>
{#if $validatingAmount && !approving}
<span class="body-bold">{$t('bridge.button.validating')}.</span>
{/if}
{#if approving}
<span class="body-bold">{$t('bridge.button.approving')}</span>
{:else if $allApproved && !validating && $enteredAmount > 0}
{:else if $allApproved && !$validatingAmount && $enteredAmount > 0}
<div class="f-items-center">
<Icon type="check" />
<span class="body-bold">{$t('bridge.button.approved')}</span>
</div>
{:else if !validating}
{:else if !$validatingAmount}
<span class="body-bold">{$t('bridge.button.approve')}</span>
{/if}
</ActionButton>
Expand All @@ -178,11 +183,11 @@
<!-- NFT actions -->
<!-- TODO: adopt for bridge design v2.1 -->
<div class="f-col w-full gap-4">
{#if $selectedToken && !isETH}
{#if $selectedToken && !isETH && !$selectedTokenIsBridged}
<ActionButton
priority="primary"
disabled={disableApprove}
loading={approving || validating}
loading={approving || $validatingAmount}
on:click={onApproveClick}>
{#if approving}
<span class="body-bold">{$t('bridge.button.approving')}</span>
Expand Down
7 changes: 5 additions & 2 deletions packages/bridge-ui-v2/src/components/Bridge/Bridge.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
import { BridgePausedError } from '$libs/error';
import { bridgeTxService } from '$libs/storage';
import { ETHToken, tokens, TokenType } from '$libs/token';
import { checkTokenApprovalStatus } from '$libs/token/checkTokenApprovalStatus';
import { getCrossChainAddress } from '$libs/token/getCrossChainAddress';
import { getTokenApprovalStatus } from '$libs/token/getTokenApprovalStatus';
import { refreshUserBalance } from '$libs/util/balance';
import { isBridgePaused } from '$libs/util/checkForPausedContracts';
import { getConnectedWallet } from '$libs/util/getConnectedWallet';
Expand All @@ -41,6 +41,7 @@
import {
activeBridge,
allApproved,
approving,
bridgeService,
destNetwork as destinationChain,
enteredAmount,
Expand Down Expand Up @@ -137,7 +138,7 @@

await pendingTransactions.add(txHash, $network.id);

await checkTokenApprovalStatus($selectedToken);
await getTokenApprovalStatus($selectedToken);

await pendingTransactions.add(txHash, $network.id);

Expand All @@ -153,6 +154,7 @@
} catch (err) {
console.error(err);
handleBridgeError(err as Error);
$approving = false;
}
}

Expand Down Expand Up @@ -232,6 +234,7 @@
} catch (err) {
console.error(err);
handleBridgeError(err as Error);
bridging = false;
}
}

Expand Down
49 changes: 30 additions & 19 deletions packages/bridge-ui-v2/src/components/Bridge/NFTBridge.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import { BridgePausedError } from '$libs/error';
import { ETHToken, type NFT } from '$libs/token';
import { fetchNFTImageUrl } from '$libs/token/fetchNFTImageUrl';
import { getCanonicalInfoForToken } from '$libs/token/getCanonicalInfo';
import { getTokenWithInfoFromAddress } from '$libs/token/getTokenWithInfoFromAddress';
import { isBridgePaused } from '$libs/util/checkForPausedContracts';
import { type Account, account } from '$stores/account';
Expand All @@ -32,6 +33,7 @@
recipientAddress,
selectedNFTs,
selectedToken,
selectedTokenIsBridged,
} from './state';
import { NFTSteps } from './types';

Expand Down Expand Up @@ -177,29 +179,38 @@
}),
);
};

const manualImportAction = async () => {
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 = () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -135,7 +135,7 @@

await pendingTransactions.add(approveTxHash, $network.id);

await checkTokenApprovalStatus($selectedToken);
await getTokenApprovalStatus($selectedToken);

successToast({
title: $t('bridge.actions.approve.success.title'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -47,8 +47,6 @@
export let validating: boolean = false;
export let contractAddress: Address | string = '';

export const prefetchImage = () => noop();

let enteredIds: number[] = [];
let scanning: boolean;

Expand Down Expand Up @@ -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(
Expand All @@ -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({
Expand All @@ -179,7 +177,7 @@
});

if (!token) {
throw new Error('No token with info');
throw new InternalError('unable to get token');
}

detectedTokenType = token.type;
Expand All @@ -188,8 +186,6 @@
idInputState = IDInputState.VALID;

$tokenBalance = token.balance;

await prefetchImage();
} else {
idInputState = IDInputState.INVALID;
}
Expand Down Expand Up @@ -262,8 +258,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;
Expand Down Expand Up @@ -331,6 +326,7 @@ Manual NFT Input
<!--
Automatic NFT Input
-->

{#if !scanned || nothingFound}
<div class="h-sep" />

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte';
import { createEventDispatcher, onMount } from 'svelte';
import { t } from 'svelte-i18n';

import { chainConfig } from '$chainConfig';
import { ProcessingFee } from '$components/Bridge/ProcessingFee';
import Recipient from '$components/Bridge/Recipient.svelte';
import { destNetwork as destinationChain, enteredAmount, selectedNFTs } from '$components/Bridge/state';
import {
destNetwork as destinationChain,
enteredAmount,
selectedNFTs,
selectedTokenIsBridged,
} from '$components/Bridge/state';
import { ChainSelector } from '$components/ChainSelector';
import { IconFlipper } from '$components/Icon';
import { NFTDisplay } from '$components/NFTs';
import { getCanonicalInfoForToken } from '$libs/token/getCanonicalInfo';
import { shortenAddress } from '$libs/util/shortenAddress';
import { network } from '$stores/network';

Expand Down Expand Up @@ -42,6 +48,16 @@

// check if any of the selected NFTs are ERC1155 tokens
$: isERC1155 = $selectedNFTs ? $selectedNFTs.some((nft) => nft.type === 'ERC1155') : false;

onMount(async () => {
const srcChainId = $network?.id;
const destChainId = $destinationChain?.id;
const nfts = $selectedNFTs;
if (!nfts || nfts.length === 0 || !srcChainId || !destChainId) return;
const [info] = await Promise.all([getCanonicalInfoForToken({ token: nfts[0], srcChainId, destChainId })]);

if (info) $selectedTokenIsBridged = nfts[0].addresses[srcChainId] !== info.address;
});
</script>

<div class="container mx-auto inline-block align-middle space-y-[25px] w-full mt-[30px]">
Expand Down
1 change: 1 addition & 0 deletions packages/bridge-ui-v2/src/components/Bridge/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export const insufficientBalance = writable<boolean>(false);
export const insufficientAllowance = writable<boolean>(false);

export const allApproved = writable(<boolean>false);
export const selectedTokenIsBridged = writable(<boolean>false);

// Derived state
export const bridgeService = derived(selectedToken, (token) => (token ? bridges[token.type] : null));
Loading
Loading