From 634fe1e1a3828038ea9b789096843c98d16a3fcd Mon Sep 17 00:00:00 2001 From: Doug <4741454+douglance@users.noreply.github.com> Date: Tue, 17 Sep 2024 15:42:01 -0400 Subject: [PATCH 1/6] feat: calculates fast confirmation times --- .../TransferPanel/TransferPanelSummary.tsx | 34 ++++++++++++ .../src/hooks/useTransferDuration.ts | 7 +++ .../src/util/WithdrawalUtils.ts | 52 +++++++++++++++++++ .../src/util/orbitChainsList.ts | 10 +++- 4 files changed, 101 insertions(+), 2 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelSummary.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelSummary.tsx index 0a5200018c..c8ca500406 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelSummary.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanelSummary.tsx @@ -1,4 +1,5 @@ import React, { useMemo } from 'react' +import { InformationCircleIcon } from '@heroicons/react/24/outline' import { twMerge } from 'tailwind-merge' import { formatAmount } from '../../util/NumberUtils' @@ -13,10 +14,12 @@ import { useNetworksRelationship } from '../../hooks/useNetworksRelationship' import { NativeCurrencyPrice, useIsBridgingEth } from './NativeCurrencyPrice' import { useAppState } from '../../state' import { Loader } from '../common/atoms/Loader' +import { Tooltip } from '../common/Tooltip' import { isTokenNativeUSDC } from '../../util/TokenUtils' import { NoteBox } from '../common/NoteBox' import { DISABLED_CHAIN_IDS } from './useTransferReadiness' import { useIsBatchTransferSupported } from '../../hooks/TransferPanel/useIsBatchTransferSupported' +import { getConfirmationTime } from '../../util/WithdrawalUtils' export type TransferPanelSummaryToken = { symbol: string @@ -259,6 +262,37 @@ export function TransferPanelSummary({ token }: TransferPanelSummaryProps) { )} + {(isDestinationChainArbitrumOne || isDestinationChainArbitrumSepolia) && ( +
+ +
+ )} ) } + +function ConfirmationTimeInfo({ chainId }: { chainId: number }) { + const { confirmationTimeInReadableFormat, isDefaultConfirmationTime } = + getConfirmationTime(chainId) + return ( + <> + Confirmation time: + + {confirmationTimeInReadableFormat} + {!isDefaultConfirmationTime && ( + + + + )} + + + ) +} diff --git a/packages/arb-token-bridge-ui/src/hooks/useTransferDuration.ts b/packages/arb-token-bridge-ui/src/hooks/useTransferDuration.ts index 7fb58bf135..68f16757ac 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useTransferDuration.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useTransferDuration.ts @@ -9,6 +9,7 @@ import { getL1BlockTime, isNetwork } from '../util/networks' +import { getConfirmationTime } from '../util/WithdrawalUtils' const DEPOSIT_TIME_MINUTES = { mainnet: 15, @@ -121,6 +122,12 @@ export function getWithdrawalConfirmationDate({ // For new txs createdAt won't be defined yet, we default to the current time in that case const createdAtDate = createdAt ? dayjs(createdAt) : dayjs() + const { confirmationTimeInSeconds, fastWithdrawalActive } = + getConfirmationTime(withdrawalFromChainId) + if (fastWithdrawalActive && confirmationTimeInSeconds) { + return createdAtDate.add(confirmationTimeInSeconds, 'second') + } + const baseChainId = getBaseChainIdByChainId({ chainId: withdrawalFromChainId }) diff --git a/packages/arb-token-bridge-ui/src/util/WithdrawalUtils.ts b/packages/arb-token-bridge-ui/src/util/WithdrawalUtils.ts index 2bf9334593..9a7b50e5db 100644 --- a/packages/arb-token-bridge-ui/src/util/WithdrawalUtils.ts +++ b/packages/arb-token-bridge-ui/src/util/WithdrawalUtils.ts @@ -9,6 +9,7 @@ import { BigNumber } from 'ethers' import { GasEstimates } from '../hooks/arbTokenBridge.types' import { Address } from './AddressUtils' import { captureSentryErrorWithExtraData } from './SentryUtils' +import { getBridgeUiConfigForChain } from './bridgeUiConfig' export async function withdrawInitTxEstimateGas({ amount, @@ -105,3 +106,54 @@ export async function withdrawInitTxEstimateGas({ } } } + +const SECONDS_IN_MINUTE = 60 +const SECONDS_IN_HOUR = 3600 +const SECONDS_IN_DAY = 86400 +const DEFAULT_CONFIRMATION_TIME = 7 * SECONDS_IN_DAY +const DEFAULT_FAST_WITHDRAWAL_TIME = SECONDS_IN_DAY + +function formatDuration(seconds: number): string { + if (seconds < SECONDS_IN_MINUTE) return `${seconds} seconds` + if (seconds < SECONDS_IN_HOUR) + return `${Math.round(seconds / SECONDS_IN_MINUTE)} minutes` + if (seconds < SECONDS_IN_DAY) + return `${Math.round(seconds / SECONDS_IN_HOUR)} hours` + return `${Math.round(seconds / SECONDS_IN_DAY)} days` +} + +/** + * Calculate confirmation time for bridge transactions. + * @param {number} chainId - The ID of the parent chain. + */ +export function getConfirmationTime(chainId: number) { + const { fastWithdrawalTime, fastWithdrawalActive } = + getBridgeUiConfigForChain(chainId) + + const isDefaultConfirmationTime = !fastWithdrawalActive + const isDefaultFastWithdrawal = fastWithdrawalActive && !fastWithdrawalTime + const isCustomFastWithdrawal = fastWithdrawalActive && !!fastWithdrawalTime + + let confirmationTimeInSeconds: number + + if (isDefaultFastWithdrawal) { + confirmationTimeInSeconds = DEFAULT_FAST_WITHDRAWAL_TIME + } else if (isCustomFastWithdrawal) { + confirmationTimeInSeconds = fastWithdrawalTime / 1000 + } else { + confirmationTimeInSeconds = DEFAULT_CONFIRMATION_TIME + } + + const confirmationTimeInReadableFormat = formatDuration( + confirmationTimeInSeconds + ) + + return { + fastWithdrawalActive, + isDefaultConfirmationTime, + isDefaultFastWithdrawal, + isCustomFastWithdrawal, + confirmationTimeInSeconds, + confirmationTimeInReadableFormat + } +} diff --git a/packages/arb-token-bridge-ui/src/util/orbitChainsList.ts b/packages/arb-token-bridge-ui/src/util/orbitChainsList.ts index 136f0d2dc5..5561227539 100644 --- a/packages/arb-token-bridge-ui/src/util/orbitChainsList.ts +++ b/packages/arb-token-bridge-ui/src/util/orbitChainsList.ts @@ -16,6 +16,8 @@ export type BridgeUiConfig = { description?: string } nativeTokenData?: NativeCurrencyBase + fastWithdrawalTime?: number + fastWithdrawalActive?: boolean } export type OrbitChainConfig = ChainWithRpcUrl & { @@ -435,7 +437,9 @@ export const orbitMainnets: { symbol: 'DMT', decimals: 18, logoUrl: '/images/SankoLogo.png' - } + }, + fastWithdrawalTime: 900000, // 15 minutes + fastWithdrawalActive: true } } } @@ -538,7 +542,9 @@ export const orbitTestnets: { [key in number]: OrbitChainConfig } = { symbol: 'BERD', decimals: 18, logoUrl: '' - } + }, + fastWithdrawalTime: 1800000, // 30 minutes + fastWithdrawalActive: true } }, 12325: { From ae9a3e21c1cb4ee6b6d21dbb1b0c4e28cb396446 Mon Sep 17 00:00:00 2001 From: Doug <4741454+douglance@users.noreply.github.com> Date: Mon, 7 Oct 2024 14:14:30 -0400 Subject: [PATCH 2/6] updates to latest designs --- .../WithdrawalConfirmationDialog.tsx | 28 +++++++++++++++++-- packages/arb-token-bridge-ui/src/constants.ts | 2 ++ .../src/util/orbitChainsData.json | 4 ++- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/WithdrawalConfirmationDialog.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/WithdrawalConfirmationDialog.tsx index 5b57633497..193f361f78 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/WithdrawalConfirmationDialog.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/WithdrawalConfirmationDialog.tsx @@ -12,7 +12,10 @@ import { useAppState } from '../../state' import { trackEvent } from '../../util/AnalyticsUtils' import { getNetworkName, isNetwork } from '../../util/networks' import { getFastBridges } from '../../util/fastBridges' -import { CONFIRMATION_PERIOD_ARTICLE_LINK } from '../../constants' +import { + CONFIRMATION_PERIOD_ARTICLE_LINK, + FAST_WITHDRAWAL_DOCS_ARTICLE_LINK +} from '../../constants' import { useNativeCurrency } from '../../hooks/useNativeCurrency' import { useNetworks } from '../../hooks/useNetworks' import { useNetworksRelationship } from '../../hooks/useNetworksRelationship' @@ -63,9 +66,11 @@ export function WithdrawalConfirmationDialog( const [checkbox1Checked, setCheckbox1Checked] = useState(false) const [checkbox2Checked, setCheckbox2Checked] = useState(false) + const [checkbox3Checked, setCheckbox3Checked] = useState(false) const { isArbitrumOne } = isNetwork(childChain.id) - const bothCheckboxesChecked = checkbox1Checked && checkbox2Checked + const bothCheckboxesChecked = + checkbox1Checked && checkbox2Checked && checkbox3Checked const estimatedConfirmationDate = getWithdrawalConfirmationDate({ createdAt: null, @@ -81,6 +86,7 @@ export function WithdrawalConfirmationDialog( setCheckbox1Checked(false) setCheckbox2Checked(false) + setCheckbox3Checked(false) setSelectedIndex(0) } @@ -157,6 +163,24 @@ export function WithdrawalConfirmationDialog( onChange={setCheckbox2Checked} /> + + I understand that ~{confirmationPeriod} is an estimate, + and it's possible the committee fails and it will + default back to the 8 days.{' '} + + Learn more. + + + } + checked={checkbox3Checked} + onChange={setCheckbox3Checked} + /> +
diff --git a/packages/arb-token-bridge-ui/src/constants.ts b/packages/arb-token-bridge-ui/src/constants.ts index a4c016d0a7..54a5b9a3b0 100644 --- a/packages/arb-token-bridge-ui/src/constants.ts +++ b/packages/arb-token-bridge-ui/src/constants.ts @@ -24,6 +24,8 @@ export const ETH_BALANCE_ARTICLE_LINK = `${SUPPORT_LINK_BASE}/hc/en-us/articles/ export const CONFIRMATION_PERIOD_ARTICLE_LINK = `${SUPPORT_LINK_BASE}/hc/en-us/articles/18213843096091` +export const FAST_WITHDRAWAL_DOCS_ARTICLE_LINK = `${DOCS_DOMAIN}/run-arbitrum-node/arbos-releases/arbos31#additional-requirement-for-arbitrum-orbit-chains-who-wish-to-enable-fast-withdrawals` + export const ORBIT_QUICKSTART_LINK = 'https://docs.arbitrum.io/launch-orbit-chain/orbit-quickstart' diff --git a/packages/arb-token-bridge-ui/src/util/orbitChainsData.json b/packages/arb-token-bridge-ui/src/util/orbitChainsData.json index 6f46f75f64..fdf6ecae69 100644 --- a/packages/arb-token-bridge-ui/src/util/orbitChainsData.json +++ b/packages/arb-token-bridge-ui/src/util/orbitChainsData.json @@ -677,7 +677,9 @@ "name": "RARI Testnet", "description": "A testnet chain designed specifically for NFT royalties and creator empowerment.", "logo": "/images/RARIMainnetLogo.svg" - } + }, + "fastWithdrawalTime": 1800000, + "fastWithdrawalActive": true } }, "1183": { From d062eff83d528b687086afbbf7acf4ce9adfe446 Mon Sep 17 00:00:00 2001 From: Doug <4741454+douglance@users.noreply.github.com> Date: Mon, 7 Oct 2024 16:30:58 -0400 Subject: [PATCH 3/6] fix e2e --- .../tests/e2e/specs/withdrawCctp.cy.ts | 6 +++++- .../tests/e2e/specs/withdrawERC20.cy.ts | 12 ++++++++++++ .../tests/e2e/specs/withdrawETH.cy.ts | 6 ++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawCctp.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawCctp.cy.ts index e1e062f1c4..19fbd0bed4 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawCctp.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawCctp.cy.ts @@ -34,7 +34,11 @@ export const confirmAndApproveCctpWithdrawal = () => { }) .should('be.visible') .click() - + cy.findByRole('switch', { + name: /possible the committee fails/i + }) + .should('be.visible') + .click() cy.findByRole('button', { name: /Continue/i }) diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawERC20.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawERC20.cy.ts index 54233ff35b..89fa9988a9 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawERC20.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawERC20.cy.ts @@ -119,6 +119,12 @@ describe('Withdraw ERC20 Token', () => { }) .should('be.visible') .click() + + cy.findByRole('switch', { + name: /possible the committee fails/i + }) + .should('be.visible') + .click() // the Continue withdrawal button should not be disabled now cy.findByRole('button', { name: /Continue/i @@ -233,6 +239,12 @@ describe('Withdraw ERC20 Token', () => { }) .should('be.visible') .click() + + cy.findByRole('switch', { + name: /possible the committee fails/i + }) + .should('be.visible') + .click() // the Continue withdrawal button should not be disabled now cy.findByRole('button', { name: /Continue/i diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawETH.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawETH.cy.ts index 1f45382c16..93d532d461 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawETH.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawETH.cy.ts @@ -72,6 +72,12 @@ describe('Withdraw ETH', () => { }) .should('be.visible') .click() + + cy.findByRole('switch', { + name: /possible the committee fails/i + }) + .should('be.visible') + .click() // the Continue withdrawal button should not be disabled now cy.findByRole('button', { name: /Continue/i From 1e0c7c5f4dd3df408976b6f0aacff6d4fe61d670 Mon Sep 17 00:00:00 2001 From: Doug <4741454+douglance@users.noreply.github.com> Date: Tue, 8 Oct 2024 07:42:51 -0400 Subject: [PATCH 4/6] only display third checkbox for chains with fast confirms --- .../WithdrawalConfirmationDialog.tsx | 48 +++++++++++-------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/WithdrawalConfirmationDialog.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/WithdrawalConfirmationDialog.tsx index 193f361f78..b10e72e60c 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/WithdrawalConfirmationDialog.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/WithdrawalConfirmationDialog.tsx @@ -21,6 +21,7 @@ import { useNetworks } from '../../hooks/useNetworks' import { useNetworksRelationship } from '../../hooks/useNetworksRelationship' import { SecurityGuaranteed, SecurityNotGuaranteed } from './SecurityLabels' import { getWithdrawalConfirmationDate } from '../../hooks/useTransferDuration' +import { getConfirmationTime } from '../../util/WithdrawalUtils' function getCalendarUrl( withdrawalDate: dayjs.Dayjs, @@ -45,6 +46,8 @@ export function WithdrawalConfirmationDialog( const { childChain, childChainProvider, parentChain } = useNetworksRelationship(networks) + const { fastWithdrawalActive } = getConfirmationTime(childChain.id) + const [selectedIndex, setSelectedIndex] = useState(0) const destinationNetworkName = getNetworkName(parentChain.id) @@ -69,8 +72,11 @@ export function WithdrawalConfirmationDialog( const [checkbox3Checked, setCheckbox3Checked] = useState(false) const { isArbitrumOne } = isNetwork(childChain.id) - const bothCheckboxesChecked = - checkbox1Checked && checkbox2Checked && checkbox3Checked + + const allCheckboxesChecked = + checkbox1Checked && + checkbox2Checked && + (fastWithdrawalActive ? checkbox3Checked : true) const estimatedConfirmationDate = getWithdrawalConfirmationDate({ createdAt: null, @@ -97,7 +103,7 @@ export function WithdrawalConfirmationDialog( className="max-w-[700px]" title={`Move funds to ${destinationNetworkName}`} actionButtonProps={{ - disabled: !bothCheckboxesChecked, + disabled: !allCheckboxesChecked, hidden: isFastBridgesTab }} > @@ -163,23 +169,25 @@ export function WithdrawalConfirmationDialog( onChange={setCheckbox2Checked} /> - - I understand that ~{confirmationPeriod} is an estimate, - and it's possible the committee fails and it will - default back to the 8 days.{' '} - - Learn more. - - - } - checked={checkbox3Checked} - onChange={setCheckbox3Checked} - /> + {fastWithdrawalActive && ( + + I understand that ~{confirmationPeriod} is an estimate, + and it's possible the committee fails and it will + default back to the 8 days.{' '} + + Learn more. + + + } + checked={checkbox3Checked} + onChange={setCheckbox3Checked} + /> + )}
From ab840afb2a4ff5a2e33d2730b10e4cb9d163c92f Mon Sep 17 00:00:00 2001 From: Doug <4741454+douglance@users.noreply.github.com> Date: Mon, 14 Oct 2024 14:19:06 -0400 Subject: [PATCH 5/6] remove e2e changes --- .../tests/e2e/specs/withdrawCctp.cy.ts | 6 +----- .../tests/e2e/specs/withdrawERC20.cy.ts | 12 ------------ .../tests/e2e/specs/withdrawETH.cy.ts | 6 ------ 3 files changed, 1 insertion(+), 23 deletions(-) diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawCctp.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawCctp.cy.ts index 19fbd0bed4..e1e062f1c4 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawCctp.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawCctp.cy.ts @@ -34,11 +34,7 @@ export const confirmAndApproveCctpWithdrawal = () => { }) .should('be.visible') .click() - cy.findByRole('switch', { - name: /possible the committee fails/i - }) - .should('be.visible') - .click() + cy.findByRole('button', { name: /Continue/i }) diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawERC20.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawERC20.cy.ts index 89fa9988a9..54233ff35b 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawERC20.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawERC20.cy.ts @@ -119,12 +119,6 @@ describe('Withdraw ERC20 Token', () => { }) .should('be.visible') .click() - - cy.findByRole('switch', { - name: /possible the committee fails/i - }) - .should('be.visible') - .click() // the Continue withdrawal button should not be disabled now cy.findByRole('button', { name: /Continue/i @@ -239,12 +233,6 @@ describe('Withdraw ERC20 Token', () => { }) .should('be.visible') .click() - - cy.findByRole('switch', { - name: /possible the committee fails/i - }) - .should('be.visible') - .click() // the Continue withdrawal button should not be disabled now cy.findByRole('button', { name: /Continue/i diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawETH.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawETH.cy.ts index 93d532d461..1f45382c16 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawETH.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawETH.cy.ts @@ -72,12 +72,6 @@ describe('Withdraw ETH', () => { }) .should('be.visible') .click() - - cy.findByRole('switch', { - name: /possible the committee fails/i - }) - .should('be.visible') - .click() // the Continue withdrawal button should not be disabled now cy.findByRole('button', { name: /Continue/i From 3cefb00d64c30438862d8a47f02041ca029d2fa5 Mon Sep 17 00:00:00 2001 From: Doug <4741454+douglance@users.noreply.github.com> Date: Mon, 14 Oct 2024 14:26:24 -0400 Subject: [PATCH 6/6] adds default testnet time --- packages/arb-token-bridge-ui/src/util/WithdrawalUtils.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/arb-token-bridge-ui/src/util/WithdrawalUtils.ts b/packages/arb-token-bridge-ui/src/util/WithdrawalUtils.ts index 9a7b50e5db..abbf5b9490 100644 --- a/packages/arb-token-bridge-ui/src/util/WithdrawalUtils.ts +++ b/packages/arb-token-bridge-ui/src/util/WithdrawalUtils.ts @@ -10,6 +10,7 @@ import { GasEstimates } from '../hooks/arbTokenBridge.types' import { Address } from './AddressUtils' import { captureSentryErrorWithExtraData } from './SentryUtils' import { getBridgeUiConfigForChain } from './bridgeUiConfig' +import { isNetwork } from './networks' export async function withdrawInitTxEstimateGas({ amount, @@ -112,6 +113,7 @@ const SECONDS_IN_HOUR = 3600 const SECONDS_IN_DAY = 86400 const DEFAULT_CONFIRMATION_TIME = 7 * SECONDS_IN_DAY const DEFAULT_FAST_WITHDRAWAL_TIME = SECONDS_IN_DAY +const DEFAULT_TESTNET_CONFIRMATION_TIME = SECONDS_IN_HOUR function formatDuration(seconds: number): string { if (seconds < SECONDS_IN_MINUTE) return `${seconds} seconds` @@ -129,6 +131,7 @@ function formatDuration(seconds: number): string { export function getConfirmationTime(chainId: number) { const { fastWithdrawalTime, fastWithdrawalActive } = getBridgeUiConfigForChain(chainId) + const isTestnet = isNetwork(chainId).isTestnet const isDefaultConfirmationTime = !fastWithdrawalActive const isDefaultFastWithdrawal = fastWithdrawalActive && !fastWithdrawalTime @@ -141,7 +144,9 @@ export function getConfirmationTime(chainId: number) { } else if (isCustomFastWithdrawal) { confirmationTimeInSeconds = fastWithdrawalTime / 1000 } else { - confirmationTimeInSeconds = DEFAULT_CONFIRMATION_TIME + confirmationTimeInSeconds = isTestnet + ? DEFAULT_TESTNET_CONFIRMATION_TIME + : DEFAULT_CONFIRMATION_TIME } const confirmationTimeInReadableFormat = formatDuration(