github.com/ethereum-optimism/optimism@v1.7.2/packages/contracts-bedrock/test/periphery/AssetReceiver.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 { TestERC20 } from "test/mocks/TestERC20.sol";
     7  import { TestERC721 } from "test/mocks/TestERC721.sol";
     8  import { AssetReceiver } from "src/periphery/AssetReceiver.sol";
     9  
    10  contract AssetReceiver_Initializer is Test {
    11      address alice = address(128);
    12      address bob = address(256);
    13  
    14      uint8 immutable DEFAULT_TOKEN_ID = 0;
    15  
    16      TestERC20 testERC20;
    17      TestERC721 testERC721;
    18      AssetReceiver assetReceiver;
    19  
    20      event ReceivedETH(address indexed from, uint256 amount);
    21      event WithdrewETH(address indexed withdrawer, address indexed recipient, uint256 amount);
    22      event WithdrewERC20(address indexed withdrawer, address indexed recipient, address indexed asset, uint256 amount);
    23      event WithdrewERC721(address indexed withdrawer, address indexed recipient, address indexed asset, uint256 id);
    24  
    25      function setUp() public {
    26          // Deploy ERC20 and ERC721 tokens
    27          testERC20 = new TestERC20();
    28          testERC721 = new TestERC721();
    29  
    30          // Deploy AssetReceiver contract
    31          assetReceiver = new AssetReceiver(address(alice));
    32          vm.label(address(assetReceiver), "AssetReceiver");
    33  
    34          // Give alice and bob some ETH
    35          vm.deal(alice, 1 ether);
    36          vm.deal(bob, 1 ether);
    37  
    38          testERC721.mint(alice, DEFAULT_TOKEN_ID);
    39  
    40          vm.label(alice, "alice");
    41          vm.label(bob, "bob");
    42      }
    43  }
    44  
    45  contract AssetReceiverTest is AssetReceiver_Initializer {
    46      /// @notice Tests if the owner was set correctly during deploy.
    47      function test_constructor_succeeds() external {
    48          assertEq(address(alice), assetReceiver.owner());
    49      }
    50  
    51      /// @notice Tests that receive works as inteded.
    52      function test_receive_succeeds() external {
    53          // Check that contract balance is 0 initially
    54          assertEq(address(assetReceiver).balance, 0);
    55  
    56          vm.expectEmit(address(assetReceiver));
    57          emit ReceivedETH(alice, 100);
    58          // Send funds
    59          vm.prank(alice);
    60          (bool success,) = address(assetReceiver).call{ value: 100 }(hex"");
    61  
    62          // Compare balance after the tx sent
    63          assertTrue(success);
    64          assertEq(address(assetReceiver).balance, 100);
    65      }
    66  
    67      /// @notice Tests withdrawETH function with only an address
    68      ///         as an argument, called by owner.
    69      function test_withdrawETH_succeeds() external {
    70          // Check contract initial balance
    71          assertEq(address(assetReceiver).balance, 0);
    72          // Fund contract with 1 eth and check caller and contract balances
    73          vm.deal(address(assetReceiver), 1 ether);
    74          assertEq(address(assetReceiver).balance, 1 ether);
    75  
    76          assertEq(address(alice).balance, 1 ether);
    77  
    78          vm.expectEmit(address(assetReceiver));
    79          emit WithdrewETH(alice, alice, 1 ether);
    80  
    81          // call withdrawETH
    82          vm.prank(alice);
    83          assetReceiver.withdrawETH(payable(alice));
    84  
    85          // check balances after the call
    86          assertEq(address(assetReceiver).balance, 0);
    87          assertEq(address(alice).balance, 2 ether);
    88      }
    89  
    90      /// @notice withdrawETH should fail if called by non-owner.
    91      function test_withdrawETH_unauthorized_reverts() external {
    92          vm.deal(address(assetReceiver), 1 ether);
    93          vm.expectRevert("UNAUTHORIZED");
    94          assetReceiver.withdrawETH(payable(alice));
    95      }
    96  
    97      /// @notice Similar as withdrawETH but specify amount to withdraw.
    98      function test_withdrawETHwithAmount_succeeds() external {
    99          assertEq(address(assetReceiver).balance, 0);
   100  
   101          vm.deal(address(assetReceiver), 1 ether);
   102          assertEq(address(assetReceiver).balance, 1 ether);
   103  
   104          assertEq(address(alice).balance, 1 ether);
   105  
   106          vm.expectEmit(address(assetReceiver));
   107          emit WithdrewETH(alice, alice, 0.5 ether);
   108  
   109          // call withdrawETH
   110          vm.prank(alice);
   111          assetReceiver.withdrawETH(payable(alice), 0.5 ether);
   112  
   113          // check balances after the call
   114          assertEq(address(assetReceiver).balance, 0.5 ether);
   115          assertEq(address(alice).balance, 1.5 ether);
   116      }
   117  
   118      /// @notice withdrawETH with address and amount as arguments called by non-owner.
   119      function test_withdrawETHwithAmount_unauthorized_reverts() external {
   120          vm.deal(address(assetReceiver), 1 ether);
   121          vm.expectRevert("UNAUTHORIZED");
   122          assetReceiver.withdrawETH(payable(alice), 0.5 ether);
   123      }
   124  
   125      /// @notice Test withdrawERC20 with token and address arguments, from owner.
   126      function test_withdrawERC20_succeeds() external {
   127          // check balances before the call
   128          assertEq(testERC20.balanceOf(address(assetReceiver)), 0);
   129  
   130          deal(address(testERC20), address(assetReceiver), 100_000);
   131          assertEq(testERC20.balanceOf(address(assetReceiver)), 100_000);
   132          assertEq(testERC20.balanceOf(alice), 0);
   133  
   134          vm.expectEmit(address(assetReceiver));
   135          emit WithdrewERC20(alice, alice, address(testERC20), 100_000);
   136  
   137          // call withdrawERC20
   138          vm.prank(alice);
   139          assetReceiver.withdrawERC20(testERC20, alice);
   140  
   141          // check balances after the call
   142          assertEq(testERC20.balanceOf(alice), 100_000);
   143          assertEq(testERC20.balanceOf(address(assetReceiver)), 0);
   144      }
   145  
   146      /// @notice Same as withdrawERC20 but call from non-owner.
   147      function test_withdrawERC20_unauthorized_reverts() external {
   148          deal(address(testERC20), address(assetReceiver), 100_000);
   149          vm.expectRevert("UNAUTHORIZED");
   150          assetReceiver.withdrawERC20(testERC20, alice);
   151      }
   152  
   153      /// @notice Similar as withdrawERC20 but specify amount to withdraw.
   154      function test_withdrawERC20withAmount_succeeds() external {
   155          // check balances before the call
   156          assertEq(testERC20.balanceOf(address(assetReceiver)), 0);
   157  
   158          deal(address(testERC20), address(assetReceiver), 100_000);
   159          assertEq(testERC20.balanceOf(address(assetReceiver)), 100_000);
   160          assertEq(testERC20.balanceOf(alice), 0);
   161  
   162          vm.expectEmit(address(assetReceiver));
   163          emit WithdrewERC20(alice, alice, address(testERC20), 50_000);
   164  
   165          // call withdrawERC20
   166          vm.prank(alice);
   167          assetReceiver.withdrawERC20(testERC20, alice, 50_000);
   168  
   169          // check balances after the call
   170          assertEq(testERC20.balanceOf(alice), 50_000);
   171          assertEq(testERC20.balanceOf(address(assetReceiver)), 50_000);
   172      }
   173  
   174      /// @notice Similar as withdrawERC20 with amount but call from non-owner.
   175      function test_withdrawERC20withAmount_unauthorized_reverts() external {
   176          deal(address(testERC20), address(assetReceiver), 100_000);
   177          vm.expectRevert("UNAUTHORIZED");
   178          assetReceiver.withdrawERC20(testERC20, alice, 50_000);
   179      }
   180  
   181      /// @notice Test withdrawERC721 from owner.
   182      function test_withdrawERC721_succeeds() external {
   183          // Check owner of the token before calling withdrawERC721
   184          assertEq(testERC721.ownerOf(DEFAULT_TOKEN_ID), alice);
   185  
   186          // Send the token from alice to the contract
   187          vm.prank(alice);
   188          testERC721.transferFrom(alice, address(assetReceiver), DEFAULT_TOKEN_ID);
   189          assertEq(testERC721.ownerOf(DEFAULT_TOKEN_ID), address(assetReceiver));
   190  
   191          vm.expectEmit(address(assetReceiver));
   192          emit WithdrewERC721(alice, alice, address(testERC721), DEFAULT_TOKEN_ID);
   193  
   194          // Call withdrawERC721
   195          vm.prank(alice);
   196          assetReceiver.withdrawERC721(testERC721, alice, DEFAULT_TOKEN_ID);
   197  
   198          // Check the owner after the call
   199          assertEq(testERC721.ownerOf(DEFAULT_TOKEN_ID), alice);
   200      }
   201  
   202      /// @notice Similar as withdrawERC721 but call from non-owner.
   203      function test_withdrawERC721_unauthorized_reverts() external {
   204          vm.prank(alice);
   205          testERC721.transferFrom(alice, address(assetReceiver), DEFAULT_TOKEN_ID);
   206          vm.expectRevert("UNAUTHORIZED");
   207          assetReceiver.withdrawERC721(testERC721, alice, DEFAULT_TOKEN_ID);
   208      }
   209  }