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 fc1005a8f6..ed274b56d9 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx @@ -1,7 +1,7 @@ import dayjs from 'dayjs' import { useState, useMemo } from 'react' import Tippy from '@tippyjs/react' -import { constants, utils } from 'ethers' +import { BigNumber, constants, utils } from 'ethers' import { useLatest } from 'react-use' import { useAccount, useChainId, useSigner } from 'wagmi' import { TransactionResponse } from '@ethersproject/providers' @@ -49,7 +49,10 @@ import { isUserRejectedError } from '../../util/isUserRejectedError' import { getUsdcTokenAddressFromSourceChainId } from '../../state/cctpState' import { DepositStatus, MergedTransaction } from '../../state/app/state' import { useNativeCurrency } from '../../hooks/useNativeCurrency' -import { AssetType } from '../../hooks/arbTokenBridge.types' +import { + AssetType, + DepositGasEstimates +} from '../../hooks/arbTokenBridge.types' import { ImportTokenModalStatus, getWarningTokenDescription, @@ -63,7 +66,10 @@ import { useNetworks } from '../../hooks/useNetworks' import { useNetworksRelationship } from '../../hooks/useNetworksRelationship' import { CctpTransferStarter } from '@/token-bridge-sdk/CctpTransferStarter' import { BridgeTransferStarterFactory } from '@/token-bridge-sdk/BridgeTransferStarterFactory' -import { BridgeTransfer } from '@/token-bridge-sdk/BridgeTransferStarter' +import { + BridgeTransfer, + TransferOverrides +} from '@/token-bridge-sdk/BridgeTransferStarter' import { addDepositToCache } from '../TransactionHistory/helpers' import { convertBridgeSdkToMergedTransaction, @@ -74,6 +80,7 @@ import { useSetInputAmount } from '../../hooks/TransferPanel/useSetInputAmount' import { getSmartContractWalletTeleportTransfersNotSupportedErrorMessage } from './useTransferReadinessUtils' import { useBalances } from '../../hooks/useBalances' import { captureSentryErrorWithExtraData } from '../../util/SentryUtils' +import { useIsBatchTransferSupported } from '../../hooks/TransferPanel/useIsBatchTransferSupported' const networkConnectionWarningToast = () => warningToast( @@ -124,6 +131,7 @@ export function TransferPanel() { isTeleportMode } = useNetworksRelationship(networks) const latestNetworks = useLatest(networks) + const isBatchTransferSupported = useIsBatchTransferSupported() const nativeCurrency = useNativeCurrency({ provider: childChainProvider }) @@ -149,7 +157,7 @@ export function TransferPanel() { // Link the amount state directly to the amount in query params - no need of useState // Both `amount` getter and setter will internally be using `useArbQueryParams` functions - const [{ amount }] = useArbQueryParams() + const [{ amount, amount2 }] = useArbQueryParams() const { setAmount, setAmount2 } = useSetInputAmount() @@ -856,11 +864,34 @@ export function TransferPanel() { ) } + const overrides: TransferOverrides = {} + + const isBatchTransfer = isBatchTransferSupported && Number(amount2) > 0 + + if (isBatchTransfer) { + // when sending additional ETH with ERC-20, we add the additional ETH value as maxSubmissionCost + const gasEstimates = (await bridgeTransferStarter.transferEstimateGas({ + amount: amountBigNumber, + signer + })) as DepositGasEstimates + + if (!gasEstimates.estimatedChildChainSubmissionCost) { + errorToast('Failed to estimate deposit maxSubmissionCost') + throw 'Failed to estimate deposit maxSubmissionCost' + } + + overrides.maxSubmissionCost = utils + .parseEther(amount2) + .add(gasEstimates.estimatedChildChainSubmissionCost) + overrides.excessFeeRefundAddress = destinationAddress + } + // finally, call the transfer function const transfer = await bridgeTransferStarter.transfer({ amount: amountBigNumber, signer, - destinationAddress + destinationAddress, + overrides: Object.keys(overrides).length > 0 ? overrides : undefined }) // transaction submitted callback 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 db050e34bd..237c001d13 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 @@ -36,9 +36,9 @@ import { } from '../../../hooks/useArbQueryParams' import { useMaxAmount } from './useMaxAmount' import { useSetInputAmount } from '../../../hooks/TransferPanel/useSetInputAmount' -import { isExperimentalFeatureEnabled } from '../../../util' import { useDialog } from '../../common/Dialog' import { useTransferReadiness } from '../useTransferReadiness' +import { useIsBatchTransferSupported } from '../../../hooks/TransferPanel/useIsBatchTransferSupported' export function SourceNetworkBox({ customFeeTokenBalances, @@ -63,6 +63,7 @@ export function SourceNetworkBox({ }) const [sourceNetworkSelectionDialogProps, openSourceNetworkSelectionDialog] = useDialog() + const isBatchTransferSupported = useIsBatchTransferSupported() const { errorMessages } = useTransferReadiness() @@ -161,25 +162,18 @@ export function SourceNetworkBox({ onChange={e => setAmount(e.target.value)} /> - {isExperimentalFeatureEnabled('batch') && - // TODO: teleport is disabled for now but it needs to be looked into more to check whether it is or can be supported - !isTeleport({ - sourceChainId: networks.sourceChain.id, - destinationChainId: networks.destinationChain.id - }) && - isDepositMode && - selectedToken && ( - setAmount2(e.target.value)} - tokenButtonOptions={{ - symbol: nativeCurrency.symbol, - disabled: true - }} - /> - )} + {isBatchTransferSupported && ( + setAmount2(e.target.value)} + tokenButtonOptions={{ + symbol: nativeCurrency.symbol, + disabled: true + }} + /> + )} {showUsdcSpecificInfo && (

diff --git a/packages/arb-token-bridge-ui/src/hooks/TransferPanel/useIsBatchTransferSupported.ts b/packages/arb-token-bridge-ui/src/hooks/TransferPanel/useIsBatchTransferSupported.ts new file mode 100644 index 0000000000..13c592d0cb --- /dev/null +++ b/packages/arb-token-bridge-ui/src/hooks/TransferPanel/useIsBatchTransferSupported.ts @@ -0,0 +1,35 @@ +import { useAppState } from '../../state' +import { isExperimentalFeatureEnabled } from '../../util' +import { useNativeCurrency } from '../useNativeCurrency' +import { useNetworks } from '../useNetworks' +import { useNetworksRelationship } from '../useNetworksRelationship' + +export const useIsBatchTransferSupported = () => { + const [networks] = useNetworks() + const { isDepositMode, isTeleportMode, childChainProvider } = + useNetworksRelationship(networks) + const { + app: { selectedToken } + } = useAppState() + const nativeCurrency = useNativeCurrency({ provider: childChainProvider }) + + if (!isExperimentalFeatureEnabled('batch')) { + return false + } + if (!selectedToken) { + return false + } + if (!isDepositMode) { + return false + } + // TODO: teleport is disabled for now but it needs to be looked into more to check whether it is or can be supported + if (isTeleportMode) { + return false + } + // TODO: disable custom native currency for now, check if this works + if (nativeCurrency.isCustom) { + return false + } + + return true +} diff --git a/packages/arb-token-bridge-ui/src/token-bridge-sdk/BridgeTransferStarter.ts b/packages/arb-token-bridge-ui/src/token-bridge-sdk/BridgeTransferStarter.ts index 3efd4df181..7dcbafa9ef 100644 --- a/packages/arb-token-bridge-ui/src/token-bridge-sdk/BridgeTransferStarter.ts +++ b/packages/arb-token-bridge-ui/src/token-bridge-sdk/BridgeTransferStarter.ts @@ -48,10 +48,16 @@ export type TransferEstimateGas = { signer: Signer } +export type TransferOverrides = { + maxSubmissionCost?: BigNumber + excessFeeRefundAddress?: string +} + export type TransferProps = { amount: BigNumber signer: Signer destinationAddress?: string + overrides?: TransferOverrides } export type RequiresNativeCurrencyApprovalProps = { 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 7d90d2b4e9..d11dcbdcf3 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 @@ -273,7 +273,12 @@ export class Erc20DepositStarter extends BridgeTransferStarter { }) } - public async transfer({ amount, signer, destinationAddress }: TransferProps) { + public async transfer({ + amount, + signer, + destinationAddress, + overrides + }: TransferProps) { if (!this.sourceChainErc20Address) { throw Error('Erc20 token address not found') } @@ -292,7 +297,8 @@ export class Erc20DepositStarter extends BridgeTransferStarter { // the gas limit may vary by about 20k due to SSTORE (zero vs nonzero) // the 30% gas limit increase should cover the difference gasLimit: { percentIncrease: BigNumber.from(30) } - } + }, + ...overrides }) const gasLimit = await this.sourceChainProvider.estimateGas(