Skip to content

Commit

Permalink
perf: load withdrawal tx history by priority (#2128)
Browse files Browse the repository at this point in the history
  • Loading branch information
spsjvc authored Dec 12, 2024
1 parent 3c4bf05 commit fe58b57
Show file tree
Hide file tree
Showing 5 changed files with 182 additions and 48 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function wait(ms: number) {
return new Promise(resolve => setTimeout(resolve, ms))
}
15 changes: 1 addition & 14 deletions packages/arb-token-bridge-ui/src/util/fetchL2Gateways.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { constants } from 'ethers'
import { Provider } from '@ethersproject/providers'
import { getArbitrumNetwork } from '@arbitrum/sdk'

Expand All @@ -19,26 +18,14 @@ import {
export async function fetchL2Gateways(l2Provider: Provider) {
const l2Network = await getArbitrumNetwork(l2Provider)

if (!l2Network.tokenBridge) {
return []
}

/* configure gateway addresses for fetching withdrawals */
const { childErc20Gateway, childCustomGateway, childWethGateway } =
l2Network.tokenBridge

const gatewaysToUse = [childErc20Gateway, childCustomGateway]
const gatewaysToUse = []
const l2ArbReverseGateway = l2ArbReverseGatewayAddresses[l2Network.chainId]
const l2DaiGateway = l2DaiGatewayAddresses[l2Network.chainId]
const l2wstETHGateway = l2wstETHGatewayAddresses[l2Network.chainId]
const l2LptGateway = l2LptGatewayAddresses[l2Network.chainId]
const l2MoonGateway = l2MoonGatewayAddresses[l2Network.chainId]
const l2UsdcGateway = l2UsdcGatewayAddresses[l2Network.chainId]

// custom gas token chains will have weth gateway set to address zero
if (childWethGateway !== constants.AddressZero) {
gatewaysToUse.push(childWethGateway)
}
if (l2ArbReverseGateway) {
gatewaysToUse.push(l2ArbReverseGateway)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ import { Provider, BlockTag } from '@ethersproject/providers'
import { Erc20Bridger, EventArgs } from '@arbitrum/sdk'
import { WithdrawalInitiatedEvent } from '@arbitrum/sdk/dist/lib/abi/L2ArbitrumGateway'

import { getNonce } from '../AddressUtils'

function dedupeEvents(
events: (EventArgs<WithdrawalInitiatedEvent> & {
txHash: string
Expand All @@ -12,6 +10,15 @@ function dedupeEvents(
return [...new Map(events.map(item => [item.txHash, item])).values()]
}

export type FetchTokenWithdrawalsFromEventLogsParams = {
sender?: string
receiver?: string
fromBlock: BlockTag
toBlock: BlockTag
l2Provider: Provider
l2GatewayAddresses?: string[]
}

/**
* Fetches initiated token withdrawals from event logs in range of [fromBlock, toBlock].
*
Expand All @@ -30,22 +37,13 @@ export async function fetchTokenWithdrawalsFromEventLogs({
toBlock,
l2Provider,
l2GatewayAddresses = []
}: {
sender?: string
receiver?: string
fromBlock: BlockTag
toBlock: BlockTag
l2Provider: Provider
l2GatewayAddresses?: string[]
}) {
}: FetchTokenWithdrawalsFromEventLogsParams) {
const erc20Bridger = await Erc20Bridger.fromProvider(l2Provider)
const promises: ReturnType<Erc20Bridger['getWithdrawalEvents']>[] = []

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

l2GatewayAddresses.forEach(gatewayAddress => {
// funds sent by this address
if (sender && senderNonce > 0) {
if (sender) {
promises.push(
erc20Bridger.getWithdrawalEvents(
l2Provider,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import { constants } from 'ethers'
import { Provider, BlockTag } from '@ethersproject/providers'
import { Erc20Bridger, getArbitrumNetwork } from '@arbitrum/sdk'

import {
fetchTokenWithdrawalsFromEventLogs,
FetchTokenWithdrawalsFromEventLogsParams
} from './fetchTokenWithdrawalsFromEventLogs'
import { getNonce } from '../AddressUtils'
import { fetchL2Gateways } from '../fetchL2Gateways'
import { 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
}
}

type TokenWithdrawalQuery = {
params: FetchTokenWithdrawalsFromEventLogsParams
priority: number
}

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

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

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

// helper function to reuse common params
function buildQueryParams({
sender,
receiver,
gateways = []
}: {
sender?: string
receiver?: string
gateways?: string[]
}): TokenWithdrawalQuery['params'] {
return {
sender,
receiver,
fromBlock,
toBlock,
l2Provider: provider,
l2GatewayAddresses: gateways
}
}

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

if (gatewaysSanitized.length === 0) {
return
}

queries.push({
params: { ...params, l2GatewayAddresses: gatewaysSanitized },
priority: ++priority
})
}

const gateways = await getGateways(provider)
const senderNonce = await 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 }))

// for iterating through all priorities in the while loop below
let currentPriority = 1

// final result
const result: FetchTokenWithdrawalsFromEventLogsSequentiallyResult = []

while (currentPriority <= priority) {
const currentPriorityQueries = queries.filter(
query => query.priority === currentPriority
)

const currentPriorityResults = await Promise.all(
currentPriorityQueries.map(query =>
fetchTokenWithdrawalsFromEventLogs(query.params)
)
)

currentPriorityResults.forEach(r => {
result.push(...r)
})

await wait(delayMs)

currentPriority++
}

return result
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ import {
fetchWithdrawalsFromSubgraph
} from './fetchWithdrawalsFromSubgraph'
import { fetchLatestSubgraphBlockNumber } from '../SubgraphUtils'
import { fetchTokenWithdrawalsFromEventLogs } from './fetchTokenWithdrawalsFromEventLogs'
import { fetchL2Gateways } from '../fetchL2Gateways'

import { Withdrawal } from '../../hooks/useTransactionHistory'
import { attachTimestampToTokenWithdrawal } from './helpers'
import { WithdrawalInitiated } from '../../hooks/arbTokenBridge.types'
import { fetchTokenWithdrawalsFromEventLogsSequentially } from './fetchTokenWithdrawalsFromEventLogsSequentially'
import { wait } from '../ExponentialBackoffUtils'

export type FetchWithdrawalsParams = {
sender?: string
Expand Down Expand Up @@ -43,8 +44,6 @@ export async function fetchWithdrawals({
const l1ChainID = (await l1Provider.getNetwork()).chainId
const l2ChainID = (await l2Provider.getNetwork()).chainId

const l2GatewayAddresses = await fetchL2Gateways(l2Provider)

if (!fromBlock) {
fromBlock = 0
}
Expand Down Expand Up @@ -85,23 +84,23 @@ export async function fetchWithdrawals({
console.log('Error fetching withdrawals from subgraph', error)
}

const [ethWithdrawalsFromEventLogs, tokenWithdrawalsFromEventLogs] =
await Promise.all([
fetchETHWithdrawalsFromEventLogs({
receiver,
fromBlock: toBlock + 1,
toBlock: 'latest',
l2Provider: l2Provider
}),
fetchTokenWithdrawalsFromEventLogs({
sender,
receiver,
fromBlock: toBlock + 1,
toBlock: 'latest',
l2Provider: l2Provider,
l2GatewayAddresses
})
])
const ethWithdrawalsFromEventLogs = await fetchETHWithdrawalsFromEventLogs({
receiver,
fromBlock: toBlock + 1,
toBlock: 'latest',
l2Provider: l2Provider
})

await wait(2_000)

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

const mappedEthWithdrawalsFromEventLogs: Withdrawal[] =
ethWithdrawalsFromEventLogs.map(tx => {
Expand Down

0 comments on commit fe58b57

Please sign in to comment.