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

feat: check destination address for SC wallets #2105

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
@@ -0,0 +1,85 @@
import { useState } from 'react'

import { Dialog, UseDialogProps } from '../common/Dialog'
import { ExternalLink } from '../common/ExternalLink'
import { useNetworks } from '../../hooks/useNetworks'
import { getExplorerUrl, getNetworkName } from '../../util/networks'
import { shortenAddress } from '../../util/CommonUtils'
import { useArbQueryParams } from '../../hooks/useArbQueryParams'
import { Checkbox } from '../common/Checkbox'

export function CustomDestinationAddressConfirmationDialog(
props: UseDialogProps
) {
const [{ destinationAddress = '' }] = useArbQueryParams()

const [networks] = useNetworks()

const networkName = getNetworkName(networks.destinationChain.id)

const [checkboxChecked, setCheckboxChecked] = useState(false)

function closeWithReset(confirmed: boolean) {
props.onClose(confirmed)
setCheckboxChecked(false)
}

return (
<Dialog
{...props}
title="Confirm Destination Address"
className="flex flex-col gap-4"
onClose={closeWithReset}
actionButtonProps={{
disabled: !checkboxChecked
}}
>
<div className="mb-4">
<p className="pb-2">
You are attempting to deposit funds to the same address{' '}
<ExternalLink
className="arb-hover underline"
href={`${getExplorerUrl(
networks.destinationChain.id
)}/address/${destinationAddress}`}
>
{shortenAddress(destinationAddress)}
</ExternalLink>{' '}
on {networkName}.
</p>
<p className="pb-2">
This is an uncommon action because your smart contract wallet is only
deployed on the currently connected chain.
</p>
</div>

<p className="mb-8 rounded-md bg-orange/10 p-4">
<Checkbox
label={
<span className="font-light">
I confirm that I have full control over the entered destination
address on {networkName} and understand that proceeding without
control may result in an{' '}
<span className="font-bold">irrecoverable loss of funds</span>.
</span>
}
checked={checkboxChecked}
onChange={setCheckboxChecked}
/>
</p>

<p>
If not sure, please reach out to us on our{' '}
<ExternalLink
className="arb-hover underline"
href="https://discord.gg/ZpZuw7p"
target="_blank"
rel="noopener noreferrer"
>
<span className="font-medium">support channel</span>
</ExternalLink>{' '}
for assistance.
</p>
</Dialog>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { useArbQueryParams } from '../../hooks/useArbQueryParams'
import { useDialog } from '../common/Dialog'
import { TokenApprovalDialog } from './TokenApprovalDialog'
import { WithdrawalConfirmationDialog } from './WithdrawalConfirmationDialog'
import { CustomDestinationAddressConfirmationDialog } from './CustomDestinationAddressConfirmationDialog'
import { TransferPanelSummary } from './TransferPanelSummary'
import { useAppContextActions } from '../App/AppContext'
import { trackEvent } from '../../util/AnalyticsUtils'
Expand Down Expand Up @@ -173,6 +174,11 @@ export function TransferPanel() {
openUSDCDepositConfirmationDialog
] = useDialog()

const [
customDestinationAddressConfirmationDialogProps,
openCustomDestinationAddressConfirmationDialog
] = useDialog()

const isCustomDestinationTransfer = !!latestDestinationAddress.current

const {
Expand Down Expand Up @@ -218,6 +224,13 @@ export function TransferPanel() {
return isDepositMode && isUnbridgedToken
}, [isDepositMode, selectedToken])

const areSenderAndCustomDestinationAddressesEqual = useMemo(() => {
return (
destinationAddress?.trim()?.toLowerCase() ===
walletAddress?.trim().toLowerCase()
)
}, [destinationAddress, walletAddress])

async function depositToken() {
if (!selectedToken) {
throw new Error('Invalid app state: no selected token')
Expand Down Expand Up @@ -335,6 +348,12 @@ export function TransferPanel() {
setShowSmartContractWalletTooltip(true)
}, 3000)

const confirmCustomDestinationAddressForSCWallets = async () => {
const waitForInput = openCustomDestinationAddressConfirmationDialog()
const [confirmed] = await waitForInput()
return confirmed
}

const transferCctp = async () => {
if (!selectedToken) {
return
Expand Down Expand Up @@ -371,6 +390,17 @@ export function TransferPanel() {
if (!withdrawalConfirmation) return
}

// confirm if the user is certain about the custom destination address, especially if it matches the connected SCW address.
// this ensures that user funds do not end up in the destination chain’s address that matches their source-chain wallet address, which they may not control.
if (
isSmartContractWallet &&
isDepositMode &&
areSenderAndCustomDestinationAddressesEqual
) {
const confirmation = await confirmCustomDestinationAddressForSCWallets()
if (!confirmation) return false
}

const cctpTransferStarter = new CctpTransferStarter({
sourceChainProvider,
destinationChainProvider
Expand Down Expand Up @@ -560,12 +590,11 @@ export function TransferPanel() {
destinationChainErc20Address
})

const { isNativeCurrencyTransfer, isWithdrawal } =
getBridgeTransferProperties({
sourceChainId,
sourceChainErc20Address,
destinationChainId
})
const { isWithdrawal } = getBridgeTransferProperties({
sourceChainId,
sourceChainErc20Address,
destinationChainId
})

if (isWithdrawal && selectedToken && !sourceChainErc20Address) {
/*
Expand All @@ -588,6 +617,17 @@ export function TransferPanel() {

const destinationAddress = latestDestinationAddress.current

// confirm if the user is certain about the custom destination address, especially if it matches the connected SCW address.
// this ensures that user funds do not end up in the destination chain’s address that matches their source-chain wallet address, which they may not control.
if (
isSmartContractWallet &&
isDepositMode &&
areSenderAndCustomDestinationAddressesEqual
) {
const confirmation = await confirmCustomDestinationAddressForSCWallets()
if (!confirmation) return false
}

const isCustomNativeTokenAmount2 =
nativeCurrency.isCustom &&
isBatchTransferSupported &&
Expand Down Expand Up @@ -971,6 +1011,10 @@ export function TransferPanel() {
amount={amount}
/>

<CustomDestinationAddressConfirmationDialog
{...customDestinationAddressConfirmationDialogProps}
/>

<div
className={twMerge(
'mb-7 flex flex-col border-y border-white/30 bg-gray-1 p-4 shadow-[0px_4px_20px_rgba(0,0,0,0.2)]',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import {
import { getAddressFromSigner, percentIncrease } from './utils'
import { depositEthEstimateGas } from '../util/EthDepositUtils'
import { fetchErc20Allowance } from '../util/TokenUtils'
import { isCustomDestinationAddressTx } from '../state/app/utils'
import { DEFAULT_GAS_PRICE_PERCENT_INCREASE } from './Erc20DepositStarter'
import { fetchNativeCurrency } from '../hooks/useNativeCurrency'

Expand Down Expand Up @@ -45,14 +44,9 @@ export class EthDepositStarter extends BridgeTransferStarter {
amount: BigNumber
destinationAddress?: string
}) {
const address = await getAddressFromSigner(signer)

const isDifferentDestinationAddress = isCustomDestinationAddressTx({
sender: address,
destination: destinationAddress
})
const isCustomDestinationAddress = !!destinationAddress

if (!isDifferentDestinationAddress) {
if (!isCustomDestinationAddress) {
return BigNumber.from(0)
}

Expand Down Expand Up @@ -171,12 +165,9 @@ export class EthDepositStarter extends BridgeTransferStarter {
const address = await getAddressFromSigner(signer)
const ethBridger = await this.getBridger()

const isDifferentDestinationAddress = isCustomDestinationAddressTx({
sender: address,
destination: destinationAddress
})
const isCustomDestinationAddress = !!destinationAddress

const depositRequest = isDifferentDestinationAddress
const depositRequest = isCustomDestinationAddress
? await ethBridger.getDepositToRequest({
amount,
from: address,
Expand All @@ -198,7 +189,7 @@ export class EthDepositStarter extends BridgeTransferStarter {
gasLimit: percentIncrease(gasLimit, BigNumber.from(5))
}

const sourceChainTransaction = isDifferentDestinationAddress
const sourceChainTransaction = isCustomDestinationAddress
? await ethBridger.depositTo({
amount,
parentSigner: signer,
Expand Down
Loading