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 }