diff --git a/packages/bridge-ui/src/bridge/ERC20Bridge.spec.ts b/packages/bridge-ui/src/bridge/ERC20Bridge.spec.ts index 6b4dd044701..df5dc137d64 100644 --- a/packages/bridge-ui/src/bridge/ERC20Bridge.spec.ts +++ b/packages/bridge-ui/src/bridge/ERC20Bridge.spec.ts @@ -42,7 +42,7 @@ jest.mock('ethers', () => ({ const wallet = new Wallet('0x'); const opts: BridgeOpts = { - amountInWei: BigNumber.from(1), + amount: BigNumber.from(1), signer: wallet, tokenAddress: '0xtoken', srcChainId: L1_CHAIN_ID, @@ -54,7 +54,7 @@ const opts: BridgeOpts = { }; const approveOpts: ApproveOpts = { - amountInWei: BigNumber.from(1), + amount: BigNumber.from(1), signer: wallet, contractAddress: '0x456', spenderAddress: '0x789', @@ -66,9 +66,7 @@ describe('bridge tests', () => { }); it('requires allowance returns true when allowance has not been set', async () => { - mockContract.allowance.mockImplementationOnce(() => - opts.amountInWei.sub(1), - ); + mockContract.allowance.mockImplementationOnce(() => opts.amount.sub(1)); mockSigner.getAddress.mockImplementationOnce(() => '0xfake'); @@ -86,9 +84,7 @@ describe('bridge tests', () => { }); it('requires allowance returns true when allowance is > than amount', async () => { - mockContract.allowance.mockImplementationOnce(() => - opts.amountInWei.add(1), - ); + mockContract.allowance.mockImplementationOnce(() => opts.amount.add(1)); mockSigner.getAddress.mockImplementationOnce(() => '0xfake'); const bridge: Bridge = new ERC20Bridge(null); @@ -105,7 +101,7 @@ describe('bridge tests', () => { }); it('requires allowance returns true when allowance is === amount', async () => { - mockContract.allowance.mockImplementationOnce(() => opts.amountInWei); + mockContract.allowance.mockImplementationOnce(() => opts.amount); mockSigner.getAddress.mockImplementationOnce(() => '0xfake'); const bridge: Bridge = new ERC20Bridge(null); @@ -122,9 +118,7 @@ describe('bridge tests', () => { }); it('approve throws when amount is already greater than whats set', async () => { - mockContract.allowance.mockImplementationOnce(() => - opts.amountInWei.add(1), - ); + mockContract.allowance.mockImplementationOnce(() => opts.amount.add(1)); mockSigner.getAddress.mockImplementationOnce(() => '0xfake'); @@ -143,9 +137,7 @@ describe('bridge tests', () => { }); it('approve succeeds when allowance is less than what is being requested', async () => { - mockContract.allowance.mockImplementationOnce(() => - opts.amountInWei.sub(1), - ); + mockContract.allowance.mockImplementationOnce(() => opts.amount.sub(1)); mockSigner.getAddress.mockImplementationOnce(() => '0xfake'); @@ -161,14 +153,12 @@ describe('bridge tests', () => { ); expect(mockContract.approve).toHaveBeenCalledWith( approveOpts.spenderAddress, - approveOpts.amountInWei, + approveOpts.amount, ); }); it('bridge throws when requires approval', async () => { - mockContract.allowance.mockImplementationOnce(() => - opts.amountInWei.sub(1), - ); + mockContract.allowance.mockImplementationOnce(() => opts.amount.sub(1)); const bridge: Bridge = new ERC20Bridge(null); @@ -182,9 +172,7 @@ describe('bridge tests', () => { }); it('bridge calls senderc20 when doesnt require approval', async () => { - mockContract.allowance.mockImplementationOnce(() => - opts.amountInWei.add(1), - ); + mockContract.allowance.mockImplementationOnce(() => opts.amount.add(1)); mockSigner.getAddress.mockImplementation(() => '0xfake'); const bridge: Bridge = new ERC20Bridge(null); @@ -198,7 +186,7 @@ describe('bridge tests', () => { opts.destChainId, '0x', opts.tokenAddress, - opts.amountInWei, + opts.amount, BigNumber.from(3140000), opts.processingFeeInWei, '0xfake', @@ -210,9 +198,7 @@ describe('bridge tests', () => { }); it('bridge calls senderc20 when doesnt requires approval, with no processing fee and memo', async () => { - mockContract.allowance.mockImplementationOnce(() => - opts.amountInWei.add(1), - ); + mockContract.allowance.mockImplementationOnce(() => opts.amount.add(1)); mockSigner.getAddress.mockImplementation(() => '0xfake'); const bridge: Bridge = new ERC20Bridge(null); @@ -220,7 +206,7 @@ describe('bridge tests', () => { expect(mockContract.sendERC20).not.toHaveBeenCalled(); const opts: BridgeOpts = { - amountInWei: BigNumber.from(1), + amount: BigNumber.from(1), signer: wallet, tokenAddress: '0xtoken', srcChainId: L1_CHAIN_ID, @@ -235,7 +221,7 @@ describe('bridge tests', () => { opts.destChainId, '0xfake', opts.tokenAddress, - opts.amountInWei, + opts.amount, BigNumber.from(3000000), BigNumber.from(0), '0xfake', diff --git a/packages/bridge-ui/src/bridge/ERC20Bridge.ts b/packages/bridge-ui/src/bridge/ERC20Bridge.ts index 9593bb55daf..c098783fb3c 100644 --- a/packages/bridge-ui/src/bridge/ERC20Bridge.ts +++ b/packages/bridge-ui/src/bridge/ERC20Bridge.ts @@ -48,7 +48,7 @@ export class ERC20Bridge implements Bridge { srcChainId: opts.srcChainId, destChainId: opts.destChainId, - depositValue: opts.amountInWei, + depositValue: opts.amount, callValue: 0, processingFee, gasLimit, @@ -104,7 +104,7 @@ export class ERC20Bridge implements Bridge { return this.spenderRequiresAllowance( opts.contractAddress, opts.signer, - opts.amountInWei, + opts.amount, opts.spenderAddress, ); } @@ -113,7 +113,7 @@ export class ERC20Bridge implements Bridge { const requiresAllowance = await this.spenderRequiresAllowance( opts.contractAddress, opts.signer, - opts.amountInWei, + opts.amount, opts.spenderAddress, ); @@ -129,10 +129,10 @@ export class ERC20Bridge implements Bridge { try { log( - `Approving ${opts.amountInWei} tokens for spender "${opts.spenderAddress}"`, + `Approving ${opts.amount} tokens for spender "${opts.spenderAddress}"`, ); - const tx = await contract.approve(opts.spenderAddress, opts.amountInWei); + const tx = await contract.approve(opts.spenderAddress, opts.amount); log('Approval sent with transaction', tx); @@ -149,7 +149,7 @@ export class ERC20Bridge implements Bridge { const requiresAllowance = await this.spenderRequiresAllowance( opts.tokenAddress, opts.signer, - opts.amountInWei, + opts.amount, opts.tokenVaultAddress, ); @@ -168,7 +168,7 @@ export class ERC20Bridge implements Bridge { message.destChainId, message.to, opts.tokenAddress, - opts.amountInWei, + opts.amount, message.gasLimit, message.processingFee, message.refundAddress, @@ -199,7 +199,7 @@ export class ERC20Bridge implements Bridge { message.destChainId, message.to, opts.tokenAddress, - opts.amountInWei, + opts.amount, message.gasLimit, message.processingFee, message.refundAddress, diff --git a/packages/bridge-ui/src/bridge/ETHBridge.spec.ts b/packages/bridge-ui/src/bridge/ETHBridge.spec.ts index e99479e61aa..1f5930857a7 100644 --- a/packages/bridge-ui/src/bridge/ETHBridge.spec.ts +++ b/packages/bridge-ui/src/bridge/ETHBridge.spec.ts @@ -51,7 +51,7 @@ describe('bridge tests', () => { const bridge: Bridge = new ETHBridge(null); const requires = await bridge.requiresAllowance({ - amountInWei: BigNumber.from(1), + amount: BigNumber.from(1), signer: new Wallet('0x'), contractAddress: '0x1234', spenderAddress: '0x', @@ -63,7 +63,7 @@ describe('bridge tests', () => { const bridge: Bridge = new ETHBridge(null); const tx = await bridge.approve({ - amountInWei: BigNumber.from(1), + amount: BigNumber.from(1), signer: new Wallet('0x'), contractAddress: '0x1234', spenderAddress: '0x', @@ -81,7 +81,7 @@ describe('bridge tests', () => { }); const opts: BridgeOpts = { - amountInWei: BigNumber.from(1), + amount: BigNumber.from(1), signer: wallet, tokenAddress: '', srcChainId: L1_CHAIN_ID, @@ -126,7 +126,7 @@ describe('bridge tests', () => { }); const opts: BridgeOpts = { - amountInWei: BigNumber.from(1), + amount: BigNumber.from(1), signer: wallet, tokenAddress: '', srcChainId: L1_CHAIN_ID, diff --git a/packages/bridge-ui/src/bridge/ETHBridge.ts b/packages/bridge-ui/src/bridge/ETHBridge.ts index fb4a3c4ea09..14ac2651f85 100644 --- a/packages/bridge-ui/src/bridge/ETHBridge.ts +++ b/packages/bridge-ui/src/bridge/ETHBridge.ts @@ -31,13 +31,13 @@ export class ETHBridge implements Bridge { const depositValue = opts.to.toLowerCase() === owner.toLowerCase() - ? opts.amountInWei + ? opts.amount : BigNumber.from(0); const callValue = opts.to.toLowerCase() === owner.toLowerCase() ? BigNumber.from(0) - : opts.amountInWei; + : opts.amount; const processingFee = opts.processingFeeInWei ?? BigNumber.from(0); diff --git a/packages/bridge-ui/src/components/BridgeForm/BridgeForm.svelte b/packages/bridge-ui/src/components/BridgeForm/BridgeForm.svelte index 13725b0f783..4a18df73f12 100644 --- a/packages/bridge-ui/src/components/BridgeForm/BridgeForm.svelte +++ b/packages/bridge-ui/src/components/BridgeForm/BridgeForm.svelte @@ -135,10 +135,14 @@ signer, ); - log(`Checking allowance for token ${token.symbol}`); + const parsedAmount = ethers.utils.parseUnits(amount, token.decimals); + + log( + `Checking allowance for token ${token.symbol} and amount ${parsedAmount}`, + ); const isRequired = await $activeBridge.requiresAllowance({ - amountInWei: ethers.utils.parseUnits(amount, token.decimals), + amount: parsedAmount, signer: signer, contractAddress: address, spenderAddress: tokenVaults[srcChain.id], @@ -201,11 +205,12 @@ ); const spenderAddress = tokenVaults[$srcChain.id]; + const parsedAmount = ethers.utils.parseUnits(amount, _token.decimals); log(`Approving token ${_token.symbol}`); const tx = await $activeBridge.approve({ - amountInWei: ethers.utils.parseUnits(amount, _token.decimals), + amount: parsedAmount, signer: $signer, contractAddress, spenderAddress, @@ -253,7 +258,7 @@ const gasEstimate = await $activeBridge.estimateGas({ ...bridgeOpts, // We need an amount, and user might not have entered one at this point - amountInWei: BigNumber.from(1), + amount: BigNumber.from(1), }); const feeData = await fetchFeeData(); @@ -296,7 +301,7 @@ return; } - const amountInWei = ethers.utils.parseUnits(amount, _token.decimals); + const parsedAmount = ethers.utils.parseUnits(amount, _token.decimals); const provider = providers[$destChain.id]; const destTokenVaultAddress = tokenVaults[$destChain.id]; @@ -322,7 +327,7 @@ ); const bridgeOpts: BridgeOpts = { - amountInWei, + amount: parsedAmount, signer: $signer, tokenAddress, srcChainId: $srcChain.id, @@ -368,7 +373,7 @@ srcChainId: $srcChain.id, destChainId: $destChain.id, symbol: _token.symbol, - amountInWei, + amount: parsedAmount, from: tx.from, hash: tx.hash, status: MessageStatus.New, @@ -444,7 +449,7 @@ try { const feeData = await fetchFeeData(); const gasEstimate = await $activeBridge.estimateGas({ - amountInWei: BigNumber.from(1), + amount: BigNumber.from(1), signer: $signer, tokenAddress: await getAddressForToken( $token, diff --git a/packages/bridge-ui/src/components/Transactions/Transaction.svelte b/packages/bridge-ui/src/components/Transactions/Transaction.svelte index 2553c4e94f0..e983537e037 100644 --- a/packages/bridge-ui/src/components/Transactions/Transaction.svelte +++ b/packages/bridge-ui/src/components/Transactions/Transaction.svelte @@ -374,7 +374,7 @@ {@const { depositValue, callValue } = transaction.message} {utils.formatEther(depositValue.eq(0) ? callValue : depositValue)} {:else} - {utils.formatUnits(transaction.amountInWei)} + {utils.formatUnits(transaction.amount, transaction.decimals)} {/if} {transaction.symbol ?? 'ETH'} diff --git a/packages/bridge-ui/src/domain/bridge.ts b/packages/bridge-ui/src/domain/bridge.ts index 0c3f59db2b2..e5dab824166 100644 --- a/packages/bridge-ui/src/domain/bridge.ts +++ b/packages/bridge-ui/src/domain/bridge.ts @@ -11,14 +11,14 @@ export enum BridgeType { } export type ApproveOpts = { - amountInWei: BigNumber; + amount: BigNumber; contractAddress: string; signer: ethers.Signer; spenderAddress: string; }; export type BridgeOpts = { - amountInWei: BigNumber; + amount: BigNumber; signer: ethers.Signer; tokenAddress: string; srcChainId: ChainID; diff --git a/packages/bridge-ui/src/domain/transaction.ts b/packages/bridge-ui/src/domain/transaction.ts index 57e71e99fa9..d23747e7723 100644 --- a/packages/bridge-ui/src/domain/transaction.ts +++ b/packages/bridge-ui/src/domain/transaction.ts @@ -23,8 +23,9 @@ export type BridgeTransaction = { msgHash?: string; message?: Message; interval?: NodeJS.Timer; - amountInWei?: BigNumber; + amount?: BigNumber; symbol?: string; + decimals?: number; srcChainId: ChainID; destChainId: ChainID; }; diff --git a/packages/bridge-ui/src/relayer-api/RelayerAPIService.spec.ts b/packages/bridge-ui/src/relayer-api/RelayerAPIService.spec.ts index 0ddd0f79f02..fb5186b4bd9 100644 --- a/packages/bridge-ui/src/relayer-api/RelayerAPIService.spec.ts +++ b/packages/bridge-ui/src/relayer-api/RelayerAPIService.spec.ts @@ -23,6 +23,7 @@ const mockContract = { queryFilter: jest.fn(), getMessageStatus: jest.fn(), symbol: jest.fn(), + decimals: jest.fn(), filters: { ERC20Sent: () => 'ERC20Sent', }, @@ -82,6 +83,7 @@ describe('RelayerAPIService', () => { mockContract.getMessageStatus.mockResolvedValue(MessageStatus.New); mockContract.queryFilter.mockResolvedValue(mockErc20Query); mockContract.symbol.mockResolvedValue('BLL'); + mockContract.decimals.mockResolvedValue(18); }); it('should get transactions from API', async () => { @@ -254,6 +256,7 @@ describe('RelayerAPIService', () => { expect(mockContract.getMessageStatus).toHaveBeenCalledTimes(10); expect(mockContract.queryFilter).toHaveBeenCalledTimes(9); expect(mockContract.symbol).toHaveBeenCalledTimes(9); + expect(mockContract.decimals).toHaveBeenCalledTimes(9); }); it('should not get transactions with wrong address', async () => { diff --git a/packages/bridge-ui/src/relayer-api/RelayerAPIService.ts b/packages/bridge-ui/src/relayer-api/RelayerAPIService.ts index 99f8db46947..9010b3a2f5c 100644 --- a/packages/bridge-ui/src/relayer-api/RelayerAPIService.ts +++ b/packages/bridge-ui/src/relayer-api/RelayerAPIService.ts @@ -89,18 +89,27 @@ export class RelayerAPIService implements RelayerAPI { ); } - private static async _getERC20SymbolAndAmount( + private static async _getERC20Details( erc20Event: ethers.Event, erc20Abi: ethers.ContractInterface, provider: ethers.providers.StaticJsonRpcProvider, - ): Promise<[string, BigNumber]> { + ): Promise<{ + amount: BigNumber; + symbol: string; + decimals: number; + }> { const { token, amount } = erc20Event.args; const erc20Contract = new Contract(token, erc20Abi, provider); const symbol: string = await erc20Contract.symbol(); - const amountInWei: BigNumber = BigNumber.from(amount); + const decimals: number = await erc20Contract.decimals(); + const bnAmount: BigNumber = BigNumber.from(amount); - return [symbol, amountInWei]; + return { + symbol, + decimals, + amount: bnAmount, + }; } private readonly providers: ProvidersRecord; @@ -185,7 +194,7 @@ export class RelayerAPIService implements RelayerAPI { return { status: tx.status, - amountInWei: BigNumber.from(tx.amount), + amount: BigNumber.from(tx.amount), symbol: tx.canonicalTokenSymbol, hash: tx.data.Raw.transactionHash, from: tx.messageOwner, @@ -223,7 +232,9 @@ export class RelayerAPIService implements RelayerAPI { message: tx.message, msgHash: tx.msgHash, status: tx.status, - amountInWei: tx.amountInWei, + amount: tx.amount, + symbol: tx.symbol, + decimals: tx.canonicalTokenDecimals, srcChainId: tx.srcChainId, destChainId: tx.destChainId, hash: tx.hash, @@ -264,9 +275,6 @@ export class RelayerAPIService implements RelayerAPI { // Update the status bridgeTx.status = status; - let amountInWei: BigNumber = tx.amountInWei; - let symbol: string; - if (tx.canonicalTokenAddress !== ethers.constants.AddressZero) { // We're dealing with an ERC20 transfer. // Let's get the symbol and amount from the TokenVault contract. @@ -281,21 +289,23 @@ export class RelayerAPIService implements RelayerAPI { receipt.blockNumber, ); + // TODO: do we want to show these transactions? // if (!erc20Event) { // return bridgeTx; // } if (!erc20Event) return; - [symbol, amountInWei] = - await RelayerAPIService._getERC20SymbolAndAmount( + const { amount, symbol, decimals } = + await RelayerAPIService._getERC20Details( erc20Event, erc20ABI, srcProvider, ); - } - bridgeTx.amountInWei = amountInWei; - bridgeTx.symbol = symbol; + bridgeTx.amount = amount; + bridgeTx.symbol = symbol; + bridgeTx.decimals = decimals; + } return bridgeTx; }); diff --git a/packages/bridge-ui/src/storage/StorageService.spec.ts b/packages/bridge-ui/src/storage/StorageService.spec.ts index 8e3d57d92be..9cc25788ca8 100644 --- a/packages/bridge-ui/src/storage/StorageService.spec.ts +++ b/packages/bridge-ui/src/storage/StorageService.spec.ts @@ -23,6 +23,7 @@ const mockContract = { queryFilter: jest.fn(), getMessageStatus: jest.fn(), symbol: jest.fn(), + decimals: jest.fn(), filters: { // Returns this string to help us // identify the filter in the tests @@ -196,7 +197,7 @@ describe('storage tests', () => { const txs = await svc.getAllByAddress('0x123'); - // There is no symbol nor amountInWei + // There is no symbol nor amount expect(txs).toEqual([ { ...mockTx, @@ -231,7 +232,7 @@ describe('storage tests', () => { // We should have these two symbol: TKOToken.symbol, - amountInWei: BigNumber.from(0x64), + amount: BigNumber.from(0x64), }, ]); }); @@ -325,7 +326,7 @@ describe('storage tests', () => { const tx = await svc.getTransactionByHash('0x123', mockTx.hash); - // There is no symbol nor amountInWei + // There is no symbol nor amount expect(tx).toEqual({ ...mockTx, receipt: { blockNumber: 1 }, @@ -349,7 +350,7 @@ describe('storage tests', () => { expect(tx).toEqual({ ...mockTx, - amountInWei: BigNumber.from(0x64), + amount: BigNumber.from(0x64), message: mockErc20Event.args.message, receipt: { blockNumber: 1, diff --git a/packages/bridge-ui/src/storage/StorageService.ts b/packages/bridge-ui/src/storage/StorageService.ts index 5265186c2da..9113d8ef6bb 100644 --- a/packages/bridge-ui/src/storage/StorageService.ts +++ b/packages/bridge-ui/src/storage/StorageService.ts @@ -6,6 +6,7 @@ import { bridgeABI, erc20ABI, tokenVaultABI } from '../constants/abi'; import type { ChainID } from '../domain/chain'; import { MessageStatus } from '../domain/message'; import type { BridgeTransaction, Transactioner } from '../domain/transaction'; +import { isETHByMessage } from '../utils/isETHByMessage'; import { jsonParseOrEmptyArray } from '../utils/jsonParseOrEmptyArray'; import { getLogger } from '../utils/logger'; import { tokenVaults } from '../vault/tokenVaults'; @@ -84,18 +85,19 @@ export class StorageService implements Transactioner { ); } - private static async _getERC20SymbolAndAmount( + private static async _getERC20Details( erc20Event: ethers.Event, erc20Abi: ethers.ContractInterface, provider: ethers.providers.StaticJsonRpcProvider, - ): Promise<[string, BigNumber]> { + ): Promise<[BigNumber, string, number]> { const { token, amount } = erc20Event.args; const erc20Contract = new Contract(token, erc20Abi, provider); const symbol: string = await erc20Contract.symbol(); - const amountInWei: BigNumber = BigNumber.from(amount); + const decimals: number = await erc20Contract.decimals(); + const bnAmount: BigNumber = BigNumber.from(amount); - return [symbol, amountInWei]; + return [bnAmount, symbol, decimals]; } private readonly storage: Storage; @@ -183,11 +185,11 @@ export class StorageService implements Transactioner { bridgeTx.status = status; - let amountInWei: BigNumber; + let amount: BigNumber; let symbol: string; + let decimals: number; - // TODO: function isERC20Transfer(message: string): boolean? - if (message.data && message.data !== '0x') { + if (!isETHByMessage(message)) { // We're dealing with an ERC20 transfer. // Let's get the symbol and amount from the TokenVault contract. @@ -205,15 +207,16 @@ export class StorageService implements Transactioner { return bridgeTx; } - [symbol, amountInWei] = await StorageService._getERC20SymbolAndAmount( + [amount, symbol, decimals] = await StorageService._getERC20Details( erc20Event, erc20ABI, srcProvider, ); } - bridgeTx.amountInWei = amountInWei; + bridgeTx.amount = amount; bridgeTx.symbol = symbol; + bridgeTx.decimals = decimals; return bridgeTx; }); @@ -291,10 +294,11 @@ export class StorageService implements Transactioner { tx.status = status; - let amountInWei: BigNumber; + let amount: BigNumber; let symbol: string; + let decimals: number; - if (message.data && message.data !== '0x') { + if (!isETHByMessage(message)) { // Dealing with an ERC20 transfer. Let's get the symbol // and amount from the TokenVault contract. @@ -312,7 +316,7 @@ export class StorageService implements Transactioner { return tx; } - [symbol, amountInWei] = await StorageService._getERC20SymbolAndAmount( + [amount, symbol, decimals] = await StorageService._getERC20Details( erc20Event, erc20ABI, srcProvider, @@ -321,8 +325,9 @@ export class StorageService implements Transactioner { const bridgeTx = { ...tx, - amountInWei, + amount, symbol, + decimals, } as BridgeTransaction; log('Enhanced transaction', bridgeTx);