diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index 8624ee7a07..ae50d87f53 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -33,7 +33,7 @@ jobs: secrets: inherit test-e2e: - name: "${{ matrix.test.name }}${{ matrix.test.type == 'orbit-eth' && ' with L3' || matrix.test.type == 'orbit-custom' && ' with custom fee token' || ''}}" + name: "Test E2E - ${{ matrix.test.name }} ${{ matrix.test.typeName }}" needs: [build, load-e2e-files] runs-on: ubuntu-latest strategy: @@ -100,16 +100,25 @@ jobs: nitro-testnode-ref: badbcbea9b43d46e115da4d7c9f2f57c31af8431 l3-node: ${{ matrix.test.type != 'regular' }} no-l3-token-bridge: ${{ matrix.test.type == 'regular' }} - args: ${{ matrix.test.type == 'orbit-custom' && '--l3-fee-token' || '' }} + args: >- + ${{ + (matrix.test.type == 'orbit-eth') && '--l3node --l3-token-bridge' || + (matrix.test.type == 'orbit-custom-6dec' && '--l3-fee-token --l3-fee-token-decimals 6') || + (matrix.test.type == 'orbit-custom-18dec' && '--l3-fee-token') || + (matrix.test.type == 'orbit-custom-20dec' && '--l3-fee-token --l3-fee-token-decimals 20') || + '' + }} - name: Run e2e tests via cypress-io/github-action uses: cypress-io/github-action@0da3c06ed8217b912deea9d8ee69630baed1737e # pin@v6.7.6 with: start: yarn start command: >- - ${{ + ${{ (matrix.test.type == 'orbit-eth') && 'yarn test:e2e:orbit --browser chrome' || - (matrix.test.type == 'orbit-custom' && 'yarn test:e2e:orbit:custom-gas-token --browser chrome') || + (matrix.test.type == 'orbit-custom-6dec' && 'yarn test:e2e:orbit:custom-gas-token --browser chrome') || + (matrix.test.type == 'orbit-custom-18dec' && 'yarn test:e2e:orbit:custom-gas-token --browser chrome') || + (matrix.test.type == 'orbit-custom-20dec' && 'yarn test:e2e:orbit:custom-gas-token --browser chrome') || (matrix.test.type == 'cctp' && 'yarn test:e2e:cctp --browser chrome') || 'yarn test:e2e --browser chrome' }} @@ -134,7 +143,7 @@ jobs: uses: actions/upload-artifact@v4 if: always() with: - name: e2e-artifacts-${{ github.sha }}-${{ matrix.test.name }}-${{ (matrix.test.type == 'cctp' && 'cctp') || (matrix.test.type == 'orbit-eth' && 'l3') || (matrix.test.type == 'orbit-custom' && 'custom-fee-token') || 'regular'}} + name: e2e-artifacts-${{ github.sha }}-${{ matrix.test.name }}-${{ matrix.test.type }} path: | ./packages/arb-token-bridge-ui/cypress/videos ./packages/arb-token-bridge-ui/cypress/screenshots diff --git a/.github/workflows/formatSpecfiles.js b/.github/workflows/formatSpecfiles.js index bb6b23e1ef..3d1976daae 100644 --- a/.github/workflows/formatSpecfiles.js +++ b/.github/workflows/formatSpecfiles.js @@ -11,14 +11,27 @@ switch (testType) { tests.push({ ...spec, type: "regular", + typeName: "", }); tests.push({ ...spec, type: "orbit-eth", + typeName: "with L3 (ETH)", }); tests.push({ ...spec, - type: "orbit-custom", + type: "orbit-custom-6dec", + typeName: "with L3 (6 decimals custom)", + }); + tests.push({ + ...spec, + type: "orbit-custom-18dec", + typeName: "with L3 (18 decimals custom)", + }); + tests.push({ + ...spec, + type: "orbit-custom-20dec", + typeName: "with L3 (20 decimals custom)", }); }); break; @@ -27,6 +40,7 @@ switch (testType) { // Running CCTP tests in parallel cause nonce issues, we're running the two tests sequentially tests.push({ name: "cctp", + typeName: "", file: "tests/e2e/specs/**/*Cctp.cy.{js,jsx,ts,tsx}", recordVideo: false, type: "cctp", diff --git a/packages/arb-token-bridge-ui/package.json b/packages/arb-token-bridge-ui/package.json index 6e272c5df7..e8d292c70b 100644 --- a/packages/arb-token-bridge-ui/package.json +++ b/packages/arb-token-bridge-ui/package.json @@ -5,7 +5,7 @@ "private": true, "dependencies": { "@apollo/client": "^3.7.11", - "@arbitrum/sdk": "^4.0.1", + "@arbitrum/sdk": "^4.0.2-beta.1", "@ethersproject/providers": "^5.7.0", "@headlessui/react": "^1.7.8", "@headlessui/tailwindcss": "^0.1.2", diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenRow.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenRow.tsx index 6017331763..3cc1bbe3d2 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenRow.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenRow.tsx @@ -32,6 +32,7 @@ import { useNetworks } from '../../hooks/useNetworks' import { useNetworksRelationship } from '../../hooks/useNetworksRelationship' import { TokenLogoFallback } from './TokenInfo' import { useBalanceOnSourceChain } from '../../hooks/useBalanceOnSourceChain' +import { useNativeCurrencyDecimalsOnSourceChain } from '../../hooks/useNativeCurrencyDecimalsOnSourceChain' function tokenListIdsToNames(ids: number[]): string { return ids @@ -244,6 +245,8 @@ function TokenBalance({ token }: { token: ERC20BridgeToken | null }) { } = useAppState() const { isLoading: isLoadingAccountType } = useAccountType() const { balance, symbol } = useTokenInfo(token) + const nativeCurrencyDecimalsOnSourceChain = + useNativeCurrencyDecimalsOnSourceChain() const isArbitrumNativeUSDC = isTokenArbitrumOneNativeUSDC(token?.address) || @@ -266,6 +269,13 @@ function TokenBalance({ token }: { token: ERC20BridgeToken | null }) { return typeof bridgeTokens[token.address] !== 'undefined' }, [bridgeTokens, isArbitrumNativeUSDC, token]) + const decimals = useMemo(() => { + if (token) { + return token.decimals + } + return nativeCurrencyDecimalsOnSourceChain + }, [nativeCurrencyDecimalsOnSourceChain, token]) + if (!tokenIsAddedToTheBridge) { return Import } @@ -279,7 +289,7 @@ function TokenBalance({ token }: { token: ERC20BridgeToken | null }) { {balance ? ( formatAmount(balance, { - decimals: token?.decimals, + decimals, symbol }) ) : ( diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx index 74c4a4c788..a631d602f7 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx @@ -6,6 +6,7 @@ import { useLatest } from 'react-use' import { useAccount, useNetwork, useSigner } from 'wagmi' import { TransactionResponse } from '@ethersproject/providers' import { twMerge } from 'tailwind-merge' +import { scaleFrom18DecimalsToNativeTokenDecimals } from '@arbitrum/sdk' import { useAppState } from '../../state' import { getNetworkName, isNetwork } from '../../util/networks' @@ -77,6 +78,7 @@ import { useIsTransferAllowed } from './hooks/useIsTransferAllowed' import { MoveFundsButton } from './MoveFundsButton' import { ProjectsListing } from '../common/ProjectsListing' import { useAmountBigNumber } from './hooks/useAmountBigNumber' +import { useNativeCurrencyDecimalsOnSourceChain } from '../../hooks/useNativeCurrencyDecimalsOnSourceChain' const signerUndefinedError = 'Signer is undefined' const transferNotAllowedError = 'Transfer not allowed' @@ -131,6 +133,8 @@ export function TransferPanel() { } } = useLatest(useNetworksRelationship(latestNetworks.current)) const isBatchTransferSupported = useIsBatchTransferSupported() + const nativeCurrencyDecimalsOnSourceChain = + useNativeCurrencyDecimalsOnSourceChain() const nativeCurrency = useNativeCurrency({ provider: childChainProvider }) @@ -616,7 +620,7 @@ export function TransferPanel() { amount: amountBigNumber, options: { approvalAmountIncrease: isCustomNativeTokenAmount2 - ? utils.parseUnits(amount2, nativeCurrency.decimals) + ? utils.parseUnits(amount2, nativeCurrencyDecimalsOnSourceChain) : undefined } }) @@ -631,7 +635,7 @@ export function TransferPanel() { amount: amountBigNumber, options: { approvalAmountIncrease: isCustomNativeTokenAmount2 - ? utils.parseUnits(amount2, nativeCurrency.decimals) + ? utils.parseUnits(amount2, nativeCurrencyDecimalsOnSourceChain) : undefined } }) @@ -744,6 +748,7 @@ export function TransferPanel() { } overrides.maxSubmissionCost = utils + // we are not scaling these to native decimals because arbitrum-sdk does it for us .parseEther(amount2) .add(gasEstimates.estimatedChildChainSubmissionCost) overrides.excessFeeRefundAddress = destinationAddress @@ -798,6 +803,20 @@ export function TransferPanel() { const timestampCreated = String(normalizeTimestamp(Date.now())) + const { isOrbitChain: isSourceOrbitChain } = isNetwork( + latestNetworks.current.sourceChain.id + ) + + const scaledAmount = + // only scale for native tokens, and + // only scale if sent from Orbit, because it's always 18 decimals there but the UI needs scaled amount + selectedToken || !isSourceOrbitChain + ? amountBigNumber + : scaleFrom18DecimalsToNativeTokenDecimals({ + amount: amountBigNumber, + decimals: nativeCurrency.decimals + }) + const txHistoryCompatibleObject = convertBridgeSdkToMergedTransaction({ bridgeTransfer, parentChainId: parentChain.id, @@ -806,7 +825,7 @@ export function TransferPanel() { walletAddress, destinationAddress, nativeCurrency, - amount: amountBigNumber, + amount: scaledAmount, amount2: isBatchTransfer ? utils.parseEther(amount2) : undefined, timestampCreated }) @@ -825,7 +844,7 @@ export function TransferPanel() { walletAddress, destinationAddress, nativeCurrency, - amount: amountBigNumber, + amount: scaledAmount, amount2: isBatchTransfer ? utils.parseEther(amount2) : undefined, timestampCreated }) diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMain/SourceNetworkBox.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMain/SourceNetworkBox.tsx index 48d493e25c..36f858e22d 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMain/SourceNetworkBox.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMain/SourceNetworkBox.tsx @@ -33,6 +33,7 @@ import { Button } from '../../common/Button' import { useSelectedTokenDecimals } from '../../../hooks/TransferPanel/useSelectedTokenDecimals' import { useNativeCurrencyBalances } from './useNativeCurrencyBalances' import { useIsCctpTransfer } from '../hooks/useIsCctpTransfer' +import { useNativeCurrencyDecimalsOnSourceChain } from '../../../hooks/useNativeCurrencyDecimalsOnSourceChain' function Amount2ToggleButton({ onClick @@ -92,6 +93,8 @@ export function SourceNetworkBox() { const decimals = useSelectedTokenDecimals() const { errorMessages } = useTransferReadiness() const nativeCurrencyBalances = useNativeCurrencyBalances() + const nativeCurrencyDecimalsOnSourceChain = + useNativeCurrencyDecimalsOnSourceChain() const isCctpTransfer = useIsCctpTransfer() @@ -145,10 +148,19 @@ export function SourceNetworkBox() { symbol: nativeCurrency.symbol, disabled: true, balance: nativeCurrencyBalances.sourceBalance - ? Number(utils.formatEther(nativeCurrencyBalances.sourceBalance)) + ? Number( + utils.formatUnits( + nativeCurrencyBalances.sourceBalance, + nativeCurrencyDecimalsOnSourceChain + ) + ) : undefined }), - [nativeCurrencyBalances, nativeCurrency.symbol] + [ + nativeCurrencyBalances, + nativeCurrency.symbol, + nativeCurrencyDecimalsOnSourceChain + ] ) return ( diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMain/TokenBalance.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMain/TokenBalance.tsx index 1e56d31e81..24f66b7437 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMain/TokenBalance.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMain/TokenBalance.tsx @@ -1,12 +1,18 @@ +import { useMemo } from 'react' import { BigNumber } from 'ethers' import { ERC20BridgeToken } from '../../../hooks/arbTokenBridge.types' -import { NativeCurrencyErc20 } from '../../../hooks/useNativeCurrency' +import { + NativeCurrencyErc20, + useNativeCurrency +} from '../../../hooks/useNativeCurrency' import { Loader } from '../../common/atoms/Loader' import { TokenSymbolWithExplorerLink } from '../../common/TokenSymbolWithExplorerLink' import { formatAmount } from '../../../util/NumberUtils' import { NetworkType } from './utils' +import { useNetworks } from '../../../hooks/useNetworks' +import { useNetworksRelationship } from '../../../hooks/useNetworksRelationship' export function TokenBalance({ forToken, @@ -22,6 +28,23 @@ export function TokenBalance({ tokenSymbolOverride?: string }) { const isParentChain = on === NetworkType.parentChain + const [networks] = useNetworks() + const { childChainProvider } = useNetworksRelationship(networks) + const nativeCurrency = useNativeCurrency({ + provider: childChainProvider + }) + + const isCustomNativeCurrency = + nativeCurrency.isCustom && + forToken?.address.toLowerCase() === nativeCurrency.address.toLowerCase() + + const decimals = useMemo(() => { + if (!isParentChain && isCustomNativeCurrency) { + // Native currency on Orbit chain, always 18 decimals + return 18 + } + return forToken?.decimals + }, [forToken?.decimals, isCustomNativeCurrency, isParentChain]) if (!forToken) { return null @@ -43,7 +66,7 @@ export function TokenBalance({ {prefix} {formatAmount(balance, { - decimals: forToken.decimals + decimals })} {' '} { diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMainInput.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMainInput.tsx index 414dc3303f..e6365719d6 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMainInput.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelMainInput.tsx @@ -21,6 +21,7 @@ import { Loader } from '../common/atoms/Loader' import { sanitizeAmountQueryParam } from '../../hooks/useArbQueryParams' import { truncateExtraDecimals } from '../../util/NumberUtils' import { useNativeCurrencyBalances } from './TransferPanelMain/useNativeCurrencyBalances' +import { useSelectedTokenDecimals } from '../../hooks/TransferPanel/useSelectedTokenDecimals' function MaxButton({ className = '', @@ -86,6 +87,7 @@ function SourceChainTokenBalance({ const [networks] = useNetworks() const { isDepositMode, childChainProvider } = useNetworksRelationship(networks) + const selectedTokenDecimals = useSelectedTokenDecimals() const nativeCurrencyBalances = useNativeCurrencyBalances() const selectedTokenBalances = useSelectedTokenBalances() @@ -103,7 +105,7 @@ function SourceChainTokenBalance({ const formattedBalance = balance !== null ? formatAmount(balance, { - decimals: selectedToken?.decimals ?? nativeCurrency.decimals + decimals: selectedTokenDecimals }) : null diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/hooks/useAmountBigNumber.ts b/packages/arb-token-bridge-ui/src/components/TransferPanel/hooks/useAmountBigNumber.ts index eb25a1680e..d578996588 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/hooks/useAmountBigNumber.ts +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/hooks/useAmountBigNumber.ts @@ -2,18 +2,15 @@ import { useMemo } from 'react' import { useArbQueryParams } from '../../../hooks/useArbQueryParams' import { useAppState } from '../../../state' import { constants, utils } from 'ethers' -import { useNetworks } from '../../../hooks/useNetworks' -import { useNetworksRelationship } from '../../../hooks/useNetworksRelationship' -import { useNativeCurrency } from '../../../hooks/useNativeCurrency' +import { useNativeCurrencyDecimalsOnSourceChain } from '../../../hooks/useNativeCurrencyDecimalsOnSourceChain' export function useAmountBigNumber() { const { app: { selectedToken } } = useAppState() const [{ amount }] = useArbQueryParams() - const [networks] = useNetworks() - const { childChainProvider } = useNetworksRelationship(networks) - const nativeCurrency = useNativeCurrency({ provider: childChainProvider }) + const nativeCurrencyDecimalsOnSourceChain = + useNativeCurrencyDecimalsOnSourceChain() return useMemo(() => { try { @@ -23,9 +20,9 @@ export function useAmountBigNumber() { return utils.parseUnits(amountSafe, selectedToken.decimals) } - return utils.parseUnits(amountSafe, nativeCurrency.decimals) + return utils.parseUnits(amountSafe, nativeCurrencyDecimalsOnSourceChain) } catch (error) { return constants.Zero } - }, [amount, selectedToken, nativeCurrency]) + }, [amount, selectedToken, nativeCurrencyDecimalsOnSourceChain]) } diff --git a/packages/arb-token-bridge-ui/src/hooks/TransferPanel/useSelectedTokenDecimals.ts b/packages/arb-token-bridge-ui/src/hooks/TransferPanel/useSelectedTokenDecimals.ts index 13a59dd41f..c17b8fa1cd 100644 --- a/packages/arb-token-bridge-ui/src/hooks/TransferPanel/useSelectedTokenDecimals.ts +++ b/packages/arb-token-bridge-ui/src/hooks/TransferPanel/useSelectedTokenDecimals.ts @@ -1,16 +1,14 @@ import { useAppState } from '../../state' -import { useNativeCurrency } from '../useNativeCurrency' -import { useNetworksRelationship } from '../useNetworksRelationship' -import { useNetworks } from '../useNetworks' +import { useNativeCurrencyDecimalsOnSourceChain } from '../useNativeCurrencyDecimalsOnSourceChain' export function useSelectedTokenDecimals() { const { app: { selectedToken } } = useAppState() - const [networks] = useNetworks() - const { childChainProvider } = useNetworksRelationship(networks) + const nativeCurrencyDecimalsOnSourceChain = + useNativeCurrencyDecimalsOnSourceChain() - const nativeCurrency = useNativeCurrency({ provider: childChainProvider }) - - return selectedToken ? selectedToken.decimals : nativeCurrency.decimals + return selectedToken + ? selectedToken.decimals + : nativeCurrencyDecimalsOnSourceChain } diff --git a/packages/arb-token-bridge-ui/src/hooks/useNativeCurrencyDecimalsOnSourceChain.ts b/packages/arb-token-bridge-ui/src/hooks/useNativeCurrencyDecimalsOnSourceChain.ts new file mode 100644 index 0000000000..d03aba45ea --- /dev/null +++ b/packages/arb-token-bridge-ui/src/hooks/useNativeCurrencyDecimalsOnSourceChain.ts @@ -0,0 +1,21 @@ +import { isNetwork } from '../util/networks' +import { useNativeCurrency } from './useNativeCurrency' +import { useNetworks } from './useNetworks' +import { useNetworksRelationship } from './useNetworksRelationship' + +export const useNativeCurrencyDecimalsOnSourceChain = () => { + const [networks] = useNetworks() + const { childChainProvider } = useNetworksRelationship(networks) + const nativeCurrency = useNativeCurrency({ + provider: childChainProvider + }) + const { isOrbitChain: isSourceChainOrbit } = isNetwork( + networks.sourceChain.id + ) + + if (isSourceChainOrbit) { + return 18 + } + + return nativeCurrency.decimals +} diff --git a/packages/arb-token-bridge-ui/src/token-bridge-sdk/Erc20DepositStarter.ts b/packages/arb-token-bridge-ui/src/token-bridge-sdk/Erc20DepositStarter.ts index 3934103f21..a5f5200cbe 100644 --- a/packages/arb-token-bridge-ui/src/token-bridge-sdk/Erc20DepositStarter.ts +++ b/packages/arb-token-bridge-ui/src/token-bridge-sdk/Erc20DepositStarter.ts @@ -1,4 +1,7 @@ -import { Erc20Bridger } from '@arbitrum/sdk' +import { + Erc20Bridger, + scaleFrom18DecimalsToNativeTokenDecimals +} from '@arbitrum/sdk' import { BigNumber, constants, utils } from 'ethers' import { ERC20__factory } from '@arbitrum/sdk/dist/lib/abi/factories/ERC20__factory' import { @@ -121,10 +124,12 @@ export class Erc20DepositStarter extends BridgeTransferStarter { .add(gasEstimates.estimatedChildChainSubmissionCost) ) ) - const estimatedDestinationChainGasFee = utils.parseUnits( - String(estimatedDestinationChainGasFeeEth), - await nativeCurrency.decimals() - ) + + const estimatedDestinationChainGasFee = + scaleFrom18DecimalsToNativeTokenDecimals({ + amount: utils.parseEther(String(estimatedDestinationChainGasFeeEth)), + decimals: await nativeCurrency.decimals() + }) // We want to bridge a certain amount of an ERC-20 token, but the Retryable fees on the destination chain will be paid in the custom fee token // We have to check if the native-token spending allowance is enough to cover the fees @@ -194,10 +199,11 @@ export class Erc20DepositStarter extends BridgeTransferStarter { ) ) - const estimatedDestinationChainGasFee = utils.parseUnits( - String(estimatedDestinationChainGasFeeEth), - await nativeCurrency.decimals() - ) + const estimatedDestinationChainGasFee = + scaleFrom18DecimalsToNativeTokenDecimals({ + amount: utils.parseEther(String(estimatedDestinationChainGasFeeEth)), + decimals: await nativeCurrency.decimals() + }) return erc20Bridger.approveGasToken({ erc20ParentAddress: this.sourceChainErc20Address, diff --git a/packages/arb-token-bridge-ui/src/util/withdrawals/helpers.ts b/packages/arb-token-bridge-ui/src/util/withdrawals/helpers.ts index 82e7706695..89ab0369be 100644 --- a/packages/arb-token-bridge-ui/src/util/withdrawals/helpers.ts +++ b/packages/arb-token-bridge-ui/src/util/withdrawals/helpers.ts @@ -3,7 +3,8 @@ import { Provider } from '@ethersproject/providers' import { BigNumber } from '@ethersproject/bignumber' import { ChildToParentMessageReader, - ChildTransactionReceipt + ChildTransactionReceipt, + scaleFrom18DecimalsToNativeTokenDecimals } from '@arbitrum/sdk' import { FetchWithdrawalsFromSubgraphResult } from './fetchWithdrawalsFromSubgraph' import { fetchErc20Data } from '../TokenUtils' @@ -87,7 +88,10 @@ export async function mapETHWithdrawalToL2ToL1EventResult({ sender: event.caller, destinationAddress: event.destination, type: AssetType.ETH, - value: callvalue, + value: scaleFrom18DecimalsToNativeTokenDecimals({ + amount: callvalue, + decimals: nativeCurrency.decimals + }), symbol: nativeCurrency.symbol, outgoingMessageState, l2TxHash: event.l2TxHash || event.transactionHash, @@ -314,7 +318,10 @@ export async function mapWithdrawalToL2ToL1EventResult({ sender: withdrawal.sender, destinationAddress: withdrawal.receiver, type: AssetType.ETH, - value: BigNumber.from(withdrawal.ethValue), + value: scaleFrom18DecimalsToNativeTokenDecimals({ + amount: BigNumber.from(withdrawal.ethValue), + decimals: nativeCurrency.decimals + }), outgoingMessageState, l2TxHash: l2TxReceipt.transactionHash, symbol: nativeCurrency.symbol, diff --git a/packages/arb-token-bridge-ui/synpress.config.ts b/packages/arb-token-bridge-ui/synpress.config.ts index 2150d747e3..84a332c1b0 100644 --- a/packages/arb-token-bridge-ui/synpress.config.ts +++ b/packages/arb-token-bridge-ui/synpress.config.ts @@ -25,7 +25,8 @@ import { getCustomDestinationAddress, ERC20TokenSymbol, ERC20TokenDecimals, - ERC20TokenName + ERC20TokenName, + getNativeTokenDecimals } from './tests/support/common' import { @@ -200,6 +201,10 @@ export default defineConfig({ config.env.ORBIT_TEST = isOrbitTest ? '1' : '0' config.env.NATIVE_TOKEN_SYMBOL = isCustomFeeToken ? 'TN' : 'ETH' config.env.NATIVE_TOKEN_ADDRESS = ethBridger.nativeToken + config.env.NATIVE_TOKEN_DECIMALS = await getNativeTokenDecimals({ + parentProvider, + childProvider + }) config.env.CUSTOM_DESTINATION_ADDRESS = await getCustomDestinationAddress() @@ -297,6 +302,10 @@ async function approveCustomFeeToken({ async function fundUserWalletNativeCurrency() { const childEthBridger = await EthBridger.fromProvider(childProvider) + const decimals = await getNativeTokenDecimals({ + parentProvider, + childProvider + }) const address = await userWallet.getAddress() @@ -306,18 +315,21 @@ async function fundUserWalletNativeCurrency() { ) const userBalance = await tokenContract.balanceOf(address) - const shouldFund = userBalance.lt(utils.parseEther('0.3')) + const shouldFund = userBalance.lt(utils.parseUnits('0.3', decimals)) if (!shouldFund) { console.log( - `User wallet has enough L3 native currency for testing, skip funding...` + `User wallet has enough custom native currency for testing, skip funding...` ) return } console.log(`Funding native currency to user wallet on L2...`) - const tx = await tokenContract.transfer(address, utils.parseEther('3')) + const tx = await tokenContract.transfer( + address, + utils.parseUnits('3', decimals) + ) await tx.wait() } diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/login.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/login.cy.ts index 5c627e2344..ce8413b0cc 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/login.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/login.cy.ts @@ -16,6 +16,7 @@ describe('Login Account', () => { let l2ETHbal const nativeTokenSymbol = Cypress.env('NATIVE_TOKEN_SYMBOL') + const nativeTokenDecimals = Cypress.env('NATIVE_TOKEN_DECIMALS') const isCustomFeeToken = nativeTokenSymbol !== 'ETH' before(() => { @@ -25,7 +26,9 @@ describe('Login Account', () => { multiCallerAddress: getL1NetworkConfig().multiCall, address: Cypress.env('ADDRESS'), rpcURL: Cypress.env('ETH_RPC_URL') - }).then(val => (l1ETHbal = formatAmount(val))) + }).then( + val => (l1ETHbal = formatAmount(val, { decimals: nativeTokenDecimals })) + ) } else { getInitialETHBalance(Cypress.env('ETH_RPC_URL')).then( val => (l1ETHbal = formatAmount(val)) diff --git a/packages/arb-token-bridge-ui/tests/support/common.ts b/packages/arb-token-bridge-ui/tests/support/common.ts index 76664a8e91..874000ada9 100644 --- a/packages/arb-token-bridge-ui/tests/support/common.ts +++ b/packages/arb-token-bridge-ui/tests/support/common.ts @@ -4,7 +4,7 @@ import { Provider, StaticJsonRpcProvider } from '@ethersproject/providers' import { BigNumber, Signer, Wallet, ethers, utils } from 'ethers' -import { MultiCaller } from '@arbitrum/sdk' +import { EthBridger, MultiCaller } from '@arbitrum/sdk' import { MULTICALL_TESTNET_ADDRESS } from '../../src/constants' import { defaultL2Network, @@ -112,7 +112,7 @@ export function getZeroToLessThanOneToken(symbol: string) { export const importTokenThroughUI = (address: string) => { // Click on the ETH dropdown (Select token button) - cy.findSelectTokenButton('ETH').click() + cy.findSelectTokenButton(Cypress.env('NATIVE_TOKEN_SYMBOL') ?? 'ETH').click() // open the Select Token popup cy.findByPlaceholderText(/Search by token name/i) @@ -183,6 +183,28 @@ export const startWebApp = (url = '/', qs: { [s: string]: string } = {}) => { }) } +export async function getNativeTokenDecimals({ + parentProvider, + childProvider +}: { + parentProvider: Provider + childProvider: Provider +}) { + const multiCaller = await MultiCaller.fromProvider(parentProvider) + const ethBridger = await EthBridger.fromProvider(childProvider) + const isCustomFeeToken = typeof ethBridger.nativeToken !== 'undefined' + + const nativeToken = isCustomFeeToken + ? ( + await multiCaller.getTokenData([ethBridger.nativeToken!], { + decimals: true + }) + )[0] + : undefined + + return nativeToken?.decimals ?? 18 +} + export const visitAfterSomeDelay = ( url: string, options?: Partial @@ -221,20 +243,29 @@ export async function generateActivityOnChains({ // whilst waiting for status we mine on both parentChain and childChain console.log('Generating activity on parentChain...') const minerParent = Wallet.createRandom().connect(parentProvider) + + const decimals = await getNativeTokenDecimals({ + parentProvider, + childProvider + }) + await fundEth({ address: await minerParent.getAddress(), provider: parentProvider, sourceWallet: wallet, - networkType: 'parentChain' + networkType: 'parentChain', + amount: utils.parseUnits('0.2', decimals) }) console.log('Generating activity on childChain...') const minerChild = Wallet.createRandom().connect(childProvider) + await fundEth({ address: await minerChild.getAddress(), provider: childProvider, sourceWallet: wallet, - networkType: 'childChain' + networkType: 'childChain', + amount: utils.parseEther('0.2') }) await Promise.allSettled([keepMining(minerParent), keepMining(minerChild)]) @@ -293,13 +324,13 @@ export async function fundEth({ provider, sourceWallet, // source wallet that will fund the `address`, networkType, - amount = utils.parseEther('2') + amount }: { address: string provider: Provider sourceWallet: Wallet networkType: NetworkType - amount?: BigNumber + amount: BigNumber }) { console.log(`Funding ETH ${address} on ${networkType}...`) const balance = await provider.getBalance(address) diff --git a/yarn.lock b/yarn.lock index df809f5989..eba2169b8c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -89,6 +89,17 @@ async-mutex "^0.4.0" ethers "^5.1.0" +"@arbitrum/sdk@^4.0.2-beta.1": + version "4.0.2-beta.1" + resolved "https://registry.yarnpkg.com/@arbitrum/sdk/-/sdk-4.0.2-beta.1.tgz#c6c6c5784a8eaa6a4edab3600f765e9f82ce5988" + integrity sha512-a5DG6+Ld3X4L8H3CTUl3gc27Bj/Pqjo+5UZiCI97SM2mHtHKWF1BgQ+FCk/8bgFUP5nS9dXBl5tPJRtCFfOe0A== + dependencies: + "@ethersproject/address" "^5.0.8" + "@ethersproject/bignumber" "^5.1.1" + "@ethersproject/bytes" "^5.0.8" + async-mutex "^0.4.0" + ethers "^5.1.0" + "@babel/code-frame@7.12.11": version "7.12.11" resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz"