diff --git a/packages/contracts/evm-contracts/contracts/token/IInverseAppProjectedNft.sol b/packages/contracts/evm-contracts/contracts/token/IInverseAppProjectedNft.sol index 75586a27..26dc8561 100644 --- a/packages/contracts/evm-contracts/contracts/token/IInverseAppProjectedNft.sol +++ b/packages/contracts/evm-contracts/contracts/token/IInverseAppProjectedNft.sol @@ -16,10 +16,18 @@ interface IInverseAppProjectedNft is IInverseProjectedNft { /// Emits the `Minted` event. /// @param _to where to send the NFT to /// @param _verificationData any additional data to verify the validity of the mint + /// @param _data any additional data to pass to the receiver contract + /// @return id of the minted token + function mint( + address _to, + bytes memory _verificationData, + bytes memory _data + ) external returns (uint256); + + /// @dev Shorthand function that calls the `mint` function with empty `_data`. function mint(address _to, bytes memory _verificationData) external returns (uint256); - /// @dev This works identically to the other function with an extra data parameter, - /// except this function just sets data to "". + /// @dev Shorthand function that calls the `mint` function with empty `_verificationData` and empty `_data`. function mint(address _to) external returns (uint256); /// @notice Returns the last nonce used (or 0 if the user has never minted) diff --git a/packages/contracts/evm-contracts/contracts/token/IInverseBaseProjectedNft.sol b/packages/contracts/evm-contracts/contracts/token/IInverseBaseProjectedNft.sol index f444e4be..81bbe61a 100644 --- a/packages/contracts/evm-contracts/contracts/token/IInverseBaseProjectedNft.sol +++ b/packages/contracts/evm-contracts/contracts/token/IInverseBaseProjectedNft.sol @@ -14,5 +14,16 @@ interface IInverseBaseProjectedNft is IInverseProjectedNft { /// Increases the `totalSupply` and `currentTokenId`. /// Reverts if `_to` is a zero address or if it refers to smart contract but does not implement IERC721Receiver-onERC721Received. /// Emits the `Minted` event. + /// @param _to where to send the NFT to + /// @param initialData data that is emitted in the `Minted` event + /// @param data any additional data to pass to the receiver contract + /// @return id of the minted token + function mint( + address _to, + string calldata initialData, + bytes memory data + ) external returns (uint256); + + /// @dev Shorthand function that calls the `mint` function with empty `data`. function mint(address _to, string calldata initialData) external returns (uint256); } diff --git a/packages/contracts/evm-contracts/contracts/token/InverseAppProjectedNft.sol b/packages/contracts/evm-contracts/contracts/token/InverseAppProjectedNft.sol index 1709dcb1..3263c3c3 100644 --- a/packages/contracts/evm-contracts/contracts/token/InverseAppProjectedNft.sol +++ b/packages/contracts/evm-contracts/contracts/token/InverseAppProjectedNft.sol @@ -71,11 +71,19 @@ contract InverseAppProjectedNft is IInverseAppProjectedNft, ERC721, Ownable { return true; } - /// @dev Mints a new token to address `_to`. + /// @dev Mints a new token to address `_to` /// Increases the `totalSupply` and `currentTokenId`. /// Reverts if `_to` is a zero address or if it refers to smart contract but does not implement IERC721Receiver-onERC721Received. /// Emits the `Minted` event. - function mint(address _to, bytes memory _verificationData) public virtual returns (uint256) { + /// @param _to where to send the NFT to + /// @param _verificationData any additional data to verify the validity of the mint + /// @param _data any additional data to pass to the receiver contract + /// @return id of the minted token + function mint( + address _to, + bytes memory _verificationData, + bytes memory _data + ) public virtual returns (uint256) { require(_to != address(0), "InverseAppProjectedNft: zero receiver address"); require( validateMint(_to, _verificationData), @@ -83,7 +91,7 @@ contract InverseAppProjectedNft is IInverseAppProjectedNft, ERC721, Ownable { ); uint256 tokenId = currentTokenId; - _safeMint(_to, tokenId); + _safeMint(_to, tokenId, _data); mintCount[_to] += 1; uint256 userTokenId = mintCount[_to]; tokenToMint[tokenId] = MintEntry(_to, userTokenId); @@ -95,8 +103,14 @@ contract InverseAppProjectedNft is IInverseAppProjectedNft, ERC721, Ownable { return tokenId; } + /// @dev Shorthand function that calls the `mint` function with empty `_data`. + function mint(address _to, bytes memory _verificationData) public virtual returns (uint256) { + return mint(_to, _verificationData, bytes("")); + } + + /// @dev Shorthand function that calls the `mint` function with empty `_verificationData` and empty `_data`. function mint(address _to) public returns (uint256) { - return mint(_to, bytes("")); + return mint(_to, bytes(""), bytes("")); } /// @dev Burns token of ID `_tokenId`. Callable only by the owner of the specified token. diff --git a/packages/contracts/evm-contracts/contracts/token/InverseBaseProjectedNft.sol b/packages/contracts/evm-contracts/contracts/token/InverseBaseProjectedNft.sol index 629b128d..03b9bc68 100644 --- a/packages/contracts/evm-contracts/contracts/token/InverseBaseProjectedNft.sol +++ b/packages/contracts/evm-contracts/contracts/token/InverseBaseProjectedNft.sol @@ -56,11 +56,19 @@ contract InverseBaseProjectedNft is IInverseBaseProjectedNft, ERC721, Ownable { /// Increases the `totalSupply` and `currentTokenId`. /// Reverts if `_to` is a zero address or if it refers to smart contract but does not implement IERC721Receiver-onERC721Received. /// Emits the `Minted` event. - function mint(address _to, string calldata initialData) public virtual returns (uint256) { + /// @param _to where to send the NFT to + /// @param initialData data that is emitted in the `Minted` event + /// @param data any additional data to pass to the receiver contract + /// @return id of the minted token + function mint( + address _to, + string calldata initialData, + bytes memory data + ) public virtual returns (uint256) { require(_to != address(0), "InverseBaseProjectedNft: zero receiver address"); uint256 tokenId = currentTokenId; - _safeMint(_to, tokenId); + _safeMint(_to, tokenId, data); totalSupply++; currentTokenId++; @@ -69,6 +77,11 @@ contract InverseBaseProjectedNft is IInverseBaseProjectedNft, ERC721, Ownable { return tokenId; } + /// @dev Shorthand function that calls the `mint` function with empty `data`. + function mint(address _to, string calldata initialData) public virtual returns (uint256) { + return mint(_to, initialData, bytes("")); + } + /// @dev Burns token of ID `_tokenId`. Callable only by the owner of the specified token. /// Reverts if `_tokenId` does not exist. function burn(uint256 _tokenId) public virtual onlyTokenOwner(_tokenId) { diff --git a/packages/contracts/evm-contracts/test/InverseAppProjectedNft.t.sol b/packages/contracts/evm-contracts/test/InverseAppProjectedNft.t.sol index 928f5525..101213d8 100644 --- a/packages/contracts/evm-contracts/test/InverseAppProjectedNft.t.sol +++ b/packages/contracts/evm-contracts/test/InverseAppProjectedNft.t.sol @@ -22,6 +22,19 @@ contract MockTokenUri is ITokenUri { } } +contract MockDataReceiver { + bytes public dataReceived; + function onERC721Received( + address, + address, + uint256, + bytes calldata data + ) public returns (bytes4) { + dataReceived = data; + return this.onERC721Received.selector; + } +} + contract InverseAppProjectedNftTest is CTest, ERC721Holder { using Strings for uint256; @@ -42,19 +55,33 @@ contract InverseAppProjectedNftTest is CTest, ERC721Holder { } function test_CanMint() public { + vm.prank(alice); + vm.expectEmit(true, true, true, true); + emit IInverseAppProjectedNft.Minted(2, address(this), 2); + nft.mint(address(this), bytes(""), bytes("")); + } + + function test_CanMintNoData() public { vm.prank(alice); vm.expectEmit(true, true, true, true); emit IInverseAppProjectedNft.Minted(2, address(this), 2); nft.mint(address(this), bytes("")); } - function test_CanMintNoVerificationData() public { + function test_CanMintNoDataNoVerificationData() public { vm.prank(alice); vm.expectEmit(true, true, true, true); emit IInverseAppProjectedNft.Minted(2, address(this), 2); nft.mint(address(this)); } + function test_MintPassesDataToReceiver() public { + MockDataReceiver receiver = new MockDataReceiver(); + bytes memory data = bytes("data"); + nft.mint(address(receiver), "", data); + assertEq(keccak256(receiver.dataReceived()), keccak256(data)); + } + function test_CanTransfer() public { nft.transferFrom(address(this), alice, ownedTokenId); } diff --git a/packages/contracts/evm-contracts/test/InverseBaseProjectedNft.t.sol b/packages/contracts/evm-contracts/test/InverseBaseProjectedNft.t.sol index b9dd702c..a0b1c656 100644 --- a/packages/contracts/evm-contracts/test/InverseBaseProjectedNft.t.sol +++ b/packages/contracts/evm-contracts/test/InverseBaseProjectedNft.t.sol @@ -22,6 +22,19 @@ contract MockTokenUri is ITokenUri { } } +contract MockDataReceiver { + bytes public dataReceived; + function onERC721Received( + address, + address, + uint256, + bytes calldata data + ) public returns (bytes4) { + dataReceived = data; + return this.onERC721Received.selector; + } +} + contract InverseBaseProjectedNftTest is CTest, ERC721Holder { using Strings for uint256; @@ -42,12 +55,26 @@ contract InverseBaseProjectedNftTest is CTest, ERC721Holder { } function test_CanMint() public { + vm.prank(alice); + vm.expectEmit(true, true, true, true); + emit IInverseBaseProjectedNft.Minted(2, "abcd"); + nft.mint(address(this), "abcd", bytes("")); + } + + function test_CanMintNoData() public { vm.prank(alice); vm.expectEmit(true, true, true, true); emit IInverseBaseProjectedNft.Minted(2, "abcd"); nft.mint(address(this), "abcd"); } + function test_MintPassesDataToReceiver() public { + MockDataReceiver receiver = new MockDataReceiver(); + bytes memory data = bytes("data"); + nft.mint(address(receiver), "", data); + assertEq(keccak256(receiver.dataReceived()), keccak256(data)); + } + function test_CanTransfer() public { nft.transferFrom(address(this), alice, ownedTokenId); }