Skip to content

Commit fd7032b

Browse files
authored
feat: integrate bridge tasks with arbitrum sdk (#760)
Signed-off-by: Tomás Migone <[email protected]>
1 parent 47e5f15 commit fd7032b

File tree

5 files changed

+207
-36
lines changed

5 files changed

+207
-36
lines changed

cli/arbitrum.ts

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import {
2+
L1ToL2MessageReader,
3+
L1ToL2MessageStatus,
4+
L1ToL2MessageWriter,
5+
L1TransactionReceipt,
6+
L2ToL1MessageReader,
7+
L2ToL1MessageStatus,
8+
L2ToL1MessageWriter,
9+
L2TransactionReceipt,
10+
} from '@arbitrum/sdk'
11+
import { providers, Signer } from 'ethers'
12+
import { Provider } from '@ethersproject/abstract-provider'
13+
14+
// L1 -> L2
15+
export async function getL1ToL2MessageWriter(
16+
txHashOrReceipt: string | providers.TransactionReceipt,
17+
l1Provider: Provider,
18+
l2Provider: Provider,
19+
signer: Signer,
20+
): Promise<L1ToL2MessageWriter> {
21+
return (await getL1ToL2Message(
22+
txHashOrReceipt,
23+
l1Provider,
24+
l2Provider,
25+
signer,
26+
)) as L1ToL2MessageWriter
27+
}
28+
29+
export async function getL1ToL2MessageReader(
30+
txHashOrReceipt: string | providers.TransactionReceipt,
31+
l1Provider: Provider,
32+
l2Provider: Provider,
33+
): Promise<L1ToL2MessageReader> {
34+
return await getL1ToL2Message(txHashOrReceipt, l1Provider, l2Provider)
35+
}
36+
37+
export async function getL1ToL2MessageStatus(
38+
txHashOrReceipt: string | providers.TransactionReceipt,
39+
l1Provider: Provider,
40+
l2Provider: Provider,
41+
): Promise<L1ToL2MessageStatus> {
42+
const message = await getL1ToL2Message(txHashOrReceipt, l1Provider, l2Provider)
43+
return await message.status()
44+
}
45+
46+
async function getL1ToL2Message(
47+
txHashOrReceipt: string | providers.TransactionReceipt,
48+
l1Provider: Provider,
49+
l2Provider: Provider,
50+
signer?: Signer,
51+
): Promise<L1ToL2MessageWriter | L1ToL2MessageReader> {
52+
const txReceipt =
53+
typeof txHashOrReceipt === 'string'
54+
? await l1Provider.getTransactionReceipt(txHashOrReceipt)
55+
: txHashOrReceipt
56+
const l2SignerOrProvider = signer ? signer.connect(l2Provider) : l2Provider
57+
const l1Receipt = new L1TransactionReceipt(txReceipt)
58+
const l1ToL2Messages = await l1Receipt.getL1ToL2Messages(l2SignerOrProvider)
59+
return l1ToL2Messages[0]
60+
}
61+
62+
// L2 -> L1
63+
export async function getL2ToL1MessageWriter(
64+
txHashOrReceipt: string | providers.TransactionReceipt,
65+
l1Provider: Provider,
66+
l2Provider: Provider,
67+
signer: Signer,
68+
): Promise<L2ToL1MessageWriter> {
69+
return (await getL2ToL1Message(
70+
txHashOrReceipt,
71+
l1Provider,
72+
l2Provider,
73+
signer,
74+
)) as L2ToL1MessageWriter
75+
}
76+
77+
export async function getL2ToL1MessageReader(
78+
txHashOrReceipt: string | providers.TransactionReceipt,
79+
l1Provider: Provider,
80+
l2Provider: Provider,
81+
): Promise<L2ToL1MessageReader> {
82+
return await getL2ToL1Message(txHashOrReceipt, l1Provider, l2Provider)
83+
}
84+
85+
export async function getL2ToL1MessageStatus(
86+
txHashOrReceipt: string | providers.TransactionReceipt,
87+
l1Provider: Provider,
88+
l2Provider: Provider,
89+
): Promise<L2ToL1MessageStatus> {
90+
const message = await getL2ToL1Message(txHashOrReceipt, l1Provider, l2Provider)
91+
return await message.status(l2Provider)
92+
}
93+
94+
async function getL2ToL1Message(
95+
txHashOrReceipt: string | providers.TransactionReceipt,
96+
l1Provider: Provider,
97+
l2Provider: Provider,
98+
signer?: Signer,
99+
) {
100+
const txReceipt =
101+
typeof txHashOrReceipt === 'string'
102+
? await l2Provider.getTransactionReceipt(txHashOrReceipt)
103+
: txHashOrReceipt
104+
const l1SignerOrProvider = signer ? signer.connect(l1Provider) : l1Provider
105+
const l2Receipt = new L2TransactionReceipt(txReceipt)
106+
const l2ToL1Messages = await l2Receipt.getL2ToL1Messages(l1SignerOrProvider)
107+
return l2ToL1Messages[0]
108+
}

cli/commands/bridge/to-l1.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { BigNumber } from 'ethers'
1010
import { JsonRpcProvider } from '@ethersproject/providers'
1111
import { providers } from 'ethers'
1212
import { L2GraphToken } from '../../../build/types/L2GraphToken'
13+
import { getL2ToL1MessageReader, getL2ToL1MessageWriter } from '../../arbitrum'
1314

1415
const FOURTEEN_DAYS_IN_SECONDS = 24 * 3600 * 14
1516

@@ -99,10 +100,11 @@ export const startSendToL1 = async (cli: CLIEnvironment, cliArgs: CLIArgs): Prom
99100
'outboundTransfer(address,address,uint256,bytes)',
100101
params,
101102
)
103+
104+
const l2ToL1Message = await getL2ToL1MessageReader(receipt, cli.wallet.provider, l2Provider)
102105
const l2Receipt = new L2TransactionReceipt(receipt)
103-
const l2ToL1Message = (await l2Receipt.getL2ToL1Messages(cli.wallet))[0]
104106

105-
const ethBlockNum = l2ToL1Message.getFirstExecutableBlock(l2Provider)
107+
const ethBlockNum = await l2ToL1Message.getFirstExecutableBlock(l2Provider)
106108
if (ethBlockNum === null) {
107109
logger.info(`L2 to L1 message can or already has been executed. If not finalized call`)
108110
} else {
@@ -157,11 +159,12 @@ export const finishSendToL1 = async (
157159
txHash = allEvents[allEvents.length - 1].transactionHash
158160
}
159161
logger.info(`Getting receipt from transaction ${txHash}`)
160-
const receipt = await l2Provider.getTransactionReceipt(txHash)
161-
162-
const l2Receipt = new L2TransactionReceipt(receipt)
163-
logger.info(`Getting L2 to L1 message...`)
164-
const l2ToL1Message = (await l2Receipt.getL2ToL1Messages(cli.wallet))[0]
162+
const l2ToL1Message = await getL2ToL1MessageWriter(
163+
txHash,
164+
cli.wallet.provider,
165+
l2Provider,
166+
cli.wallet,
167+
)
165168

166169
if (wait) {
167170
const retryDelayMs = cliArgs.retryDelaySeconds ? cliArgs.retryDelaySeconds * 1000 : 60000

cli/commands/bridge/to-l2.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { loadEnv, CLIArgs, CLIEnvironment } from '../../env'
66
import { logger } from '../../logging'
77
import { getProvider, sendTransaction, toGRT, ensureAllowance, toBN } from '../../network'
88
import { chainIdIsL2, estimateRetryableTxGas } from '../../cross-chain'
9+
import { getL1ToL2MessageWriter } from '../../arbitrum'
910

1011
const logAutoRedeemReason = (autoRedeemRec) => {
1112
if (autoRedeemRec == null) {
@@ -105,9 +106,12 @@ export const sendToL2 = async (cli: CLIEnvironment, cliArgs: CLIArgs): Promise<v
105106
// get l2 ticket status
106107
if (txReceipt.status == 1) {
107108
logger.info('Waiting for message to propagate to L2...')
108-
const l1Receipt = new L1TransactionReceipt(txReceipt)
109-
const l1ToL2Messages = await l1Receipt.getL1ToL2Messages(cli.wallet.connect(l2Provider))
110-
const l1ToL2Message = l1ToL2Messages[0]
109+
const l1ToL2Message = await getL1ToL2MessageWriter(
110+
txReceipt,
111+
cli.wallet.provider,
112+
l2Provider,
113+
cli.wallet,
114+
)
111115
try {
112116
await checkAndRedeemMessage(l1ToL2Message)
113117
} catch (e) {

tasks/bridge/deposits.ts

Lines changed: 44 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { task } from 'hardhat/config'
22
import { cliOpts } from '../../cli/defaults'
33
import { ethers } from 'ethers'
44
import { Table } from 'console-table-printer'
5+
import { L1ToL2MessageStatus } from '@arbitrum/sdk'
6+
import { getL1ToL2MessageStatus } from '../../cli/arbitrum'
57

68
export const TASK_BRIDGE_DEPOSITS = 'bridge:deposits'
79

@@ -27,15 +29,20 @@ task(TASK_BRIDGE_DEPOSITS, 'List deposits initiated on L1GraphTokenGateway')
2729
const endBlock = taskArgs.endBlock ? parseInt(taskArgs.endBlock) : 'latest'
2830
console.log(`Searching blocks from block ${startBlock} to block ${endBlock}`)
2931

30-
const events = (
31-
await gateway.queryFilter(gateway.filters.DepositInitiated(), startBlock, endBlock)
32-
).map((e) => ({
33-
blockNumber: e.blockNumber,
34-
transactionHash: e.transactionHash,
35-
from: e.args.from,
36-
to: e.args.to,
37-
amount: ethers.utils.formatEther(e.args.amount),
38-
}))
32+
const events = await Promise.all(
33+
(
34+
await gateway.queryFilter(gateway.filters.DepositInitiated(), startBlock, endBlock)
35+
).map(async (e) => ({
36+
blockNumber: `${e.blockNumber} (${new Date(
37+
(await graph.l1.provider.getBlock(e.blockNumber)).timestamp * 1000,
38+
).toLocaleString()})`,
39+
tx: `${e.transactionHash} ${e.args.from} -> ${e.args.to}`,
40+
amount: ethers.utils.formatEther(e.args.amount),
41+
status: emojifyRetryableStatus(
42+
await getL1ToL2MessageStatus(e.transactionHash, graph.l1.provider, graph.l2.provider),
43+
),
44+
})),
45+
)
3946

4047
const total = events.reduce(
4148
(acc, e) => acc.add(ethers.utils.parseEther(e.amount)),
@@ -45,23 +52,46 @@ task(TASK_BRIDGE_DEPOSITS, 'List deposits initiated on L1GraphTokenGateway')
4552
`Found ${events.length} deposits with a total of ${ethers.utils.formatEther(total)} GRT`,
4653
)
4754

55+
console.log(
56+
'L1 to L2 message status reference: 🚧 = not yet created, ❌ = creation failed, ⚠️ = funds deposited on L2, ✅ = redeemed, ⌛ = expired',
57+
)
58+
4859
printEvents(events)
4960
})
5061

5162
function printEvents(events: any[]) {
5263
const tablePrinter = new Table({
64+
charLength: { '🚧': 2, '✅': 2, '⚠️': 1, '⌛': 2, '❌': 2 },
5365
columns: [
54-
{ name: 'blockNumber', color: 'green' },
66+
{ name: 'status', color: 'green', alignment: 'center' },
67+
{ name: 'blockNumber', color: 'green', alignment: 'center' },
5568
{
56-
name: 'transactionHash',
69+
name: 'tx',
5770
color: 'green',
71+
alignment: 'center',
72+
maxLen: 88,
5873
},
59-
{ name: 'from', color: 'green' },
60-
{ name: 'to', color: 'green' },
61-
{ name: 'amount', color: 'green' },
74+
{ name: 'amount', color: 'green', alignment: 'center' },
6275
],
6376
})
6477

6578
events.map((e) => tablePrinter.addRow(e))
6679
tablePrinter.printTable()
6780
}
81+
82+
function emojifyRetryableStatus(status: L1ToL2MessageStatus): string {
83+
switch (status) {
84+
case L1ToL2MessageStatus.NOT_YET_CREATED:
85+
return '🚧'
86+
case L1ToL2MessageStatus.CREATION_FAILED:
87+
return '❌'
88+
case L1ToL2MessageStatus.FUNDS_DEPOSITED_ON_L2:
89+
return '⚠️ '
90+
case L1ToL2MessageStatus.REDEEMED:
91+
return '✅'
92+
case L1ToL2MessageStatus.EXPIRED:
93+
return '⌛'
94+
default:
95+
return '❌'
96+
}
97+
}

tasks/bridge/withdrawals.ts

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { task } from 'hardhat/config'
22
import { cliOpts } from '../../cli/defaults'
33
import { ethers } from 'ethers'
44
import { Table } from 'console-table-printer'
5+
import { L2ToL1MessageStatus } from '@arbitrum/sdk'
6+
import { getL2ToL1MessageStatus } from '../../cli/arbitrum'
57

68
export const TASK_BRIDGE_WITHDRAWALS = 'bridge:withdrawals'
79

@@ -27,15 +29,20 @@ task(TASK_BRIDGE_WITHDRAWALS, 'List withdrawals initiated on L2GraphTokenGateway
2729
const endBlock = taskArgs.endBlock ? parseInt(taskArgs.endBlock) : 'latest'
2830
console.log(`Searching blocks from block ${startBlock} to block ${endBlock}`)
2931

30-
const events = (
31-
await gateway.queryFilter(gateway.filters.WithdrawalInitiated(), startBlock, endBlock)
32-
).map((e) => ({
33-
blockNumber: e.blockNumber,
34-
transactionHash: e.transactionHash,
35-
from: e.args.from,
36-
to: e.args.to,
37-
amount: ethers.utils.formatEther(e.args.amount),
38-
}))
32+
const events = await Promise.all(
33+
(
34+
await gateway.queryFilter(gateway.filters.WithdrawalInitiated(), startBlock, endBlock)
35+
).map(async (e) => ({
36+
blockNumber: `${e.blockNumber} (${new Date(
37+
(await graph.l2.provider.getBlock(e.blockNumber)).timestamp * 1000,
38+
).toLocaleString()})`,
39+
tx: `${e.transactionHash} ${e.args.from} -> ${e.args.to}`,
40+
amount: ethers.utils.formatEther(e.args.amount),
41+
status: emojifyL2ToL1Status(
42+
await getL2ToL1MessageStatus(e.transactionHash, graph.l1.provider, graph.l2.provider),
43+
),
44+
})),
45+
)
3946

4047
const total = events.reduce(
4148
(acc, e) => acc.add(ethers.utils.parseEther(e.amount)),
@@ -45,23 +52,42 @@ task(TASK_BRIDGE_WITHDRAWALS, 'List withdrawals initiated on L2GraphTokenGateway
4552
`Found ${events.length} withdrawals for a total of ${ethers.utils.formatEther(total)} GRT`,
4653
)
4754

55+
console.log(
56+
'L2 to L1 message status reference: 🚧 = unconfirmed, ⚠️ = confirmed, ✅ = executed',
57+
)
58+
4859
printEvents(events)
4960
})
5061

5162
function printEvents(events: any[]) {
5263
const tablePrinter = new Table({
64+
charLength: { '🚧': 2, '✅': 2, '⚠️': 1, '❌': 2 },
5365
columns: [
66+
{ name: 'status', color: 'green', alignment: 'center' },
5467
{ name: 'blockNumber', color: 'green' },
5568
{
56-
name: 'transactionHash',
69+
name: 'tx',
5770
color: 'green',
71+
alignment: 'center',
72+
maxLen: 88,
5873
},
59-
{ name: 'from', color: 'green' },
60-
{ name: 'to', color: 'green' },
6174
{ name: 'amount', color: 'green' },
6275
],
6376
})
6477

6578
events.map((e) => tablePrinter.addRow(e))
6679
tablePrinter.printTable()
6780
}
81+
82+
function emojifyL2ToL1Status(status: L2ToL1MessageStatus): string {
83+
switch (status) {
84+
case L2ToL1MessageStatus.UNCONFIRMED:
85+
return '🚧'
86+
case L2ToL1MessageStatus.CONFIRMED:
87+
return '⚠️ '
88+
case L2ToL1MessageStatus.EXECUTED:
89+
return '✅'
90+
default:
91+
return '❌'
92+
}
93+
}

0 commit comments

Comments
 (0)