From c85e1629d0b9b544880f65f0e4050456579c87d1 Mon Sep 17 00:00:00 2001 From: Korbinian Date: Fri, 6 Oct 2023 12:17:45 +0200 Subject: [PATCH] feat(bridge-ui-v2): manual NFT import step (#14842) --- .../bridge-ui-v2/__mocks__/@wagmi/core.ts | 4 + packages/bridge-ui-v2/src/app.config.ts | 4 + .../{ => AddressInput}/AddressInput.svelte | 65 +++-- .../components/Bridge/AddressInput/state.ts | 7 + .../src/components/Bridge/Amount.svelte | 2 +- .../src/components/Bridge/Bridge.svelte | 247 ++++++++++++++---- .../src/components/Bridge/BridgeTabs.svelte | 4 +- .../src/components/Bridge/IDInput.svelte | 58 ++++ .../src/components/Bridge/Recipient.svelte | 4 +- .../ChainSelector/ChainSelectorWrapper.svelte | 9 +- .../TokenDropdown/AddCustomERC20.svelte | 11 +- packages/bridge-ui-v2/src/i18n/en.json | 19 +- .../bridge-ui-v2/src/libs/error/errors.ts | 4 + .../src/libs/fee/recommendProcessingFee.ts | 47 +++- .../src/libs/token/checkOwnership.test.ts | 94 +++++++ .../src/libs/token/checkOwnership.ts | 70 +++++ .../src/libs/token/detectContractType.test.ts | 85 ++++++ .../src/libs/token/detectContractType.ts | 118 +++++---- .../src/libs/token/getTokenInfo.test.ts | 86 ++++++ .../src/libs/token/getTokenInfo.ts | 49 ++++ packages/bridge-ui-v2/src/libs/token/types.ts | 4 +- 21 files changed, 835 insertions(+), 156 deletions(-) rename packages/bridge-ui-v2/src/components/Bridge/{ => AddressInput}/AddressInput.svelte (59%) create mode 100644 packages/bridge-ui-v2/src/components/Bridge/AddressInput/state.ts create mode 100644 packages/bridge-ui-v2/src/components/Bridge/IDInput.svelte create mode 100644 packages/bridge-ui-v2/src/libs/token/checkOwnership.test.ts create mode 100644 packages/bridge-ui-v2/src/libs/token/checkOwnership.ts create mode 100644 packages/bridge-ui-v2/src/libs/token/detectContractType.test.ts create mode 100644 packages/bridge-ui-v2/src/libs/token/getTokenInfo.test.ts create mode 100644 packages/bridge-ui-v2/src/libs/token/getTokenInfo.ts diff --git a/packages/bridge-ui-v2/__mocks__/@wagmi/core.ts b/packages/bridge-ui-v2/__mocks__/@wagmi/core.ts index 88658482f17..0700d6c0cd0 100644 --- a/packages/bridge-ui-v2/__mocks__/@wagmi/core.ts +++ b/packages/bridge-ui-v2/__mocks__/@wagmi/core.ts @@ -10,6 +10,10 @@ export const getContract = vi.fn(); export const fetchBalance = vi.fn(); +export const fetchToken = vi.fn(); + +export const readContract = vi.fn(); + export const configureChains = vi.fn(() => { return { publicClient: 'mockPublicClient' }; }); diff --git a/packages/bridge-ui-v2/src/app.config.ts b/packages/bridge-ui-v2/src/app.config.ts index fa68452bea8..6f833e2df84 100644 --- a/packages/bridge-ui-v2/src/app.config.ts +++ b/packages/bridge-ui-v2/src/app.config.ts @@ -2,6 +2,10 @@ export const recommentProcessingFee = { ethGasLimit: BigInt(900_000), erc20NotDeployedGasLimit: BigInt(3_100_000), erc20DeployedGasLimit: BigInt(1_100_000), + erc721NotDeployedGasLimit: BigInt(3_400_000), + erc721DeployedGasLimit: BigInt(1_100_000), + erc1155NotDeployedGasLimit: BigInt(4_000_000), + erc1155DeployedGasLimit: BigInt(1_100_000), }; export const processingFeeComponent = { diff --git a/packages/bridge-ui-v2/src/components/Bridge/AddressInput.svelte b/packages/bridge-ui-v2/src/components/Bridge/AddressInput/AddressInput.svelte similarity index 59% rename from packages/bridge-ui-v2/src/components/Bridge/AddressInput.svelte rename to packages/bridge-ui-v2/src/components/Bridge/AddressInput/AddressInput.svelte index be417f388d9..88d058e2615 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/AddressInput.svelte +++ b/packages/bridge-ui-v2/src/components/Bridge/AddressInput/AddressInput.svelte @@ -8,22 +8,33 @@ import { Icon } from '$components/Icon'; import { uid } from '$libs/util/uid'; - enum State { - Valid = 'valid', - Invalid = 'invalid', - TooShort = 'too_short', - } + import { AddressInputState as State } from './state'; + + export let ethereumAddress: Address | string = ''; + export let labelText = $t('inputs.address_input.label.default'); + export let isDisabled = false; + export let quiet = false; + export let state: State = State.Default; + + export const validateAddress = () => { + validateEthereumAddress(ethereumAddress); + }; + + export const clearAddress = () => { + if (input) input.value = ''; + validateEthereumAddress(''); + }; + + export const focus = () => input.focus(); let input: HTMLInputElement; let inputId = `input-${uid()}`; - let state: State; - - export let ethereumAddress: Address | string = ''; const dispatch = createEventDispatcher(); const validateEthereumAddress = (address: string | EventTarget | null) => { let addr: string; + if (!address) return; if (address && address instanceof EventTarget) { addr = (address as HTMLInputElement).value; @@ -49,41 +60,37 @@ }; $: validateEthereumAddress(ethereumAddress); - - export const clear = () => { - input.value = ''; - validateEthereumAddress(''); - }; - - export const focus = () => input.focus();
- +
validateEthereumAddress(e.target)} - class="w-full input-box withValdiation py-6 pr-16 px-[26px] title-subsection-bold placeholder:text-tertiary-content - {state === State.Valid ? 'success' : ethereumAddress ? 'error' : ''} + class="w-full input-box withValdiation py-6 pr-16 px-[26px] title-subsection-bold placeholder:text-tertiary-content {$$props.class} + {state === State.Valid ? 'success' : ethereumAddress && state !== State.Validating ? 'error' : ''} " /> -
-
- {#if state === State.Invalid && ethereumAddress} - - {:else if state === State.TooShort && ethereumAddress} - - {:else if state === State.Valid} - - {/if} -
+ +{#if !quiet} +
+ {#if state === State.Invalid && ethereumAddress} + + {:else if state === State.TooShort && ethereumAddress} + + {:else if state === State.Valid} + + {/if} +
+{/if} diff --git a/packages/bridge-ui-v2/src/components/Bridge/AddressInput/state.ts b/packages/bridge-ui-v2/src/components/Bridge/AddressInput/state.ts new file mode 100644 index 00000000000..5ba71e8ea8c --- /dev/null +++ b/packages/bridge-ui-v2/src/components/Bridge/AddressInput/state.ts @@ -0,0 +1,7 @@ +export enum AddressInputState { + Default = 'default', + Valid = 'valid', + Invalid = 'invalid', + TooShort = 'too_short', + Validating = 'validating', +} diff --git a/packages/bridge-ui-v2/src/components/Bridge/Amount.svelte b/packages/bridge-ui-v2/src/components/Bridge/Amount.svelte index 355321307f1..b979f82a4cc 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/Amount.svelte +++ b/packages/bridge-ui-v2/src/components/Bridge/Amount.svelte @@ -43,7 +43,7 @@ // Public API export function clearAmount() { - inputBox.clear(); + inputBox?.clear(); $enteredAmount = BigInt(0); } diff --git a/packages/bridge-ui-v2/src/components/Bridge/Bridge.svelte b/packages/bridge-ui-v2/src/components/Bridge/Bridge.svelte index 8bae4c9b842..e27a64774a7 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/Bridge.svelte +++ b/packages/bridge-ui-v2/src/components/Bridge/Bridge.svelte @@ -1,6 +1,7 @@ {#if $activeBridge === BridgeTypes.FUNGIBLE} @@ -334,9 +431,7 @@ - {#if $selectedToken?.symbol === 'BLL' && !$selectedToken?.imported} - - {/if} +
@@ -361,13 +456,73 @@ -
- - -
-
+
+ {#if activeStep === NFTSteps.IMPORT} +
+ +
+ + +
+ {#if detectedTokenType === TokenType.ERC721 && contractAddress} + + {:else if detectedTokenType === TokenType.ERC1155 && contractAddress} + + {/if} + + +
+ {#if !isOwnerOfAllToken && nftIdArray?.length > 0 && !validating} + + {/if} +
+ + {#if detectedTokenType === TokenType.ERC1155} + + {/if} +
+ {:else if activeStep === NFTSteps.REVIEW} +
+
+

Contract: {contractAddress}

+

IDs: {nftIdArray.join(', ')}

+
+
+ {:else if activeStep === NFTSteps.CONFIRM} +
+ +
+ {/if} +
+ + +
+
+
+ {#if activeStep !== NFTSteps.IMPORT} + + {/if} + +
+
{/if} diff --git a/packages/bridge-ui-v2/src/components/Bridge/BridgeTabs.svelte b/packages/bridge-ui-v2/src/components/Bridge/BridgeTabs.svelte index 9a8115cc7bb..a055db56825 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/BridgeTabs.svelte +++ b/packages/bridge-ui-v2/src/components/Bridge/BridgeTabs.svelte @@ -20,13 +20,13 @@ {#if PUBLIC_NFT_BRIDGE_ENABLED === 'true'}
diff --git a/packages/bridge-ui-v2/src/components/Bridge/IDInput.svelte b/packages/bridge-ui-v2/src/components/Bridge/IDInput.svelte new file mode 100644 index 00000000000..6828c020c28 --- /dev/null +++ b/packages/bridge-ui-v2/src/components/Bridge/IDInput.svelte @@ -0,0 +1,58 @@ + + +
+
+ +
+
+ + + +
+
diff --git a/packages/bridge-ui-v2/src/components/Bridge/Recipient.svelte b/packages/bridge-ui-v2/src/components/Bridge/Recipient.svelte index f406f92e1bf..83e186b6f73 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/Recipient.svelte +++ b/packages/bridge-ui-v2/src/components/Bridge/Recipient.svelte @@ -10,12 +10,12 @@ import { uid } from '$libs/util/uid'; import { account } from '$stores/account'; - import AddressInput from './AddressInput.svelte'; + import AddressInput from './AddressInput/AddressInput.svelte'; import { recipientAddress } from './state'; // Public API export const clearRecipient = () => { - addressInput.clear(); // update UI + addressInput.clearAddress(); // update UI $recipientAddress = null; // update state }; diff --git a/packages/bridge-ui-v2/src/components/ChainSelector/ChainSelectorWrapper.svelte b/packages/bridge-ui-v2/src/components/ChainSelector/ChainSelectorWrapper.svelte index 422c28de329..4f851492f0b 100644 --- a/packages/bridge-ui-v2/src/components/ChainSelector/ChainSelectorWrapper.svelte +++ b/packages/bridge-ui-v2/src/components/ChainSelector/ChainSelectorWrapper.svelte @@ -10,6 +10,8 @@ import { chainIdToChain, chains } from '$libs/chain'; import { network } from '$stores/network'; + let destChainElement: ChainSelector; + function handleSourceChange(): void { updateDestOptions(); } @@ -62,6 +64,11 @@ - + diff --git a/packages/bridge-ui-v2/src/components/TokenDropdown/AddCustomERC20.svelte b/packages/bridge-ui-v2/src/components/TokenDropdown/AddCustomERC20.svelte index 7ff29467023..746f351a65d 100644 --- a/packages/bridge-ui-v2/src/components/TokenDropdown/AddCustomERC20.svelte +++ b/packages/bridge-ui-v2/src/components/TokenDropdown/AddCustomERC20.svelte @@ -1,20 +1,20 @@