From b2f23889e903ca933dde00bc7f20d88f78bc72a7 Mon Sep 17 00:00:00 2001 From: Francisco Ramos Date: Wed, 12 Jul 2023 21:08:22 +0200 Subject: [PATCH] feat(bridge-ui-v2): Faucet (#14145) Co-authored-by: Korbinian --- .../src/components/Alert/Alert.svelte | 2 +- .../src/components/Bridge/Bridge.svelte | 24 +-- .../src/components/Button/Button.svelte | 22 ++- .../ChainSelector/ChainSelector.svelte | 60 ++++-- .../ConnectButton/ConnectButton.svelte | 16 +- .../DesktopOrLarger/DesktopOrLarger.svelte | 25 +++ .../src/components/DesktopOrLarger/index.ts | 1 + .../src/components/Faucet/Faucet.svelte | 177 ++++++++++++++++-- .../components/LoadingMask/LoadingMask.svelte | 24 +++ .../src/components/LoadingMask/index.ts | 1 + .../src/components/Modal/Modal.svelte | 12 ++ .../src/components/Modal/index.ts | 1 + .../NotificationToast/ItemToast.svelte | 5 +- .../NotificationToast.svelte | 16 +- .../ProcessingFee/ProcessingFee.svelte | 13 +- .../TokenDropdown/DialogView.svelte | 48 +++++ .../TokenDropdown/DropdownView.svelte | 46 +++++ .../TokenDropdown/TokenDropdown.svelte | 93 +++------ .../TokenDropdown/symbolToIconMap.ts | 9 + packages/bridge-ui-v2/src/i18n/en.json | 25 ++- .../src/libs/connect/web3modal.ts | 3 - .../src/libs/{free => fee}/index.ts | 0 .../src/libs/{free => fee}/processingFees.ts | 0 .../{free => fee}/recommendProcessingFee.ts | 0 .../src/libs/{free => fee}/types.ts | 0 .../src/libs/token/checkMintable.ts | 48 +++++ packages/bridge-ui-v2/src/libs/token/index.ts | 2 + packages/bridge-ui-v2/src/libs/token/mint.ts | 36 ++++ packages/bridge-ui-v2/src/libs/token/types.ts | 12 +- .../bridge-ui-v2/src/routes/+layout.svelte | 6 + .../bridge-ui-v2/src/styles/components.css | 4 - packages/bridge-ui-v2/src/styles/override.css | 13 ++ packages/bridge-ui-v2/tailwind.config.js | 1 + packages/bridge-ui-v2/tsconfig.json | 3 +- 34 files changed, 595 insertions(+), 153 deletions(-) create mode 100644 packages/bridge-ui-v2/src/components/DesktopOrLarger/DesktopOrLarger.svelte create mode 100644 packages/bridge-ui-v2/src/components/DesktopOrLarger/index.ts create mode 100644 packages/bridge-ui-v2/src/components/LoadingMask/LoadingMask.svelte create mode 100644 packages/bridge-ui-v2/src/components/LoadingMask/index.ts create mode 100644 packages/bridge-ui-v2/src/components/Modal/Modal.svelte create mode 100644 packages/bridge-ui-v2/src/components/Modal/index.ts create mode 100644 packages/bridge-ui-v2/src/components/TokenDropdown/DialogView.svelte create mode 100644 packages/bridge-ui-v2/src/components/TokenDropdown/DropdownView.svelte create mode 100644 packages/bridge-ui-v2/src/components/TokenDropdown/symbolToIconMap.ts rename packages/bridge-ui-v2/src/libs/{free => fee}/index.ts (100%) rename packages/bridge-ui-v2/src/libs/{free => fee}/processingFees.ts (100%) rename packages/bridge-ui-v2/src/libs/{free => fee}/recommendProcessingFee.ts (100%) rename packages/bridge-ui-v2/src/libs/{free => fee}/types.ts (100%) create mode 100644 packages/bridge-ui-v2/src/libs/token/checkMintable.ts create mode 100644 packages/bridge-ui-v2/src/libs/token/mint.ts diff --git a/packages/bridge-ui-v2/src/components/Alert/Alert.svelte b/packages/bridge-ui-v2/src/components/Alert/Alert.svelte index 56685c17bde..8dff979fdfa 100644 --- a/packages/bridge-ui-v2/src/components/Alert/Alert.svelte +++ b/packages/bridge-ui-v2/src/components/Alert/Alert.svelte @@ -37,7 +37,7 @@ }; const classes = classNames( - 'alert gap-[5px] py-[12px] px-[20px] rounded-[10px]', + 'alert flex gap-[5px] py-[12px] px-[20px] rounded-[10px]', type ? typeMap[type].class : null, forceColumnFlow ? 'grid-flow-col text-left' : null, $$props.class, diff --git a/packages/bridge-ui-v2/src/components/Bridge/Bridge.svelte b/packages/bridge-ui-v2/src/components/Bridge/Bridge.svelte index 0907877d42d..5f707c337b9 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/Bridge.svelte +++ b/packages/bridge-ui-v2/src/components/Bridge/Bridge.svelte @@ -1,5 +1,4 @@
- +
@@ -50,7 +30,7 @@
- +
diff --git a/packages/bridge-ui-v2/src/components/Button/Button.svelte b/packages/bridge-ui-v2/src/components/Button/Button.svelte index 21389e619ba..0bcab9c66ff 100644 --- a/packages/bridge-ui-v2/src/components/Button/Button.svelte +++ b/packages/bridge-ui-v2/src/components/Button/Button.svelte @@ -1,4 +1,5 @@ diff --git a/packages/bridge-ui-v2/src/components/ChainSelector/ChainSelector.svelte b/packages/bridge-ui-v2/src/components/ChainSelector/ChainSelector.svelte index ec192b9a57f..ab2e071186c 100644 --- a/packages/bridge-ui-v2/src/components/ChainSelector/ChainSelector.svelte +++ b/packages/bridge-ui-v2/src/components/ChainSelector/ChainSelector.svelte @@ -1,27 +1,27 @@ + + {#if connected} - + {:else} - - -
+ {#if alertMessage} + + {alertMessage} + + {/if} - - {$t('faucet.message.warning')} - + {#if connected && wrongChain} + + + {:else} + + {/if}
diff --git a/packages/bridge-ui-v2/src/components/LoadingMask/LoadingMask.svelte b/packages/bridge-ui-v2/src/components/LoadingMask/LoadingMask.svelte new file mode 100644 index 00000000000..cfec438c309 --- /dev/null +++ b/packages/bridge-ui-v2/src/components/LoadingMask/LoadingMask.svelte @@ -0,0 +1,24 @@ + + +
+ + {text} +
diff --git a/packages/bridge-ui-v2/src/components/LoadingMask/index.ts b/packages/bridge-ui-v2/src/components/LoadingMask/index.ts new file mode 100644 index 00000000000..cefb208a5d7 --- /dev/null +++ b/packages/bridge-ui-v2/src/components/LoadingMask/index.ts @@ -0,0 +1 @@ +export { default as LoadingMask } from './LoadingMask.svelte'; diff --git a/packages/bridge-ui-v2/src/components/Modal/Modal.svelte b/packages/bridge-ui-v2/src/components/Modal/Modal.svelte new file mode 100644 index 00000000000..44449b9bd9a --- /dev/null +++ b/packages/bridge-ui-v2/src/components/Modal/Modal.svelte @@ -0,0 +1,12 @@ + + + + + +
+
diff --git a/packages/bridge-ui-v2/src/components/Modal/index.ts b/packages/bridge-ui-v2/src/components/Modal/index.ts new file mode 100644 index 00000000000..2a1af70eefa --- /dev/null +++ b/packages/bridge-ui-v2/src/components/Modal/index.ts @@ -0,0 +1 @@ +export { default as Modal } from './Modal.svelte'; diff --git a/packages/bridge-ui-v2/src/components/NotificationToast/ItemToast.svelte b/packages/bridge-ui-v2/src/components/NotificationToast/ItemToast.svelte index c1252a38b7a..3b2659a8efa 100644 --- a/packages/bridge-ui-v2/src/components/NotificationToast/ItemToast.svelte +++ b/packages/bridge-ui-v2/src/components/NotificationToast/ItemToast.svelte @@ -59,9 +59,10 @@ - - - diff --git a/packages/bridge-ui-v2/src/components/TokenDropdown/DialogView.svelte b/packages/bridge-ui-v2/src/components/TokenDropdown/DialogView.svelte new file mode 100644 index 00000000000..ec090ed98be --- /dev/null +++ b/packages/bridge-ui-v2/src/components/TokenDropdown/DialogView.svelte @@ -0,0 +1,48 @@ + + + + + + diff --git a/packages/bridge-ui-v2/src/components/TokenDropdown/DropdownView.svelte b/packages/bridge-ui-v2/src/components/TokenDropdown/DropdownView.svelte new file mode 100644 index 00000000000..7548ff10e51 --- /dev/null +++ b/packages/bridge-ui-v2/src/components/TokenDropdown/DropdownView.svelte @@ -0,0 +1,46 @@ + + + + diff --git a/packages/bridge-ui-v2/src/components/TokenDropdown/TokenDropdown.svelte b/packages/bridge-ui-v2/src/components/TokenDropdown/TokenDropdown.svelte index fd7c88dc19d..b0fa14f4777 100644 --- a/packages/bridge-ui-v2/src/components/TokenDropdown/TokenDropdown.svelte +++ b/packages/bridge-ui-v2/src/components/TokenDropdown/TokenDropdown.svelte @@ -1,104 +1,73 @@ + +
-
    - {#each tokens as token (token.symbol)} -
  • selectToken(token)} - on:keydown={getTokenKeydownHandler(token)}> -
    - - - - {token.symbol} -
    -
  • - {/each} -
+ {#if isDesktopOrLarger} + + {:else} + + {/if}
diff --git a/packages/bridge-ui-v2/src/components/TokenDropdown/symbolToIconMap.ts b/packages/bridge-ui-v2/src/components/TokenDropdown/symbolToIconMap.ts new file mode 100644 index 00000000000..0e580602bf8 --- /dev/null +++ b/packages/bridge-ui-v2/src/components/TokenDropdown/symbolToIconMap.ts @@ -0,0 +1,9 @@ +import type { ComponentType } from 'svelte'; + +import { BllIcon, EthIcon, HorseIcon } from '$components/Icon'; + +export const symbolToIconMap: Record = { + ETH: EthIcon, + BLL: BllIcon, + HORSE: HorseIcon, +}; diff --git a/packages/bridge-ui-v2/src/i18n/en.json b/packages/bridge-ui-v2/src/i18n/en.json index 73968653920..433598fd692 100644 --- a/packages/bridge-ui-v2/src/i18n/en.json +++ b/packages/bridge-ui-v2/src/i18n/en.json @@ -59,12 +59,25 @@ "faucet": { "title": "Faucet", "subtitle": "Mint test tokens.", - "button.mint": "Mint", - "message.warning": "To request Test Token, you need to mint it on Sepolia. If you are currently on Taiko, please switch your network first. Additionally, ensure that you have a small amount of ETH in your Sepolia wallet to complete the transaction." + "minting_tx": "Transaction sent to mint {token} tokens. Click here to see it in the explorer.", + "button": { + "mint": "Mint", + "checking": "Checking mintability", + "minting": "Minting" + }, + "wrong_chain": { + "message": "It appears your are on the wrong network. Switch to {network} to mint tokens.", + "button": "Switch to {network}" + }, + "warning": { + "no_connected": "Please connect your wallet to mint tokens.", + "insufficient_balance": "You don't have enough ETH to complete the transaction. Please add some ETH to your wallet.", + "token_minted": "You have already minted this token." + } }, "token_dropdown": { - "placeholder": "Select token" + "label": "Select token" }, "amount_input": { @@ -80,9 +93,13 @@ "messages": { "account": { - "required": "Please connect your wallet", + "required": "Please connect your wallet.", "connected": "Account connected", "disconnected": "Account disconnected" + }, + "network": { + "switching": "Switching network", + "rejected": "Request to switch chain rejected." } }, diff --git a/packages/bridge-ui-v2/src/libs/connect/web3modal.ts b/packages/bridge-ui-v2/src/libs/connect/web3modal.ts index 84ce1d50f68..786e52b96c3 100644 --- a/packages/bridge-ui-v2/src/libs/connect/web3modal.ts +++ b/packages/bridge-ui-v2/src/libs/connect/web3modal.ts @@ -12,9 +12,6 @@ const ethereumClient = new EthereumClient(wagmiConfig, chains); export const web3modal = new Web3Modal( { projectId, - enableNetworkViews: true, - enableAccountView: true, - enableExplorer: true, chainImages: { [PUBLIC_L1_CHAIN_ID]: '/ethereum-chain.png', [PUBLIC_L2_CHAIN_ID]: '/taiko-chain.png', diff --git a/packages/bridge-ui-v2/src/libs/free/index.ts b/packages/bridge-ui-v2/src/libs/fee/index.ts similarity index 100% rename from packages/bridge-ui-v2/src/libs/free/index.ts rename to packages/bridge-ui-v2/src/libs/fee/index.ts diff --git a/packages/bridge-ui-v2/src/libs/free/processingFees.ts b/packages/bridge-ui-v2/src/libs/fee/processingFees.ts similarity index 100% rename from packages/bridge-ui-v2/src/libs/free/processingFees.ts rename to packages/bridge-ui-v2/src/libs/fee/processingFees.ts diff --git a/packages/bridge-ui-v2/src/libs/free/recommendProcessingFee.ts b/packages/bridge-ui-v2/src/libs/fee/recommendProcessingFee.ts similarity index 100% rename from packages/bridge-ui-v2/src/libs/free/recommendProcessingFee.ts rename to packages/bridge-ui-v2/src/libs/fee/recommendProcessingFee.ts diff --git a/packages/bridge-ui-v2/src/libs/free/types.ts b/packages/bridge-ui-v2/src/libs/fee/types.ts similarity index 100% rename from packages/bridge-ui-v2/src/libs/free/types.ts rename to packages/bridge-ui-v2/src/libs/fee/types.ts diff --git a/packages/bridge-ui-v2/src/libs/token/checkMintable.ts b/packages/bridge-ui-v2/src/libs/token/checkMintable.ts new file mode 100644 index 00000000000..d1bb15cb2f6 --- /dev/null +++ b/packages/bridge-ui-v2/src/libs/token/checkMintable.ts @@ -0,0 +1,48 @@ +import { type Chain, getContract, getPublicClient, getWalletClient } from '@wagmi/core'; +import { formatEther } from 'viem'; + +import { freeMintErc20ABI } from '$abi'; + +import { MintableError, type Token } from './types'; + +// Throws an error if: +// 1. User is not connected to the network +// 2. User has already minted this token +// 3. User has insufficient balance to mint this token +export async function checkMintable(token: Token, network: Chain) { + const chainId = network.id; + const walletClient = await getWalletClient({ chainId }); + + if (!walletClient) { + throw new Error(`user is not connected to ${network.name}`, { cause: MintableError.NOT_CONNECTED }); + } + + const tokenContract = getContract({ + walletClient, + abi: freeMintErc20ABI, + address: token.addresses[chainId], + }); + + // Check whether the user has already minted this token + const userAddress = walletClient.account.address; + const hasMinted = await tokenContract.read.minters([userAddress]); + + if (hasMinted) { + throw Error(`token ${token.symbol} has already been minted`, { cause: MintableError.TOKEN_MINTED }); + } + + // Check whether the user has enough balance to mint. + // Compute the cost of the transaction: + const publicClient = getPublicClient({ chainId }); + const estimatedGas = await tokenContract.estimateGas.mint([userAddress]); + const gasPrice = await publicClient.getGasPrice(); + const estimatedCost = estimatedGas * gasPrice; + + const userBalance = await publicClient.getBalance({ address: userAddress }); + + if (estimatedCost > userBalance) { + throw Error(`user has insufficient balance to mint ${token.symbol}: ${formatEther(userBalance)}`, { + cause: MintableError.INSUFFICIENT_BALANCE, + }); + } +} diff --git a/packages/bridge-ui-v2/src/libs/token/index.ts b/packages/bridge-ui-v2/src/libs/token/index.ts index 1b22cc5bd3f..e2246d1f63d 100644 --- a/packages/bridge-ui-v2/src/libs/token/index.ts +++ b/packages/bridge-ui-v2/src/libs/token/index.ts @@ -1,2 +1,4 @@ +export { checkMintable } from './checkMintable'; +export { mint } from './mint'; export * from './tokens'; export * from './types'; diff --git a/packages/bridge-ui-v2/src/libs/token/mint.ts b/packages/bridge-ui-v2/src/libs/token/mint.ts new file mode 100644 index 00000000000..7bd03b1cf1b --- /dev/null +++ b/packages/bridge-ui-v2/src/libs/token/mint.ts @@ -0,0 +1,36 @@ +import { getContract, type WalletClient } from '@wagmi/core'; + +import { freeMintErc20ABI } from '$abi'; + +import { getLogger } from '../util/logger'; +import type { Token } from './types'; + +const log = getLogger('token:mint'); + +export async function mint(token: Token, walletClient: WalletClient) { + const tokenSymbol = token.symbol; + const userAddress = walletClient.account.address; + const chainId = walletClient.chain.id; + + const tokenContract = getContract({ + walletClient, + abi: freeMintErc20ABI, + address: token.addresses[chainId], + }); + + log(`Minting ${tokenSymbol} for account "${userAddress}"`); + + try { + const txHash = await tokenContract.write.mint([userAddress]); + + log(`Minting transaction hash for ${tokenSymbol}: ${txHash}`); + + return txHash; + } catch (error) { + console.error(error); + + throw new Error(`found a problem minting ${tokenSymbol}`, { + cause: error, + }); + } +} diff --git a/packages/bridge-ui-v2/src/libs/token/types.ts b/packages/bridge-ui-v2/src/libs/token/types.ts index a1c0bd67bce..c47b27f7fa3 100644 --- a/packages/bridge-ui-v2/src/libs/token/types.ts +++ b/packages/bridge-ui-v2/src/libs/token/types.ts @@ -1,12 +1,20 @@ +import type { Address } from 'abitype'; + export type Token = { name: string; - addresses: Record; + addresses: Record; symbol: string; decimals: number; }; export type TokenEnv = { name: string; - address: string; + address: Address; symbol: string; }; + +export enum MintableError { + NOT_CONNECTED = 'NOT_CONNECTED', + TOKEN_MINTED = 'TOKEN_MINTED', + INSUFFICIENT_BALANCE = 'INSUFFICIENT_BALANCE', +} diff --git a/packages/bridge-ui-v2/src/routes/+layout.svelte b/packages/bridge-ui-v2/src/routes/+layout.svelte index ebc2cf78fa9..76f0e336502 100644 --- a/packages/bridge-ui-v2/src/routes/+layout.svelte +++ b/packages/bridge-ui-v2/src/routes/+layout.svelte @@ -14,6 +14,7 @@ onDestroy(stopWatching); +
@@ -21,6 +22,11 @@
+ + diff --git a/packages/bridge-ui-v2/src/styles/components.css b/packages/bridge-ui-v2/src/styles/components.css index c43d2297c5e..7b82bbfa2c6 100644 --- a/packages/bridge-ui-v2/src/styles/components.css +++ b/packages/bridge-ui-v2/src/styles/components.css @@ -118,9 +118,5 @@ @apply modal-backdrop bg-overlay-background; } - .mask { - @apply absolute top-0 right-0 bottom-0 left-0 overflow-hidden; - } - /* TODO: add more components here */ } diff --git a/packages/bridge-ui-v2/src/styles/override.css b/packages/bridge-ui-v2/src/styles/override.css index 0d50a0d592c..d35f042ad08 100644 --- a/packages/bridge-ui-v2/src/styles/override.css +++ b/packages/bridge-ui-v2/src/styles/override.css @@ -21,3 +21,16 @@ input[type='number'] { /* Primary background: '#0B101B' => grey-900 */ background-color: hsl(221, 42%, 7%); } + +/** + * By design we don't have any opacity on disabled buttons, + * also font color is tertiary-content + */ +.btn.btn-disabled, +.btn[disabled], +.btn:disabled { + --tw-border-opacity: 1; + --tw-bg-opacity: 1; + --tw-text-opacity: 1; + @apply text-tertiary-content; +} diff --git a/packages/bridge-ui-v2/tailwind.config.js b/packages/bridge-ui-v2/tailwind.config.js index 106fc54a1d5..bb9345b7ef8 100644 --- a/packages/bridge-ui-v2/tailwind.config.js +++ b/packages/bridge-ui-v2/tailwind.config.js @@ -140,6 +140,7 @@ export default { content: 'var(--tertiary-content)', interactive: { hover: 'var(--tertiary-interactive-hover)', + accent: 'var(--tertiary-interactive-accent)', }, }, diff --git a/packages/bridge-ui-v2/tsconfig.json b/packages/bridge-ui-v2/tsconfig.json index 2cd1c44be31..112aee87f87 100644 --- a/packages/bridge-ui-v2/tsconfig.json +++ b/packages/bridge-ui-v2/tsconfig.json @@ -15,7 +15,8 @@ "paths": { "$components/*": ["./src/components/*"], "$stores/*": ["./src/stores/*"], - "$libs/*": ["./src/libs/*"] + "$libs/*": ["./src/libs/*"], + "$abi": ["./src/abi/index.ts"] }, // https://vitest.dev/config/#globals