From a4aeeee1493d74613c09880862ebd2dfb989edcb Mon Sep 17 00:00:00 2001 From: Francisco Ramos Date: Mon, 24 Jul 2023 12:21:07 +0200 Subject: [PATCH] feat(bridge-ui-v2): bridging ETH and ERC20 (#14225) --- packages/bridge-ui-v2/src/app.config.ts | 4 + .../components/Activities/Activities.svelte | 4 +- .../src/components/Alert/Alert.svelte | 24 +- .../src/components/Alert/FlatAlert.svelte | 47 ++++ .../src/components/Alert/index.ts | 1 + .../src/components/Alert/types.ts | 8 + .../src/components/Bridge/Actions.svelte | 96 +++++++ .../src/components/Bridge/Amount.svelte | 223 ++++++++++++++++ .../components/Bridge/Amount/Amount.svelte | 180 ------------- .../components/Bridge/Amount/Balance.svelte | 57 ----- .../src/components/Bridge/Amount/index.ts | 1 - .../src/components/Bridge/Bridge.svelte | 242 ++++++++++++++++-- .../Bridge/ProcessingFee/ProcessingFee.svelte | 24 +- .../src/components/Bridge/Recipient.svelte | 123 +++++++++ .../Bridge/Recipient/Recipient.svelte | 14 - .../src/components/Bridge/Recipient/index.ts | 1 - .../Bridge/SwitchChainsButton.svelte | 2 +- .../src/components/Bridge/state.ts | 27 +- .../ChainSelector/ChainSelector.svelte | 23 +- .../src/components/Faucet/Faucet.svelte | 39 ++- .../src/components/Icon/Icon.svelte | 11 +- .../NotificationToast/ItemToast.svelte | 5 + .../NotificationToast.svelte | 4 + .../src/components/NotificationToast/types.ts | 2 +- packages/bridge-ui-v2/src/i18n/en.json | 75 ++++-- .../src/libs/bridge/ERC1155Bridge.ts | 6 + .../src/libs/bridge/ERC20Bridge.ts | 118 ++++++++- .../src/libs/bridge/ERC721Bridge.ts | 6 + .../bridge-ui-v2/src/libs/bridge/ETHBridge.ts | 44 +++- .../bridge-ui-v2/src/libs/bridge/bridges.ts | 6 +- .../src/libs/bridge/checkBalanceToBridge.ts | 17 +- .../src/libs/bridge/getMaxAmountToBridge.ts | 7 +- .../bridge-ui-v2/src/libs/bridge/types.ts | 24 +- .../bridge-ui-v2/src/libs/error/errors.ts | 50 +++- .../src/libs/fee/recommendProcessingFee.ts | 4 +- .../bridge-ui-v2/src/libs/token/getAddress.ts | 10 +- .../bridge-ui-v2/src/libs/token/getBalance.ts | 5 +- .../src/libs/token/getCrossChainAddress.ts | 5 +- .../src/libs/token/isDeployedCrossChain.ts | 2 +- .../bridge-ui-v2/src/libs/token/mint.test.ts | 2 +- packages/bridge-ui-v2/src/libs/token/mint.ts | 20 +- .../src/libs/token/tokens.test.ts | 28 -- .../bridge-ui-v2/src/libs/token/tokens.ts | 19 +- packages/bridge-ui-v2/src/libs/token/types.ts | 14 + .../bridge-ui-v2/src/libs/util/Deferred.ts | 14 + .../src/libs/util/getConnectedWallet.ts | 4 +- .../src/libs/util/shortenAddress.test.ts | 12 + .../src/libs/util/shortenAddress.ts | 4 + packages/bridge-ui-v2/src/stores/index.ts | 1 + .../src/stores/pendingTransactions.ts | 79 ++++++ 50 files changed, 1285 insertions(+), 453 deletions(-) create mode 100644 packages/bridge-ui-v2/src/components/Alert/FlatAlert.svelte create mode 100644 packages/bridge-ui-v2/src/components/Alert/types.ts create mode 100644 packages/bridge-ui-v2/src/components/Bridge/Actions.svelte create mode 100644 packages/bridge-ui-v2/src/components/Bridge/Amount.svelte delete mode 100644 packages/bridge-ui-v2/src/components/Bridge/Amount/Amount.svelte delete mode 100644 packages/bridge-ui-v2/src/components/Bridge/Amount/Balance.svelte delete mode 100644 packages/bridge-ui-v2/src/components/Bridge/Amount/index.ts create mode 100644 packages/bridge-ui-v2/src/components/Bridge/Recipient.svelte delete mode 100644 packages/bridge-ui-v2/src/components/Bridge/Recipient/Recipient.svelte delete mode 100644 packages/bridge-ui-v2/src/components/Bridge/Recipient/index.ts delete mode 100644 packages/bridge-ui-v2/src/libs/token/tokens.test.ts create mode 100644 packages/bridge-ui-v2/src/libs/util/Deferred.ts create mode 100644 packages/bridge-ui-v2/src/libs/util/shortenAddress.test.ts create mode 100644 packages/bridge-ui-v2/src/libs/util/shortenAddress.ts create mode 100644 packages/bridge-ui-v2/src/stores/pendingTransactions.ts diff --git a/packages/bridge-ui-v2/src/app.config.ts b/packages/bridge-ui-v2/src/app.config.ts index 0c49606a95..64e9bd3fb0 100644 --- a/packages/bridge-ui-v2/src/app.config.ts +++ b/packages/bridge-ui-v2/src/app.config.ts @@ -13,3 +13,7 @@ export const bridge = { noOwnerGasLimit: BigInt(140000), noTokenDeployedGasLimit: BigInt(3000000), }; + +export const pendingTransaction = { + waitTimeout: 300000, +}; diff --git a/packages/bridge-ui-v2/src/components/Activities/Activities.svelte b/packages/bridge-ui-v2/src/components/Activities/Activities.svelte index 9e3633b7dc..c5549a215e 100644 --- a/packages/bridge-ui-v2/src/components/Activities/Activities.svelte +++ b/packages/bridge-ui-v2/src/components/Activities/Activities.svelte @@ -9,9 +9,9 @@ import TableView from './TableView.svelte'; - +
- +
diff --git a/packages/bridge-ui-v2/src/components/Alert/Alert.svelte b/packages/bridge-ui-v2/src/components/Alert/Alert.svelte index 8dff979fdf..13844da0cd 100644 --- a/packages/bridge-ui-v2/src/components/Alert/Alert.svelte +++ b/packages/bridge-ui-v2/src/components/Alert/Alert.svelte @@ -1,13 +1,11 @@
diff --git a/packages/bridge-ui-v2/src/components/Alert/FlatAlert.svelte b/packages/bridge-ui-v2/src/components/Alert/FlatAlert.svelte new file mode 100644 index 0000000000..6fc7fd4d02 --- /dev/null +++ b/packages/bridge-ui-v2/src/components/Alert/FlatAlert.svelte @@ -0,0 +1,47 @@ + + +
+ +
+ {message} +
+
diff --git a/packages/bridge-ui-v2/src/components/Alert/index.ts b/packages/bridge-ui-v2/src/components/Alert/index.ts index e3f24bb7c7..5d62a66c91 100644 --- a/packages/bridge-ui-v2/src/components/Alert/index.ts +++ b/packages/bridge-ui-v2/src/components/Alert/index.ts @@ -1 +1,2 @@ export { default as Alert } from './Alert.svelte'; +export { default as FlatAlert } from './FlatAlert.svelte'; diff --git a/packages/bridge-ui-v2/src/components/Alert/types.ts b/packages/bridge-ui-v2/src/components/Alert/types.ts new file mode 100644 index 0000000000..7a50f31c3e --- /dev/null +++ b/packages/bridge-ui-v2/src/components/Alert/types.ts @@ -0,0 +1,8 @@ +import type { IconType } from '$components/Icon'; + +export type AlertType = 'success' | 'warning' | 'error' | 'info'; + +export type AlertIconDetails = { + iconType: IconType; + iconFillClass: string; +}; diff --git a/packages/bridge-ui-v2/src/components/Bridge/Actions.svelte b/packages/bridge-ui-v2/src/components/Bridge/Actions.svelte new file mode 100644 index 0000000000..a815a4cb17 --- /dev/null +++ b/packages/bridge-ui-v2/src/components/Bridge/Actions.svelte @@ -0,0 +1,96 @@ + + +
+ {#if isSelectedERC20} + + + {/if} + + +
diff --git a/packages/bridge-ui-v2/src/components/Bridge/Amount.svelte b/packages/bridge-ui-v2/src/components/Bridge/Amount.svelte new file mode 100644 index 0000000000..30f8bb4e9d --- /dev/null +++ b/packages/bridge-ui-v2/src/components/Bridge/Amount.svelte @@ -0,0 +1,223 @@ + + +
+
+ +
+ {$t('amount_input.balance')}: + + {#if $computingBalance} + + + {:else} + {renderBalance($tokenBalance)} + {/if} + + +
+
+ +
+
+ + + +
+ + {#if $insufficientBalance} + + {/if} + + {#if $insufficientAllowance} + + {/if} +
+
diff --git a/packages/bridge-ui-v2/src/components/Bridge/Amount/Amount.svelte b/packages/bridge-ui-v2/src/components/Bridge/Amount/Amount.svelte deleted file mode 100644 index 9775ac2855..0000000000 --- a/packages/bridge-ui-v2/src/components/Bridge/Amount/Amount.svelte +++ /dev/null @@ -1,180 +0,0 @@ - - -
-
- - -
- -
- - - -
- - {#if insufficientBalance} - -
- -
- {$t('amount_input.error.insufficient_balance')} -
-
- {/if} - - {#if insufficientAllowance} -
- -
- {$t('amount_input.error.insufficient_allowance')} -
-
- {/if} -
diff --git a/packages/bridge-ui-v2/src/components/Bridge/Amount/Balance.svelte b/packages/bridge-ui-v2/src/components/Bridge/Amount/Balance.svelte deleted file mode 100644 index ca4c24ccce..0000000000 --- a/packages/bridge-ui-v2/src/components/Bridge/Amount/Balance.svelte +++ /dev/null @@ -1,57 +0,0 @@ - - -
- {$t('amount_input.balance')}: - - {#if computingTokenBalance} - - - {:else} - {renderTokenBalance(value)} - {/if} - -
diff --git a/packages/bridge-ui-v2/src/components/Bridge/Amount/index.ts b/packages/bridge-ui-v2/src/components/Bridge/Amount/index.ts deleted file mode 100644 index e8c536613e..0000000000 --- a/packages/bridge-ui-v2/src/components/Bridge/Amount/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as Amount } from './Amount.svelte'; diff --git a/packages/bridge-ui-v2/src/components/Bridge/Bridge.svelte b/packages/bridge-ui-v2/src/components/Bridge/Bridge.svelte index 86a1baa5d1..8cdbc5aad0 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/Bridge.svelte +++ b/packages/bridge-ui-v2/src/components/Bridge/Bridge.svelte @@ -1,23 +1,36 @@ - +
-
-
- - -
- - - -
- -
- -
- - -
+
+ + + + + +
- + + + + + + +
- +
diff --git a/packages/bridge-ui-v2/src/components/Bridge/ProcessingFee/ProcessingFee.svelte b/packages/bridge-ui-v2/src/components/Bridge/ProcessingFee/ProcessingFee.svelte index b75865e414..1bb3f044c2 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/ProcessingFee/ProcessingFee.svelte +++ b/packages/bridge-ui-v2/src/components/Bridge/ProcessingFee/ProcessingFee.svelte @@ -33,6 +33,12 @@ let modalOpen = false; let inputBox: InputBox; + // Public API + export function resetProcessingFee() { + inputBox.clear(); + selectedFeeMethod = ProcessingFeeMethod.RECOMMENDED; + } + function closeModal() { // Let's check if we are closing with CUSTOM method selected and zero amount entered if (selectedFeeMethod === ProcessingFeeMethod.CUSTOM && $processingFee === BigInt(0)) { @@ -67,11 +73,11 @@ inputBox.focus(); } - function onInputBoxChange(event: Event) { + function inputProcessFee(event: Event) { if (selectedFeeMethod !== ProcessingFeeMethod.CUSTOM) return; - const input = event.target as HTMLInputElement; - $processingFee = parseToWei(input.value); + const { value } = event.target as HTMLInputElement; + $processingFee = parseToWei(value); } async function updateProcessingFee(method: ProcessingFeeMethod, recommendedAmount: bigint) { @@ -118,10 +124,10 @@
- {$t('processing_fee.title')} + {$t('processing_fee.title')} TODO: add description about processing fee
- +
@@ -142,6 +148,8 @@

{$t('processing_fee.title')}

+

{$t('processing_fee.description')}

+
  • @@ -226,7 +234,7 @@ placeholder="0.01" disabled={selectedFeeMethod !== ProcessingFeeMethod.CUSTOM} class="w-full input-box outline-none p-6 pr-16 title-subsection-bold placeholder:text-tertiary-content" - on:input={onInputBoxChange} + on:input={inputProcessFee} bind:this={inputBox} /> ETH
@@ -236,10 +244,10 @@ on:click={cancelModal} type="neutral" class="px-[28px] py-[10px] rounded-full w-auto bg-transparent !border border-primary-brand hover:border-primary-interactive-hover"> - {$t('processing_fee.button.cancel')} + {$t('common.cancel')}
diff --git a/packages/bridge-ui-v2/src/components/Bridge/Recipient.svelte b/packages/bridge-ui-v2/src/components/Bridge/Recipient.svelte new file mode 100644 index 0000000000..867f0397d7 --- /dev/null +++ b/packages/bridge-ui-v2/src/components/Bridge/Recipient.svelte @@ -0,0 +1,123 @@ + + +
+
+
+ {$t('recipient.title')} + TODO: add description about processing fee +
+ +
+ + + {#if displayedRecipient} + {shortenAddress(displayedRecipient, 15, 13)} + {:else} + {$t('recipient.placeholder')} + {/if} + + + + + +
diff --git a/packages/bridge-ui-v2/src/components/Bridge/Recipient/Recipient.svelte b/packages/bridge-ui-v2/src/components/Bridge/Recipient/Recipient.svelte deleted file mode 100644 index 61c168ad7b..0000000000 --- a/packages/bridge-ui-v2/src/components/Bridge/Recipient/Recipient.svelte +++ /dev/null @@ -1,14 +0,0 @@ - - -
- - TODO: add description about custom recipient -
diff --git a/packages/bridge-ui-v2/src/components/Bridge/Recipient/index.ts b/packages/bridge-ui-v2/src/components/Bridge/Recipient/index.ts deleted file mode 100644 index fc42fed1b7..0000000000 --- a/packages/bridge-ui-v2/src/components/Bridge/Recipient/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as Recipient } from './Recipient.svelte'; diff --git a/packages/bridge-ui-v2/src/components/Bridge/SwitchChainsButton.svelte b/packages/bridge-ui-v2/src/components/Bridge/SwitchChainsButton.svelte index 18af65694e..4c64c0ad7c 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/SwitchChainsButton.svelte +++ b/packages/bridge-ui-v2/src/components/Bridge/SwitchChainsButton.svelte @@ -27,5 +27,5 @@ class="f-center rounded-full bg-secondary-icon w-[30px] h-[30px]" disabled={!$destNetwork} on:click={switchToDestChain}> - + diff --git a/packages/bridge-ui-v2/src/components/Bridge/state.ts b/packages/bridge-ui-v2/src/components/Bridge/state.ts index 6343509800..8dc806c305 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/state.ts +++ b/packages/bridge-ui-v2/src/components/Bridge/state.ts @@ -1,6 +1,7 @@ -import type { Chain } from '@wagmi/core'; -import { writable } from 'svelte/store'; +import type { Address, Chain, FetchBalanceResult } from '@wagmi/core'; +import { derived, writable } from 'svelte/store'; +import { bridges } from '$libs/bridge'; import type { Token } from '$libs/token'; // Note: we could combine this with Context API, but since we'll only @@ -13,6 +14,28 @@ import type { Token } from '$libs/token'; // prevent other components outside the Bridge from accessing this store. export const selectedToken = writable>(null); +export const tokenBalance = writable>(null); export const enteredAmount = writable(BigInt(0)); export const destNetwork = writable>(null); export const processingFee = writable(BigInt(0)); +export const recipientAddress = writable>(null); + +// Loading state +export const bridging = writable(false); +export const approving = writable(false); +export const computingBalance = writable(false); + +// Errors state +export const errorComputingBalance = writable(false); + +// There are two possible errors that can happen when the user +// enters an amount: +// 1. Insufficient balance +// 2. Insufficient allowance +// The first one is an error and the user cannot proceed. The second one +// is a warning but the user must approve allowance before bridging +export const insufficientBalance = writable(false); +export const insufficientAllowance = writable(false); + +// Derived state +export const bridgeService = derived(selectedToken, (token) => (token ? bridges[token.type] : null)); diff --git a/packages/bridge-ui-v2/src/components/ChainSelector/ChainSelector.svelte b/packages/bridge-ui-v2/src/components/ChainSelector/ChainSelector.svelte index ae34f6184b..ba5dc4a936 100644 --- a/packages/bridge-ui-v2/src/components/ChainSelector/ChainSelector.svelte +++ b/packages/bridge-ui-v2/src/components/ChainSelector/ChainSelector.svelte @@ -10,13 +10,23 @@ import { warningToast } from '$components/NotificationToast'; import { PUBLIC_L1_CHAIN_ID, PUBLIC_L2_CHAIN_ID } from '$env/static/public'; import { chains } from '$libs/chain'; + import { classNames } from '$libs/util/classNames'; import { uid } from '$libs/util/uid'; import { account } from '$stores/account'; - export let label: string; + export let label = ''; export let value: Maybe = null; export let switchWallet = false; export let readOnly = false; + export let small = false; + + let classes = classNames('ChainSelector', $$props.class); + let buttonClasses = classNames( + 'px-2 body-small-regular bg-neutral-background', + small ? 'py-[6px]' : 'py-[10px]', + small ? 'rounded-md' : 'rounded-[10px]', + small ? 'w-auto' : 'w-full', + ); let chainToIconMap: Record = { [PUBLIC_L1_CHAIN_ID]: EthIcon, @@ -82,9 +92,12 @@ onDestroy(closeModal); -
+
- + {#if label} + + {/if} +