From 8a7575b37aa8d901275a955b03e94e99e1086710 Mon Sep 17 00:00:00 2001 From: Yash Date: Thu, 18 Aug 2022 17:28:59 +0530 Subject: [PATCH 1/3] wip: token-erc721 tests --- src/test/token/TokenERC721.t.sol | 370 +++++++++++++++++++++++++++++++ src/test/utils/BaseTest.sol | 6 +- 2 files changed, 373 insertions(+), 3 deletions(-) create mode 100644 src/test/token/TokenERC721.t.sol diff --git a/src/test/token/TokenERC721.t.sol b/src/test/token/TokenERC721.t.sol new file mode 100644 index 000000000..8b9aff970 --- /dev/null +++ b/src/test/token/TokenERC721.t.sol @@ -0,0 +1,370 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.0; + +import { TokenERC721 } from "contracts/token/TokenERC721.sol"; + +// Test imports +import "contracts/lib/TWStrings.sol"; +import "../utils/BaseTest.sol"; +import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; +import "@openzeppelin/contracts/utils/Strings.sol"; + +contract TokenERC721Test is BaseTest { + using StringsUpgradeable for uint256; + + event TokensMinted(address indexed mintedTo, uint256 indexed tokenIdMinted, string uri); + event TokensMintedWithSignature( + address indexed signer, + address indexed mintedTo, + uint256 indexed tokenIdMinted, + TokenERC721.MintRequest mintRequest + ); + event OwnerUpdated(address indexed prevOwner, address indexed newOwner); + + TokenERC721 public tokenContract; + bytes32 internal typehashMintRequest; + bytes32 internal nameHash; + bytes32 internal versionHash; + bytes32 internal typehashEip712; + bytes32 internal domainSeparator; + + bytes private emptyEncodedBytes = abi.encode("", ""); + + TokenERC721.MintRequest _mintrequest; + bytes _signature; + + address internal deployerSigner; + address internal recipient; + + using stdStorage for StdStorage; + + function setUp() public override { + super.setUp(); + deployerSigner = signer; + recipient = address(0x123); + tokenContract = TokenERC721(getContract("TokenERC721")); + + erc20.mint(deployerSigner, 1_000); + vm.deal(deployerSigner, 1_000); + + erc20.mint(recipient, 1_000); + vm.deal(recipient, 1_000); + + typehashMintRequest = keccak256( + "MintRequest(address to,address royaltyRecipient,uint256 royaltyBps,address primarySaleRecipient,string uri,uint256 price,address currency,uint128 validityStartTimestamp,uint128 validityEndTimestamp,bytes32 uid)" + ); + nameHash = keccak256(bytes("TokenERC721")); + versionHash = keccak256(bytes("1")); + typehashEip712 = keccak256( + "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" + ); + domainSeparator = keccak256(abi.encode(typehashEip712, nameHash, versionHash, block.chainid, address(tokenContract))); + + // construct default mintrequest + _mintrequest.to = recipient; + _mintrequest.royaltyRecipient = royaltyRecipient; + _mintrequest.royaltyBps = royaltyBps; + _mintrequest.primarySaleRecipient = saleRecipient; + _mintrequest.uri = "ipfs://"; + _mintrequest.price = 0; + _mintrequest.currency = address(0); + _mintrequest.validityStartTimestamp = 1000; + _mintrequest.validityEndTimestamp = 2000; + _mintrequest.uid = bytes32(0); + + _signature = signMintRequest(_mintrequest, privateKey); + } + + function signMintRequest(TokenERC721.MintRequest memory _request, uint256 _privateKey) + internal + returns (bytes memory) + { + bytes memory encodedRequest = abi.encode( + typehashMintRequest, + _request.to, + _request.royaltyRecipient, + _request.royaltyBps, + _request.primarySaleRecipient, + keccak256(bytes(_request.uri)), + _request.price, + _request.currency, + _request.validityStartTimestamp, + _request.validityEndTimestamp, + _request.uid + ); + bytes32 structHash = keccak256(encodedRequest); + bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); + + (uint8 v, bytes32 r, bytes32 s) = vm.sign(_privateKey, typedDataHash); + bytes memory sig = abi.encodePacked(r, s, v); + + return sig; + } + + /*/////////////////////////////////////////////////////////////// + Unit tests: `mintWithSignature` + //////////////////////////////////////////////////////////////*/ + + function test_state_mintWithSignature_ZeroPrice() public { + vm.warp(1000); + + // initial balances and state + uint256 nextTokenId = tokenContract.nextTokenIdToMint(); + uint256 currentTotalSupply = tokenContract.totalSupply(); + uint256 currentBalanceOfRecipient = tokenContract.balanceOf(recipient); + + // mint with signature + vm.prank(recipient); + tokenContract.mintWithSignature(_mintrequest, _signature); + + // check state after minting + assertEq(tokenContract.nextTokenIdToMint(), nextTokenId + 1); + assertEq(tokenContract.tokenURI(nextTokenId), string(_mintrequest.uri)); + assertEq(tokenContract.totalSupply(), currentTotalSupply + 1); + assertEq(tokenContract.balanceOf(recipient), currentBalanceOfRecipient + 1); + assertEq(tokenContract.ownerOf(nextTokenId), recipient); + } + + function test_state_mintWithSignature_NonZeroPrice_ERC20() public { + vm.warp(1000); + + // update mintrequest data + _mintrequest.price = 1; + _mintrequest.currency = address(erc20); + _signature = signMintRequest(_mintrequest, privateKey); + + // approve erc20 tokens to tokenContract + vm.prank(recipient); + erc20.approve(address(tokenContract), 1); + + // initial balances and state + uint256 nextTokenId = tokenContract.nextTokenIdToMint(); + uint256 currentTotalSupply = tokenContract.totalSupply(); + uint256 currentBalanceOfRecipient = tokenContract.balanceOf(recipient); + + uint256 erc20BalanceOfSeller = erc20.balanceOf(address(saleRecipient)); + uint256 erc20BalanceOfRecipient = erc20.balanceOf(address(recipient)); + + // mint with signature + vm.prank(recipient); + tokenContract.mintWithSignature(_mintrequest, _signature); + + // check state after minting + assertEq(tokenContract.nextTokenIdToMint(), nextTokenId + 1); + assertEq(tokenContract.tokenURI(nextTokenId), string(_mintrequest.uri)); + assertEq(tokenContract.totalSupply(), currentTotalSupply + 1); + assertEq(tokenContract.balanceOf(recipient), currentBalanceOfRecipient + 1); + assertEq(tokenContract.ownerOf(nextTokenId), recipient); + + // check erc20 balances after minting + assertEq(erc20.balanceOf(recipient), erc20BalanceOfRecipient - _mintrequest.price); + assertEq(erc20.balanceOf(address(saleRecipient)), erc20BalanceOfSeller + _mintrequest.price); + } + + function test_state_mintWithSignature_NonZeroPrice_NativeToken() public { + vm.warp(1000); + + // update mintrequest data + _mintrequest.price = 1; + _mintrequest.currency = address(NATIVE_TOKEN); + _signature = signMintRequest(_mintrequest, privateKey); + + // initial balances and state + uint256 nextTokenId = tokenContract.nextTokenIdToMint(); + uint256 currentTotalSupply = tokenContract.totalSupply(); + uint256 currentBalanceOfRecipient = tokenContract.balanceOf(recipient); + + uint256 etherBalanceOfSeller = address(saleRecipient).balance; + uint256 etherBalanceOfRecipient = address(recipient).balance; + + // mint with signature + vm.prank(recipient); + tokenContract.mintWithSignature{ value: 1 }(_mintrequest, _signature); + + // check state after minting + assertEq(tokenContract.nextTokenIdToMint(), nextTokenId + 1); + assertEq(tokenContract.tokenURI(nextTokenId), string(_mintrequest.uri)); + assertEq(tokenContract.totalSupply(), currentTotalSupply + 1); + assertEq(tokenContract.balanceOf(recipient), currentBalanceOfRecipient + 1); + assertEq(tokenContract.ownerOf(nextTokenId), recipient); + + // check erc20 balances after minting + assertEq(address(recipient).balance, etherBalanceOfRecipient - _mintrequest.price); + assertEq(address(saleRecipient).balance, etherBalanceOfSeller + _mintrequest.price); + } + + function test_revert_mintWithSignature_MustSendTotalPrice() public { + vm.warp(1000); + + _mintrequest.price = 1; + _mintrequest.currency = address(NATIVE_TOKEN); + _signature = signMintRequest(_mintrequest, privateKey); + + vm.prank(recipient); + vm.expectRevert("must send total price."); + tokenContract.mintWithSignature{ value: 0 }(_mintrequest, _signature); + } + + function test_revert_mintWithSignature_InvalidSignature() public { + vm.warp(1000); + + uint256 incorrectKey = 3456; + _signature = signMintRequest(_mintrequest, incorrectKey); + + vm.prank(recipient); + vm.expectRevert("invalid signature"); + tokenContract.mintWithSignature(_mintrequest, _signature); + } + + function test_revert_mintWithSignature_RequestExpired() public { + + _signature = signMintRequest(_mintrequest, privateKey); + + // warp time more out of range + vm.warp(3000); + + vm.prank(recipient); + vm.expectRevert("request expired"); + tokenContract.mintWithSignature(_mintrequest, _signature); + } + + /*/////////////////////////////////////////////////////////////// + Unit tests: `mintTo` + //////////////////////////////////////////////////////////////*/ + + function test_state_mintTo() public { + string memory _tokenURI = "tokenURI"; + + uint256 nextTokenId = tokenContract.nextTokenIdToMint(); + uint256 currentTotalSupply = tokenContract.totalSupply(); + uint256 currentBalanceOfRecipient = tokenContract.balanceOf(recipient); + + vm.prank(deployerSigner); + tokenContract.mintTo(recipient, _tokenURI); + + assertEq(tokenContract.nextTokenIdToMint(), nextTokenId + 1); + assertEq(tokenContract.tokenURI(nextTokenId), _tokenURI); + assertEq(tokenContract.totalSupply(), currentTotalSupply + 1); + assertEq(tokenContract.balanceOf(recipient), currentBalanceOfRecipient + 1); + assertEq(tokenContract.ownerOf(nextTokenId), recipient); + } + + function test_revert_mintTo_NotAuthorized() public { + string memory _tokenURI = "tokenURI"; + bytes32 role = keccak256("MINTER_ROLE"); + + vm.expectRevert( + abi.encodePacked( + "AccessControl: account ", + TWStrings.toHexString(uint160(address(0x1)), 20), + " is missing role ", + TWStrings.toHexString(uint256(role), 32) + ) + ); + vm.prank(address(0x1)); + tokenContract.mintTo(recipient, _tokenURI); + } + + /*/////////////////////////////////////////////////////////////// + Unit tests: `burn` + //////////////////////////////////////////////////////////////*/ + + function test_state_burn_TokenOwner() public { + string memory _tokenURI = "tokenURI"; + + uint256 nextTokenId = tokenContract.nextTokenIdToMint(); + uint256 currentTotalSupply = tokenContract.totalSupply(); + uint256 currentBalanceOfRecipient = tokenContract.balanceOf(recipient); + + vm.prank(deployerSigner); + tokenContract.mintTo(recipient, _tokenURI); + + vm.prank(recipient); + tokenContract.burn(nextTokenId); + + assertEq(tokenContract.nextTokenIdToMint(), nextTokenId + 1); + assertEq(tokenContract.tokenURI(nextTokenId), _tokenURI); + assertEq(tokenContract.totalSupply(), currentTotalSupply); + assertEq(tokenContract.balanceOf(recipient), currentBalanceOfRecipient); + + vm.expectRevert("ERC721: owner query for nonexistent token"); + assertEq(tokenContract.ownerOf(nextTokenId), address(0)); + } + + function test_state_burn_TokenOperator() public { + string memory _tokenURI = "tokenURI"; + + address operator = address(0x789); + + uint256 nextTokenId = tokenContract.nextTokenIdToMint(); + uint256 currentTotalSupply = tokenContract.totalSupply(); + uint256 currentBalanceOfRecipient = tokenContract.balanceOf(recipient); + + vm.prank(deployerSigner); + tokenContract.mintTo(recipient, _tokenURI); + + vm.prank(recipient); + tokenContract.setApprovalForAll(operator, true); + + vm.prank(operator); + tokenContract.burn(nextTokenId); + + assertEq(tokenContract.nextTokenIdToMint(), nextTokenId + 1); + assertEq(tokenContract.tokenURI(nextTokenId), _tokenURI); + assertEq(tokenContract.totalSupply(), currentTotalSupply); + assertEq(tokenContract.balanceOf(recipient), currentBalanceOfRecipient); + + vm.expectRevert("ERC721: owner query for nonexistent token"); + assertEq(tokenContract.ownerOf(nextTokenId), address(0)); + } + + function test_revert_burn_NotOwnerNorApproved() public { + string memory _tokenURI = "tokenURI"; + + uint256 nextTokenId = tokenContract.nextTokenIdToMint(); + + vm.prank(deployerSigner); + tokenContract.mintTo(recipient, _tokenURI); + + vm.prank(address(0x789)); + vm.expectRevert("ERC721Burnable: caller is not owner nor approved"); + tokenContract.burn(nextTokenId); + } + + /*/////////////////////////////////////////////////////////////// + Unit tests: owner + //////////////////////////////////////////////////////////////*/ + + function test_state_setOwner() public { + + address newOwner = address(0x123); + bytes32 role = tokenContract.DEFAULT_ADMIN_ROLE(); + + vm.prank(deployerSigner); + tokenContract.grantRole(role, newOwner); + + vm.prank(deployerSigner); + tokenContract.setOwner(newOwner); + + assertEq(tokenContract.owner(), newOwner); + } + + function test_revert_setOwner_NotModuleAdmin() public { + vm.expectRevert("new owner not module admin."); + vm.prank(deployerSigner); + tokenContract.setOwner(address(0x1234)); + } + + function test_event_setOwner() public { + address newOwner = address(0x123); + bytes32 role = tokenContract.DEFAULT_ADMIN_ROLE(); + + vm.startPrank(deployerSigner); + tokenContract.grantRole(role, newOwner); + + vm.expectEmit(true, true, true, true); + emit OwnerUpdated(deployerSigner, newOwner); + + tokenContract.setOwner(newOwner); + } +} diff --git a/src/test/utils/BaseTest.sol b/src/test/utils/BaseTest.sol index e9a8e7922..6f1d01cdc 100644 --- a/src/test/utils/BaseTest.sol +++ b/src/test/utils/BaseTest.sol @@ -103,7 +103,7 @@ abstract contract BaseTest is DSTest, Test { abi.encodeCall( TokenERC20.initialize, ( - deployer, + signer, NAME, SYMBOL, CONTRACT_URI, @@ -119,7 +119,7 @@ abstract contract BaseTest is DSTest, Test { abi.encodeCall( TokenERC721.initialize, ( - deployer, + signer, NAME, SYMBOL, CONTRACT_URI, @@ -137,7 +137,7 @@ abstract contract BaseTest is DSTest, Test { abi.encodeCall( TokenERC1155.initialize, ( - deployer, + signer, NAME, SYMBOL, CONTRACT_URI, From 24d3f381525e2417d91a596ee98f98d7519d7415 Mon Sep 17 00:00:00 2001 From: Yash Date: Thu, 18 Aug 2022 17:29:38 +0530 Subject: [PATCH 2/3] run prettier --- src/test/token/TokenERC721.t.sol | 6 +++--- src/test/utils/BaseTest.sol | 11 +---------- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/src/test/token/TokenERC721.t.sol b/src/test/token/TokenERC721.t.sol index 8b9aff970..ba625a190 100644 --- a/src/test/token/TokenERC721.t.sol +++ b/src/test/token/TokenERC721.t.sol @@ -58,7 +58,9 @@ contract TokenERC721Test is BaseTest { typehashEip712 = keccak256( "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" ); - domainSeparator = keccak256(abi.encode(typehashEip712, nameHash, versionHash, block.chainid, address(tokenContract))); + domainSeparator = keccak256( + abi.encode(typehashEip712, nameHash, versionHash, block.chainid, address(tokenContract)) + ); // construct default mintrequest _mintrequest.to = recipient; @@ -217,7 +219,6 @@ contract TokenERC721Test is BaseTest { } function test_revert_mintWithSignature_RequestExpired() public { - _signature = signMintRequest(_mintrequest, privateKey); // warp time more out of range @@ -336,7 +337,6 @@ contract TokenERC721Test is BaseTest { //////////////////////////////////////////////////////////////*/ function test_state_setOwner() public { - address newOwner = address(0x123); bytes32 role = tokenContract.DEFAULT_ADMIN_ROLE(); diff --git a/src/test/utils/BaseTest.sol b/src/test/utils/BaseTest.sol index 6f1d01cdc..a086d5db9 100644 --- a/src/test/utils/BaseTest.sol +++ b/src/test/utils/BaseTest.sol @@ -102,16 +102,7 @@ abstract contract BaseTest is DSTest, Test { "TokenERC20", abi.encodeCall( TokenERC20.initialize, - ( - signer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - platformFeeRecipient, - platformFeeBps - ) + (signer, NAME, SYMBOL, CONTRACT_URI, forwarders(), saleRecipient, platformFeeRecipient, platformFeeBps) ) ); deployContractProxy( From 1f24e80aab5d448997a3c890ede5ef4874729e26 Mon Sep 17 00:00:00 2001 From: Yash Date: Thu, 18 Aug 2022 19:27:46 +0530 Subject: [PATCH 3/3] more tests --- src/test/sdk/extension/PlatformFee.t.sol | 2 +- src/test/token/TokenERC721.t.sol | 257 +++++++++++++++++++++++ 2 files changed, 258 insertions(+), 1 deletion(-) diff --git a/src/test/sdk/extension/PlatformFee.t.sol b/src/test/sdk/extension/PlatformFee.t.sol index 9f0110352..086922a7d 100644 --- a/src/test/sdk/extension/PlatformFee.t.sol +++ b/src/test/sdk/extension/PlatformFee.t.sol @@ -53,7 +53,7 @@ contract ExtensionPlatformFee is DSTest, Test { ext.setPlatformFeeInfo(address(1), 1000); } - function test_event_defaultRoyalty() public { + function test_event_platformFeeInfo() public { ext.setCondition(true); address _platformFeeRecipient = address(0x123); diff --git a/src/test/token/TokenERC721.t.sol b/src/test/token/TokenERC721.t.sol index ba625a190..d3c8b9377 100644 --- a/src/test/token/TokenERC721.t.sol +++ b/src/test/token/TokenERC721.t.sol @@ -20,6 +20,10 @@ contract TokenERC721Test is BaseTest { TokenERC721.MintRequest mintRequest ); event OwnerUpdated(address indexed prevOwner, address indexed newOwner); + event DefaultRoyalty(address indexed newRoyaltyRecipient, uint256 newRoyaltyBps); + event RoyaltyForToken(uint256 indexed tokenId, address indexed royaltyRecipient, uint256 royaltyBps); + event PrimarySaleRecipientUpdated(address indexed recipient); + event PlatformFeeInfoUpdated(address indexed platformFeeRecipient, uint256 platformFeeBps); TokenERC721 public tokenContract; bytes32 internal typehashMintRequest; @@ -229,6 +233,17 @@ contract TokenERC721Test is BaseTest { tokenContract.mintWithSignature(_mintrequest, _signature); } + function test_event_mintWithSignature() public { + vm.warp(1000); + + vm.expectEmit(true, true, true, true); + emit TokensMintedWithSignature(deployerSigner, recipient, 0, _mintrequest); + + // mint with signature + vm.prank(recipient); + tokenContract.mintWithSignature(_mintrequest, _signature); + } + /*/////////////////////////////////////////////////////////////// Unit tests: `mintTo` //////////////////////////////////////////////////////////////*/ @@ -266,6 +281,17 @@ contract TokenERC721Test is BaseTest { tokenContract.mintTo(recipient, _tokenURI); } + function test_event_mintTo() public { + string memory _tokenURI = "tokenURI"; + + vm.expectEmit(true, true, true, true); + emit TokensMinted(recipient, 0, _tokenURI); + + // mint + vm.prank(deployerSigner); + tokenContract.mintTo(recipient, _tokenURI); + } + /*/////////////////////////////////////////////////////////////// Unit tests: `burn` //////////////////////////////////////////////////////////////*/ @@ -367,4 +393,235 @@ contract TokenERC721Test is BaseTest { tokenContract.setOwner(newOwner); } + + /*/////////////////////////////////////////////////////////////// + Unit tests: royalty + //////////////////////////////////////////////////////////////*/ + + function test_state_setDefaultRoyaltyInfo() public { + address _royaltyRecipient = address(0x123); + uint256 _royaltyBps = 1000; + + vm.prank(deployerSigner); + tokenContract.setDefaultRoyaltyInfo(_royaltyRecipient, _royaltyBps); + + (address newRoyaltyRecipient, uint256 newRoyaltyBps) = tokenContract.getDefaultRoyaltyInfo(); + assertEq(newRoyaltyRecipient, _royaltyRecipient); + assertEq(newRoyaltyBps, _royaltyBps); + + (address receiver, uint256 royaltyAmount) = tokenContract.royaltyInfo(0, 100); + assertEq(receiver, _royaltyRecipient); + assertEq(royaltyAmount, (100 * 1000) / 10_000); + } + + function test_revert_setDefaultRoyaltyInfo_NotAuthorized() public { + address _royaltyRecipient = address(0x123); + uint256 _royaltyBps = 1000; + bytes32 role = tokenContract.DEFAULT_ADMIN_ROLE(); + + vm.expectRevert( + abi.encodePacked( + "AccessControl: account ", + TWStrings.toHexString(uint160(address(0x1)), 20), + " is missing role ", + TWStrings.toHexString(uint256(role), 32) + ) + ); + vm.prank(address(0x1)); + tokenContract.setDefaultRoyaltyInfo(_royaltyRecipient, _royaltyBps); + } + + function test_revert_setDefaultRoyaltyInfo_ExceedsRoyaltyBps() public { + address _royaltyRecipient = address(0x123); + uint256 _royaltyBps = 10001; + + vm.expectRevert("exceed royalty bps"); + vm.prank(deployerSigner); + tokenContract.setDefaultRoyaltyInfo(_royaltyRecipient, _royaltyBps); + } + + function test_state_setRoyaltyInfoForToken() public { + uint256 _tokenId = 1; + address _recipient = address(0x123); + uint256 _bps = 1000; + + vm.prank(deployerSigner); + tokenContract.setRoyaltyInfoForToken(_tokenId, _recipient, _bps); + + (address receiver, uint256 royaltyAmount) = tokenContract.royaltyInfo(_tokenId, 100); + assertEq(receiver, _recipient); + assertEq(royaltyAmount, (100 * 1000) / 10_000); + } + + function test_revert_setRoyaltyInfo_NotAuthorized() public { + uint256 _tokenId = 1; + address _recipient = address(0x123); + uint256 _bps = 1000; + bytes32 role = tokenContract.DEFAULT_ADMIN_ROLE(); + + vm.expectRevert( + abi.encodePacked( + "AccessControl: account ", + TWStrings.toHexString(uint160(address(0x1)), 20), + " is missing role ", + TWStrings.toHexString(uint256(role), 32) + ) + ); + vm.prank(address(0x1)); + tokenContract.setRoyaltyInfoForToken(_tokenId, _recipient, _bps); + } + + function test_revert_setRoyaltyInfoForToken_ExceedsRoyaltyBps() public { + uint256 _tokenId = 1; + address _recipient = address(0x123); + uint256 _bps = 10001; + + vm.expectRevert("exceed royalty bps"); + vm.prank(deployerSigner); + tokenContract.setRoyaltyInfoForToken(_tokenId, _recipient, _bps); + } + + function test_event_defaultRoyalty() public { + address _royaltyRecipient = address(0x123); + uint256 _royaltyBps = 1000; + + vm.expectEmit(true, true, true, true); + emit DefaultRoyalty(_royaltyRecipient, _royaltyBps); + + vm.prank(deployerSigner); + tokenContract.setDefaultRoyaltyInfo(_royaltyRecipient, _royaltyBps); + } + + function test_event_royaltyForToken() public { + uint256 _tokenId = 1; + address _recipient = address(0x123); + uint256 _bps = 1000; + + vm.expectEmit(true, true, true, true); + emit RoyaltyForToken(_tokenId, _recipient, _bps); + + vm.prank(deployerSigner); + tokenContract.setRoyaltyInfoForToken(_tokenId, _recipient, _bps); + } + + /*/////////////////////////////////////////////////////////////// + Unit tests: primary sale + //////////////////////////////////////////////////////////////*/ + + function test_state_setPrimarySaleRecipient() public { + address _primarySaleRecipient = address(0x123); + + vm.prank(deployerSigner); + tokenContract.setPrimarySaleRecipient(_primarySaleRecipient); + + address recipient = tokenContract.primarySaleRecipient(); + assertEq(recipient, _primarySaleRecipient); + } + + function test_revert_setPrimarySaleRecipient_NotAuthorized() public { + address _primarySaleRecipient = address(0x123); + bytes32 role = tokenContract.DEFAULT_ADMIN_ROLE(); + + vm.expectRevert( + abi.encodePacked( + "AccessControl: account ", + TWStrings.toHexString(uint160(address(0x1)), 20), + " is missing role ", + TWStrings.toHexString(uint256(role), 32) + ) + ); + vm.prank(address(0x1)); + tokenContract.setPrimarySaleRecipient(_primarySaleRecipient); + } + + function test_event_setPrimarySaleRecipient() public { + address _primarySaleRecipient = address(0x123); + + vm.expectEmit(true, true, true, true); + emit PrimarySaleRecipientUpdated(_primarySaleRecipient); + + vm.prank(deployerSigner); + tokenContract.setPrimarySaleRecipient(_primarySaleRecipient); + } + + /*/////////////////////////////////////////////////////////////// + Unit tests: platform fee + //////////////////////////////////////////////////////////////*/ + + function test_state_setPlatformFeeInfo() public { + address _platformFeeRecipient = address(0x123); + uint256 _platformFeeBps = 1000; + + vm.prank(deployerSigner); + tokenContract.setPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps); + + (address recipient, uint16 bps) = tokenContract.getPlatformFeeInfo(); + assertEq(_platformFeeRecipient, recipient); + assertEq(_platformFeeBps, bps); + } + + function test_revert_setPlatformFeeInfo_ExceedsMaxBps() public { + address _platformFeeRecipient = address(0x123); + uint256 _platformFeeBps = 10001; + + vm.expectRevert("bps <= 10000."); + vm.prank(deployerSigner); + tokenContract.setPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps); + } + + function test_revert_setPlatformFeeInfo_NotAuthorized() public { + bytes32 role = tokenContract.DEFAULT_ADMIN_ROLE(); + + vm.expectRevert( + abi.encodePacked( + "AccessControl: account ", + TWStrings.toHexString(uint160(address(0x1)), 20), + " is missing role ", + TWStrings.toHexString(uint256(role), 32) + ) + ); + vm.prank(address(0x1)); + tokenContract.setPlatformFeeInfo(address(1), 1000); + } + + function test_event_platformFeeInfo() public { + address _platformFeeRecipient = address(0x123); + uint256 _platformFeeBps = 1000; + + vm.expectEmit(true, true, true, true); + emit PlatformFeeInfoUpdated(_platformFeeRecipient, _platformFeeBps); + + vm.prank(deployerSigner); + tokenContract.setPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps); + } + + /*/////////////////////////////////////////////////////////////// + Unit tests: contract metadata + //////////////////////////////////////////////////////////////*/ + + function test_state_setContractURI() public { + string memory uri = "uri_string"; + + vm.prank(deployerSigner); + tokenContract.setContractURI(uri); + + string memory _contractURI = tokenContract.contractURI(); + + assertEq(_contractURI, uri); + } + + function test_revert_setContractURI() public { + bytes32 role = tokenContract.DEFAULT_ADMIN_ROLE(); + + vm.expectRevert( + abi.encodePacked( + "AccessControl: account ", + TWStrings.toHexString(uint160(address(0x1)), 20), + " is missing role ", + TWStrings.toHexString(uint256(role), 32) + ) + ); + vm.prank(address(0x1)); + tokenContract.setContractURI(""); + } }