From bec2657993091b5d6a3df33cae9040981b3cb702 Mon Sep 17 00:00:00 2001 From: Francisco Date: Mon, 17 Jul 2023 19:34:50 +0200 Subject: [PATCH 01/39] minor change --- .../src/components/Bridge/AmountInput/Balance.svelte | 4 ++-- .../components/ChainSelector/ChainSelector.svelte | 4 ++-- .../bridge-ui-v2/src/components/Faucet/Faucet.svelte | 12 ++++++------ 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/bridge-ui-v2/src/components/Bridge/AmountInput/Balance.svelte b/packages/bridge-ui-v2/src/components/Bridge/AmountInput/Balance.svelte index 18000188fa6..1c204943709 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/AmountInput/Balance.svelte +++ b/packages/bridge-ui-v2/src/components/Bridge/AmountInput/Balance.svelte @@ -27,8 +27,8 @@ userAddress: account.address, chainId: srcChainId, }); - } catch (error) { - console.error(error); + } catch (err) { + console.error(err); errorComputingTokenBalance = true; } finally { computingTokenBalance = false; diff --git a/packages/bridge-ui-v2/src/components/ChainSelector/ChainSelector.svelte b/packages/bridge-ui-v2/src/components/ChainSelector/ChainSelector.svelte index a95f2fcb20f..007d288f5ad 100644 --- a/packages/bridge-ui-v2/src/components/ChainSelector/ChainSelector.svelte +++ b/packages/bridge-ui-v2/src/components/ChainSelector/ChainSelector.svelte @@ -56,8 +56,8 @@ try { await switchNetwork({ chainId: chain.id }); closeModal(); - } catch (error) { - console.error(error); + } catch (err) { + console.error(err); if (error instanceof UserRejectedRequestError) { warningToast($t('messages.network.rejected')); diff --git a/packages/bridge-ui-v2/src/components/Faucet/Faucet.svelte b/packages/bridge-ui-v2/src/components/Faucet/Faucet.svelte index c1348c80eb3..b69cb192642 100644 --- a/packages/bridge-ui-v2/src/components/Faucet/Faucet.svelte +++ b/packages/bridge-ui-v2/src/components/Faucet/Faucet.svelte @@ -31,8 +31,8 @@ try { await switchNetwork({ chainId: +PUBLIC_L1_CHAIN_ID }); - } catch (error) { - console.error(error); + } catch (err) { + console.error(err); if (error instanceof UserRejectedRequestError) { warningToast($t('messages.network.rejected')); @@ -70,8 +70,8 @@ ); // TODO: pending transaction logic - } catch (error) { - console.error(error); + } catch (err) { + console.error(err); // const { cause } = error as Error; } finally { @@ -100,8 +100,8 @@ try { await checkMintable(token, network); mintButtonEnabled = true; - } catch (error) { - console.error(error); + } catch (err) { + console.error(err); const { cause } = error as Error; From 3a84b4fd726147ee142dc64362fc10789ebb3cf7 Mon Sep 17 00:00:00 2001 From: Francisco Date: Wed, 19 Jul 2023 00:41:31 +0200 Subject: [PATCH 02/39] wip --- packages/bridge-ui-v2/src/app.config.ts | 4 + .../Bridge/AmountInput/AmountInput.svelte | 15 ++++ .../src/components/Bridge/state.ts | 7 +- .../bridge-ui-v2/src/libs/bridge/ETHBridge.ts | 78 +++++++++++++++++++ .../bridge-ui-v2/src/libs/bridge/types.ts | 71 +++++++++++++++++ 5 files changed, 172 insertions(+), 3 deletions(-) create mode 100644 packages/bridge-ui-v2/src/libs/bridge/ETHBridge.ts create mode 100644 packages/bridge-ui-v2/src/libs/bridge/types.ts diff --git a/packages/bridge-ui-v2/src/app.config.ts b/packages/bridge-ui-v2/src/app.config.ts index 5584560c844..cc0e4c917ee 100644 --- a/packages/bridge-ui-v2/src/app.config.ts +++ b/packages/bridge-ui-v2/src/app.config.ts @@ -8,3 +8,7 @@ export const processingFeeComponent = { closingDelayOptionClick: 300, intervalComputeRecommendedFee: 20000, }; + +export const bridge = { + nonOwnerGasLimit: BigInt(140000), +}; diff --git a/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte b/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte index d2cd50a02cc..0db5dbb8d4d 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte +++ b/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte @@ -1,12 +1,26 @@
@@ -20,6 +34,7 @@ type="number" placeholder="0.01" min="0" + on:input={updateAmount} class="w-full input-box outline-none py-6 pr-16 px-[26px] title-subsection-bold placeholder:text-tertiary-content" />
diff --git a/packages/bridge-ui-v2/src/components/Bridge/state.ts b/packages/bridge-ui-v2/src/components/Bridge/state.ts index 834e1045738..63435098002 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/state.ts +++ b/packages/bridge-ui-v2/src/components/Bridge/state.ts @@ -12,6 +12,7 @@ import type { Token } from '$libs/token'; // but once again, we don't need such level of security that we have to // prevent other components outside the Bridge from accessing this store. -export const selectedToken = writable>(); -export const destNetwork = writable>(); -export const processingFee = writable(); +export const selectedToken = writable>(null); +export const enteredAmount = writable(BigInt(0)); +export const destNetwork = writable>(null); +export const processingFee = writable(BigInt(0)); diff --git a/packages/bridge-ui-v2/src/libs/bridge/ETHBridge.ts b/packages/bridge-ui-v2/src/libs/bridge/ETHBridge.ts new file mode 100644 index 00000000000..e163be6fabf --- /dev/null +++ b/packages/bridge-ui-v2/src/libs/bridge/ETHBridge.ts @@ -0,0 +1,78 @@ +import { getContract } from '@wagmi/core'; + +import { bridgeABI } from '$abi'; +import { bridge } from '$config'; +import { getLogger } from '$libs/util/logger'; + +import type { ETHBridgeArgs, Message } from './types'; + +const log = getLogger('ETHBridge'); + +export class ETHBridge { + private static async _prepareTransaction(args: ETHBridgeArgs) { + const { to, memo = '', amount, srcChainId, destChainId, walletClient, bridgeAddress, processingFee } = args; + + const bridgeContract = getContract({ + walletClient, + abi: bridgeABI, + address: bridgeAddress, + }); + + const owner = walletClient.account.address; + + // TODO: contract actually supports bridging to ourselves as well as + // to another address at the same time + const [depositValue, callValue] = + to.toLowerCase() === owner.toLowerCase() ? [amount, BigInt(0)] : [BigInt(0), amount]; + + const gasLimit = processingFee > 0 ? bridge.nonOwnerGasLimit : BigInt(0); + + const message: Message = { + to, + owner, + sender: owner, + refundAddress: owner, + + srcChainId, + destChainId, + + gasLimit, + callValue, + depositValue, + processingFee, + + memo, + data: '0x', + }; + + log('Preparing transaction with message', message); + + return { bridgeContract, owner, message }; + } + + // async estimateGas(args: ): Promise { + // const { contract, message } = await ETHBridge._prepareTransaction(opts); + + // const value = message.depositValue + // .add(message.processingFee) + // .add(message.callValue); + + // log(`Estimating gas for sendMessage. Value to send: ${value}`); + + // try { + // // See https://docs.ethers.org/v5/api/contract/contract/#contract-estimateGas + // const gasEstimate = await contract.estimateGas.sendMessage(message, { + // value, + // }); + + // log('Estimated gas', gasEstimate.toString()); + + // return gasEstimate; + // } catch (error) { + // console.error(error); + // throw new Error('failed to estimate gas for sendMessage', { + // cause: error, + // }); + // } + // } +} diff --git a/packages/bridge-ui-v2/src/libs/bridge/types.ts b/packages/bridge-ui-v2/src/libs/bridge/types.ts new file mode 100644 index 00000000000..b10399d153d --- /dev/null +++ b/packages/bridge-ui-v2/src/libs/bridge/types.ts @@ -0,0 +1,71 @@ +import type { WalletClient } from '@wagmi/core'; +import type { Address } from 'abitype'; + +export enum BridgeType { + ETH = 'ETH', + + // https://ethereum.org/en/developers/docs/standards/tokens/erc-20/ + ERC20 = 'ERC20', + + // https://ethereum.org/en/developers/docs/standards/tokens/erc-721/ + ERC721 = 'ERC721', + + // https://ethereum.org/en/developers/docs/standards/tokens/erc-1155/ + ERC1155 = 'ERC1155', +} + +export type Message = { + // Message sender address (auto filled) + sender: Address; + // Source chain ID (auto filled) + srcChainId: number; + // Destination chain ID where the `to` address lives (auto filled) + destChainId: number; + // Owner address of the bridged asset. + owner: Address; + // Destination owner address + to: Address; + // Alternate address to send any refund. If blank, defaults to owner. + refundAddress: Address; + // Deposited Ether minus the processingFee. + depositValue: bigint; + // callValue to invoke on the destination chain, for ERC20 transfers. + callValue: bigint; + // Processing fee for the relayer. Zero if owner will process themself. + processingFee: bigint; + // gasLimit to invoke on the destination chain, for ERC20 transfers. + gasLimit: bigint; + // Message ID. Will be set in contract + id?: number; + // callData to invoke on the destination chain, for ERC20 transfers. + data?: string; + // Optional memo. + memo?: string; +}; + +export type ApproveArgs = { + amount: bigint; + tokenAddress: Address; + spenderAddress: Address; + walletClient: WalletClient; +}; + +export type BridgeArgs = { + to: Address; + srcChainId: number; + destChainId: number; + amount: bigint; + walletClient: WalletClient; + processingFee: bigint; + memo?: string; +}; + +export type ETHBridgeArgs = BridgeArgs & { + bridgeAddress: Address; +}; + +export type ERC20BridgeArgs = BridgeArgs & { + tokenAddress: Address; + tokenVaultAddress: Address; + isBridgedTokenAlreadyDeployed?: boolean; +}; From 82d6d88068d02fc6b3ea469c6ca44944427342a6 Mon Sep 17 00:00:00 2001 From: Francisco Date: Wed, 19 Jul 2023 08:02:40 +0200 Subject: [PATCH 03/39] wip --- packages/bridge-ui-v2/src/libs/bridge/ETHBridge.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/bridge-ui-v2/src/libs/bridge/ETHBridge.ts b/packages/bridge-ui-v2/src/libs/bridge/ETHBridge.ts index e163be6fabf..895dd88780e 100644 --- a/packages/bridge-ui-v2/src/libs/bridge/ETHBridge.ts +++ b/packages/bridge-ui-v2/src/libs/bridge/ETHBridge.ts @@ -25,6 +25,7 @@ export class ETHBridge { const [depositValue, callValue] = to.toLowerCase() === owner.toLowerCase() ? [amount, BigInt(0)] : [BigInt(0), amount]; + // If there is a processing fee const gasLimit = processingFee > 0 ? bridge.nonOwnerGasLimit : BigInt(0); const message: Message = { From 8adfaeaedfc8dfa6619aef331488ad7327036c8e Mon Sep 17 00:00:00 2001 From: Francisco Date: Wed, 19 Jul 2023 10:58:37 +0200 Subject: [PATCH 04/39] wip --- packages/bridge-ui-v2/src/app.config.ts | 3 +- .../Bridge/AmountInput/AmountInput.svelte | 11 +++- .../Bridge/AmountInput/Balance.svelte | 7 ++- .../Bridge/ProcessingFee/NoneOption.svelte | 2 +- .../src/components/Faucet/Faucet.svelte | 10 ++-- .../src/libs/bridge/ERC20Bridge.ts | 59 +++++++++++++++++++ .../bridge-ui-v2/src/libs/bridge/ETHBridge.ts | 38 +++--------- .../bridge-ui-v2/src/libs/bridge/types.ts | 27 ++++++--- .../bridge-ui-v2/src/libs/chain/chains.ts | 3 +- .../bridge-ui-v2/src/libs/token/getBalance.ts | 3 +- packages/bridge-ui-v2/src/libs/token/types.ts | 2 +- 11 files changed, 112 insertions(+), 53 deletions(-) create mode 100644 packages/bridge-ui-v2/src/libs/bridge/ERC20Bridge.ts diff --git a/packages/bridge-ui-v2/src/app.config.ts b/packages/bridge-ui-v2/src/app.config.ts index cc0e4c917ee..0c49606a95c 100644 --- a/packages/bridge-ui-v2/src/app.config.ts +++ b/packages/bridge-ui-v2/src/app.config.ts @@ -10,5 +10,6 @@ export const processingFeeComponent = { }; export const bridge = { - nonOwnerGasLimit: BigInt(140000), + noOwnerGasLimit: BigInt(140000), + noTokenDeployedGasLimit: BigInt(3000000), }; diff --git a/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte b/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte index 0db5dbb8d4d..03a74586c3a 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte +++ b/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte @@ -1,14 +1,19 @@
- +
; + export let value: Maybe; + let computingTokenBalance = false; let errorComputingTokenBalance = false; @@ -21,7 +22,7 @@ errorComputingTokenBalance = false; try { - tokenBalance = await getTokenBalance({ + value = await getTokenBalance({ token, destChainId, userAddress: account.address, @@ -50,7 +51,7 @@ {:else} - {renderTokenBalance(tokenBalance)} + {renderTokenBalance(value)} {/if}
diff --git a/packages/bridge-ui-v2/src/components/Bridge/ProcessingFee/NoneOption.svelte b/packages/bridge-ui-v2/src/components/Bridge/ProcessingFee/NoneOption.svelte index fb5dbd614f8..99af71c167a 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/ProcessingFee/NoneOption.svelte +++ b/packages/bridge-ui-v2/src/components/Bridge/ProcessingFee/NoneOption.svelte @@ -1,5 +1,5 @@
diff --git a/packages/bridge-ui-v2/src/components/Faucet/Faucet.svelte b/packages/bridge-ui-v2/src/components/Faucet/Faucet.svelte index 1aa4751af24..4e95732bad3 100644 --- a/packages/bridge-ui-v2/src/components/Faucet/Faucet.svelte +++ b/packages/bridge-ui-v2/src/components/Faucet/Faucet.svelte @@ -49,15 +49,11 @@ // A token and a source chain must be selected in order to be able to mint if (!selectedToken || !$network) return; - // ... and of course, our wallet must be connected - const walletClient = await getWalletClient(); - if (!walletClient) return; - // Let's begin the minting process minting = true; try { - const txHash = await mint(selectedToken, walletClient); + const txHash = await mint(selectedToken); successToast( $t('faucet.minting_tx', { @@ -98,7 +94,7 @@ reasonNotMintable = ''; try { - await checkMintable(token, network); + await checkMintable(token, network.id); mintButtonEnabled = true; } catch (err) { console.error(err); diff --git a/packages/bridge-ui-v2/src/libs/bridge/ERC20Bridge.ts b/packages/bridge-ui-v2/src/libs/bridge/ERC20Bridge.ts index eabb0154f28..9ac7401e892 100644 --- a/packages/bridge-ui-v2/src/libs/bridge/ERC20Bridge.ts +++ b/packages/bridge-ui-v2/src/libs/bridge/ERC20Bridge.ts @@ -4,11 +4,11 @@ import { bridgeABI, tokenVaultABI } from '$abi'; import { bridge } from '$config'; import { getLogger } from '$libs/util/logger'; -import type { ERC20BridgeArgs, Message, SendERC20Args } from './types'; +import type { Bridge, ERC20BridgeArgs, SendERC20Args } from './types'; const log = getLogger('ERC20Bridge'); -export class ERC20Bridge { +export class ERC20Bridge implements Bridge { private static async _prepareTransaction(args: ERC20BridgeArgs) { const { to, @@ -28,7 +28,7 @@ export class ERC20Bridge { address: tokenVaultAddress, }); - const refundAddress = walletClient.account.address + const refundAddress = walletClient.account.address; const gasLimit = !isTokenAlreadyDeployed ? BigInt(bridge.noTokenDeployedGasLimit) @@ -52,7 +52,7 @@ export class ERC20Bridge { return { tokenVaultContract, sendERC20Args }; } - static async estimateGas(args: ERC20BridgeArgs): Promise { + async estimateGas(args: ERC20BridgeArgs): Promise { const { tokenVaultContract, sendERC20Args } = await ERC20Bridge._prepareTransaction(args); return tokenVaultContract.estimateGas.sendERC20([...sendERC20Args]); } diff --git a/packages/bridge-ui-v2/src/libs/bridge/ETHBridge.ts b/packages/bridge-ui-v2/src/libs/bridge/ETHBridge.ts index 3225d9028dc..db8bb2fbe0c 100644 --- a/packages/bridge-ui-v2/src/libs/bridge/ETHBridge.ts +++ b/packages/bridge-ui-v2/src/libs/bridge/ETHBridge.ts @@ -4,11 +4,11 @@ import { bridgeABI } from '$abi'; import { bridge } from '$config'; import { getLogger } from '$libs/util/logger'; -import type { ETHBridgeArgs, Message } from './types'; +import type { Bridge, ETHBridgeArgs, Message } from './types'; const log = getLogger('ETHBridge'); -export class ETHBridge { +export class ETHBridge implements Bridge { private static async _prepareTransaction(args: ETHBridgeArgs) { const { to, memo = '', amount, srcChainId, destChainId, walletClient, bridgeAddress, processingFee } = args; @@ -52,7 +52,7 @@ export class ETHBridge { return { bridgeContract, message }; } - static async estimateGas(args: ETHBridgeArgs): Promise { + async estimateGas(args: ETHBridgeArgs): Promise { const { bridgeContract, message } = await ETHBridge._prepareTransaction(args); return bridgeContract.estimateGas.sendMessage([message]); } diff --git a/packages/bridge-ui-v2/src/libs/bridge/hasEnoughBalanceToBridge.ts b/packages/bridge-ui-v2/src/libs/bridge/hasEnoughBalanceToBridge.ts new file mode 100644 index 00000000000..1e5e8a6dd41 --- /dev/null +++ b/packages/bridge-ui-v2/src/libs/bridge/hasEnoughBalanceToBridge.ts @@ -0,0 +1,30 @@ +import { getPublicClient, getWalletClient } from '@wagmi/core'; + +import { ETHBridge } from './ETHBridge'; +import type { Bridge, BridgeArgs } from './types'; + +export async function hasEnoughBalanceToBridge(bridge: Bridge, bridgeArgs: BridgeArgs) { + const walletClient = await getWalletClient(); + if (!walletClient) { + throw Error('wallet is not connected'); + } + + const userAddress = walletClient.account.address; + const publicClient = getPublicClient(); + + // Calculate the estimated cost of bridging + const estimatedGas = await bridge.estimateGas(bridgeArgs); + const gasPrice = await publicClient.getGasPrice(); + const estimatedCost = estimatedGas * gasPrice; + + const userBalance = await publicClient.getBalance({ address: userAddress }); + + let balanceAvailable = userBalance; + + if (bridge instanceof ETHBridge) { + // If it's ETH, we need to subtract the amount we're trying to bridge + balanceAvailable = userBalance - bridgeArgs.amount; + } + + return balanceAvailable > estimatedCost; +} diff --git a/packages/bridge-ui-v2/src/libs/bridge/index.ts b/packages/bridge-ui-v2/src/libs/bridge/index.ts new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/bridge-ui-v2/src/libs/bridge/types.ts b/packages/bridge-ui-v2/src/libs/bridge/types.ts index 76d5f910922..f303e7578b6 100644 --- a/packages/bridge-ui-v2/src/libs/bridge/types.ts +++ b/packages/bridge-ui-v2/src/libs/bridge/types.ts @@ -46,14 +46,14 @@ export type Message = { // TokenVault sendERC20(...args) export type SendERC20Args = [ - bigint, // destChainId - Address, // to - Address, // token - bigint, // amount - bigint, // gasLimit - bigint, // processingFee - Address, // refundAddress - string, // memo + bigint, // destChainId + Address, // to + Address, // token + bigint, // amount + bigint, // gasLimit + bigint, // processingFee + Address, // refundAddress + string, // memo ]; // TODO: future sendToken(op: BridgeTransferOp) @@ -94,3 +94,7 @@ export type ERC20BridgeArgs = BridgeArgs & { tokenVaultAddress: Address; isTokenAlreadyDeployed?: boolean; }; + +export interface Bridge { + estimateGas(args: BridgeArgs): Promise; +} diff --git a/packages/bridge-ui-v2/src/libs/token/checkMintable.test.ts b/packages/bridge-ui-v2/src/libs/token/checkMintable.test.ts index 3066604fa6c..146c490a1f0 100644 --- a/packages/bridge-ui-v2/src/libs/token/checkMintable.test.ts +++ b/packages/bridge-ui-v2/src/libs/token/checkMintable.test.ts @@ -45,16 +45,20 @@ describe('checkMintable', () => { vi.mocked(getPublicClient).mockReturnValue(mockPublicClient); }); + beforeEach(() => { + vi.clearAllMocks(); + }); + it('should throw when wallet is not connected', async () => { vi.mocked(getWalletClient).mockResolvedValueOnce(null); try { - await checkMintable(BLLToken, mainnetChain); + await checkMintable(BLLToken, mainnetChain.id); expect.fail('should have thrown'); } catch (error) { const { cause } = error as Error; expect(cause).toBe(MintableError.NOT_CONNECTED); - expect(getWalletClient).toHaveBeenCalledWith({ chainId: mainnetChain.id }); + expect(getWalletClient).toHaveBeenCalled(); } }); @@ -62,7 +66,7 @@ describe('checkMintable', () => { vi.mocked(mockTokenContract.read.minters).mockResolvedValueOnce(true); try { - await checkMintable(BLLToken, mainnetChain); + await checkMintable(BLLToken, mainnetChain.id); expect.fail('should have thrown'); } catch (error) { const { cause } = error as Error; @@ -91,7 +95,7 @@ describe('checkMintable', () => { vi.mocked(mockPublicClient.getBalance).mockResolvedValueOnce(BigInt(100)); try { - await checkMintable(BLLToken, mainnetChain); + await checkMintable(BLLToken, mainnetChain.id); expect.fail('should have thrown'); } catch (error) { const { cause } = error as Error; @@ -117,7 +121,7 @@ describe('checkMintable', () => { vi.mocked(mockPublicClient.getBalance).mockResolvedValueOnce(BigInt(300)); try { - await checkMintable(BLLToken, mainnetChain); + await checkMintable(BLLToken, mainnetChain.id); } catch (error) { expect.fail('should not have thrown'); } diff --git a/packages/bridge-ui-v2/src/libs/token/checkMintable.ts b/packages/bridge-ui-v2/src/libs/token/checkMintable.ts index 848de485316..5e78890bb24 100644 --- a/packages/bridge-ui-v2/src/libs/token/checkMintable.ts +++ b/packages/bridge-ui-v2/src/libs/token/checkMintable.ts @@ -6,15 +6,13 @@ import { freeMintErc20ABI } from '$abi'; import { MintableError, type Token } from './types'; // Throws an error if: -// 1. User is not connected to the network +// 1. Wallet is not connected // 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 }); - +export async function checkMintable(token: Token, chainId: number) { + const walletClient = await getWalletClient(); if (!walletClient) { - throw Error(`user is not connected to ${network.name}`, { cause: MintableError.NOT_CONNECTED }); + throw Error(`wallet is not connected`, { cause: MintableError.NOT_CONNECTED }); } const tokenContract = getContract({ @@ -28,12 +26,13 @@ export async function checkMintable(token: Token, network: Chain) { const hasMinted = await tokenContract.read.minters([userAddress]); if (hasMinted) { - throw Error(`token ${token.symbol} has already been minted`, { cause: MintableError.TOKEN_MINTED }); + throw Error(`token already 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 publicClient = getPublicClient(); + const estimatedGas = await tokenContract.estimateGas.mint([userAddress]); const gasPrice = await publicClient.getGasPrice(); const estimatedCost = estimatedGas * gasPrice; @@ -41,7 +40,7 @@ export async function checkMintable(token: Token, network: Chain) { const userBalance = await publicClient.getBalance({ address: userAddress }); if (estimatedCost > userBalance) { - throw Error(`user has insufficient balance to mint ${token.symbol}: ${formatEther(userBalance)}`, { + throw Error('user has insufficient balance', { cause: MintableError.INSUFFICIENT_BALANCE, }); } diff --git a/packages/bridge-ui-v2/src/libs/token/getBalance.test.ts b/packages/bridge-ui-v2/src/libs/token/getBalance.test.ts index a6846bb2be6..7b9d00d2e70 100644 --- a/packages/bridge-ui-v2/src/libs/token/getBalance.test.ts +++ b/packages/bridge-ui-v2/src/libs/token/getBalance.test.ts @@ -68,7 +68,6 @@ describe('getBalance', () => { }); expect(fetchBalance).toHaveBeenCalledWith({ address: mockWalletClient.account.address, - chainId: Number(PUBLIC_L1_CHAIN_ID), token: BLLToken.addresses[PUBLIC_L1_CHAIN_ID], }); }); diff --git a/packages/bridge-ui-v2/src/libs/token/getBalance.ts b/packages/bridge-ui-v2/src/libs/token/getBalance.ts index d09257cecdb..f4715362bd5 100644 --- a/packages/bridge-ui-v2/src/libs/token/getBalance.ts +++ b/packages/bridge-ui-v2/src/libs/token/getBalance.ts @@ -20,7 +20,7 @@ export async function getBalance({ token, userAddress, chainId, destChainId }: G let tokenBalance: FetchBalanceResult | null = null; if (isETH(token)) { - tokenBalance = await fetchBalance({ address: userAddress, chainId }); + tokenBalance = await fetchBalance({ address: userAddress }); } else { // We are dealing with an ERC20 token. We need to first find out its address // on the current chain in order to fetch the balance. @@ -31,7 +31,6 @@ export async function getBalance({ token, userAddress, chainId, destChainId }: G // Wagmi is such an amazing library. We had to do this // more manually before. tokenBalance = await fetchBalance({ - chainId, address: userAddress, token: tokenAddress, }); diff --git a/packages/bridge-ui-v2/src/libs/token/mint.ts b/packages/bridge-ui-v2/src/libs/token/mint.ts index cfd35b3b331..302d1ab0c1e 100644 --- a/packages/bridge-ui-v2/src/libs/token/mint.ts +++ b/packages/bridge-ui-v2/src/libs/token/mint.ts @@ -1,4 +1,4 @@ -import { getContract, type WalletClient } from '@wagmi/core'; +import { getContract, getWalletClient, type WalletClient } from '@wagmi/core'; import { freeMintErc20ABI } from '$abi'; @@ -7,7 +7,12 @@ import type { Token } from './types'; const log = getLogger('token:mint'); -export async function mint(token: Token, walletClient: WalletClient) { +export async function mint(token: Token) { + const walletClient = await getWalletClient(); + if (!walletClient) { + throw Error('wallet is not connected'); + } + const tokenSymbol = token.symbol; const userAddress = walletClient.account.address; const chainId = walletClient.chain.id; From ccd044bac4c351290f7f276893eceb3090daa831 Mon Sep 17 00:00:00 2001 From: Francisco Date: Wed, 19 Jul 2023 14:54:44 +0200 Subject: [PATCH 07/39] max button --- packages/bridge-ui-v2/src/app.config.ts | 4 + .../Bridge/AmountInput/AmountInput.svelte | 86 +++++++++++++++++-- .../Bridge/ProcessingFee/ProcessingFee.svelte | 20 ++--- .../src/components/Faucet/Faucet.svelte | 2 +- .../src/components/InputBox/InputBox.svelte | 11 ++- .../src/libs/bridge/ERC1155Bridge.ts | 7 ++ .../src/libs/bridge/ERC20Bridge.ts | 10 ++- .../src/libs/bridge/ERC721Bridge.ts | 7 ++ .../bridge-ui-v2/src/libs/bridge/ETHBridge.ts | 15 +++- .../bridge-ui-v2/src/libs/bridge/bridges.ts | 12 +++ .../src/libs/bridge/estimateCostOfBridging.ts | 12 +++ .../libs/bridge/hasEnoughBalanceToBridge.ts | 22 ++--- .../bridge-ui-v2/src/libs/bridge/index.ts | 2 + .../src/libs/bridge/maxAmountToBridge | 0 .../bridge-ui-v2/src/libs/bridge/types.ts | 2 - .../src/libs/token/checkMintable.ts | 9 +- packages/bridge-ui-v2/src/libs/token/mint.ts | 8 +- .../bridge-ui-v2/src/libs/util/getWallet.ts | 11 +++ .../src/libs/util/truncateDecimal.ts | 4 + 19 files changed, 188 insertions(+), 56 deletions(-) create mode 100644 packages/bridge-ui-v2/src/libs/bridge/ERC1155Bridge.ts create mode 100644 packages/bridge-ui-v2/src/libs/bridge/ERC721Bridge.ts create mode 100644 packages/bridge-ui-v2/src/libs/bridge/bridges.ts create mode 100644 packages/bridge-ui-v2/src/libs/bridge/estimateCostOfBridging.ts create mode 100644 packages/bridge-ui-v2/src/libs/bridge/maxAmountToBridge create mode 100644 packages/bridge-ui-v2/src/libs/util/getWallet.ts create mode 100644 packages/bridge-ui-v2/src/libs/util/truncateDecimal.ts diff --git a/packages/bridge-ui-v2/src/app.config.ts b/packages/bridge-ui-v2/src/app.config.ts index 0c49606a95c..398f621f63d 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 amountInputComponent = { + estimatedCostBridging: BigInt(136000000) +} diff --git a/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte b/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte index a570b32d1d8..975fc7abf3f 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte +++ b/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte @@ -1,19 +1,28 @@
@@ -45,8 +108,15 @@ type="number" placeholder="0.01" min="0" + loading={computingMaxETH} on:input={updateAmount} + bind:this={inputBox} class="w-full input-box outline-none py-6 pr-16 px-[26px] title-subsection-bold placeholder:text-tertiary-content" /> - +
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 18b8fcb0994..47dc3807f93 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/ProcessingFee/ProcessingFee.svelte +++ b/packages/bridge-ui-v2/src/components/Bridge/ProcessingFee/ProcessingFee.svelte @@ -29,7 +29,7 @@ let errorCalculatingEnoughEth = false; let modalOpen = false; - let customInput: InputBox; + let inputBox: InputBox; function closeModal() { // Let's check if we are closing with CUSTOM method selected and zero amount entered @@ -51,11 +51,11 @@ setTimeout(closeModal, processingFeeComponent.closingDelayOptionClick); } - function focusCustomInput() { - customInput?.focus(); + function focusInputBox() { + inputBox.focus(); } - function onCustomInputChange(event: Event) { + function onInputBoxChange(event: Event) { if (selectedFeeMethod !== ProcessingFeeMethod.CUSTOM) return; const input = event.target as HTMLInputElement; @@ -66,20 +66,20 @@ switch (method) { case ProcessingFeeMethod.RECOMMENDED: $processingFee = recommendedAmount; - customInput?.clear(); + inputBox?.clear(); break; case ProcessingFeeMethod.CUSTOM: // Get a previous value entered if exists, otherwise default to 0 - $processingFee = parseToWei(customInput?.value()); + $processingFee = parseToWei(inputBox?.getValue()); // We need to wait for Svelte to set the attribute `disabled` on the input // to false to be able to focus it - tick().then(focusCustomInput); + tick().then(focusInputBox); break; case ProcessingFeeMethod.NONE: $processingFee = BigInt(0); - customInput?.clear(); + inputBox?.clear(); break; } @@ -214,8 +214,8 @@ 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={onCustomInputChange} - bind:this={customInput} /> + on:input={onInputBoxChange} + bind:this={inputBox} /> ETH
diff --git a/packages/bridge-ui-v2/src/components/Faucet/Faucet.svelte b/packages/bridge-ui-v2/src/components/Faucet/Faucet.svelte index 4e95732bad3..31380fc82c7 100644 --- a/packages/bridge-ui-v2/src/components/Faucet/Faucet.svelte +++ b/packages/bridge-ui-v2/src/components/Faucet/Faucet.svelte @@ -1,5 +1,5 @@ - + diff --git a/packages/bridge-ui-v2/src/libs/bridge/ERC1155Bridge.ts b/packages/bridge-ui-v2/src/libs/bridge/ERC1155Bridge.ts new file mode 100644 index 00000000000..ddad81ecab1 --- /dev/null +++ b/packages/bridge-ui-v2/src/libs/bridge/ERC1155Bridge.ts @@ -0,0 +1,7 @@ +import type { Bridge } from './types'; + +export class ERC1155Bridge implements Bridge { + async estimateGas(): Promise { + return Promise.resolve(BigInt(0)); + } +} diff --git a/packages/bridge-ui-v2/src/libs/bridge/ERC20Bridge.ts b/packages/bridge-ui-v2/src/libs/bridge/ERC20Bridge.ts index 9ac7401e892..06adac8d72f 100644 --- a/packages/bridge-ui-v2/src/libs/bridge/ERC20Bridge.ts +++ b/packages/bridge-ui-v2/src/libs/bridge/ERC20Bridge.ts @@ -1,7 +1,8 @@ -import { getContract } from '@wagmi/core'; +import { getContract, getWalletClient } from '@wagmi/core'; -import { bridgeABI, tokenVaultABI } from '$abi'; +import { tokenVaultABI } from '$abi'; import { bridge } from '$config'; +import { getConnectedWallet } from '$libs/util/getWallet'; import { getLogger } from '$libs/util/logger'; import type { Bridge, ERC20BridgeArgs, SendERC20Args } from './types'; @@ -10,12 +11,13 @@ const log = getLogger('ERC20Bridge'); export class ERC20Bridge implements Bridge { private static async _prepareTransaction(args: ERC20BridgeArgs) { + const walletClient = await getConnectedWallet(); + const { to, memo = '', amount, destChainId, - walletClient, tokenAddress, processingFee, tokenVaultAddress, @@ -52,7 +54,7 @@ export class ERC20Bridge implements Bridge { return { tokenVaultContract, sendERC20Args }; } - async estimateGas(args: ERC20BridgeArgs): Promise { + async estimateGas(args: ERC20BridgeArgs) { const { tokenVaultContract, sendERC20Args } = await ERC20Bridge._prepareTransaction(args); return tokenVaultContract.estimateGas.sendERC20([...sendERC20Args]); } diff --git a/packages/bridge-ui-v2/src/libs/bridge/ERC721Bridge.ts b/packages/bridge-ui-v2/src/libs/bridge/ERC721Bridge.ts new file mode 100644 index 00000000000..a9d03418e55 --- /dev/null +++ b/packages/bridge-ui-v2/src/libs/bridge/ERC721Bridge.ts @@ -0,0 +1,7 @@ +import type { Bridge } from './types'; + +export class ERC721Bridge implements Bridge { + async estimateGas(): Promise { + return Promise.resolve(BigInt(0)); + } +} diff --git a/packages/bridge-ui-v2/src/libs/bridge/ETHBridge.ts b/packages/bridge-ui-v2/src/libs/bridge/ETHBridge.ts index db8bb2fbe0c..cb09d6aba25 100644 --- a/packages/bridge-ui-v2/src/libs/bridge/ETHBridge.ts +++ b/packages/bridge-ui-v2/src/libs/bridge/ETHBridge.ts @@ -2,6 +2,7 @@ import { getContract } from '@wagmi/core'; import { bridgeABI } from '$abi'; import { bridge } from '$config'; +import { getConnectedWallet } from '$libs/util/getWallet'; import { getLogger } from '$libs/util/logger'; import type { Bridge, ETHBridgeArgs, Message } from './types'; @@ -10,7 +11,9 @@ const log = getLogger('ETHBridge'); export class ETHBridge implements Bridge { private static async _prepareTransaction(args: ETHBridgeArgs) { - const { to, memo = '', amount, srcChainId, destChainId, walletClient, bridgeAddress, processingFee } = args; + const walletClient = await getConnectedWallet(); + + const { to, memo = '', amount, srcChainId, destChainId, bridgeAddress, processingFee } = args; const bridgeContract = getContract({ walletClient, @@ -52,8 +55,14 @@ export class ETHBridge implements Bridge { return { bridgeContract, message }; } - async estimateGas(args: ETHBridgeArgs): Promise { + async estimateGas(args: ETHBridgeArgs) { const { bridgeContract, message } = await ETHBridge._prepareTransaction(args); - return bridgeContract.estimateGas.sendMessage([message]); + const { depositValue, callValue, processingFee } = message; + + const value = depositValue + callValue + processingFee; + + log('Estimating gas for sendMessage call. Sending value', value); + + return bridgeContract.estimateGas.sendMessage([message], { value }); } } diff --git a/packages/bridge-ui-v2/src/libs/bridge/bridges.ts b/packages/bridge-ui-v2/src/libs/bridge/bridges.ts new file mode 100644 index 00000000000..8eefde0a6dd --- /dev/null +++ b/packages/bridge-ui-v2/src/libs/bridge/bridges.ts @@ -0,0 +1,12 @@ +import { ERC20Bridge } from './ERC20Bridge'; +import { ERC721Bridge } from './ERC721Bridge'; +import { ERC1155Bridge } from './ERC1155Bridge'; +import { ETHBridge } from './ETHBridge'; +import type { Bridge, BridgeType } from './types'; + +export const bridges: Record = { + ETH: new ETHBridge(), + ERC20: new ERC20Bridge(), + ERC721: new ERC721Bridge(), + ERC1155: new ERC1155Bridge(), +}; diff --git a/packages/bridge-ui-v2/src/libs/bridge/estimateCostOfBridging.ts b/packages/bridge-ui-v2/src/libs/bridge/estimateCostOfBridging.ts new file mode 100644 index 00000000000..adcb8c81d74 --- /dev/null +++ b/packages/bridge-ui-v2/src/libs/bridge/estimateCostOfBridging.ts @@ -0,0 +1,12 @@ +import { getPublicClient } from '@wagmi/core'; + +import type { Bridge, BridgeArgs } from './types'; + +export async function estimateCostOfBridging(bridge: Bridge, bridgeArgs: BridgeArgs) { + const publicClient = getPublicClient(); + + // Calculate the estimated cost of bridging + const estimatedGas = await bridge.estimateGas(bridgeArgs); + const gasPrice = await publicClient.getGasPrice(); + return estimatedGas * gasPrice; +} diff --git a/packages/bridge-ui-v2/src/libs/bridge/hasEnoughBalanceToBridge.ts b/packages/bridge-ui-v2/src/libs/bridge/hasEnoughBalanceToBridge.ts index 1e5e8a6dd41..97eb14716e6 100644 --- a/packages/bridge-ui-v2/src/libs/bridge/hasEnoughBalanceToBridge.ts +++ b/packages/bridge-ui-v2/src/libs/bridge/hasEnoughBalanceToBridge.ts @@ -1,28 +1,24 @@ -import { getPublicClient, getWalletClient } from '@wagmi/core'; +import { getPublicClient } from '@wagmi/core'; +import { getConnectedWallet } from '$libs/util/getWallet'; + +import { estimateCostOfBridging } from './estimateCostOfBridging'; import { ETHBridge } from './ETHBridge'; import type { Bridge, BridgeArgs } from './types'; export async function hasEnoughBalanceToBridge(bridge: Bridge, bridgeArgs: BridgeArgs) { - const walletClient = await getWalletClient(); - if (!walletClient) { - throw Error('wallet is not connected'); - } + const walletClient = await getConnectedWallet(); - const userAddress = walletClient.account.address; - const publicClient = getPublicClient(); - - // Calculate the estimated cost of bridging - const estimatedGas = await bridge.estimateGas(bridgeArgs); - const gasPrice = await publicClient.getGasPrice(); - const estimatedCost = estimatedGas * gasPrice; + const estimatedCost = await estimateCostOfBridging(bridge, bridgeArgs); + const publicClient = getPublicClient(); + const userAddress = walletClient.account.address; const userBalance = await publicClient.getBalance({ address: userAddress }); let balanceAvailable = userBalance; if (bridge instanceof ETHBridge) { - // If it's ETH, we need to subtract the amount we're trying to bridge + // If dealing with ETH, we need to subtract the amount we're trying to bridge balanceAvailable = userBalance - bridgeArgs.amount; } diff --git a/packages/bridge-ui-v2/src/libs/bridge/index.ts b/packages/bridge-ui-v2/src/libs/bridge/index.ts index e69de29bb2d..12f1eaa2504 100644 --- a/packages/bridge-ui-v2/src/libs/bridge/index.ts +++ b/packages/bridge-ui-v2/src/libs/bridge/index.ts @@ -0,0 +1,2 @@ +export { bridges } from './bridges'; +export { hasEnoughBalanceToBridge } from './hasEnoughBalanceToBridge'; diff --git a/packages/bridge-ui-v2/src/libs/bridge/maxAmountToBridge b/packages/bridge-ui-v2/src/libs/bridge/maxAmountToBridge new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/bridge-ui-v2/src/libs/bridge/types.ts b/packages/bridge-ui-v2/src/libs/bridge/types.ts index f303e7578b6..78f70703849 100644 --- a/packages/bridge-ui-v2/src/libs/bridge/types.ts +++ b/packages/bridge-ui-v2/src/libs/bridge/types.ts @@ -72,7 +72,6 @@ export type ApproveArgs = { amount: bigint; tokenAddress: Address; spenderAddress: Address; - walletClient: WalletClient; }; export type BridgeArgs = { @@ -80,7 +79,6 @@ export type BridgeArgs = { srcChainId: number; destChainId: number; amount: bigint; - walletClient: WalletClient; processingFee: bigint; memo?: string; }; diff --git a/packages/bridge-ui-v2/src/libs/token/checkMintable.ts b/packages/bridge-ui-v2/src/libs/token/checkMintable.ts index 5e78890bb24..fb1d63a790b 100644 --- a/packages/bridge-ui-v2/src/libs/token/checkMintable.ts +++ b/packages/bridge-ui-v2/src/libs/token/checkMintable.ts @@ -1,7 +1,7 @@ -import { type Chain, getContract, getPublicClient, getWalletClient } from '@wagmi/core'; -import { formatEther } from 'viem'; +import { getContract, getPublicClient, getWalletClient } from '@wagmi/core'; import { freeMintErc20ABI } from '$abi'; +import { getConnectedWallet } from '$libs/util/getWallet'; import { MintableError, type Token } from './types'; @@ -10,10 +10,7 @@ import { MintableError, type Token } from './types'; // 2. User has already minted this token // 3. User has insufficient balance to mint this token export async function checkMintable(token: Token, chainId: number) { - const walletClient = await getWalletClient(); - if (!walletClient) { - throw Error(`wallet is not connected`, { cause: MintableError.NOT_CONNECTED }); - } + const walletClient = await getConnectedWallet(); const tokenContract = getContract({ walletClient, diff --git a/packages/bridge-ui-v2/src/libs/token/mint.ts b/packages/bridge-ui-v2/src/libs/token/mint.ts index 302d1ab0c1e..8a20061d1ff 100644 --- a/packages/bridge-ui-v2/src/libs/token/mint.ts +++ b/packages/bridge-ui-v2/src/libs/token/mint.ts @@ -1,6 +1,7 @@ -import { getContract, getWalletClient, type WalletClient } from '@wagmi/core'; +import { getContract, getWalletClient } from '@wagmi/core'; import { freeMintErc20ABI } from '$abi'; +import { getConnectedWallet } from '$libs/util/getWallet'; import { getLogger } from '../util/logger'; import type { Token } from './types'; @@ -8,10 +9,7 @@ import type { Token } from './types'; const log = getLogger('token:mint'); export async function mint(token: Token) { - const walletClient = await getWalletClient(); - if (!walletClient) { - throw Error('wallet is not connected'); - } + const walletClient = await getConnectedWallet(); const tokenSymbol = token.symbol; const userAddress = walletClient.account.address; diff --git a/packages/bridge-ui-v2/src/libs/util/getWallet.ts b/packages/bridge-ui-v2/src/libs/util/getWallet.ts new file mode 100644 index 00000000000..d609ef62344 --- /dev/null +++ b/packages/bridge-ui-v2/src/libs/util/getWallet.ts @@ -0,0 +1,11 @@ +import { getWalletClient } from '@wagmi/core'; + +export async function getConnectedWallet() { + const walletClient = await getWalletClient(); + + if (!walletClient) { + throw Error('wallet is not connected'); + } + + return walletClient; +} diff --git a/packages/bridge-ui-v2/src/libs/util/truncateDecimal.ts b/packages/bridge-ui-v2/src/libs/util/truncateDecimal.ts new file mode 100644 index 00000000000..92b1c8af865 --- /dev/null +++ b/packages/bridge-ui-v2/src/libs/util/truncateDecimal.ts @@ -0,0 +1,4 @@ +export function truncateDecimal(num: number, decimalPlaces: number) { + const factor = 10 ** decimalPlaces + return Math.floor(num * factor) / factor +} From 0c49995fbdd0d0d786052dc66445efc0df21624b Mon Sep 17 00:00:00 2001 From: Francisco Date: Wed, 19 Jul 2023 14:59:46 +0200 Subject: [PATCH 08/39] wip --- packages/bridge-ui-v2/src/app.config.ts | 4 ++-- .../src/components/Bridge/AmountInput/AmountInput.svelte | 2 +- packages/bridge-ui-v2/src/libs/bridge/ERC20Bridge.ts | 2 +- packages/bridge-ui-v2/src/libs/util/truncateDecimal.ts | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/bridge-ui-v2/src/app.config.ts b/packages/bridge-ui-v2/src/app.config.ts index 398f621f63d..36e2e497ccc 100644 --- a/packages/bridge-ui-v2/src/app.config.ts +++ b/packages/bridge-ui-v2/src/app.config.ts @@ -15,5 +15,5 @@ export const bridge = { }; export const amountInputComponent = { - estimatedCostBridging: BigInt(136000000) -} + estimatedCostBridging: BigInt(136000000), +}; diff --git a/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte b/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte index 975fc7abf3f..db224e30ade 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte +++ b/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte @@ -4,6 +4,7 @@ import { formatEther, parseUnits } from 'viem'; import { InputBox } from '$components/InputBox'; + import { amountInputComponent } from '$config'; import { bridges } from '$libs/bridge'; import { estimateCostOfBridging } from '$libs/bridge/estimateCostOfBridging'; import { ETHBridge } from '$libs/bridge/ETHBridge'; @@ -16,7 +17,6 @@ import { destNetwork, enteredAmount, processingFee, selectedToken } from '../state'; import Balance from './Balance.svelte'; - import { amountInputComponent } from '$config'; let inputId = `input-${uid()}`; let tokenBalance: FetchBalanceResult; diff --git a/packages/bridge-ui-v2/src/libs/bridge/ERC20Bridge.ts b/packages/bridge-ui-v2/src/libs/bridge/ERC20Bridge.ts index 06adac8d72f..753d29537c1 100644 --- a/packages/bridge-ui-v2/src/libs/bridge/ERC20Bridge.ts +++ b/packages/bridge-ui-v2/src/libs/bridge/ERC20Bridge.ts @@ -1,4 +1,4 @@ -import { getContract, getWalletClient } from '@wagmi/core'; +import { getContract } from '@wagmi/core'; import { tokenVaultABI } from '$abi'; import { bridge } from '$config'; diff --git a/packages/bridge-ui-v2/src/libs/util/truncateDecimal.ts b/packages/bridge-ui-v2/src/libs/util/truncateDecimal.ts index 92b1c8af865..bd3391864cb 100644 --- a/packages/bridge-ui-v2/src/libs/util/truncateDecimal.ts +++ b/packages/bridge-ui-v2/src/libs/util/truncateDecimal.ts @@ -1,4 +1,4 @@ export function truncateDecimal(num: number, decimalPlaces: number) { - const factor = 10 ** decimalPlaces - return Math.floor(num * factor) / factor + const factor = 10 ** decimalPlaces; + return Math.floor(num * factor) / factor; } From 2908484fb98ccbd5d15427b6047ea4b0be2b6338 Mon Sep 17 00:00:00 2001 From: Francisco Date: Wed, 19 Jul 2023 16:16:20 +0200 Subject: [PATCH 09/39] wip --- packages/bridge-ui-v2/src/app.config.ts | 4 - .../Bridge/AmountInput/AmountInput.svelte | 113 ++++++++---------- .../src/components/InputBox/InputBox.svelte | 2 +- packages/bridge-ui-v2/src/i18n/en.json | 6 +- .../src/libs/bridge/getMaxToBridge.ts | 60 ++++++++++ .../libs/bridge/hasEnoughBalanceToBridge.ts | 11 +- .../bridge-ui-v2/src/libs/bridge/index.ts | 2 + .../src/libs/bridge/maxAmountToBridge | 0 .../src/libs/token/checkMintable.ts | 2 +- packages/bridge-ui-v2/src/libs/token/mint.ts | 2 +- 10 files changed, 125 insertions(+), 77 deletions(-) create mode 100644 packages/bridge-ui-v2/src/libs/bridge/getMaxToBridge.ts delete mode 100644 packages/bridge-ui-v2/src/libs/bridge/maxAmountToBridge diff --git a/packages/bridge-ui-v2/src/app.config.ts b/packages/bridge-ui-v2/src/app.config.ts index 36e2e497ccc..0c49606a95c 100644 --- a/packages/bridge-ui-v2/src/app.config.ts +++ b/packages/bridge-ui-v2/src/app.config.ts @@ -13,7 +13,3 @@ export const bridge = { noOwnerGasLimit: BigInt(140000), noTokenDeployedGasLimit: BigInt(3000000), }; - -export const amountInputComponent = { - estimatedCostBridging: BigInt(136000000), -}; diff --git a/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte b/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte index db224e30ade..6ac72fe4eb5 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte +++ b/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte @@ -4,27 +4,48 @@ import { formatEther, parseUnits } from 'viem'; import { InputBox } from '$components/InputBox'; - import { amountInputComponent } from '$config'; - import { bridges } from '$libs/bridge'; - import { estimateCostOfBridging } from '$libs/bridge/estimateCostOfBridging'; - import { ETHBridge } from '$libs/bridge/ETHBridge'; - import type { ETHBridgeArgs } from '$libs/bridge/types'; - import { chainContractsMap, chains } from '$libs/chain'; - import { ETHToken, isETH, type Token } from '$libs/token'; + import { getMaxToBridge } from '$libs/bridge/getMaxToBridge'; import { uid } from '$libs/util/uid'; import { account } from '$stores/account'; import { network } from '$stores/network'; import { destNetwork, enteredAmount, processingFee, selectedToken } from '../state'; import Balance from './Balance.svelte'; + import { warningToast } from '$components/NotificationToast'; let inputId = `input-${uid()}`; let tokenBalance: FetchBalanceResult; let inputBox: InputBox; - let computingMaxETH = false; + let computingMaxAmount = false; + let errorAmount = false; + + // Handler for when the user leaves the amount input. + // Will inform the users if they have enough balance to bridge + // the amount they entered + async function checkAmount() { + if (!$selectedToken || !$network || !$account?.address) return; + + const maxAmount = await getMaxToBridge({ + token: $selectedToken, + balance: tokenBalance.value, + processingFee: $processingFee, + srcChainId: $network.id, + destChainId: $destNetwork?.id, + userAddress: $account.address, + amount: $enteredAmount, + }); + + if ($enteredAmount > maxAmount) { + errorAmount = true; + } + } + // Handle for when the user changes the amount input. + // Will update the entered amount in the store function updateAmount(event: Event) { + errorAmount = false; + if (!$selectedToken) return; const target = event.target as HTMLInputElement; @@ -41,58 +62,28 @@ $enteredAmount = amount; } + // We call this function when clicking on the "MAX" button async function useMaxAmount() { - if (!$selectedToken || !$network) return; - - if (isETH($selectedToken)) { - computingMaxETH = true; - - try { - // Let's estimate the cost of briding 1 ETH - // and then subtract it from the balance, - // minus the processing fee - - const ethBridge = bridges['ETH']; - const to = $account.address; - const srcChainId = $network.id; - - // If no destination chain is selected, grab another - // chain that's not the connected one - const destChainId = $destNetwork ? $destNetwork.id : chains.find((chain) => chain.id !== srcChainId)?.id; - - const amount = BigInt(1); // whatever amount just to get an estimation - const { bridgeAddress } = chainContractsMap[srcChainId.toString()]; - - const bridgeArgs = { - to, - amount, - srcChainId, - destChainId, - bridgeAddress, - processingFee: $processingFee, - } as ETHBridgeArgs; - - const estimatedCost = await estimateCostOfBridging(ethBridge, bridgeArgs); - const maxAmount = tokenBalance.value - $processingFee - estimatedCost; - - setETHAmount(maxAmount); - } catch (err) { - console.error(err); - - // Unfortunately something happened and we couldn't estimate the cost - // of bridging. Let's substract our own estimation - const maxAmount = tokenBalance.value - $processingFee - amountInputComponent.estimatedCostBridging; - - setETHAmount(maxAmount); - } finally { - computingMaxETH = false; - } - } else { - inputBox.setValue(tokenBalance.formatted); - - // Unfortunately setting the inputbox via API doesn't trigger - // the `input` event, so we need to manually update the amount - $enteredAmount = tokenBalance.value; + if (!$selectedToken || !$network || !$account?.address) return; + + computingMaxAmount = true; + + try { + const maxAmount = await getMaxToBridge({ + token: $selectedToken, + balance: tokenBalance.value, + processingFee: $processingFee, + srcChainId: $network.id, + destChainId: $destNetwork?.id, + userAddress: $account.address, + }); + + setETHAmount(maxAmount); + } catch (err) { + console.error(err); + warningToast($t('amount_input.button.failed_max')); + } finally { + computingMaxAmount = false; } } @@ -108,13 +99,13 @@ type="number" placeholder="0.01" min="0" - loading={computingMaxETH} + loading={computingMaxAmount} on:input={updateAmount} bind:this={inputBox} class="w-full input-box outline-none py-6 pr-16 px-[26px] title-subsection-bold placeholder:text-tertiary-content" /> diff --git a/packages/bridge-ui-v2/src/components/InputBox/InputBox.svelte b/packages/bridge-ui-v2/src/components/InputBox/InputBox.svelte index 7e16595de59..dfeb441ce45 100644 --- a/packages/bridge-ui-v2/src/components/InputBox/InputBox.svelte +++ b/packages/bridge-ui-v2/src/components/InputBox/InputBox.svelte @@ -13,4 +13,4 @@ export const focus = () => input.focus(); - + diff --git a/packages/bridge-ui-v2/src/i18n/en.json b/packages/bridge-ui-v2/src/i18n/en.json index cc9e3ca9999..d96c75e14f4 100644 --- a/packages/bridge-ui-v2/src/i18n/en.json +++ b/packages/bridge-ui-v2/src/i18n/en.json @@ -84,7 +84,11 @@ "amount_input": { "label": "Amount", "balance": "Balance", - "button.max": "Max" + "button.max": "Max", + "button": { + "max": "Max", + "failed_max": "Coult not estimate max amount to bridge." + } }, "chain_selector": { diff --git a/packages/bridge-ui-v2/src/libs/bridge/getMaxToBridge.ts b/packages/bridge-ui-v2/src/libs/bridge/getMaxToBridge.ts new file mode 100644 index 00000000000..bb9f2ce5e82 --- /dev/null +++ b/packages/bridge-ui-v2/src/libs/bridge/getMaxToBridge.ts @@ -0,0 +1,60 @@ +import type { Address } from 'viem'; + +import { bridge } from '$config'; +import { chainContractsMap, chains } from '$libs/chain'; +import { isETH, type Token } from '$libs/token'; +import { getLogger } from '$libs/util/logger'; + +import { bridges } from './bridges'; +import { estimateCostOfBridging } from './estimateCostOfBridging'; +import type { ETHBridgeArgs } from './types'; + +type GetMaxToBridgeArgs = { + token: Token; + balance: bigint; + srcChainId: number; + userAddress: Address; + processingFee: bigint; + destChainId?: number; + amount?: bigint; +}; + +const log = getLogger('getMaxToBridge'); + +export async function getMaxToBridge({ + token, + balance, + srcChainId, + userAddress, + processingFee, + destChainId, + amount, +}: GetMaxToBridgeArgs) { + if (isETH(token)) { + const to = userAddress; + const { bridgeAddress } = chainContractsMap[srcChainId.toString()]; + + const bridgeArgs = { + to, + srcChainId, + bridgeAddress, + processingFee, + + // If no amount passed in, use whatever just to get an estimation + amount: amount ?? BigInt(1), + + // If no destination chain is selected, find another chain to estimate + // TODO: we might want to really find a compatible chain to bridge to + destChainId: destChainId ?? chains.find((chain) => chain.id !== srcChainId)?.id, + } as ETHBridgeArgs; + + const estimatedCost = await estimateCostOfBridging(bridges.ETH, bridgeArgs); + + log('Estimated cost of bridging', estimatedCost, 'with argument', bridgeArgs, ); + + return balance - processingFee - estimatedCost; + } + + // For ERC20 tokens, we can bridge the whole balance + return balance; +} diff --git a/packages/bridge-ui-v2/src/libs/bridge/hasEnoughBalanceToBridge.ts b/packages/bridge-ui-v2/src/libs/bridge/hasEnoughBalanceToBridge.ts index 97eb14716e6..7fb713adbcc 100644 --- a/packages/bridge-ui-v2/src/libs/bridge/hasEnoughBalanceToBridge.ts +++ b/packages/bridge-ui-v2/src/libs/bridge/hasEnoughBalanceToBridge.ts @@ -1,19 +1,14 @@ -import { getPublicClient } from '@wagmi/core'; - -import { getConnectedWallet } from '$libs/util/getWallet'; +import { type Address, getPublicClient } from '@wagmi/core'; import { estimateCostOfBridging } from './estimateCostOfBridging'; import { ETHBridge } from './ETHBridge'; import type { Bridge, BridgeArgs } from './types'; -export async function hasEnoughBalanceToBridge(bridge: Bridge, bridgeArgs: BridgeArgs) { - const walletClient = await getConnectedWallet(); - +export async function hasEnoughBalanceToBridge(bridge: Bridge, bridgeArgs: BridgeArgs, address: Address) { const estimatedCost = await estimateCostOfBridging(bridge, bridgeArgs); const publicClient = getPublicClient(); - const userAddress = walletClient.account.address; - const userBalance = await publicClient.getBalance({ address: userAddress }); + const userBalance = await publicClient.getBalance({ address }); let balanceAvailable = userBalance; diff --git a/packages/bridge-ui-v2/src/libs/bridge/index.ts b/packages/bridge-ui-v2/src/libs/bridge/index.ts index 12f1eaa2504..7325863e4d3 100644 --- a/packages/bridge-ui-v2/src/libs/bridge/index.ts +++ b/packages/bridge-ui-v2/src/libs/bridge/index.ts @@ -1,2 +1,4 @@ export { bridges } from './bridges'; +export { estimateCostOfBridging } from './estimateCostOfBridging'; export { hasEnoughBalanceToBridge } from './hasEnoughBalanceToBridge'; +export * from './types'; diff --git a/packages/bridge-ui-v2/src/libs/bridge/maxAmountToBridge b/packages/bridge-ui-v2/src/libs/bridge/maxAmountToBridge deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/packages/bridge-ui-v2/src/libs/token/checkMintable.ts b/packages/bridge-ui-v2/src/libs/token/checkMintable.ts index fb1d63a790b..2c173659ea2 100644 --- a/packages/bridge-ui-v2/src/libs/token/checkMintable.ts +++ b/packages/bridge-ui-v2/src/libs/token/checkMintable.ts @@ -1,4 +1,4 @@ -import { getContract, getPublicClient, getWalletClient } from '@wagmi/core'; +import { getContract, getPublicClient } from '@wagmi/core'; import { freeMintErc20ABI } from '$abi'; import { getConnectedWallet } from '$libs/util/getWallet'; diff --git a/packages/bridge-ui-v2/src/libs/token/mint.ts b/packages/bridge-ui-v2/src/libs/token/mint.ts index 8a20061d1ff..c9585583711 100644 --- a/packages/bridge-ui-v2/src/libs/token/mint.ts +++ b/packages/bridge-ui-v2/src/libs/token/mint.ts @@ -1,4 +1,4 @@ -import { getContract, getWalletClient } from '@wagmi/core'; +import { getContract } from '@wagmi/core'; import { freeMintErc20ABI } from '$abi'; import { getConnectedWallet } from '$libs/util/getWallet'; From a222c3fd0ae8498ee49ec97a62028c85f1f0d64a Mon Sep 17 00:00:00 2001 From: Francisco Date: Wed, 19 Jul 2023 17:34:20 +0200 Subject: [PATCH 10/39] wip --- .../Bridge/AmountInput/AmountInput.svelte | 53 ++++++++++++------- .../src/components/InputBox/InputBox.svelte | 7 +-- .../src/libs/bridge/getMaxToBridge.ts | 3 +- .../bridge-ui-v2/src/libs/util/debounce.ts | 14 +++++ .../bridge-ui-v2/src/styles/components.css | 4 ++ 5 files changed, 57 insertions(+), 24 deletions(-) create mode 100644 packages/bridge-ui-v2/src/libs/util/debounce.ts diff --git a/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte b/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte index 6ac72fe4eb5..f1b8fe306b7 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte +++ b/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte @@ -1,7 +1,7 @@ - + diff --git a/packages/bridge-ui-v2/src/libs/bridge/getMaxToBridge.ts b/packages/bridge-ui-v2/src/libs/bridge/getMaxToBridge.ts index bb9f2ce5e82..66ee66e1df9 100644 --- a/packages/bridge-ui-v2/src/libs/bridge/getMaxToBridge.ts +++ b/packages/bridge-ui-v2/src/libs/bridge/getMaxToBridge.ts @@ -45,12 +45,13 @@ export async function getMaxToBridge({ // If no destination chain is selected, find another chain to estimate // TODO: we might want to really find a compatible chain to bridge to + // if we have multiple layers destChainId: destChainId ?? chains.find((chain) => chain.id !== srcChainId)?.id, } as ETHBridgeArgs; const estimatedCost = await estimateCostOfBridging(bridges.ETH, bridgeArgs); - log('Estimated cost of bridging', estimatedCost, 'with argument', bridgeArgs, ); + log('Estimated cost of bridging', estimatedCost, 'with argument', bridgeArgs); return balance - processingFee - estimatedCost; } diff --git a/packages/bridge-ui-v2/src/libs/util/debounce.ts b/packages/bridge-ui-v2/src/libs/util/debounce.ts new file mode 100644 index 00000000000..75cdc862732 --- /dev/null +++ b/packages/bridge-ui-v2/src/libs/util/debounce.ts @@ -0,0 +1,14 @@ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function debounce ReturnType>( + callback: T, + timeout: number, +): (...args: Parameters) => void { + let timer: ReturnType; + + return (...args: Parameters) => { + clearTimeout(timer); + timer = setTimeout(() => { + callback(...args); + }, timeout); + }; +} diff --git a/packages/bridge-ui-v2/src/styles/components.css b/packages/bridge-ui-v2/src/styles/components.css index 7b82bbfa2c6..bc172348a8d 100644 --- a/packages/bridge-ui-v2/src/styles/components.css +++ b/packages/bridge-ui-v2/src/styles/components.css @@ -86,6 +86,10 @@ focus:shadow-[0_0_0_3px_#E81899]; } + .input-box.error { + @apply !shadow-[0_0_0_3px_#F15C5D]; + } + /* Separatos */ .h-sep { @apply divider h-[1px] border-divider-border; From 626b9282f3bc64698e190d00f73d6fc4d1a85d9c Mon Sep 17 00:00:00 2001 From: Francisco Date: Wed, 19 Jul 2023 17:53:14 +0200 Subject: [PATCH 11/39] wip --- .../components/Bridge/AmountInput/AmountInput.svelte | 11 ++++++++++- packages/bridge-ui-v2/src/i18n/en.json | 4 ++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte b/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte index f1b8fe306b7..c67ffaba2e0 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte +++ b/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte @@ -13,6 +13,7 @@ import Balance from './Balance.svelte'; import { warningToast } from '$components/NotificationToast'; import { debounce } from '$libs/util/debounce'; + import Icon from '$components/Icon/Icon.svelte'; let inputId = `input-${uid()}`; let tokenBalance: FetchBalanceResult; @@ -102,6 +103,7 @@ +
+ {#if errorAmount} - BAM!!! + +
+ +
+ {$t('amount_input.error.insufficient_balance')} +
+
{/if} diff --git a/packages/bridge-ui-v2/src/i18n/en.json b/packages/bridge-ui-v2/src/i18n/en.json index d96c75e14f4..56ab85e6681 100644 --- a/packages/bridge-ui-v2/src/i18n/en.json +++ b/packages/bridge-ui-v2/src/i18n/en.json @@ -84,11 +84,11 @@ "amount_input": { "label": "Amount", "balance": "Balance", - "button.max": "Max", "button": { "max": "Max", "failed_max": "Coult not estimate max amount to bridge." - } + }, + "error.insufficient_balance": "Insufficient balance" }, "chain_selector": { From 93ceb18e072e3b3696e61c45644e92f1867a64d1 Mon Sep 17 00:00:00 2001 From: Francisco Date: Wed, 19 Jul 2023 18:35:20 +0200 Subject: [PATCH 12/39] wip --- .../Bridge/AmountInput/AmountInput.svelte | 24 +++++++++++++++---- .../bridge-ui-v2/src/styles/components.css | 2 +- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte b/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte index c67ffaba2e0..aeea3fc793b 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte +++ b/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte @@ -1,7 +1,7 @@
diff --git a/packages/bridge-ui-v2/src/styles/components.css b/packages/bridge-ui-v2/src/styles/components.css index bc172348a8d..839066bb4d9 100644 --- a/packages/bridge-ui-v2/src/styles/components.css +++ b/packages/bridge-ui-v2/src/styles/components.css @@ -83,7 +83,7 @@ /* focus:border-[3px] */ /* focus:border-primary-border-accent */ - focus:shadow-[0_0_0_3px_#E81899]; + focus:!shadow-[0_0_0_3px_#E81899]; } .input-box.error { From 3d9bf59d780ae94835f7c10875fdb2c8a8a920bd Mon Sep 17 00:00:00 2001 From: Francisco Date: Wed, 19 Jul 2023 19:18:06 +0200 Subject: [PATCH 13/39] amount input validation --- .../Bridge/AmountInput/AmountInput.svelte | 15 +++++++++++---- .../src/libs/bridge/getMaxToBridge.ts | 1 - 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte b/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte index aeea3fc793b..1a143cdb150 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte +++ b/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte @@ -3,17 +3,17 @@ import { t } from 'svelte-i18n'; import { formatEther, parseUnits } from 'viem'; + import Icon from '$components/Icon/Icon.svelte'; import { InputBox } from '$components/InputBox'; + import { warningToast } from '$components/NotificationToast'; import { getMaxToBridge } from '$libs/bridge/getMaxToBridge'; + import { debounce } from '$libs/util/debounce'; import { uid } from '$libs/util/uid'; import { account } from '$stores/account'; import { network } from '$stores/network'; import { destNetwork, enteredAmount, processingFee, selectedToken } from '../state'; import Balance from './Balance.svelte'; - import { warningToast } from '$components/NotificationToast'; - import { debounce } from '$libs/util/debounce'; - import Icon from '$components/Icon/Icon.svelte'; let inputId = `input-${uid()}`; let tokenBalance: FetchBalanceResult; @@ -22,6 +22,9 @@ let computingMaxAmount = false; let errorAmount = false; + // Simple, let's get the max amount to bridge and see if it's less + // than what the user has entered. For ETH, will actually get an error + // when trying to get that max amount, if the user has entered too much ETH async function checkEnteredAmount() { if ( !$selectedToken || @@ -61,6 +64,8 @@ // We want to debounce this function for input events const debouncedCheckEnteredAmount = debounce(checkEnteredAmount, 300); + // Will trigger on input events. We update the entered amount + // and check it's validity function updateAmount(event: Event) { errorAmount = false; @@ -82,7 +87,10 @@ $enteredAmount = amount; } + // Will trigger when the user clicks on the "Max" button async function useMaxAmount() { + errorAmount = false; + if (!$selectedToken || !$network || !$account?.address) return; computingMaxAmount = true; @@ -98,7 +106,6 @@ }); setETHAmount(maxAmount); - checkEnteredAmount(); } catch (err) { console.error(err); warningToast($t('amount_input.button.failed_max')); diff --git a/packages/bridge-ui-v2/src/libs/bridge/getMaxToBridge.ts b/packages/bridge-ui-v2/src/libs/bridge/getMaxToBridge.ts index 66ee66e1df9..041567139a4 100644 --- a/packages/bridge-ui-v2/src/libs/bridge/getMaxToBridge.ts +++ b/packages/bridge-ui-v2/src/libs/bridge/getMaxToBridge.ts @@ -1,6 +1,5 @@ import type { Address } from 'viem'; -import { bridge } from '$config'; import { chainContractsMap, chains } from '$libs/chain'; import { isETH, type Token } from '$libs/token'; import { getLogger } from '$libs/util/logger'; From 1be938676fa292543f45b2c197f0502cc3d0cb51 Mon Sep 17 00:00:00 2001 From: Francisco Date: Wed, 19 Jul 2023 19:21:47 +0200 Subject: [PATCH 14/39] minor change --- packages/bridge-ui-v2/src/libs/bridge/types.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/bridge-ui-v2/src/libs/bridge/types.ts b/packages/bridge-ui-v2/src/libs/bridge/types.ts index 78f70703849..7dd0f52172c 100644 --- a/packages/bridge-ui-v2/src/libs/bridge/types.ts +++ b/packages/bridge-ui-v2/src/libs/bridge/types.ts @@ -1,4 +1,3 @@ -import type { WalletClient } from '@wagmi/core'; import type { Address, Hex } from 'viem'; export enum BridgeType { From 8d5fbec53a322d5bb1eeb164f532279698fcc589 Mon Sep 17 00:00:00 2001 From: Francisco Date: Wed, 19 Jul 2023 19:27:13 +0200 Subject: [PATCH 15/39] minor comment --- packages/bridge-ui-v2/src/libs/bridge/ETHBridge.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/bridge-ui-v2/src/libs/bridge/ETHBridge.ts b/packages/bridge-ui-v2/src/libs/bridge/ETHBridge.ts index cb09d6aba25..ddaeaa5437a 100644 --- a/packages/bridge-ui-v2/src/libs/bridge/ETHBridge.ts +++ b/packages/bridge-ui-v2/src/libs/bridge/ETHBridge.ts @@ -28,7 +28,8 @@ export class ETHBridge implements Bridge { const [depositValue, callValue] = to.toLowerCase() === owner.toLowerCase() ? [amount, BigInt(0)] : [BigInt(0), amount]; - // If there is a processing fee + // If there is a processing fee, use the specified message gas limit + // if not called by the owner const gasLimit = processingFee > 0 ? bridge.noOwnerGasLimit : BigInt(0); const message: Message = { From 535487433dfbc42b71ca9444acb66563ae0a82b5 Mon Sep 17 00:00:00 2001 From: Francisco Date: Wed, 19 Jul 2023 19:29:38 +0200 Subject: [PATCH 16/39] minor TODO --- .../src/components/Bridge/AmountInput/AmountInput.svelte | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte b/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte index 1a143cdb150..75f71980857 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte +++ b/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte @@ -22,7 +22,7 @@ let computingMaxAmount = false; let errorAmount = false; - // Simple, let's get the max amount to bridge and see if it's less + // Let's get the max amount to bridge and see if it's less // than what the user has entered. For ETH, will actually get an error // when trying to get that max amount, if the user has entered too much ETH async function checkEnteredAmount() { @@ -116,6 +116,7 @@ // Let's also trigger the check when either the processingFee or // the selectedToken change and debounce it, just in case + // TODO: better way? maybe store.subscribe(), or different component $: $processingFee && $selectedToken && debouncedCheckEnteredAmount(); From 0be1db3e996b3ff2ef353e876caa0a5ffb9b6c0f Mon Sep 17 00:00:00 2001 From: Francisco Date: Wed, 19 Jul 2023 19:36:38 +0200 Subject: [PATCH 17/39] fix test --- .../components/ChainSelector/ChainSelector.svelte | 2 +- .../src/libs/token/checkMintable.test.ts | 15 +-------------- .../bridge-ui-v2/src/libs/token/checkMintable.ts | 5 ++--- packages/bridge-ui-v2/src/libs/token/mint.test.ts | 2 +- packages/bridge-ui-v2/src/libs/token/types.ts | 1 - 5 files changed, 5 insertions(+), 20 deletions(-) diff --git a/packages/bridge-ui-v2/src/components/ChainSelector/ChainSelector.svelte b/packages/bridge-ui-v2/src/components/ChainSelector/ChainSelector.svelte index 007d288f5ad..ae34f6184bd 100644 --- a/packages/bridge-ui-v2/src/components/ChainSelector/ChainSelector.svelte +++ b/packages/bridge-ui-v2/src/components/ChainSelector/ChainSelector.svelte @@ -59,7 +59,7 @@ } catch (err) { console.error(err); - if (error instanceof UserRejectedRequestError) { + if (err instanceof UserRejectedRequestError) { warningToast($t('messages.network.rejected')); } } finally { diff --git a/packages/bridge-ui-v2/src/libs/token/checkMintable.test.ts b/packages/bridge-ui-v2/src/libs/token/checkMintable.test.ts index 146c490a1f0..503542c036e 100644 --- a/packages/bridge-ui-v2/src/libs/token/checkMintable.test.ts +++ b/packages/bridge-ui-v2/src/libs/token/checkMintable.test.ts @@ -49,19 +49,6 @@ describe('checkMintable', () => { vi.clearAllMocks(); }); - it('should throw when wallet is not connected', async () => { - vi.mocked(getWalletClient).mockResolvedValueOnce(null); - - try { - await checkMintable(BLLToken, mainnetChain.id); - expect.fail('should have thrown'); - } catch (error) { - const { cause } = error as Error; - expect(cause).toBe(MintableError.NOT_CONNECTED); - expect(getWalletClient).toHaveBeenCalled(); - } - }); - it('should throw when user has already minted', async () => { vi.mocked(mockTokenContract.read.minters).mockResolvedValueOnce(true); @@ -100,7 +87,7 @@ describe('checkMintable', () => { } catch (error) { const { cause } = error as Error; expect(cause).toBe(MintableError.INSUFFICIENT_BALANCE); - expect(getPublicClient).toHaveBeenCalledWith({ chainId: mainnetChain.id }); + expect(getPublicClient).toHaveBeenCalled(); expect(mockTokenContract.estimateGas.mint).toHaveBeenCalledWith([mockWalletClient.account.address]); expect(mockPublicClient.getBalance).toHaveBeenCalledWith({ address: mockWalletClient.account.address }); } diff --git a/packages/bridge-ui-v2/src/libs/token/checkMintable.ts b/packages/bridge-ui-v2/src/libs/token/checkMintable.ts index 2c173659ea2..2c91d47a4eb 100644 --- a/packages/bridge-ui-v2/src/libs/token/checkMintable.ts +++ b/packages/bridge-ui-v2/src/libs/token/checkMintable.ts @@ -6,9 +6,8 @@ import { getConnectedWallet } from '$libs/util/getWallet'; import { MintableError, type Token } from './types'; // Throws an error if: -// 1. Wallet is not connected -// 2. User has already minted this token -// 3. User has insufficient balance to mint this token +// 1. User has already minted this token +// 2. User has insufficient balance to mint this token export async function checkMintable(token: Token, chainId: number) { const walletClient = await getConnectedWallet(); diff --git a/packages/bridge-ui-v2/src/libs/token/mint.test.ts b/packages/bridge-ui-v2/src/libs/token/mint.test.ts index 379c5798d88..2be21d8334d 100644 --- a/packages/bridge-ui-v2/src/libs/token/mint.test.ts +++ b/packages/bridge-ui-v2/src/libs/token/mint.test.ts @@ -28,7 +28,7 @@ describe('mint', () => { vi.mocked(getContract).mockReturnValue(mockTokenContract); vi.mocked(mockTokenContract.write.mint).mockResolvedValue('0x123456'); - await expect(mint(BLLToken, mockWalletClient)).resolves.toEqual('0x123456'); + await expect(mint(BLLToken)).resolves.toEqual('0x123456'); expect(mockTokenContract.write.mint).toHaveBeenCalledWith([mockWalletClient.account.address]); }); }); diff --git a/packages/bridge-ui-v2/src/libs/token/types.ts b/packages/bridge-ui-v2/src/libs/token/types.ts index 5c66fd46aa9..230e5814d1d 100644 --- a/packages/bridge-ui-v2/src/libs/token/types.ts +++ b/packages/bridge-ui-v2/src/libs/token/types.ts @@ -14,7 +14,6 @@ export type TokenEnv = { }; export enum MintableError { - NOT_CONNECTED = 'NOT_CONNECTED', TOKEN_MINTED = 'TOKEN_MINTED', INSUFFICIENT_BALANCE = 'INSUFFICIENT_BALANCE', } From 8e51d03454e5d32588acad145cf45e7db20191a4 Mon Sep 17 00:00:00 2001 From: Francisco Date: Wed, 19 Jul 2023 19:41:56 +0200 Subject: [PATCH 18/39] remove unnecessary file --- .../libs/bridge/hasEnoughBalanceToBridge.ts | 21 ------------------- 1 file changed, 21 deletions(-) delete mode 100644 packages/bridge-ui-v2/src/libs/bridge/hasEnoughBalanceToBridge.ts diff --git a/packages/bridge-ui-v2/src/libs/bridge/hasEnoughBalanceToBridge.ts b/packages/bridge-ui-v2/src/libs/bridge/hasEnoughBalanceToBridge.ts deleted file mode 100644 index 7fb713adbcc..00000000000 --- a/packages/bridge-ui-v2/src/libs/bridge/hasEnoughBalanceToBridge.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { type Address, getPublicClient } from '@wagmi/core'; - -import { estimateCostOfBridging } from './estimateCostOfBridging'; -import { ETHBridge } from './ETHBridge'; -import type { Bridge, BridgeArgs } from './types'; - -export async function hasEnoughBalanceToBridge(bridge: Bridge, bridgeArgs: BridgeArgs, address: Address) { - const estimatedCost = await estimateCostOfBridging(bridge, bridgeArgs); - - const publicClient = getPublicClient(); - const userBalance = await publicClient.getBalance({ address }); - - let balanceAvailable = userBalance; - - if (bridge instanceof ETHBridge) { - // If dealing with ETH, we need to subtract the amount we're trying to bridge - balanceAvailable = userBalance - bridgeArgs.amount; - } - - return balanceAvailable > estimatedCost; -} From 5780ea5065365756f8288a06e8718a38d9a3a921 Mon Sep 17 00:00:00 2001 From: Francisco Date: Wed, 19 Jul 2023 19:42:10 +0200 Subject: [PATCH 19/39] fix error --- packages/bridge-ui-v2/src/libs/bridge/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/bridge-ui-v2/src/libs/bridge/index.ts b/packages/bridge-ui-v2/src/libs/bridge/index.ts index 7325863e4d3..5187917dc4b 100644 --- a/packages/bridge-ui-v2/src/libs/bridge/index.ts +++ b/packages/bridge-ui-v2/src/libs/bridge/index.ts @@ -1,4 +1,3 @@ export { bridges } from './bridges'; export { estimateCostOfBridging } from './estimateCostOfBridging'; -export { hasEnoughBalanceToBridge } from './hasEnoughBalanceToBridge'; export * from './types'; From 3ba05aa1a6d953a04cae337ce72e8f74c51bbfd9 Mon Sep 17 00:00:00 2001 From: Francisco Date: Wed, 19 Jul 2023 19:45:30 +0200 Subject: [PATCH 20/39] fix ts error --- packages/bridge-ui-v2/src/components/Faucet/Faucet.svelte | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/bridge-ui-v2/src/components/Faucet/Faucet.svelte b/packages/bridge-ui-v2/src/components/Faucet/Faucet.svelte index 31380fc82c7..2b7e621a568 100644 --- a/packages/bridge-ui-v2/src/components/Faucet/Faucet.svelte +++ b/packages/bridge-ui-v2/src/components/Faucet/Faucet.svelte @@ -102,9 +102,6 @@ const { cause } = err as Error; switch (cause) { - case MintableError.NOT_CONNECTED: - reasonNotMintable = $t('faucet.warning.no_connected'); - break; case MintableError.INSUFFICIENT_BALANCE: reasonNotMintable = $t('faucet.warning.insufficient_balance'); break; From 9e499647704df9f4f45613eb12f311d6b34c5ad2 Mon Sep 17 00:00:00 2001 From: Francisco Date: Wed, 19 Jul 2023 19:48:29 +0200 Subject: [PATCH 21/39] fix lint --- packages/bridge-ui-v2/src/libs/bridge/ETHBridge.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/bridge-ui-v2/src/libs/bridge/ETHBridge.ts b/packages/bridge-ui-v2/src/libs/bridge/ETHBridge.ts index ddaeaa5437a..45c54f813a9 100644 --- a/packages/bridge-ui-v2/src/libs/bridge/ETHBridge.ts +++ b/packages/bridge-ui-v2/src/libs/bridge/ETHBridge.ts @@ -28,7 +28,7 @@ export class ETHBridge implements Bridge { const [depositValue, callValue] = to.toLowerCase() === owner.toLowerCase() ? [amount, BigInt(0)] : [BigInt(0), amount]; - // If there is a processing fee, use the specified message gas limit + // If there is a processing fee, use the specified message gas limit // if not called by the owner const gasLimit = processingFee > 0 ? bridge.noOwnerGasLimit : BigInt(0); From c1f27b93bf691a8413517b10f5f7c1735bfe4c84 Mon Sep 17 00:00:00 2001 From: Francisco Date: Wed, 19 Jul 2023 19:54:34 +0200 Subject: [PATCH 22/39] minor change --- .../src/components/Bridge/AmountInput/AmountInput.svelte | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte b/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte index 75f71980857..02e08753648 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte +++ b/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte @@ -53,8 +53,9 @@ } catch (err) { console.error(err); - // Viem might throw an error that contains the following message, indicating + // Viem will throw an error that contains the following message, indicating // that the user won't have enough to pay the transaction + // TODO: better way to handle this. Error codes? if (`${err}`.toLocaleLowerCase().match('transaction exceeds the balance')) { errorAmount = true; } @@ -128,7 +129,7 @@
Date: Wed, 19 Jul 2023 20:00:53 +0200 Subject: [PATCH 23/39] minor change --- packages/bridge-ui-v2/src/libs/bridge/ERC20Bridge.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/bridge-ui-v2/src/libs/bridge/ERC20Bridge.ts b/packages/bridge-ui-v2/src/libs/bridge/ERC20Bridge.ts index 753d29537c1..e9cca0ff5e5 100644 --- a/packages/bridge-ui-v2/src/libs/bridge/ERC20Bridge.ts +++ b/packages/bridge-ui-v2/src/libs/bridge/ERC20Bridge.ts @@ -56,6 +56,12 @@ export class ERC20Bridge implements Bridge { async estimateGas(args: ERC20BridgeArgs) { const { tokenVaultContract, sendERC20Args } = await ERC20Bridge._prepareTransaction(args); - return tokenVaultContract.estimateGas.sendERC20([...sendERC20Args]); + const [,,,,,processingFee] = sendERC20Args; + + const value = processingFee; + + log('Estimating gas for sendERC20 call. Sending value', value); + + return tokenVaultContract.estimateGas.sendERC20([...sendERC20Args], { value }); } } From 893b37e959242436bfa0f345f645b256140f964f Mon Sep 17 00:00:00 2001 From: Francisco Date: Wed, 19 Jul 2023 20:24:59 +0200 Subject: [PATCH 24/39] fix lint --- packages/bridge-ui-v2/src/libs/bridge/ERC20Bridge.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/bridge-ui-v2/src/libs/bridge/ERC20Bridge.ts b/packages/bridge-ui-v2/src/libs/bridge/ERC20Bridge.ts index e9cca0ff5e5..074bc062a27 100644 --- a/packages/bridge-ui-v2/src/libs/bridge/ERC20Bridge.ts +++ b/packages/bridge-ui-v2/src/libs/bridge/ERC20Bridge.ts @@ -56,7 +56,7 @@ export class ERC20Bridge implements Bridge { async estimateGas(args: ERC20BridgeArgs) { const { tokenVaultContract, sendERC20Args } = await ERC20Bridge._prepareTransaction(args); - const [,,,,,processingFee] = sendERC20Args; + const [, , , , , processingFee] = sendERC20Args; const value = processingFee; From b459d3958555dc088f146ada88acd10d4def3345 Mon Sep 17 00:00:00 2001 From: Francisco Date: Wed, 19 Jul 2023 21:43:22 +0200 Subject: [PATCH 25/39] align to new design --- .../src/components/Bridge/Bridge.svelte | 2 +- .../Bridge/ProcessingFee/ProcessingFee.svelte | 36 +++++++++++++++---- packages/bridge-ui-v2/src/i18n/en.json | 4 +++ 3 files changed, 35 insertions(+), 7 deletions(-) diff --git a/packages/bridge-ui-v2/src/components/Bridge/Bridge.svelte b/packages/bridge-ui-v2/src/components/Bridge/Bridge.svelte index 770e2c091a4..b76eba55b09 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/Bridge.svelte +++ b/packages/bridge-ui-v2/src/components/Bridge/Bridge.svelte @@ -61,7 +61,7 @@
-
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 47dc3807f93..515dc7d000d 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/ProcessingFee/ProcessingFee.svelte +++ b/packages/bridge-ui-v2/src/components/Bridge/ProcessingFee/ProcessingFee.svelte @@ -16,9 +16,11 @@ import { processingFee } from '../state'; import NoneOption from './NoneOption.svelte'; import RecommendedFee from './RecommendedFee.svelte'; + import { Button } from '$components/Button'; let dialogId = `dialog-${uid()}`; let selectedFeeMethod = ProcessingFeeMethod.RECOMMENDED; + let prevOptionSelected = ProcessingFeeMethod.RECOMMENDED; let recommendedAmount = BigInt(0); let calculatingRecommendedAmount = false; @@ -42,10 +44,20 @@ } function openModal() { + // Keep track of the selected method before opening the modal + // so if we cancel we can go back to the previous method + prevOptionSelected = selectedFeeMethod; + modalOpen = true; } - function closeOnOptionClick() { + function cancelModal() { + inputBox.clear(); + selectedFeeMethod = prevOptionSelected; + closeModalWithDelay(); + } + + function closeModalWithDelay() { // By adding delay there is enough time to see the selected option // before closing the modal. Better experience for the user. setTimeout(closeModal, processingFeeComponent.closingDelayOptionClick); @@ -122,7 +134,7 @@ {/if} - + {#if !hasEnoughEth} @@ -207,7 +219,7 @@ -
+
ETH
+ +
+ + +
diff --git a/packages/bridge-ui-v2/src/i18n/en.json b/packages/bridge-ui-v2/src/i18n/en.json index 56ab85e6681..3cb29e904c1 100644 --- a/packages/bridge-ui-v2/src/i18n/en.json +++ b/packages/bridge-ui-v2/src/i18n/en.json @@ -32,6 +32,10 @@ "custom": { "label": "Custom", "text": "Customize your processing fee" + }, + "button": { + "confirm": "Confirm", + "cancel": "Cancel" } }, From d67cc8eee0977937b61f4796019f7ebf16a9753e Mon Sep 17 00:00:00 2001 From: Francisco Date: Wed, 19 Jul 2023 21:51:31 +0200 Subject: [PATCH 26/39] minor change --- .../src/components/Bridge/AmountInput/AmountInput.svelte | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte b/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte index 02e08753648..d9163922f05 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte +++ b/packages/bridge-ui-v2/src/components/Bridge/AmountInput/AmountInput.svelte @@ -138,8 +138,9 @@ on:input={updateAmount} bind:this={inputBox} class="w-full input-box outline-none py-6 pr-16 px-[26px] title-subsection-bold placeholder:text-tertiary-content" /> +
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 515dc7d000d..c7c4ba88ac0 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/ProcessingFee/ProcessingFee.svelte +++ b/packages/bridge-ui-v2/src/components/Bridge/ProcessingFee/ProcessingFee.svelte @@ -4,6 +4,7 @@ import { formatEther } from 'viem'; import { Alert } from '$components/Alert'; + import { Button } from '$components/Button'; import { Icon } from '$components/Icon'; import { InputBox } from '$components/InputBox'; import { LoadingText } from '$components/LoadingText'; @@ -16,7 +17,6 @@ import { processingFee } from '../state'; import NoneOption from './NoneOption.svelte'; import RecommendedFee from './RecommendedFee.svelte'; - import { Button } from '$components/Button'; let dialogId = `dialog-${uid()}`; let selectedFeeMethod = ProcessingFeeMethod.RECOMMENDED; @@ -54,7 +54,7 @@ function cancelModal() { inputBox.clear(); selectedFeeMethod = prevOptionSelected; - closeModalWithDelay(); + closeModal(); } function closeModalWithDelay() { diff --git a/packages/bridge-ui-v2/src/components/Faucet/Faucet.svelte b/packages/bridge-ui-v2/src/components/Faucet/Faucet.svelte index 2b7e621a568..a6fbdc75d07 100644 --- a/packages/bridge-ui-v2/src/components/Faucet/Faucet.svelte +++ b/packages/bridge-ui-v2/src/components/Faucet/Faucet.svelte @@ -145,7 +145,11 @@ {#if connected && wrongChain} -