From 7fa506cfa279fe0642250379a9370c4b31e9e783 Mon Sep 17 00:00:00 2001 From: Korbinian Date: Wed, 27 Sep 2023 17:01:06 +0200 Subject: [PATCH] import steps, validating ownership etc --- .../{ => AddressInput}/AddressInput.svelte | 41 ++-- .../components/Bridge/AddressInput/state.ts | 7 + .../src/components/Bridge/Bridge.svelte | 175 ++++++++++++++---- .../Bridge/ChainSelectorWrapper.svelte | 9 +- .../src/components/Bridge/IDInput.svelte | 58 +++--- .../src/components/Bridge/Recipient.svelte | 2 +- .../TokenDropdown/AddCustomERC20.svelte | 2 +- .../src/libs/token/detectContractType.ts | 67 +++---- 8 files changed, 224 insertions(+), 137 deletions(-) rename packages/bridge-ui-v2/src/components/Bridge/{ => AddressInput}/AddressInput.svelte (67%) create mode 100644 packages/bridge-ui-v2/src/components/Bridge/AddressInput/state.ts 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 67% rename from packages/bridge-ui-v2/src/components/Bridge/AddressInput.svelte rename to packages/bridge-ui-v2/src/components/Bridge/AddressInput/AddressInput.svelte index 7e9aab6a3da..75f8ae1b0b2 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/AddressInput.svelte +++ b/packages/bridge-ui-v2/src/components/Bridge/AddressInput/AddressInput.svelte @@ -8,18 +8,16 @@ import { Icon } from '$components/Icon'; import { uid } from '$libs/util/uid'; - export let ethereumAddress: Address | string = ''; - export let labelText = $t('inputs.address_input.label'); + import { AddressInputState as State } from './state'; - enum State { - Valid = 'valid', - Invalid = 'invalid', - TooShort = 'too_short', - } + 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; let input: HTMLInputElement; let inputId = `input-${uid()}`; - let state: State; const dispatch = createEventDispatcher(); @@ -65,26 +63,29 @@
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/Bridge.svelte b/packages/bridge-ui-v2/src/components/Bridge/Bridge.svelte index a34d33972c4..1f93d2a0d7b 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/Bridge.svelte +++ b/packages/bridge-ui-v2/src/components/Bridge/Bridge.svelte @@ -1,8 +1,10 @@ {#if $activeBridge === BridgeTypes.FUNGIBLE} - +
@@ -396,30 +466,55 @@
- - - + labelText={$t('inputs.address_input.label.contract')} + quiet /> + +
+ {#if detectedTokenType === TokenType.ERC721 && contractAddress} + + {:else if detectedTokenType === TokenType.ERC1155 && contractAddress} + + {/if} + + +
+ {#if !isOwnerOfAllToken && nftIdArray?.length > 0 && !validating} + + {/if} +
+
{:else if activeStep === NFTSteps.REVIEW}
- +
+

Contract: {contractAddress}

+

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

+
{:else if activeStep === NFTSteps.CONFIRM}
{/if} - {nftIdArray}
- + {#if activeStep !== NFTSteps.IMPORT} + + {/if} diff --git a/packages/bridge-ui-v2/src/components/Bridge/ChainSelectorWrapper.svelte b/packages/bridge-ui-v2/src/components/Bridge/ChainSelectorWrapper.svelte index 4354e8b006b..3224e8c07e5 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/ChainSelectorWrapper.svelte +++ b/packages/bridge-ui-v2/src/components/Bridge/ChainSelectorWrapper.svelte @@ -9,6 +9,8 @@ import { chains } from '$libs/chain'; import { network } from '$stores/network'; + let destChainElement: ChainSelector; + function handleSourceChange(): void { updateDestOptions(); } @@ -39,6 +41,11 @@ - + diff --git a/packages/bridge-ui-v2/src/components/Bridge/IDInput.svelte b/packages/bridge-ui-v2/src/components/Bridge/IDInput.svelte index a4da115a985..ecb121b781a 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/IDInput.svelte +++ b/packages/bridge-ui-v2/src/components/Bridge/IDInput.svelte @@ -2,72 +2,66 @@ import { createEventDispatcher } from 'svelte'; import { t } from 'svelte-i18n'; - import { FlatAlert } from '$components/Alert'; import { Icon } from '$components/Icon'; + import { uid } from '$libs/util/uid'; export let numbersArray: number[] = []; + export let isDisabled = false; + export let enteredIds: string = ''; - enum State { - Valid, - Invalid, - Neutral, - } + // enum State { + // Valid, + // Invalid, + // Neutral, + // } const dispatch = createEventDispatcher(); - let inputId = 'numberInput'; - let state = State.Neutral; - let value: string = ''; + let inputId = `input-${uid()}`; + // let state = State.Neutral; $: { - value = value.replace(/\s+/g, ''); - if (value === '' || value.endsWith(',')) { - state = State.Neutral; + enteredIds = enteredIds.replace(/\s+/g, ''); + if (enteredIds === '' || enteredIds.endsWith(',')) { + // state = State.Neutral; numbersArray = []; } else { - const inputArray = value.split(','); + const inputArray = enteredIds.split(','); const isValid = inputArray.every((item) => /^[0-9]+$/.test(item)); - state = isValid ? State.Valid : State.Invalid; + // state = isValid ? State.Valid : State.Invalid; numbersArray = isValid ? inputArray.map((num) => parseInt(num)).filter(Boolean) : []; } } function validateInput(e: Event) { const target = e.target as HTMLInputElement; - value = target.value.replace(/\s+/g, ''); - dispatch('input', { value, numbersArray }); + enteredIds = target.value.replace(/\s+/g, ''); + dispatch('input', { enteredIds, numbersArray }); } function clear() { - value = ''; + enteredIds = ''; numbersArray = []; - state = State.Neutral; - dispatch('input', { value, numbersArray }); + // state = State.Neutral; + dispatch('input', { enteredIds, numbersArray }); }
- +
+ class="w-full input-box withValdiation py-6 pr-16 px-[26px] title-subsection-bold placeholder:text-tertiary-content {$$props.class}" /> +
- -
- {#if state === State.Invalid && value !== ''} - - {:else if state === State.Valid} - - {/if} -
diff --git a/packages/bridge-ui-v2/src/components/Bridge/Recipient.svelte b/packages/bridge-ui-v2/src/components/Bridge/Recipient.svelte index f406f92e1bf..e4ea75a1494 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/Recipient.svelte +++ b/packages/bridge-ui-v2/src/components/Bridge/Recipient.svelte @@ -10,7 +10,7 @@ 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 diff --git a/packages/bridge-ui-v2/src/components/TokenDropdown/AddCustomERC20.svelte b/packages/bridge-ui-v2/src/components/TokenDropdown/AddCustomERC20.svelte index 7ff29467023..398027c4742 100644 --- a/packages/bridge-ui-v2/src/components/TokenDropdown/AddCustomERC20.svelte +++ b/packages/bridge-ui-v2/src/components/TokenDropdown/AddCustomERC20.svelte @@ -7,7 +7,7 @@ import { formatUnits } from 'viem'; import { FlatAlert } from '$components/Alert'; - import AddressInput from '$components/Bridge/AddressInput.svelte'; + import AddressInput from '$components/Bridge/AddressInput/AddressInput.svelte'; import { Button } from '$components/Button'; import { Icon } from '$components/Icon'; import Erc20 from '$components/Icon/ERC20.svelte'; diff --git a/packages/bridge-ui-v2/src/libs/token/detectContractType.ts b/packages/bridge-ui-v2/src/libs/token/detectContractType.ts index c12e225a60a..308426a7ab8 100644 --- a/packages/bridge-ui-v2/src/libs/token/detectContractType.ts +++ b/packages/bridge-ui-v2/src/libs/token/detectContractType.ts @@ -1,65 +1,48 @@ import { readContract } from '@wagmi/core'; +import { ContractFunctionExecutionError, UnknownTypeError } from 'viem'; -export async function detectContractType(contractAddress: string, tokenId: number) { - // eslint-disable-next-line no-console - console.info('detectContractType', contractAddress); +import { erc721ABI, erc1155ABI } from '$abi'; +import { getLogger } from '$libs/util/logger'; - // Use abi from @wagmi/core, and get it setup in wagmi.config.ts - const ERC721_ABI = [ - { - constant: true, - inputs: [{ name: 'tokenId', type: 'uint256' }], - name: 'ownerOf', - outputs: [{ name: '', type: 'address' }], - payable: false, - stateMutability: 'view', - type: 'function', - }, - ]; +import { TokenType } from './types'; - const ERC1155_ABI = [ - { - constant: true, - inputs: [ - { name: 'owner', type: 'address' }, - { name: 'operator', type: 'address' }, - ], - name: 'isApprovedForAll', - outputs: [{ name: '', type: 'bool' }], - payable: false, - stateMutability: 'view', - type: 'function', - }, - ]; +const log = getLogger('detectContractType'); + +export async function detectContractType(contractAddress: string) { + log('detectContractType', { contractAddress }); try { await readContract({ address: contractAddress as `0x${string}`, // TODO: type Address - abi: ERC721_ABI, + abi: erc721ABI, functionName: 'ownerOf', - args: [tokenId], + args: [0n], }); - // TODO: please use getLogger from util/logger - // eslint-disable-next-line no-console - console.info('ERC721'); - return 'ERC721'; // TODO: use TokenType + log('is ERC721'); + return TokenType.ERC721; } catch (err) { - // eslint-disable-next-line no-console - console.log(err); + if (err instanceof ContractFunctionExecutionError) { + if (err.cause.message.includes('ERC721: invalid token ID')) { + // valid erc721 contract, but invalid token id + log('is ERC721'); + return TokenType.ERC721; + } + } + + log('is not ERC721', err); try { await readContract({ address: contractAddress as `0x${string}`, - abi: ERC1155_ABI, + abi: erc1155ABI, functionName: 'isApprovedForAll', args: ['0x0000000000000000000000000000000000000000', '0x0000000000000000000000000000000000000000'], }); - // eslint-disable-next-line no-console - console.info('ERC1155'); - return 'ERC1155'; // TODO: use TokenType + log('is ERC1155'); + return TokenType.ERC1155; } catch (err) { // eslint-disable-next-line no-console console.log(err); - return 'UNKNOWN'; // TODO: throw UnknownTypeError and handle in the UI? + throw new UnknownTypeError({ type: 'Unknown tokentype' }); } } }