forked from OffchainLabs/token-bridge-contracts
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathL2CustomGateway.t.sol
342 lines (273 loc) · 13.1 KB
/
L2CustomGateway.t.sol
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
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
import "./L2ArbitrumGateway.t.sol";
import {L2CustomGateway, ERC20} from "contracts/tokenbridge/arbitrum/gateway/L2CustomGateway.sol";
import {L2GatewayToken} from "contracts/tokenbridge/libraries/L2GatewayToken.sol";
import {UpgradeableBeacon} from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol";
import {AddressAliasHelper} from "contracts/tokenbridge/libraries/AddressAliasHelper.sol";
import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol";
import {TransparentUpgradeableProxy} from
"@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
contract L2CustomGatewayTest is L2ArbitrumGatewayTest {
L2CustomGateway public l2CustomGateway;
address public l1CustomToken = makeAddr("l1CustomToken");
function setUp() public virtual {
l2CustomGateway = new L2CustomGateway();
l2Gateway = L2ArbitrumGateway(address(l2CustomGateway));
L2CustomGateway(l2CustomGateway).initialize(l1Counterpart, router);
}
/* solhint-disable func-name-mixedcase */
function test_calculateL2TokenAddress_NonRegistered() public {
address nonRegisteredL1Token = makeAddr("nonRegisteredL1Token");
assertEq(
l2CustomGateway.calculateL2TokenAddress(nonRegisteredL1Token),
address(0),
"Invalid L2 token"
);
}
function test_calculateL2TokenAddress_Registered() public virtual {
address l2CustomToken = _registerToken();
assertEq(
l2CustomGateway.calculateL2TokenAddress(l1CustomToken),
l2CustomToken,
"Invalid L2 token"
);
}
function test_finalizeInboundTransfer() public virtual override {
/// deposit params
bytes memory gatewayData = new bytes(0);
bytes memory callHookData = new bytes(0);
// register custom token
address l2CustomToken = _registerToken();
/// events
vm.expectEmit(true, true, true, true);
emit DepositFinalized(l1CustomToken, sender, receiver, amount);
/// finalize deposit
vm.prank(AddressAliasHelper.applyL1ToL2Alias(l1Counterpart));
l2CustomGateway.finalizeInboundTransfer(
l1CustomToken, sender, receiver, amount, abi.encode(gatewayData, callHookData)
);
/// check tokens have been minted to receiver;
assertEq(ERC20(l2CustomToken).balanceOf(receiver), amount, "Invalid receiver balance");
}
function test_finalizeInboundTransfer_NoL2TokenFound() public {
/// deposit params
bytes memory gatewayData = new bytes(0);
bytes memory callHookData = new bytes(0);
// check that withdrawal is triggered occurs when deposit is halted
bytes memory expectedData = l2CustomGateway.getOutboundCalldata(
l1CustomToken, address(l2CustomGateway), sender, amount, new bytes(0)
);
vm.expectEmit(true, true, true, true);
emit TxToL1(address(l2CustomGateway), l1Counterpart, 0, expectedData);
vm.expectEmit(true, true, true, true);
emit WithdrawalInitiated(l1CustomToken, address(l2CustomGateway), sender, 0, 0, amount);
/// finalize deposit
vm.etch(0x0000000000000000000000000000000000000064, address(arbSysMock).code);
vm.prank(AddressAliasHelper.applyL1ToL2Alias(l1Counterpart));
l2CustomGateway.finalizeInboundTransfer(
l1CustomToken, sender, receiver, amount, abi.encode(gatewayData, callHookData)
);
}
function test_finalizeInboundTransfer_UnexpectedL1Address() public {
/// deposit params
bytes memory gatewayData = new bytes(0);
bytes memory callHookData = new bytes(0);
/// L2 token returns unexpected L1 address
address l2CustomToken = _registerToken();
address notOriginalL1Token = makeAddr("notOriginalL1Token");
vm.mockCall(
address(l2CustomToken),
abi.encodeWithSignature("l1Address()"),
abi.encode(notOriginalL1Token)
);
// check that withdrawal is triggered occurs when deposit is halted
bytes memory expectedData = l2CustomGateway.getOutboundCalldata(
l1CustomToken, address(l2CustomGateway), sender, amount, new bytes(0)
);
vm.expectEmit(true, true, true, true);
emit TxToL1(address(l2CustomGateway), l1Counterpart, 0, expectedData);
vm.expectEmit(true, true, true, true);
emit WithdrawalInitiated(l1CustomToken, address(l2CustomGateway), sender, 0, 0, amount);
/// finalize deposit
vm.etch(0x0000000000000000000000000000000000000064, address(arbSysMock).code);
vm.prank(AddressAliasHelper.applyL1ToL2Alias(l1Counterpart));
l2CustomGateway.finalizeInboundTransfer(
l1CustomToken, sender, receiver, amount, abi.encode(gatewayData, callHookData)
);
}
function test_finalizeInboundTransfer_NoL1AddressImplemented() public {
/// deposit params
bytes memory gatewayData = new bytes(0);
bytes memory callHookData = new bytes(0);
/// L2 token returns doesn't implement l1Address()
address[] memory l1Tokens = new address[](1);
l1Tokens[0] = l1CustomToken;
address[] memory l2Tokens = new address[](1);
l2Tokens[0] = address(new Empty());
vm.prank(AddressAliasHelper.applyL1ToL2Alias(l1Counterpart));
l2CustomGateway.registerTokenFromL1(l1Tokens, l2Tokens);
// check that withdrawal is triggered occurs
bytes memory expectedData = l2CustomGateway.getOutboundCalldata(
l1CustomToken, address(l2CustomGateway), sender, amount, new bytes(0)
);
vm.expectEmit(true, true, true, true);
emit TxToL1(address(l2CustomGateway), l1Counterpart, 0, expectedData);
vm.expectEmit(true, true, true, true);
emit WithdrawalInitiated(l1CustomToken, address(l2CustomGateway), sender, 0, 0, amount);
/// finalize deposit
vm.etch(0x0000000000000000000000000000000000000064, address(arbSysMock).code);
vm.prank(AddressAliasHelper.applyL1ToL2Alias(l1Counterpart));
l2CustomGateway.finalizeInboundTransfer(
l1CustomToken, sender, receiver, amount, abi.encode(gatewayData, callHookData)
);
}
function test_finalizeInboundTransfer_WithCallHook() public virtual override {
/// deposit params
bytes memory gatewayData = abi.encode(
abi.encode(bytes("Name")), abi.encode(bytes("Symbol")), abi.encode(uint256(18))
);
bytes memory callHookData = new bytes(0x1);
// register custom token
address l2CustomToken = _registerToken();
/// events
vm.expectEmit(true, true, true, true);
emit DepositFinalized(l1CustomToken, sender, receiver, amount);
/// finalize deposit
vm.prank(AddressAliasHelper.applyL1ToL2Alias(l1Counterpart));
l2CustomGateway.finalizeInboundTransfer(
l1CustomToken, sender, receiver, amount, abi.encode(gatewayData, callHookData)
);
/// check tokens have been minted to receiver;
assertEq(ERC20(l2CustomToken).balanceOf(receiver), amount, "Invalid receiver balance");
}
function test_initialize() public {
L2CustomGateway gateway = new L2CustomGateway();
L2CustomGateway(gateway).initialize(l1Counterpart, router);
assertEq(gateway.counterpartGateway(), l1Counterpart, "Invalid counterpartGateway");
assertEq(gateway.router(), router, "Invalid router");
}
function test_initialize_revert_BadRouter() public {
L2CustomGateway gateway = new L2CustomGateway();
vm.expectRevert("BAD_ROUTER");
L2CustomGateway(gateway).initialize(l1Counterpart, address(0));
}
function test_initialize_revert_InvalidCounterpart() public {
L2CustomGateway gateway = new L2CustomGateway();
vm.expectRevert("INVALID_COUNTERPART");
L2CustomGateway(gateway).initialize(address(0), router);
}
function test_initialize_revert_AlreadyInit() public {
L2CustomGateway gateway = new L2CustomGateway();
L2CustomGateway(gateway).initialize(l1Counterpart, router);
vm.expectRevert("ALREADY_INIT");
L2CustomGateway(gateway).initialize(l1Counterpart, router);
}
function test_outboundTransfer() public virtual override {
// create and init custom l2Token
address l2CustomToken = _registerToken();
// mint token to user
deal(l2CustomToken, sender, 100 ether);
// withdrawal params
bytes memory data = new bytes(0);
// events
uint256 expectedId = 0;
bytes memory expectedData =
l2CustomGateway.getOutboundCalldata(l1CustomToken, sender, receiver, amount, data);
vm.expectEmit(true, true, true, true);
emit TxToL1(sender, l1Counterpart, expectedId, expectedData);
vm.expectEmit(true, true, true, true);
emit WithdrawalInitiated(l1CustomToken, sender, receiver, expectedId, 0, amount);
// withdraw
vm.etch(0x0000000000000000000000000000000000000064, address(arbSysMock).code);
vm.prank(sender);
l2CustomGateway.outboundTransfer(l1CustomToken, receiver, amount, 0, 0, data);
}
function test_outboundTransfer_4Args() public virtual override {
// create and init custom l2Token
address l2CustomToken = _registerToken();
// mint token to user
deal(l2CustomToken, sender, 100 ether);
// withdrawal params
bytes memory data = new bytes(0);
// events
uint256 expectedId = 0;
bytes memory expectedData =
l2CustomGateway.getOutboundCalldata(l1CustomToken, sender, receiver, amount, data);
vm.expectEmit(true, true, true, true);
emit TxToL1(sender, l1Counterpart, expectedId, expectedData);
vm.expectEmit(true, true, true, true);
emit WithdrawalInitiated(l1CustomToken, sender, receiver, expectedId, 0, amount);
// withdraw
vm.etch(0x0000000000000000000000000000000000000064, address(arbSysMock).code);
vm.prank(sender);
l2CustomGateway.outboundTransfer(l1CustomToken, receiver, amount, data);
}
function test_outboundTransfer_revert_NotExpectedL1Token() public virtual override {
// create and init custom l2Token
address l2CustomToken = _registerToken();
// mock invalid L1 token ref
address notOriginalL1Token = makeAddr("notOriginalL1Token");
vm.mockCall(
address(l2CustomToken),
abi.encodeWithSignature("l1Address()"),
abi.encode(notOriginalL1Token)
);
vm.expectRevert("NOT_EXPECTED_L1_TOKEN");
l2Gateway.outboundTransfer(l1CustomToken, address(101), 200, 0, 0, new bytes(0));
}
function test_postUpgradeInit_revert_NotFromAdmin() public {
ProxyAdmin pa = new ProxyAdmin();
L2CustomGateway _l2Gateway = new L2CustomGateway();
L2CustomGateway proxy = L2CustomGateway(
address(new TransparentUpgradeableProxy(address(_l2Gateway), address(pa), ""))
);
// no other logic implemented currently
vm.expectRevert("NOT_FROM_ADMIN");
proxy.postUpgradeInit();
}
function test_registerTokenFromL1() public {
address[] memory l1Tokens = new address[](2);
l1Tokens[0] = makeAddr("l1Token0");
l1Tokens[1] = makeAddr("l1Token1");
address[] memory l2Tokens = new address[](2);
l2Tokens[0] = makeAddr("l2Token0");
l2Tokens[1] = makeAddr("l2Token1");
// expect events
vm.expectEmit(true, true, true, true);
emit TokenSet(l1Tokens[0], l2Tokens[0]);
emit TokenSet(l1Tokens[1], l2Tokens[1]);
// register
vm.prank(AddressAliasHelper.applyL1ToL2Alias(l1Counterpart));
l2CustomGateway.registerTokenFromL1(l1Tokens, l2Tokens);
// checks
assertEq(l2CustomGateway.l1ToL2Token(l1Tokens[0]), l2Tokens[0], "Invalid registeration 0");
assertEq(l2CustomGateway.l1ToL2Token(l1Tokens[1]), l2Tokens[1], "Invalid registeration 1");
}
function test_registerTokenFromL1_revert_OnlyCounterpartGateway() public {
vm.expectRevert("ONLY_COUNTERPART_GATEWAY");
l2CustomGateway.registerTokenFromL1(new address[](0), new address[](0));
}
////
// Internal helper functions
////
function _registerToken() internal virtual returns (address) {
address[] memory l1Tokens = new address[](1);
l1Tokens[0] = l1CustomToken;
address[] memory l2Tokens = new address[](1);
l2Tokens[0] = address(new L2CustomToken(address(l2CustomGateway), address(l1CustomToken)));
vm.prank(AddressAliasHelper.applyL1ToL2Alias(l1Counterpart));
l2CustomGateway.registerTokenFromL1(l1Tokens, l2Tokens);
return l2Tokens[0];
}
////
// Event declarations
////
event TokenSet(address indexed l1Address, address indexed l2Address);
}
contract L2CustomToken is L2GatewayToken {
constructor(address _l2CustomGateway, address _l1CustomToken) {
L2GatewayToken._initialize("L2 token", "L2", 18, _l2CustomGateway, _l1CustomToken);
}
}
contract Empty {}