diff --git a/packages/contracts-bedrock/test/L2/OptimismMintableERC721.t.sol b/packages/contracts-bedrock/test/L2/OptimismMintableERC721.t.sol index d66a702d01ac5..696cff0842e4d 100644 --- a/packages/contracts-bedrock/test/L2/OptimismMintableERC721.t.sol +++ b/packages/contracts-bedrock/test/L2/OptimismMintableERC721.t.sol @@ -3,10 +3,12 @@ pragma solidity 0.8.15; import { ERC721, IERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import { IERC721Enumerable } from "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol"; +import { IERC721Metadata } from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol"; import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; import { CommonTest } from "test/setup/CommonTest.sol"; import { OptimismMintableERC721, IOptimismMintableERC721 } from "src/L2/OptimismMintableERC721.sol"; +import { SemverComp } from "src/libraries/SemverComp.sol"; /// @title OptimismMintableERC721_TestInit /// @notice Reusable test initialization for `OptimismMintableERC721` tests. @@ -37,15 +39,18 @@ abstract contract OptimismMintableERC721_TestInit is CommonTest { /// @notice Tests the `constructor` of the `OptimismMintableERC721` contract. contract OptimismMintableERC721_Constructor_Test is OptimismMintableERC721_TestInit { /// @notice Tests that the constructor initializes state variables correctly with valid inputs. - function test_constructor_succeeds() external view { - assertEq(L2NFT.name(), "L2NFT"); - assertEq(L2NFT.symbol(), "L2T"); - assertEq(L2NFT.remoteToken(), address(L1NFT)); - assertEq(L2NFT.bridge(), address(l2ERC721Bridge)); - assertEq(L2NFT.remoteChainId(), 1); - assertEq(L2NFT.REMOTE_TOKEN(), address(L1NFT)); - assertEq(L2NFT.BRIDGE(), address(l2ERC721Bridge)); - assertEq(L2NFT.REMOTE_CHAIN_ID(), 1); + function testFuzz_constructor_validParams_succeeds(uint256 _remoteChainId) external { + vm.assume(_remoteChainId != 0); + OptimismMintableERC721 nft = + new OptimismMintableERC721(address(l2ERC721Bridge), _remoteChainId, address(L1NFT), "L2NFT", "L2T"); + assertEq(nft.name(), "L2NFT"); + assertEq(nft.symbol(), "L2T"); + assertEq(nft.remoteToken(), address(L1NFT)); + assertEq(nft.bridge(), address(l2ERC721Bridge)); + assertEq(nft.remoteChainId(), _remoteChainId); + assertEq(nft.REMOTE_TOKEN(), address(L1NFT)); + assertEq(nft.BRIDGE(), address(l2ERC721Bridge)); + assertEq(nft.REMOTE_CHAIN_ID(), _remoteChainId); } /// @notice Tests that the constructor reverts when the bridge address is zero. @@ -72,28 +77,31 @@ contract OptimismMintableERC721_Constructor_Test is OptimismMintableERC721_TestI contract OptimismMintableERC721_SafeMint_Test is OptimismMintableERC721_TestInit { /// @notice Tests that the `safeMint` function successfully mints a token when called by the /// bridge. - function test_safeMint_succeeds() external { + function testFuzz_safeMint_validParams_succeeds(address _to, uint256 _tokenId) external { + vm.assume(_to != address(0)); + vm.assume(_to.code.length == 0); + // Expect a transfer event. vm.expectEmit(true, true, true, true); - emit Transfer(address(0), alice, 1); + emit Transfer(address(0), _to, _tokenId); // Expect a mint event. vm.expectEmit(true, true, true, true); - emit Mint(alice, 1); + emit Mint(_to, _tokenId); // Mint the token. vm.prank(address(l2ERC721Bridge)); - L2NFT.safeMint(alice, 1); + L2NFT.safeMint(_to, _tokenId); - // Token should be owned by alice. - assertEq(L2NFT.ownerOf(1), alice); + // Token should be owned by the recipient. + assertEq(L2NFT.ownerOf(_tokenId), _to); } - /// @notice Tests that the `safeMint` function reverts when called by an address other than the bridge. - function test_safeMint_notBridge_reverts() external { - // Try to mint the token. + /// @notice Tests that the `safeMint` function reverts when called by a non-bridge address. + function testFuzz_safeMint_notBridge_reverts(address _caller) external { + vm.assume(_caller != address(l2ERC721Bridge)); vm.expectRevert("OptimismMintableERC721: only bridge can call this function"); - vm.prank(address(alice)); + vm.prank(_caller); L2NFT.safeMint(alice, 1); } } @@ -103,38 +111,39 @@ contract OptimismMintableERC721_SafeMint_Test is OptimismMintableERC721_TestInit contract OptimismMintableERC721_Burn_Test is OptimismMintableERC721_TestInit { /// @notice Tests that the `burn` function successfully burns a token when called by the /// bridge. - function test_burn_succeeds() external { + function testFuzz_burn_validParams_succeeds(uint256 _tokenId) external { // Mint the token first. vm.prank(address(l2ERC721Bridge)); - L2NFT.safeMint(alice, 1); + L2NFT.safeMint(alice, _tokenId); // Expect a transfer event. vm.expectEmit(true, true, true, true); - emit Transfer(alice, address(0), 1); + emit Transfer(alice, address(0), _tokenId); // Expect a burn event. vm.expectEmit(true, true, true, true); - emit Burn(alice, 1); + emit Burn(alice, _tokenId); // Burn the token. vm.prank(address(l2ERC721Bridge)); - L2NFT.burn(alice, 1); + L2NFT.burn(alice, _tokenId); - // Token should be owned by address(0). + // Token should no longer exist. vm.expectRevert("ERC721: invalid token ID"); - L2NFT.ownerOf(1); + L2NFT.ownerOf(_tokenId); } - /// @notice Tests that the `burn` function reverts when called by an address other than the - /// bridge. - function test_burn_notBridge_reverts() external { + /// @notice Tests that the `burn` function reverts when called by a non-bridge address. + function testFuzz_burn_notBridge_reverts(address _caller) external { + vm.assume(_caller != address(l2ERC721Bridge)); + // Mint the token first. vm.prank(address(l2ERC721Bridge)); L2NFT.safeMint(alice, 1); // Try to burn the token. vm.expectRevert("OptimismMintableERC721: only bridge can call this function"); - vm.prank(address(alice)); + vm.prank(_caller); L2NFT.burn(alice, 1); } } @@ -144,7 +153,7 @@ contract OptimismMintableERC721_Burn_Test is OptimismMintableERC721_TestInit { contract OptimismMintableERC721_SupportsInterface_Test is OptimismMintableERC721_TestInit { /// @notice Tests that the `supportsInterface` function returns true for /// IOptimismMintableERC721, IERC721Enumerable, IERC721 and IERC165 interfaces. - function test_supportsInterface_succeeds() external view { + function test_supportsInterface_supportedInterfaces_succeeds() external view { // Checks if the contract supports the IOptimismMintableERC721 interface. assertTrue(L2NFT.supportsInterface(type(IOptimismMintableERC721).interfaceId)); // Checks if the contract supports the IERC721Enumerable interface. @@ -154,6 +163,26 @@ contract OptimismMintableERC721_SupportsInterface_Test is OptimismMintableERC721 // Checks if the contract supports the IERC165 interface. assertTrue(L2NFT.supportsInterface(type(IERC165).interfaceId)); } + + /// @notice Tests that the `supportsInterface` function returns false for unsupported + /// interfaces. + function testFuzz_supportsInterface_unsupportedInterface_fails(bytes4 _interfaceId) external view { + vm.assume(_interfaceId != type(IOptimismMintableERC721).interfaceId); + vm.assume(_interfaceId != type(IERC721Enumerable).interfaceId); + vm.assume(_interfaceId != type(IERC721).interfaceId); + vm.assume(_interfaceId != type(IERC721Metadata).interfaceId); + vm.assume(_interfaceId != type(IERC165).interfaceId); + assertFalse(L2NFT.supportsInterface(_interfaceId)); + } +} + +/// @title OptimismMintableERC721_Version_Test +/// @notice Tests the `version` function of the `OptimismMintableERC721` contract. +contract OptimismMintableERC721_Version_Test is OptimismMintableERC721_TestInit { + /// @notice Tests that version returns a valid semver string. + function test_version_validFormat_succeeds() external view { + SemverComp.parse(L2NFT.version()); + } } /// @title OptimismMintableERC721_Uncategorized_Test @@ -161,14 +190,14 @@ contract OptimismMintableERC721_SupportsInterface_Test is OptimismMintableERC721 /// `OptimismMintableERC721` contract. contract OptimismMintableERC721_Uncategorized_Test is OptimismMintableERC721_TestInit { /// @notice Tests that the `tokenURI` function returns the correct URI for a minted token. - function test_tokenURI_succeeds() external { + function testFuzz_tokenURI_validTokenId_succeeds(uint256 _tokenId) external { // Mint the token first. vm.prank(address(l2ERC721Bridge)); - L2NFT.safeMint(alice, 1); + L2NFT.safeMint(alice, _tokenId); // Token URI should be correct. assertEq( - L2NFT.tokenURI(1), + L2NFT.tokenURI(_tokenId), string( abi.encodePacked( "ethereum:", @@ -176,7 +205,7 @@ contract OptimismMintableERC721_Uncategorized_Test is OptimismMintableERC721_Tes "@", Strings.toString(1), "/tokenURI?uint256=", - Strings.toString(1) + Strings.toString(_tokenId) ) ) );