github.com/ethereum-optimism/optimism@v1.7.2/packages/contracts-bedrock/test/L1/L1ERC721Bridge.t.sol (about) 1 // SPDX-License-Identifier: MIT 2 pragma solidity 0.8.15; 3 4 // Testing utilities 5 import { Bridge_Initializer } from "test/setup/Bridge_Initializer.sol"; 6 import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; 7 8 // Target contract dependencies 9 import { L2ERC721Bridge } from "src/L2/L2ERC721Bridge.sol"; 10 import { Predeploys } from "src/libraries/Predeploys.sol"; 11 import { SuperchainConfig } from "src/L1/SuperchainConfig.sol"; 12 import { CrossDomainMessenger } from "src/universal/CrossDomainMessenger.sol"; 13 14 // Target contract 15 import { L1ERC721Bridge } from "src/L1/L1ERC721Bridge.sol"; 16 17 /// @dev Test ERC721 contract. 18 contract TestERC721 is ERC721 { 19 constructor() ERC721("Test", "TST") { } 20 21 function mint(address to, uint256 tokenId) public { 22 _mint(to, tokenId); 23 } 24 } 25 26 contract L1ERC721Bridge_Test is Bridge_Initializer { 27 TestERC721 internal localToken; 28 TestERC721 internal remoteToken; 29 uint256 internal constant tokenId = 1; 30 31 event ERC721BridgeInitiated( 32 address indexed localToken, 33 address indexed remoteToken, 34 address indexed from, 35 address to, 36 uint256 tokenId, 37 bytes extraData 38 ); 39 40 event ERC721BridgeFinalized( 41 address indexed localToken, 42 address indexed remoteToken, 43 address indexed from, 44 address to, 45 uint256 tokenId, 46 bytes extraData 47 ); 48 49 /// @dev Sets up the testing environment. 50 /// @notice Marked virtual to be overridden in 51 /// test/kontrol/deployment/DeploymentSummary.t.sol 52 function setUp() public virtual override { 53 super.setUp(); 54 55 localToken = new TestERC721(); 56 remoteToken = new TestERC721(); 57 58 // Mint alice a token. 59 localToken.mint(alice, tokenId); 60 61 // Approve the bridge to transfer the token. 62 vm.prank(alice); 63 localToken.approve(address(l1ERC721Bridge), tokenId); 64 } 65 66 /// @dev Tests that the impl is created with the correct values. 67 /// @notice Marked virtual to be overridden in 68 /// test/kontrol/deployment/DeploymentSummary.t.sol 69 function test_constructor_succeeds() public virtual { 70 L1ERC721Bridge impl = L1ERC721Bridge(deploy.mustGetAddress("L1ERC721Bridge")); 71 assertEq(address(impl.MESSENGER()), address(0)); 72 assertEq(address(impl.messenger()), address(0)); 73 assertEq(address(impl.OTHER_BRIDGE()), Predeploys.L2_ERC721_BRIDGE); 74 assertEq(address(impl.otherBridge()), Predeploys.L2_ERC721_BRIDGE); 75 assertEq(address(impl.superchainConfig()), address(0)); 76 } 77 78 /// @dev Tests that the proxy is initialized with the correct values. 79 function test_initialize_succeeds() public { 80 assertEq(address(l1ERC721Bridge.MESSENGER()), address(l1CrossDomainMessenger)); 81 assertEq(address(l1ERC721Bridge.messenger()), address(l1CrossDomainMessenger)); 82 assertEq(address(l1ERC721Bridge.OTHER_BRIDGE()), Predeploys.L2_ERC721_BRIDGE); 83 assertEq(address(l1ERC721Bridge.otherBridge()), Predeploys.L2_ERC721_BRIDGE); 84 assertEq(address(l1ERC721Bridge.superchainConfig()), address(superchainConfig)); 85 } 86 87 /// @dev Tests that the ERC721 can be bridged successfully. 88 function test_bridgeERC721_succeeds() public { 89 // Expect a call to the messenger. 90 vm.expectCall( 91 address(l1CrossDomainMessenger), 92 abi.encodeCall( 93 l1CrossDomainMessenger.sendMessage, 94 ( 95 address(l2ERC721Bridge), 96 abi.encodeCall( 97 L2ERC721Bridge.finalizeBridgeERC721, 98 (address(remoteToken), address(localToken), alice, alice, tokenId, hex"5678") 99 ), 100 1234 101 ) 102 ) 103 ); 104 105 // Expect an event to be emitted. 106 vm.expectEmit(true, true, true, true); 107 emit ERC721BridgeInitiated(address(localToken), address(remoteToken), alice, alice, tokenId, hex"5678"); 108 109 // Bridge the token. 110 vm.prank(alice); 111 l1ERC721Bridge.bridgeERC721(address(localToken), address(remoteToken), tokenId, 1234, hex"5678"); 112 113 // Token is locked in the bridge. 114 assertEq(l1ERC721Bridge.deposits(address(localToken), address(remoteToken), tokenId), true); 115 assertEq(localToken.ownerOf(tokenId), address(l1ERC721Bridge)); 116 } 117 118 /// @dev Tests that the ERC721 bridge reverts for non externally owned accounts. 119 function test_bridgeERC721_fromContract_reverts() external { 120 // Bridge the token. 121 vm.etch(alice, hex"01"); 122 vm.prank(alice); 123 vm.expectRevert("ERC721Bridge: account is not externally owned"); 124 l1ERC721Bridge.bridgeERC721(address(localToken), address(remoteToken), tokenId, 1234, hex"5678"); 125 126 // Token is not locked in the bridge. 127 assertEq(l1ERC721Bridge.deposits(address(localToken), address(remoteToken), tokenId), false); 128 assertEq(localToken.ownerOf(tokenId), alice); 129 } 130 131 /// @dev Tests that the ERC721 bridge reverts for a zero address local token. 132 function test_bridgeERC721_localTokenZeroAddress_reverts() external { 133 // Bridge the token. 134 vm.prank(alice); 135 vm.expectRevert(bytes("")); 136 l1ERC721Bridge.bridgeERC721(address(0), address(remoteToken), tokenId, 1234, hex"5678"); 137 138 // Token is not locked in the bridge. 139 assertEq(l1ERC721Bridge.deposits(address(localToken), address(remoteToken), tokenId), false); 140 assertEq(localToken.ownerOf(tokenId), alice); 141 } 142 143 /// @dev Tests that the ERC721 bridge reverts for a zero address remote token. 144 function test_bridgeERC721_remoteTokenZeroAddress_reverts() external { 145 // Bridge the token. 146 vm.prank(alice); 147 vm.expectRevert("L1ERC721Bridge: remote token cannot be address(0)"); 148 l1ERC721Bridge.bridgeERC721(address(localToken), address(0), tokenId, 1234, hex"5678"); 149 150 // Token is not locked in the bridge. 151 assertEq(l1ERC721Bridge.deposits(address(localToken), address(remoteToken), tokenId), false); 152 assertEq(localToken.ownerOf(tokenId), alice); 153 } 154 155 /// @dev Tests that the ERC721 bridge reverts for an incorrect owner. 156 function test_bridgeERC721_wrongOwner_reverts() external { 157 // Bridge the token. 158 vm.prank(bob); 159 vm.expectRevert("ERC721: transfer from incorrect owner"); 160 l1ERC721Bridge.bridgeERC721(address(localToken), address(remoteToken), tokenId, 1234, hex"5678"); 161 162 // Token is not locked in the bridge. 163 assertEq(l1ERC721Bridge.deposits(address(localToken), address(remoteToken), tokenId), false); 164 assertEq(localToken.ownerOf(tokenId), alice); 165 } 166 167 /// @dev Tests that the ERC721 bridge successfully sends a token 168 /// to a different address than the owner. 169 function test_bridgeERC721To_succeeds() external { 170 // Expect a call to the messenger. 171 vm.expectCall( 172 address(l1CrossDomainMessenger), 173 abi.encodeCall( 174 l1CrossDomainMessenger.sendMessage, 175 ( 176 address(Predeploys.L2_ERC721_BRIDGE), 177 abi.encodeCall( 178 L2ERC721Bridge.finalizeBridgeERC721, 179 (address(remoteToken), address(localToken), alice, bob, tokenId, hex"5678") 180 ), 181 1234 182 ) 183 ) 184 ); 185 186 // Expect an event to be emitted. 187 vm.expectEmit(true, true, true, true); 188 emit ERC721BridgeInitiated(address(localToken), address(remoteToken), alice, bob, tokenId, hex"5678"); 189 190 // Bridge the token. 191 vm.prank(alice); 192 l1ERC721Bridge.bridgeERC721To(address(localToken), address(remoteToken), bob, tokenId, 1234, hex"5678"); 193 194 // Token is locked in the bridge. 195 assertEq(l1ERC721Bridge.deposits(address(localToken), address(remoteToken), tokenId), true); 196 assertEq(localToken.ownerOf(tokenId), address(l1ERC721Bridge)); 197 } 198 199 /// @dev Tests that the ERC721 bridge reverts for non externally owned accounts 200 /// when sending to a different address than the owner. 201 function test_bridgeERC721To_localTokenZeroAddress_reverts() external { 202 // Bridge the token. 203 vm.prank(alice); 204 vm.expectRevert(bytes("")); 205 l1ERC721Bridge.bridgeERC721To(address(0), address(remoteToken), bob, tokenId, 1234, hex"5678"); 206 207 // Token is not locked in the bridge. 208 assertEq(l1ERC721Bridge.deposits(address(localToken), address(remoteToken), tokenId), false); 209 assertEq(localToken.ownerOf(tokenId), alice); 210 } 211 212 /// @dev Tests that the ERC721 bridge reverts for a zero address remote token 213 /// when sending to a different address than the owner. 214 function test_bridgeERC721To_remoteTokenZeroAddress_reverts() external { 215 // Bridge the token. 216 vm.prank(alice); 217 vm.expectRevert("L1ERC721Bridge: remote token cannot be address(0)"); 218 l1ERC721Bridge.bridgeERC721To(address(localToken), address(0), bob, tokenId, 1234, hex"5678"); 219 220 // Token is not locked in the bridge. 221 assertEq(l1ERC721Bridge.deposits(address(localToken), address(remoteToken), tokenId), false); 222 assertEq(localToken.ownerOf(tokenId), alice); 223 } 224 225 /// @dev Tests that the ERC721 bridge reverts for an incorrect owner 226 //// when sending to a different address than the owner. 227 function test_bridgeERC721To_wrongOwner_reverts() external { 228 // Bridge the token. 229 vm.prank(bob); 230 vm.expectRevert("ERC721: transfer from incorrect owner"); 231 l1ERC721Bridge.bridgeERC721To(address(localToken), address(remoteToken), bob, tokenId, 1234, hex"5678"); 232 233 // Token is not locked in the bridge. 234 assertEq(l1ERC721Bridge.deposits(address(localToken), address(remoteToken), tokenId), false); 235 assertEq(localToken.ownerOf(tokenId), alice); 236 } 237 238 /// @dev Tests that the ERC721 bridge successfully finalizes a withdrawal. 239 function test_finalizeBridgeERC721_succeeds() external { 240 // Bridge the token. 241 vm.prank(alice); 242 l1ERC721Bridge.bridgeERC721(address(localToken), address(remoteToken), tokenId, 1234, hex"5678"); 243 244 // Expect an event to be emitted. 245 vm.expectEmit(true, true, true, true); 246 emit ERC721BridgeFinalized(address(localToken), address(remoteToken), alice, alice, tokenId, hex"5678"); 247 248 // Finalize a withdrawal. 249 vm.mockCall( 250 address(l1CrossDomainMessenger), 251 abi.encodeWithSelector(l1CrossDomainMessenger.xDomainMessageSender.selector), 252 abi.encode(Predeploys.L2_ERC721_BRIDGE) 253 ); 254 vm.prank(address(l1CrossDomainMessenger)); 255 l1ERC721Bridge.finalizeBridgeERC721(address(localToken), address(remoteToken), alice, alice, tokenId, hex"5678"); 256 257 // Token is not locked in the bridge. 258 assertEq(l1ERC721Bridge.deposits(address(localToken), address(remoteToken), tokenId), false); 259 assertEq(localToken.ownerOf(tokenId), alice); 260 } 261 262 /// @dev Tests that the ERC721 bridge finalize reverts when not called 263 /// by the remote bridge. 264 function test_finalizeBridgeERC721_notViaLocalMessenger_reverts() external { 265 // Finalize a withdrawal. 266 vm.prank(alice); 267 vm.expectRevert("ERC721Bridge: function can only be called from the other bridge"); 268 l1ERC721Bridge.finalizeBridgeERC721(address(localToken), address(remoteToken), alice, alice, tokenId, hex"5678"); 269 } 270 271 /// @dev Tests that the ERC721 bridge finalize reverts when not called 272 /// from the remote messenger. 273 function test_finalizeBridgeERC721_notFromRemoteMessenger_reverts() external { 274 // Finalize a withdrawal. 275 vm.mockCall( 276 address(l1CrossDomainMessenger), 277 abi.encodeWithSelector(l1CrossDomainMessenger.xDomainMessageSender.selector), 278 abi.encode(alice) 279 ); 280 vm.prank(address(l1CrossDomainMessenger)); 281 vm.expectRevert("ERC721Bridge: function can only be called from the other bridge"); 282 l1ERC721Bridge.finalizeBridgeERC721(address(localToken), address(remoteToken), alice, alice, tokenId, hex"5678"); 283 } 284 285 /// @dev Tests that the ERC721 bridge finalize reverts when the local token 286 /// is set as the bridge itself. 287 function test_finalizeBridgeERC721_selfToken_reverts() external { 288 // Finalize a withdrawal. 289 vm.mockCall( 290 address(l1CrossDomainMessenger), 291 abi.encodeWithSelector(l1CrossDomainMessenger.xDomainMessageSender.selector), 292 abi.encode(Predeploys.L2_ERC721_BRIDGE) 293 ); 294 vm.prank(address(l1CrossDomainMessenger)); 295 vm.expectRevert("L1ERC721Bridge: local token cannot be self"); 296 l1ERC721Bridge.finalizeBridgeERC721( 297 address(l1ERC721Bridge), address(remoteToken), alice, alice, tokenId, hex"5678" 298 ); 299 } 300 301 /// @dev Tests that the ERC721 bridge finalize reverts when the remote token 302 /// is not escrowed in the L1 bridge. 303 function test_finalizeBridgeERC721_notEscrowed_reverts() external { 304 // Finalize a withdrawal. 305 vm.mockCall( 306 address(l1CrossDomainMessenger), 307 abi.encodeWithSelector(l1CrossDomainMessenger.xDomainMessageSender.selector), 308 abi.encode(Predeploys.L2_ERC721_BRIDGE) 309 ); 310 vm.prank(address(l1CrossDomainMessenger)); 311 vm.expectRevert("L1ERC721Bridge: Token ID is not escrowed in the L1 Bridge"); 312 l1ERC721Bridge.finalizeBridgeERC721(address(localToken), address(remoteToken), alice, alice, tokenId, hex"5678"); 313 } 314 } 315 316 contract L1ERC721Bridge_Pause_Test is Bridge_Initializer { 317 /// @dev Verifies that the `paused` accessor returns the same value as the `paused` function of the 318 /// `superchainConfig`. 319 function test_paused_succeeds() external { 320 assertEq(l1ERC721Bridge.paused(), superchainConfig.paused()); 321 } 322 323 /// @dev Ensures that the `paused` function of the bridge contract actually calls the `paused` function of the 324 /// `superchainConfig`. 325 function test_pause_callsSuperchainConfig_succeeds() external { 326 vm.expectCall(address(superchainConfig), abi.encodeWithSelector(SuperchainConfig.paused.selector)); 327 l1ERC721Bridge.paused(); 328 } 329 330 /// @dev Checks that the `paused` state of the bridge matches the `paused` state of the `superchainConfig` after 331 /// it's been changed. 332 function test_pause_matchesSuperchainConfig_succeeds() external { 333 assertFalse(l1StandardBridge.paused()); 334 assertEq(l1StandardBridge.paused(), superchainConfig.paused()); 335 336 vm.prank(superchainConfig.guardian()); 337 superchainConfig.pause("identifier"); 338 339 assertTrue(l1StandardBridge.paused()); 340 assertEq(l1StandardBridge.paused(), superchainConfig.paused()); 341 } 342 } 343 344 contract L1ERC721Bridge_Pause_TestFail is Bridge_Initializer { 345 /// @dev Sets up the test by pausing the bridge, giving ether to the bridge and mocking 346 /// the calls to the xDomainMessageSender so that it returns the correct value. 347 function setUp() public override { 348 super.setUp(); 349 vm.prank(superchainConfig.guardian()); 350 superchainConfig.pause("identifier"); 351 assertTrue(l1ERC721Bridge.paused()); 352 353 vm.mockCall( 354 address(l1ERC721Bridge.messenger()), 355 abi.encodeWithSelector(CrossDomainMessenger.xDomainMessageSender.selector), 356 abi.encode(address(l1ERC721Bridge.otherBridge())) 357 ); 358 } 359 360 // @dev Ensures that the `bridgeERC721` function reverts when the bridge is paused. 361 function test_pause_finalizeBridgeERC721_reverts() external { 362 vm.prank(address(l1ERC721Bridge.messenger())); 363 vm.expectRevert("L1ERC721Bridge: paused"); 364 l1ERC721Bridge.finalizeBridgeERC721({ 365 _localToken: address(0), 366 _remoteToken: address(0), 367 _from: address(0), 368 _to: address(0), 369 _tokenId: 0, 370 _extraData: hex"" 371 }); 372 } 373 }