Skip to content

Commit

Permalink
Merge branch 'master' into token-query-params
Browse files Browse the repository at this point in the history
  • Loading branch information
brtkx authored Dec 23, 2024
2 parents 402aed4 + 7e457c3 commit 18fba1a
Show file tree
Hide file tree
Showing 17 changed files with 195 additions and 132 deletions.
3 changes: 3 additions & 0 deletions packages/arb-token-bridge-ui/public/images/LightningIcon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { useMemo } from 'react'
import { InformationCircleIcon } from '@heroicons/react/24/outline'
import { twMerge } from 'tailwind-merge'
import Image from 'next/image'

import { formatAmount } from '../../util/NumberUtils'
import { getNetworkName, isNetwork } from '../../util/networks'
Expand All @@ -20,6 +21,7 @@ import { DISABLED_CHAIN_IDS } from './useTransferReadiness'
import { useSelectedToken } from '../../hooks/useSelectedToken'
import { useIsBatchTransferSupported } from '../../hooks/TransferPanel/useIsBatchTransferSupported'
import { getConfirmationTime } from '../../util/WithdrawalUtils'
import LightningIcon from '@/images/LightningIcon.svg'

export type TransferPanelSummaryToken = {
symbol: string
Expand Down Expand Up @@ -282,21 +284,27 @@ function ConfirmationTimeInfo({ chainId }: { chainId: number }) {
return (
<>
<span className="whitespace-nowrap">Confirmation time:</span>
<span className="flex items-center font-medium">
<span className="flex flex-col items-start font-medium sm:flex-row sm:items-center">
<span className="hidden sm:inline">
{confirmationTimeInReadableFormat}
</span>
<span className="sm:hidden">
{confirmationTimeInReadableFormatShort}
</span>
{fastWithdrawalActive && (
<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>
<div className="flex items-center">
<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="h-3 w-3 sm:ml-1" />
</Tooltip>
<div className="ml-1 flex space-x-0.5 text-[#FFD000]">
<Image src={LightningIcon} alt="Lightning Icon" />
<span className="font-normal">FAST</span>
</div>
</div>
)}
</span>
</>
Expand Down
13 changes: 12 additions & 1 deletion packages/arb-token-bridge-ui/src/util/networks.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { StaticJsonRpcProvider } from '@ethersproject/providers'
import { Provider, StaticJsonRpcProvider } from '@ethersproject/providers'
import {
ArbitrumNetwork,
getChildrenForNetwork,
Expand All @@ -11,6 +11,7 @@ import { loadEnvironmentVariableWithFallback } from './index'
import { getBridgeUiConfigForChain } from './bridgeUiConfig'
import { chainIdToInfuraUrl } from './infura'
import { fetchErc20Data } from './TokenUtils'
import { orbitChains } from './orbitChainsList'

export enum ChainId {
// L1
Expand Down Expand Up @@ -581,6 +582,16 @@ export function getSupportedChainIds({
})
}

export function isAlchemyChain(chainId: number) {
const chain = orbitChains[chainId]

if (typeof chain === 'undefined') {
return false
}

return chain.rpcUrl.toLowerCase().includes('alchemy.com')
}

export function mapCustomChainToNetworkData(chain: ChainWithRpcUrl) {
// custom chain details need to be added to various objects to make it work with the UI
//
Expand Down
7 changes: 4 additions & 3 deletions packages/arb-token-bridge-ui/src/util/orbitChainsData.json
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,8 @@
"name": "SX Network",
"symbol": "SX",
"logoUrl": "/images/sxTokenLogo.png"
}
},
"fastWithdrawalTime": 21600000
}
},
{
Expand Down Expand Up @@ -899,7 +900,7 @@
"childWethGateway": "0x0000000000000000000000000000000000000000"
},
"bridgeUiConfig": {
"color": "#b1fb00",
"color": "#7aad00",
"network": {
"name": "Unite Mainnet",
"logo": "/images/unite-mainnet_Logo.png"
Expand Down Expand Up @@ -1462,7 +1463,7 @@
"childWethGateway": "0x0000000000000000000000000000000000000000"
},
"bridgeUiConfig": {
"color": "#b1fb00",
"color": "#7aad00",
"network": {
"name": "Unite Testnet",
"logo": "/images/unite-testnet_Logo.png"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,80 +1,61 @@
import { constants } from 'ethers'
import { Provider, BlockTag } from '@ethersproject/providers'
import { Erc20Bridger, getArbitrumNetwork } from '@arbitrum/sdk'
import { Erc20Bridger } from '@arbitrum/sdk'

import {
fetchTokenWithdrawalsFromEventLogs,
FetchTokenWithdrawalsFromEventLogsParams
} from './fetchTokenWithdrawalsFromEventLogs'
import { getNonce } from '../AddressUtils'
import { fetchL2Gateways } from '../fetchL2Gateways'
import { backOff, wait } from '../ExponentialBackoffUtils'

async function getGateways(provider: Provider): Promise<{
standardGateway: string
wethGateway: string
customGateway: string
otherGateways: string[]
}> {
const network = await getArbitrumNetwork(provider)

const standardGateway = network.tokenBridge?.childErc20Gateway
const customGateway = network.tokenBridge?.childCustomGateway
const wethGateway = network.tokenBridge?.childWethGateway
const otherGateways = await fetchL2Gateways(provider)

return {
standardGateway: standardGateway ?? constants.AddressZero,
wethGateway: wethGateway ?? constants.AddressZero,
customGateway: customGateway ?? constants.AddressZero,
otherGateways
}
}
import { backOff, wait } from '../ExponentialBackoffUtils'

type TokenWithdrawalQuery = {
type FetchTokenWithdrawalsFromEventLogsQuery = {
params: FetchTokenWithdrawalsFromEventLogsParams
priority: number
}

export type Query = {
sender?: string
receiver?: string
gateways?: string[]
}

export type FetchTokenWithdrawalsFromEventLogsSequentiallyParams = {
sender?: string
receiver?: string
provider: Provider
fromBlock?: BlockTag
toBlock?: BlockTag
/**
* How long to delay in-between queries of different priority.
* How long to delay in-between queries of different priority. Defaults to 0.
*/
delayMs?: number
queries: Query[]
}

export type FetchTokenWithdrawalsFromEventLogsSequentiallyResult = Awaited<
ReturnType<Erc20Bridger['getWithdrawalEvents']>
>

export async function fetchTokenWithdrawalsFromEventLogsSequentially({
sender,
receiver,
provider,
fromBlock = 0,
toBlock = 'latest',
delayMs = 2_000
delayMs = 0,
queries: queriesProp
}: FetchTokenWithdrawalsFromEventLogsSequentiallyParams): Promise<FetchTokenWithdrawalsFromEventLogsSequentiallyResult> {
// keep track of priority; increment as queries are added
let priority = 0

// keep track of queries
const queries: TokenWithdrawalQuery[] = []
const queries: FetchTokenWithdrawalsFromEventLogsQuery[] = []

// helper function to reuse common params
function buildQueryParams({
sender,
receiver,
gateways = []
}: {
sender?: string
receiver?: string
gateways?: string[]
}): TokenWithdrawalQuery['params'] {
}: Query): FetchTokenWithdrawalsFromEventLogsQuery['params'] {
return {
sender,
receiver,
Expand All @@ -86,7 +67,7 @@ export async function fetchTokenWithdrawalsFromEventLogsSequentially({
}

// for sanitizing, adding queries and incrementing priority
function addQuery(params: TokenWithdrawalQuery['params']) {
function addQuery(params: FetchTokenWithdrawalsFromEventLogsQuery['params']) {
const gateways = params.l2GatewayAddresses ?? []
const gatewaysSanitized = gateways.filter(g => g !== constants.AddressZero)

Expand All @@ -100,22 +81,9 @@ export async function fetchTokenWithdrawalsFromEventLogsSequentially({
})
}

const gateways = await getGateways(provider)
const senderNonce = await backOff(() => getNonce(sender, { provider }))

// sender queries; only add if nonce > 0
if (senderNonce > 0) {
addQuery(buildQueryParams({ sender, gateways: [gateways.standardGateway] }))
addQuery(buildQueryParams({ sender, gateways: [gateways.wethGateway] }))
addQuery(buildQueryParams({ sender, gateways: [gateways.customGateway] }))
addQuery(buildQueryParams({ sender, gateways: gateways.otherGateways }))
}

// receiver queries
addQuery(buildQueryParams({ receiver, gateways: [gateways.standardGateway] }))
addQuery(buildQueryParams({ receiver, gateways: [gateways.wethGateway] }))
addQuery(buildQueryParams({ receiver, gateways: [gateways.customGateway] }))
addQuery(buildQueryParams({ receiver, gateways: gateways.otherGateways }))
queriesProp.forEach(query => {
addQuery(buildQueryParams(query))
})

// for iterating through all priorities in the while loop below
let currentPriority = 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,37 @@ import { fetchLatestSubgraphBlockNumber } from '../SubgraphUtils'
import { Withdrawal } from '../../hooks/useTransactionHistory'
import { attachTimestampToTokenWithdrawal } from './helpers'
import { WithdrawalInitiated } from '../../hooks/arbTokenBridge.types'
import { fetchTokenWithdrawalsFromEventLogsSequentially } from './fetchTokenWithdrawalsFromEventLogsSequentially'
import {
Query,
fetchTokenWithdrawalsFromEventLogsSequentially
} from './fetchTokenWithdrawalsFromEventLogsSequentially'
import { backOff, wait } from '../ExponentialBackoffUtils'
import { isAlchemyChain } from '../networks'
import { getArbitrumNetwork } from '@arbitrum/sdk'
import { fetchL2Gateways } from '../fetchL2Gateways'
import { constants } from 'ethers'
import { getNonce } from '../AddressUtils'

async function getGateways(provider: Provider): Promise<{
standardGateway: string
wethGateway: string
customGateway: string
otherGateways: string[]
}> {
const network = await getArbitrumNetwork(provider)

const standardGateway = network.tokenBridge?.childErc20Gateway
const customGateway = network.tokenBridge?.childCustomGateway
const wethGateway = network.tokenBridge?.childWethGateway
const otherGateways = await fetchL2Gateways(provider)

return {
standardGateway: standardGateway ?? constants.AddressZero,
wethGateway: wethGateway ?? constants.AddressZero,
customGateway: customGateway ?? constants.AddressZero,
otherGateways
}
}

export type FetchWithdrawalsParams = {
sender?: string
Expand Down Expand Up @@ -84,6 +113,47 @@ export async function fetchWithdrawals({
console.log('Error fetching withdrawals from subgraph', error)
}

const gateways = await getGateways(l2Provider)
const senderNonce = await getNonce(sender, { provider: l2Provider })

const queries: Query[] = []

// alchemy as a raas has a global rate limit across their chains, so we have to fetch sequentially and wait in-between requests to work around this
const isAlchemy = isAlchemyChain(l2ChainID)
const delayMs = isAlchemy ? 2_000 : 0

const allGateways = [
gateways.standardGateway,
gateways.wethGateway,
gateways.customGateway,
...gateways.otherGateways
]

// sender queries; only add if nonce > 0
if (senderNonce > 0) {
if (isAlchemy) {
// for alchemy, fetch sequentially
queries.push({ sender, gateways: [gateways.standardGateway] })
queries.push({ sender, gateways: [gateways.wethGateway] })
queries.push({ sender, gateways: [gateways.customGateway] })
queries.push({ sender, gateways: gateways.otherGateways })
} else {
// for other chains, fetch in parallel
queries.push({ sender, gateways: allGateways })
}
}

if (isAlchemy) {
// for alchemy, fetch sequentially
queries.push({ receiver, gateways: [gateways.standardGateway] })
queries.push({ receiver, gateways: [gateways.wethGateway] })
queries.push({ receiver, gateways: [gateways.customGateway] })
queries.push({ receiver, gateways: gateways.otherGateways })
} else {
// for other chains, fetch in parallel
queries.push({ receiver, gateways: allGateways })
}

const ethWithdrawalsFromEventLogs = await backOff(() =>
fetchETHWithdrawalsFromEventLogs({
receiver,
Expand All @@ -95,15 +165,16 @@ export async function fetchWithdrawals({
})
)

await wait(2_000)
await wait(delayMs)

const tokenWithdrawalsFromEventLogs =
await fetchTokenWithdrawalsFromEventLogsSequentially({
sender,
receiver,
fromBlock: toBlock + 1,
toBlock: 'latest',
provider: l2Provider
provider: l2Provider,
queries
})

const mappedEthWithdrawalsFromEventLogs: Withdrawal[] =
Expand Down
3 changes: 2 additions & 1 deletion packages/arb-token-bridge-ui/synpress.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,8 @@ export default defineConfig({
},
baseUrl: 'http://localhost:3000',
specPattern: tests,
supportFile: 'tests/support/index.ts'
supportFile: 'tests/support/index.ts',
defaultCommandTimeout: 20_000
}
})

Expand Down
4 changes: 2 additions & 2 deletions packages/arb-token-bridge-ui/tests/e2e/cypress.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
findGasFeeSummary,
findGasFeeForChain,
findMoveFundsButton,
startTransfer,
clickMoveFundsButton,
findSelectTokenButton,
openTransactionDetails,
closeTransactionDetails,
Expand Down Expand Up @@ -66,7 +66,7 @@ declare global {
findGasFeeForChain: typeof findGasFeeForChain
findGasFeeSummary: typeof findGasFeeSummary
findMoveFundsButton: typeof findMoveFundsButton
startTransfer: typeof startTransfer
clickMoveFundsButton: typeof clickMoveFundsButton
findSelectTokenButton: typeof findSelectTokenButton
openTransactionDetails: typeof openTransactionDetails
closeTransactionDetails: typeof closeTransactionDetails
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ describe('Approve token for deposit', () => {
timeout: 50000,
interval: 500
})
cy.findMoveFundsButton().click()
cy.clickMoveFundsButton({ shouldConfirmInMetamask: false })
cy.findByText(/pay a one-time approval fee/).click()
cy.findByRole('button', {
name: /Pay approval fee of/
Expand All @@ -50,7 +50,7 @@ describe('Approve token for deposit', () => {
* If confirm spending fails, test is still considered to be passing by Cypress
* We add another check to make sure the test fails if needed
*/
cy.wait(10_000)
cy.wait(25_000)
cy.rejectMetamaskTransaction()
})
})
Expand Down
Loading

0 comments on commit 18fba1a

Please sign in to comment.