github.com/ethereum-optimism/optimism@v1.7.2/packages/contracts-bedrock/test/periphery/TransferOnion.t.sol (about)

     1  // SPDX-License-Identifier: MIT
     2  pragma solidity 0.8.15;
     3  
     4  // Testing utilities
     5  import { Test } from "forge-std/Test.sol";
     6  import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
     7  
     8  // Target contract
     9  import { TransferOnion } from "src/periphery/TransferOnion.sol";
    10  
    11  /// @title  TransferOnionTest
    12  /// @notice Test coverage of TransferOnion
    13  contract TransferOnionTest is Test {
    14      /// @notice TransferOnion
    15      TransferOnion internal onion;
    16  
    17      /// @notice Token constructor argument
    18      address internal _token;
    19  
    20      /// @notice Sender constructor argument
    21      address internal _sender;
    22  
    23      /// @notice Sets up addresses, deploys contracts and funds the owner.
    24      function setUp() public {
    25          ERC20 token = new ERC20("Token", "TKN");
    26          _token = address(token);
    27          _sender = makeAddr("sender");
    28      }
    29  
    30      /// @notice Deploy the TransferOnion with a dummy shell.
    31      function _deploy() public {
    32          _deploy(bytes32(0));
    33      }
    34  
    35      /// @notice Deploy the TransferOnion with a specific shell.
    36      function _deploy(bytes32 _shell) public {
    37          onion = new TransferOnion({ _token: ERC20(_token), _sender: _sender, _shell: _shell });
    38      }
    39  
    40      /// @notice Build the onion data.
    41      function _onionize(TransferOnion.Layer[] memory _layers)
    42          public
    43          pure
    44          returns (bytes32, TransferOnion.Layer[] memory)
    45      {
    46          uint256 length = _layers.length;
    47          bytes32 hash = bytes32(0);
    48          for (uint256 i; i < length; i++) {
    49              TransferOnion.Layer memory layer = _layers[i];
    50              _layers[i].shell = hash;
    51              hash = keccak256(abi.encode(layer.recipient, layer.amount, hash));
    52          }
    53          return (hash, _layers);
    54      }
    55  
    56      /// @notice The constructor sets the variables as expected.
    57      function test_constructor_succeeds() external {
    58          _deploy();
    59  
    60          assertEq(address(onion.TOKEN()), _token);
    61          assertEq(onion.SENDER(), _sender);
    62          assertEq(onion.shell(), bytes32(0));
    63      }
    64  
    65      /// @notice Tests unwrapping the onion.
    66      function test_unwrap_succeeds() external {
    67          // Commit to transferring tiny amounts of tokens
    68          TransferOnion.Layer[] memory _layers = new TransferOnion.Layer[](2);
    69          _layers[0] = TransferOnion.Layer(address(1), 1, bytes32(0));
    70          _layers[1] = TransferOnion.Layer(address(2), 2, bytes32(0));
    71  
    72          // Build the onion shell
    73          (bytes32 shell, TransferOnion.Layer[] memory layers) = _onionize(_layers);
    74          _deploy(shell);
    75  
    76          assertEq(onion.shell(), shell);
    77  
    78          address token = address(onion.TOKEN());
    79          address sender = onion.SENDER();
    80  
    81          // give 3 units of token to sender
    82          deal(token, onion.SENDER(), 3);
    83          vm.prank(sender);
    84          ERC20(token).approve(address(onion), 3);
    85  
    86          // To build the inputs, to `peel`, need to reverse the list
    87          TransferOnion.Layer[] memory inputs = new TransferOnion.Layer[](2);
    88          int256 length = int256(layers.length);
    89          for (int256 i = length - 1; i >= 0; i--) {
    90              uint256 ui = uint256(i);
    91              uint256 revidx = uint256(length) - ui - 1;
    92              TransferOnion.Layer memory layer = layers[ui];
    93              inputs[revidx] = layer;
    94          }
    95  
    96          // The accounts have no balance
    97          assertEq(ERC20(_token).balanceOf(address(1)), 0);
    98          assertEq(ERC20(_token).balanceOf(address(2)), 0);
    99  
   100          onion.peel(inputs);
   101  
   102          // Now the accounts have the expected balance
   103          assertEq(ERC20(_token).balanceOf(address(1)), 1);
   104          assertEq(ERC20(_token).balanceOf(address(2)), 2);
   105      }
   106  }