Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: abstract hooks from transfer panel #1838

Closed
wants to merge 26 commits into from
Closed
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
2334562
refactor: abstract hooks from transfer panel
fionnachan Aug 13, 2024
e1c2134
remove unneeded hook and move code
fionnachan Aug 13, 2024
e813c92
Merge remote-tracking branch 'origin/master' into abstract-hooks-from…
fionnachan Aug 15, 2024
5f67b0e
abstract same tx check logic
fionnachan Aug 15, 2024
0cc7c14
Merge remote-tracking branch 'origin/master' into abstract-hooks-from…
fionnachan Aug 16, 2024
09ced78
Merge branch 'master' into abstract-hooks-from-txpanel
fionnachan Aug 16, 2024
eccb808
Merge remote-tracking branch 'origin/master' into abstract-hooks-from…
fionnachan Aug 16, 2024
6a23084
clean up get destination address error logic
fionnachan Aug 16, 2024
4aa69ca
clean up
fionnachan Aug 19, 2024
b081e3c
Merge remote-tracking branch 'origin/master' into abstract-hooks-from…
fionnachan Aug 19, 2024
c0ff0ef
Merge remote-tracking branch 'origin/master' into abstract-hooks-from…
fionnachan Aug 20, 2024
e94954b
update
fionnachan Aug 20, 2024
4a5a5c8
Merge branch 'master' into abstract-hooks-from-txpanel
fionnachan Aug 20, 2024
a050cdc
Merge branch 'master' into abstract-hooks-from-txpanel
fionnachan Aug 28, 2024
8f82293
Merge branch 'master' into abstract-hooks-from-txpanel
fionnachan Aug 28, 2024
5ef62e1
Merge branch 'master' into abstract-hooks-from-txpanel
fionnachan Aug 30, 2024
2e28673
Merge remote-tracking branch 'origin/master' into abstract-hooks-from…
fionnachan Sep 3, 2024
48bd7c4
Merge branch 'master' into abstract-hooks-from-txpanel
fionnachan Sep 9, 2024
940f84c
Merge remote-tracking branch 'origin/master' into abstract-hooks-from…
fionnachan Sep 12, 2024
a7b0330
Merge remote-tracking branch 'origin/master' into abstract-hooks-from…
fionnachan Sep 18, 2024
7ec77bb
conflict
fionnachan Sep 18, 2024
cc8ca93
Merge remote-tracking branch 'origin/master' into abstract-hooks-from…
fionnachan Sep 19, 2024
7ecd609
Merge remote-tracking branch 'origin/master' into abstract-hooks-from…
fionnachan Sep 20, 2024
12be011
Merge remote-tracking branch 'origin/master' into abstract-hooks-from…
fionnachan Sep 20, 2024
0dfd4ef
update
fionnachan Sep 20, 2024
a6c2d1e
Merge remote-tracking branch 'origin/master' into abstract-hooks-from…
fionnachan Sep 20, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import dayjs from 'dayjs'
import { useState, useMemo } from 'react'
import { useState, useMemo, useCallback } from 'react'
import Tippy from '@tippyjs/react'
import { constants, utils } from 'ethers'
import { useLatest } from 'react-use'
Expand All @@ -8,7 +8,7 @@ import { TransactionResponse } from '@ethersproject/providers'
import { twMerge } from 'tailwind-merge'

import { useAppState } from '../../state'
import { getNetworkName, isNetwork } from '../../util/networks'
import { getNetworkName } from '../../util/networks'
import { Button } from '../common/Button'
import {
TokenDepositCheckDialog,
Expand All @@ -25,8 +25,6 @@ import { trackEvent } from '../../util/AnalyticsUtils'
import { TransferPanelMain } from './TransferPanelMain'
import { isGatewayRegistered } from '../../util/TokenUtils'
import { useSwitchNetworkWithConfig } from '../../hooks/useSwitchNetworkWithConfig'
import { useIsConnectedToArbitrum } from '../../hooks/useIsConnectedToArbitrum'
import { useIsConnectedToOrbitChain } from '../../hooks/useIsConnectedToOrbitChain'
import { errorToast, warningToast } from '../common/atoms/Toast'
import { useAccountType } from '../../hooks/useAccountType'
import { DOCS_DOMAIN } from '../../constants'
Expand Down Expand Up @@ -72,10 +70,11 @@ import { useSetInputAmount } from '../../hooks/TransferPanel/useSetInputAmount'
import { getSmartContractWalletTeleportTransfersNotSupportedErrorMessage } from './useTransferReadinessUtils'
import { useBalances } from '../../hooks/useBalances'
import { captureSentryErrorWithExtraData } from '../../util/SentryUtils'
import { useIsCctpTransfer } from './hooks/useIsCctpTransfer'
import { useDestinationAddressError } from './hooks/useDestinationAddressError'
import { useIsBatchTransferSupported } from '../../hooks/TransferPanel/useIsBatchTransferSupported'
import { normalizeTimestamp } from '../../state/app/utils'
import { useDestinationAddressError } from './hooks/useDestinationAddressError'
import { useIsCctpTransfer } from './hooks/useIsCctpTransfer'
import { getDestinationAddressError } from './hooks/useDestinationAddressError'

const signerUndefinedError = 'Signer is undefined'

Expand All @@ -94,8 +93,7 @@ export function TransferPanel() {
app: {
connectionState,
selectedToken,
arbTokenBridgeLoaded,
arbTokenBridge: { eth, token },
arbTokenBridge: { token },
warningTokens
}
} = useAppState()
Expand Down Expand Up @@ -132,11 +130,6 @@ export function TransferPanel() {

const isCctpTransfer = useIsCctpTransfer()

const latestEth = useLatest(eth)

const isConnectedToArbitrum = useLatest(useIsConnectedToArbitrum())
const isConnectedToOrbitChain = useLatest(useIsConnectedToOrbitChain())

// 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, amount2 }] = useArbQueryParams()
Expand All @@ -161,6 +154,8 @@ export function TransferPanel() {

const { destinationAddress } = useDestinationAddressStore()

const { destinationAddressError } = useDestinationAddressError()

const {
updateEthParentBalance,
updateErc20ParentBalances,
Expand All @@ -171,8 +166,6 @@ export function TransferPanel() {

const { transferReady } = useTransferReadiness()

const { destinationAddressError } = useDestinationAddressError()

const { color: destinationChainUIcolor } = getBridgeUiConfigForChain(
networks.destinationChain.id
)
Expand All @@ -194,6 +187,27 @@ export function TransferPanel() {
connectionState
})

const isWarningToken = useMemo(() => {
if (!selectedToken) {
return false
}
return !!warningTokens[selectedToken.address.toLowerCase()]
}, [selectedToken, warningTokens])

const showWarningTokenToast = useCallback(() => {
const warningToken =
selectedToken && warningTokens[selectedToken.address.toLowerCase()]

if (!warningToken) {
return
}

const description = getWarningTokenDescription(warningToken.type)
warningToast(
`${warningToken.address} is ${description}; it will likely have unusual behavior when deployed as as standard token to Arbitrum. It is not recommended that you deploy it. (See ${DOCS_DOMAIN}/for-devs/concepts/token-bridge/token-bridge-erc20 for more info.)`
)
}, [selectedToken, warningTokens])

const isBridgingANewStandardToken = useMemo(() => {
const isUnbridgedToken =
selectedToken !== null && typeof selectedToken.l2Address === 'undefined'
Expand Down Expand Up @@ -334,39 +348,24 @@ export function TransferPanel() {
if (!selectedToken) {
return
}
if (!walletAddress) {
if (!signer) {
throw Error(signerUndefinedError)
}

if (!isTransferAllowed) {
return
}
if (!signer) {
throw signerUndefinedError

if (isWarningToken) {
showWarningTokenToast()
return
}

setTransferring(true)
const childChainName = getNetworkName(childChain.id)
const isConnectedToTheWrongChain =
chainId !== latestNetworks.current.sourceChain.id
await connectedToWrongChainHandler()

if (isConnectedToTheWrongChain) {
trackEvent('Switch Network and Transfer', {
type: isDepositMode ? 'Deposit' : 'Withdrawal',
tokenSymbol: 'USDC',
assetType: 'ERC-20',
accountType: isSmartContractWallet ? 'Smart Contract' : 'EOA',
network: childChainName,
amount: Number(amount),
version: 2
})
setTransferring(true)

const switchTargetChainId = latestNetworks.current.sourceChain.id
try {
await switchNetworkAsync?.(switchTargetChainId)
} catch (error) {
captureSentryErrorWithExtraData({
error,
originFunction: 'transferCctp switchNetworkAsync'
})
}
}
const childChainName = getNetworkName(childChain.id)

try {
const { sourceChainProvider, destinationChainProvider, sourceChain } =
Expand Down Expand Up @@ -541,26 +540,76 @@ export function TransferPanel() {
return false
}
return true
}, [destinationAddressError, isConnected, walletAddress])
}, [isConnected, walletAddress, destinationAddressError])

const connectedToWrongChainHandler = useCallback(async () => {
const childChainName = getNetworkName(childChain.id)
const isConnectedToTheWrongChain =
chainId !== latestNetworks.current.sourceChain.id

if (!isConnectedToTheWrongChain) {
return
}
const isBatchTransfer = isBatchTransferSupported && Number(amount2) > 0
trackEvent('Switch Network and Transfer', {
type: isTeleportMode
? 'Teleport'
: isDepositMode
? 'Deposit'
: 'Withdrawal',
tokenSymbol: isCctp ? 'USDC' : selectedToken?.symbol,
assetType: selectedToken ? 'ERC-20' : 'ETH',
accountType: isSmartContractWallet ? 'Smart Contract' : 'EOA',
network: childChainName,
amount: Number(amount),
amount2: isBatchTransfer ? Number(amount2) : undefined,
version: 2
})
const switchTargetChainId = latestNetworks.current.sourceChain.id

const transfer = async () => {
try {
setTransferring(true)
if (chainId !== networks.sourceChain.id) {
await switchNetworkAsync?.(networks.sourceChain.id)
}
} finally {
setTransferring(false)
await switchNetworkAsync?.(switchTargetChainId)
} catch (error) {
captureSentryErrorWithExtraData({
error,
originFunction: `transfer${isCctp ? 'Cctp' : ''} switchNetworkAsync`
})
}
}, [
amount,
amount2,
chainId,
childChain.id,
isBatchTransferSupported,
isCctp,
isDepositMode,
isSmartContractWallet,
isTeleportMode,
latestNetworks,
selectedToken,
switchNetworkAsync
])

const transfer = async () => {
if (!signer) {
throw signerUndefinedError
}

if (!isTransferAllowed) {
return
}

if (!signer) {
throw signerUndefinedError
if (isWarningToken) {
showWarningTokenToast()
return
}

await connectedToWrongChainHandler()

setTransferring(true)

const childChainName = getNetworkName(childChain.id)

// SC ETH transfers aren't enabled yet. Safety check, shouldn't be able to get here.
if (isSmartContractWallet && !selectedToken) {
console.error("ETH transfers aren't enabled for smart contract wallets.")
Expand All @@ -575,81 +624,11 @@ export function TransferPanel() {
return
}

const childChainName = getNetworkName(childChain.id)

setTransferring(true)

const isBatchTransfer = isBatchTransferSupported && Number(amount2) > 0

try {
const warningToken =
selectedToken && warningTokens[selectedToken.address.toLowerCase()]
if (warningToken) {
const description = getWarningTokenDescription(warningToken.type)
warningToast(
`${selectedToken?.address} is ${description}; it will likely have unusual behavior when deployed as as standard token to Arbitrum. It is not recommended that you deploy it. (See ${DOCS_DOMAIN}/for-devs/concepts/token-bridge/token-bridge-erc20 for more info.)`
)
return
}

const depositRequiresChainSwitch = () => {
const isParentChainEthereum = isNetwork(
parentChain.id
).isEthereumMainnetOrTestnet

return (
isDepositMode &&
((isParentChainEthereum && isConnectedToArbitrum.current) ||
isConnectedToOrbitChain.current)
)
}

const withdrawalRequiresChainSwitch = () => {
const isConnectedToEthereum =
!isConnectedToArbitrum.current && !isConnectedToOrbitChain.current

const { isOrbitChain: isSourceChainOrbit } = isNetwork(childChain.id)

return (
!isDepositMode &&
(isConnectedToEthereum ||
(isConnectedToArbitrum.current && isSourceChainOrbit))
)
}

if (depositRequiresChainSwitch() || withdrawalRequiresChainSwitch()) {
trackEvent('Switch Network and Transfer', {
type: isTeleportMode
? 'Teleport'
: isDepositMode
? 'Deposit'
: 'Withdrawal',
tokenSymbol: selectedToken?.symbol,
assetType: selectedToken ? 'ERC-20' : 'ETH',
accountType: isSmartContractWallet ? 'Smart Contract' : 'EOA',
network: childChainName,
amount: Number(amount),
amount2: isBatchTransfer ? Number(amount2) : undefined,
version: 2
})

const switchTargetChainId = latestNetworks.current.sourceChain.id

await switchNetworkAsync?.(switchTargetChainId)

// keep checking till we know the connected chain-pair are correct for transfer
while (
depositRequiresChainSwitch() ||
withdrawalRequiresChainSwitch() ||
!latestEth.current ||
!arbTokenBridgeLoaded
) {
await new Promise(r => setTimeout(r, 100))
}

await new Promise(r => setTimeout(r, 3000))
}

const sourceChainId = latestNetworks.current.sourceChain.id
const destinationChainId = latestNetworks.current.destinationChain.id

Expand Down Expand Up @@ -697,11 +676,6 @@ export function TransferPanel() {
return
}

if (destinationAddressError) {
console.error(destinationAddressError)
return
}

const isCustomNativeTokenAmount2 =
nativeCurrency.isCustom &&
isBatchTransferSupported &&
Expand Down Expand Up @@ -745,17 +719,6 @@ export function TransferPanel() {
// is selected token deployed on parent-chain?
if (!tokenAddress) Error('Token not deployed on source chain.')

// warning token handling
const warningToken =
selectedToken && warningTokens[selectedToken.address.toLowerCase()]
if (warningToken) {
const description = getWarningTokenDescription(warningToken.type)
warningToast(
`${selectedToken?.address} is ${description}; it will likely have unusual behavior when deployed as as standard token to Arbitrum. It is not recommended that you deploy it. (See ${DOCS_DOMAIN}/for-devs/concepts/token-bridge/token-bridge-erc20 for more info.)`
)
return
}

// token suspension handling
const isTokenSuspended = !(await isGatewayRegistered({
erc20ParentChainAddress: selectedToken.address,
Expand Down
Loading