-
Notifications
You must be signed in to change notification settings - Fork 94
Expand file tree
/
Copy pathethcommands.ts
More file actions
452 lines (404 loc) · 14.2 KB
/
ethcommands.ts
File metadata and controls
452 lines (404 loc) · 14.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
import { runStress } from "./stress";
import { ContractFactory, ethers, Wallet } from "ethers";
import * as consts from "./consts";
import { namedAccount, namedAddress } from "./accounts";
import * as L1GatewayRouter from "@arbitrum/token-bridge-contracts/build/contracts/contracts/tokenbridge/ethereum/gateway/L1GatewayRouter.sol/L1GatewayRouter.json";
import * as L1AtomicTokenBridgeCreator from "@arbitrum/token-bridge-contracts/build/contracts/contracts/tokenbridge/ethereum/L1AtomicTokenBridgeCreator.sol/L1AtomicTokenBridgeCreator.json";
import * as ERC20PresetFixedSupplyArtifact from "@openzeppelin/contracts/build/contracts/ERC20PresetFixedSupply.json";
import * as ERC20 from "@openzeppelin/contracts/build/contracts/ERC20.json";
import * as fs from "fs";
import { ARB_OWNER } from "./consts";
const path = require("path");
async function sendTransaction(argv: any, threadId: number) {
const account = namedAccount(argv.from, threadId).connect(argv.provider)
const startNonce = await account.getTransactionCount("pending")
for (let index = 0; index < argv.times; index++) {
const response = await
account.sendTransaction({
to: namedAddress(argv.to, threadId),
value: ethers.utils.parseEther(argv.ethamount),
data: argv.data,
nonce: startNonce + index,
})
console.log(response)
if (argv.wait) {
const receipt = await response.wait()
console.log(receipt)
}
if (argv.delay > 0) {
await new Promise(f => setTimeout(f, argv.delay));
}
}
}
async function bridgeFunds(argv: any, parentChainUrl: string, chainUrl: string, inboxAddr: string) {
argv.provider = new ethers.providers.WebSocketProvider(parentChainUrl);
argv.to = "address_" + inboxAddr;
argv.data =
"0x0f4d14e9000000000000000000000000000000000000000000000000000082f79cd90000";
await runStress(argv, sendTransaction);
argv.provider.destroy();
if (argv.wait) {
const l2provider = new ethers.providers.WebSocketProvider(chainUrl);
const account = namedAccount(argv.from, argv.threadId).connect(l2provider)
const sleep = (ms: number) => new Promise(r => setTimeout(r, ms));
while (true) {
const balance = await account.getBalance()
if (balance.gte(ethers.utils.parseEther(argv.ethamount))) {
return
}
await sleep(100)
}
}
}
async function bridgeNativeToken(argv: any, parentChainUrl: string, chainUrl: string, inboxAddr: string, token: string) {
argv.provider = new ethers.providers.WebSocketProvider(parentChainUrl);
argv.to = "address_" + inboxAddr;
/// approve inbox to use fee token
const bridgerParentChain = namedAccount(argv.from, argv.threadId).connect(argv.provider)
const nativeTokenContract = new ethers.Contract(token, ERC20.abi, bridgerParentChain)
await nativeTokenContract.approve(inboxAddr, ethers.utils.parseEther(argv.amount))
/// deposit fee token
const iface = new ethers.utils.Interface(["function depositERC20(uint256 amount)"])
argv.data = iface.encodeFunctionData("depositERC20", [ethers.utils.parseEther(argv.amount)]);
await runStress(argv, sendTransaction);
argv.provider.destroy();
if (argv.wait) {
const childProvider = new ethers.providers.WebSocketProvider(chainUrl);
const bridger = namedAccount(argv.from, argv.threadId).connect(childProvider)
const sleep = (ms: number) => new Promise(r => setTimeout(r, ms));
while (true) {
const balance = await bridger.getBalance()
if (balance.gte(ethers.utils.parseEther(argv.amount))) {
return
}
await sleep(100)
}
}
}
export const bridgeFundsCommand = {
command: "bridge-funds",
describe: "sends funds from l1 to l2",
builder: {
ethamount: {
string: true,
describe: "amount to transfer (in eth)",
default: "10",
},
from: {
string: true,
describe: "account (see general help)",
default: "funnel",
},
wait: {
boolean: true,
describe: "wait till l2 has balance of ethamount",
default: false,
},
},
handler: async (argv: any) => {
const deploydata = JSON.parse(
fs
.readFileSync(path.join(consts.configpath, "deployment.json"))
.toString()
);
const inboxAddr = ethers.utils.hexlify(deploydata.inbox);
await bridgeFunds(argv, argv.l1url, argv.l2url, inboxAddr)
},
};
export const bridgeToL3Command = {
command: "bridge-to-l3",
describe: "sends funds from l2 to l3",
builder: {
ethamount: {
string: true,
describe: "amount to transfer (in eth)",
default: "10",
},
from: {
string: true,
describe: "account (see general help)",
default: "funnel",
},
wait: {
boolean: true,
describe: "wait till l3 has balance of ethamount",
default: false,
},
},
handler: async (argv: any) => {
const deploydata = JSON.parse(
fs
.readFileSync(path.join(consts.configpath, "l3deployment.json"))
.toString()
);
const inboxAddr = ethers.utils.hexlify(deploydata.inbox);
await bridgeFunds(argv, argv.l2url, argv.l3url, inboxAddr)
},
};
export const bridgeNativeTokenToL3Command = {
command: "bridge-native-token-to-l3",
describe: "bridge native token from l2 to l3",
builder: {
amount: {
string: true,
describe: "amount to transfer",
default: "10",
},
from: {
string: true,
describe: "account (see general help)",
default: "funnel",
},
wait: {
boolean: true,
describe: "wait till l3 has balance of amount",
default: false,
},
},
handler: async (argv: any) => {
const deploydata = JSON.parse(
fs
.readFileSync(path.join(consts.configpath, "l3deployment.json"))
.toString()
);
const inboxAddr = ethers.utils.hexlify(deploydata.inbox);
const nativeTokenAddr = ethers.utils.hexlify(deploydata["native-token"]);
argv.ethamount = "0"
await bridgeNativeToken(argv, argv.l2url, argv.l3url, inboxAddr, nativeTokenAddr)
},
};
export const transferL3ChainOwnershipCommand = {
command: "transfer-l3-chain-ownership",
describe: "transfer L3 chain ownership to upgrade executor",
builder: {
creator: {
string: true,
describe: "address of the token bridge creator",
},
wait: {
boolean: true,
describe: "wait till ownership is transferred",
default: false,
},
},
handler: async (argv: any) => {
// get inbox address from config file
const deploydata = JSON.parse(
fs
.readFileSync(path.join(consts.configpath, "l3deployment.json"))
.toString()
);
const inboxAddr = ethers.utils.hexlify(deploydata.inbox);
// get L3 upgrade executor address from token bridge creator
const l2provider = new ethers.providers.WebSocketProvider(argv.l2url);
const tokenBridgeCreator = new ethers.Contract(argv.creator, L1AtomicTokenBridgeCreator.abi, l2provider);
const [,,,,,,,l3UpgradeExecutorAddress,] = await tokenBridgeCreator.inboxToL2Deployment(inboxAddr);
// set TX params
argv.provider = new ethers.providers.WebSocketProvider(argv.l3url);
argv.to = "address_" + ARB_OWNER;
argv.from = "l3owner";
argv.ethamount = "0";
// add L3 UpgradeExecutor to chain owners
const arbOwnerIface = new ethers.utils.Interface([
"function addChainOwner(address newOwner) external",
"function removeChainOwner(address ownerToRemove) external"
])
argv.data = arbOwnerIface.encodeFunctionData("addChainOwner", [l3UpgradeExecutorAddress]);
await runStress(argv, sendTransaction);
// remove L3 owner from chain owners
argv.data = arbOwnerIface.encodeFunctionData("removeChainOwner", [namedAccount("l3owner").address]);
await runStress(argv, sendTransaction);
argv.provider.destroy();
}
};
export const createERC20Command = {
command: "create-erc20",
describe: "creates simple ERC20 on L2",
builder: {
deployer: {
string: true,
describe: "account (see general help)"
},
mintTo: {
string: true,
describe: "account (see general help)",
},
bridgeable: {
boolean: true,
describe: "if true, deploy on L1 and bridge to L2",
},
},
handler: async (argv: any) => {
console.log("create-erc20");
if (argv.bridgeable) {
// deploy token on l1 and bridge to l2
const l1l2tokenbridge = JSON.parse(
fs
.readFileSync(path.join(consts.tokenbridgedatapath, "l1l2_network.json"))
.toString()
);
const l1provider = new ethers.providers.WebSocketProvider(argv.l1url);
const l2provider = new ethers.providers.WebSocketProvider(argv.l2url);
const deployerWallet = new Wallet(
ethers.utils.sha256(ethers.utils.toUtf8Bytes(argv.deployer)),
l1provider
);
const tokenFactory = new ContractFactory(
ERC20PresetFixedSupplyArtifact.abi,
ERC20PresetFixedSupplyArtifact.bytecode,
deployerWallet
);
const token = await tokenFactory.deploy("AppTestToken", "APP", ethers.utils.parseEther("1000000000"), deployerWallet.address);
await token.deployTransaction.wait();
console.log("Contract deployed at L1 address:", token.address);
await (await token.functions.transfer(namedAccount(argv.mintTo).address, ethers.utils.parseEther("100000000"))).wait();
const l1GatewayRouter = new ethers.Contract(l1l2tokenbridge.l2Network.tokenBridge.l1GatewayRouter, L1GatewayRouter.abi, deployerWallet);
await (await token.functions.approve(l1l2tokenbridge.l2Network.tokenBridge.l1ERC20Gateway, ethers.constants.MaxUint256)).wait();
await (await l1GatewayRouter.functions.outboundTransfer(
token.address, namedAccount(argv.mintTo).address, ethers.utils.parseEther("100000000"), 100000000, 1000000000, "0x000000000000000000000000000000000000000000000000000fffffffffff0000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000", {
value: ethers.utils.parseEther("1"),
}
)).wait();
const tokenL2Addr = (await l1GatewayRouter.functions.calculateL2TokenAddress(token.address))[0];
// wait for l2 token to be deployed
for (let i = 0; i < 60; i++) {
if (await l2provider.getCode(tokenL2Addr) === "0x") {
await new Promise(f => setTimeout(f, 1000));
} else {
break;
}
}
if (await l2provider.getCode(tokenL2Addr) === "0x") {
throw new Error("Failed to bridge token to L2");
}
console.log("Contract deployed at L2 address:", tokenL2Addr);
l1provider.destroy();
l2provider.destroy();
return;
}
// no l1-l2 token bridge, deploy token on l2 directly
argv.provider = new ethers.providers.WebSocketProvider(argv.l2url);
const deployerWallet = new Wallet(
ethers.utils.sha256(ethers.utils.toUtf8Bytes(argv.deployer)),
argv.provider
);
const contractFactory = new ContractFactory(
ERC20PresetFixedSupplyArtifact.abi,
ERC20PresetFixedSupplyArtifact.bytecode,
deployerWallet
);
const contract = await contractFactory.deploy("AppTestToken", "APP", ethers.utils.parseEther("1000000000"), namedAccount(argv.mintTo).address);
await contract.deployTransaction.wait();
console.log("Contract deployed at address:", contract.address);
argv.provider.destroy();
},
};
export const sendL1Command = {
command: "send-l1",
describe: "sends funds between l1 accounts",
builder: {
ethamount: {
string: true,
describe: "amount to transfer (in eth)",
default: "10",
},
from: {
string: true,
describe: "account (see general help)",
default: "funnel",
},
to: {
string: true,
describe: "address (see general help)",
default: "funnel",
},
wait: {
boolean: true,
describe: "wait for transaction to complete",
default: false,
},
data: { string: true, describe: "data" },
},
handler: async (argv: any) => {
argv.provider = new ethers.providers.WebSocketProvider(argv.l1url);
await runStress(argv, sendTransaction);
argv.provider.destroy();
},
};
export const sendL2Command = {
command: "send-l2",
describe: "sends funds between l2 accounts",
builder: {
ethamount: {
string: true,
describe: "amount to transfer (in eth)",
default: "10",
},
from: {
string: true,
describe: "account (see general help)",
default: "funnel",
},
to: {
string: true,
describe: "address (see general help)",
default: "funnel",
},
wait: {
boolean: true,
describe: "wait for transaction to complete",
default: false,
},
data: { string: true, describe: "data" },
},
handler: async (argv: any) => {
argv.provider = new ethers.providers.WebSocketProvider(argv.l2url);
await runStress(argv, sendTransaction);
argv.provider.destroy();
},
};
export const sendL3Command = {
command: "send-l3",
describe: "sends funds between l3 accounts",
builder: {
ethamount: {
string: true,
describe: "amount to transfer (in eth)",
default: "10",
},
from: {
string: true,
describe: "account (see general help)",
default: "funnel",
},
to: {
string: true,
describe: "address (see general help)",
default: "funnel",
},
wait: {
boolean: true,
describe: "wait for transaction to complete",
default: false,
},
data: { string: true, describe: "data" },
},
handler: async (argv: any) => {
argv.provider = new ethers.providers.WebSocketProvider(argv.l3url);
await runStress(argv, sendTransaction);
argv.provider.destroy();
},
};
export const sendRPCCommand = {
command: "send-rpc",
describe: "sends rpc command",
builder: {
method: { string: true, describe: "rpc method to call", default: "eth_syncing" },
url: { string: true, describe: "url to send rpc call", default: "http://sequencer:8547"},
params: { array : true, describe: "array of parameter name/values" },
},
handler: async (argv: any) => {
const rpcProvider = new ethers.providers.JsonRpcProvider(argv.url)
await rpcProvider.send(argv.method, argv.params)
}
}