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: calculates fast confirmation times #1901

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Original file line number Diff line number Diff line change
@@ -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'
Expand All @@ -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
Expand Down Expand Up @@ -259,6 +262,37 @@ export function TransferPanelSummary({ token }: TransferPanelSummaryProps) {
)}
</span>
</div>
{(isDestinationChainArbitrumOne || isDestinationChainArbitrumSepolia) && (
<div
className={twMerge(
'grid grid-cols-[260px_auto] items-center text-sm font-light'
)}
>
<ConfirmationTimeInfo chainId={networks.sourceChain.id} />
</div>
)}
</TransferPanelSummaryContainer>
)
}

function ConfirmationTimeInfo({ chainId }: { chainId: number }) {
const { confirmationTimeInReadableFormat, isDefaultConfirmationTime } =
getConfirmationTime(chainId)
return (
<>
<span>Confirmation time:</span>
<span className="flex items-center font-medium">
{confirmationTimeInReadableFormat}
{!isDefaultConfirmationTime && (
<Tooltip
content={
'Fast Withdrawals relies on a committee of validators. In the event of a committee outage, your withdrawal falls back to the 7 day challenge period secured by Arbitrum Fraud Proofs.'
}
>
<InformationCircleIcon className="ml-1 h-3 w-3" />
</Tooltip>
)}
</span>
</>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,16 @@ 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'
import { SecurityGuaranteed, SecurityNotGuaranteed } from './SecurityLabels'
import { getWithdrawalConfirmationDate } from '../../hooks/useTransferDuration'
import { getConfirmationTime } from '../../util/WithdrawalUtils'

function getCalendarUrl(
withdrawalDate: dayjs.Dayjs,
Expand All @@ -42,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)
Expand All @@ -63,9 +69,14 @@ 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 allCheckboxesChecked =
checkbox1Checked &&
checkbox2Checked &&
(fastWithdrawalActive ? checkbox3Checked : true)

const estimatedConfirmationDate = getWithdrawalConfirmationDate({
createdAt: null,
Expand All @@ -81,6 +92,7 @@ export function WithdrawalConfirmationDialog(

setCheckbox1Checked(false)
setCheckbox2Checked(false)
setCheckbox3Checked(false)
setSelectedIndex(0)
}

Expand All @@ -91,7 +103,7 @@ export function WithdrawalConfirmationDialog(
className="max-w-[700px]"
title={`Move funds to ${destinationNetworkName}`}
actionButtonProps={{
disabled: !bothCheckboxesChecked,
disabled: !allCheckboxesChecked,
hidden: isFastBridgesTab
}}
>
Expand Down Expand Up @@ -157,6 +169,26 @@ export function WithdrawalConfirmationDialog(
onChange={setCheckbox2Checked}
/>

{fastWithdrawalActive && (
<Checkbox
label={
<span className="font-light">
I understand that ~{confirmationPeriod} is an estimate,
and it&apos;s possible the committee fails and it will
default back to the 8 days.{' '}
<ExternalLink
href={FAST_WITHDRAWAL_DOCS_ARTICLE_LINK}
className="underline"
>
Learn more.
</ExternalLink>
</span>
}
checked={checkbox3Checked}
onChange={setCheckbox3Checked}
/>
)}

<div className="flex">
<SecurityGuaranteed />
</div>
Expand Down
2 changes: 2 additions & 0 deletions packages/arb-token-bridge-ui/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,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'

Expand Down
7 changes: 7 additions & 0 deletions packages/arb-token-bridge-ui/src/hooks/useTransferDuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
getL1BlockTime,
isNetwork
} from '../util/networks'
import { getConfirmationTime } from '../util/WithdrawalUtils'

const DEPOSIT_TIME_MINUTES = {
mainnet: 15,
Expand Down Expand Up @@ -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
})
Expand Down
57 changes: 57 additions & 0 deletions packages/arb-token-bridge-ui/src/util/WithdrawalUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import { BigNumber } from 'ethers'
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,
Expand Down Expand Up @@ -105,3 +107,58 @@ 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
const DEFAULT_TESTNET_CONFIRMATION_TIME = SECONDS_IN_HOUR

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`
}
fionnachan marked this conversation as resolved.
Show resolved Hide resolved

/**
* 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 isTestnet = isNetwork(chainId).isTestnet

const isDefaultConfirmationTime = !fastWithdrawalActive
const isDefaultFastWithdrawal = fastWithdrawalActive && !fastWithdrawalTime
const isCustomFastWithdrawal = fastWithdrawalActive && !!fastWithdrawalTime

let confirmationTimeInSeconds: number

if (isDefaultFastWithdrawal) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need different values for testnets and mainnets, so there should be some handling here

confirmationTimeInSeconds = DEFAULT_FAST_WITHDRAWAL_TIME
} else if (isCustomFastWithdrawal) {
confirmationTimeInSeconds = fastWithdrawalTime / 1000
} else {
confirmationTimeInSeconds = isTestnet
? DEFAULT_TESTNET_CONFIRMATION_TIME
: DEFAULT_CONFIRMATION_TIME
}

const confirmationTimeInReadableFormat = formatDuration(
confirmationTimeInSeconds
)

return {
fastWithdrawalActive,
isDefaultConfirmationTime,
isDefaultFastWithdrawal,
isCustomFastWithdrawal,
confirmationTimeInSeconds,
confirmationTimeInReadableFormat
}
}
4 changes: 3 additions & 1 deletion packages/arb-token-bridge-ui/src/util/orbitChainsData.json
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
},
{
Expand Down
2 changes: 2 additions & 0 deletions packages/arb-token-bridge-ui/src/util/orbitChainsList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ export type BridgeUiConfig = {
description?: string
}
nativeTokenData?: NativeCurrencyBase
fastWithdrawalTime?: number
fastWithdrawalActive?: boolean
}

export type OrbitChainConfig = ChainWithRpcUrl & {
Expand Down