--- a/pallets/nonfungible/src/erc.rs +++ b/pallets/nonfungible/src/erc.rs @@ -515,10 +515,18 @@ Ok(()) } - /// @dev Not implemented - fn get_approved(&self, _token_id: U256) -> Result
{ - // TODO: Not implemetable - Err("not implemented".into()) + /// @notice Get the approved address for a single NFT + /// @dev Throws if `tokenId` is not a valid NFT + /// @param tokenId The NFT to find the approved address for + /// @return The approved address for this NFT, or the zero address if there is none + fn get_approved(&self, token_id: U256) -> Result
{ + let token_id = token_id.try_into()?; + let operator = >::get_allowance(self, token_id).map_err(dispatch_to_evm::)?; + Ok(if let Some(operator) = operator { + *operator.as_eth() + } else { + Address::zero() + }) } /// @notice Tells whether the given `owner` approves the `operator`. --- a/pallets/nonfungible/src/lib.rs +++ b/pallets/nonfungible/src/lib.rs @@ -1082,6 +1082,17 @@ } } + pub fn get_allowance( + collection: &NonfungibleHandle, + token_id: TokenId, + ) -> Result, DispatchError> { + ensure!( + >::get((collection.id, token_id)).is_some(), + >::TokenNotFound + ); + Ok(>::get((collection.id, token_id))) + } + /// Set allowance for the spender to `transfer` or `burn` sender's token. /// /// - `token`: Token the spender is allowed to `transfer` or `burn`. --- a/pallets/nonfungible/src/stubs/UniqueNFT.sol +++ b/pallets/nonfungible/src/stubs/UniqueNFT.sol @@ -1156,7 +1156,10 @@ dummy = 0; } - /// @dev Not implemented + /// @notice Get the approved address for a single NFT + /// @dev Throws if `tokenId` is not a valid NFT + /// @param tokenId The NFT to find the approved address for + /// @return The approved address for this NFT, or the zero address if there is none /// @dev EVM selector for this function is: 0x081812fc, /// or in textual repr: getApproved(uint256) function getApproved(uint256 tokenId) public view returns (address) { --- a/tests/src/eth/api/UniqueNFT.sol +++ b/tests/src/eth/api/UniqueNFT.sol @@ -781,7 +781,10 @@ /// or in textual repr: setApprovalForAll(address,bool) function setApprovalForAll(address operator, bool approved) external; - /// @dev Not implemented + /// @notice Get the approved address for a single NFT + /// @dev Throws if `tokenId` is not a valid NFT + /// @param tokenId The NFT to find the approved address for + /// @return The approved address for this NFT, or the zero address if there is none /// @dev EVM selector for this function is: 0x081812fc, /// or in textual repr: getApproved(uint256) function getApproved(uint256 tokenId) external view returns (address); --- a/tests/src/eth/nonFungible.test.ts +++ b/tests/src/eth/nonFungible.test.ts @@ -230,6 +230,14 @@ const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner); { + const badTokenId = await contract.methods.nextTokenId().call() + 1; + await expect(contract.methods.getApproved(badTokenId).call()).to.be.rejectedWith('revert TokenNotFound'); + } + { + const approved = await contract.methods.getApproved(tokenId).call(); + expect(approved).to.be.equal('0x0000000000000000000000000000000000000000'); + } + { const result = await contract.methods.approve(spender, tokenId).send({from: owner}); const event = result.events.Approval; @@ -238,6 +246,10 @@ expect(event.returnValues.approved).to.be.equal(spender); expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`); } + { + const approved = await contract.methods.getApproved(tokenId).call(); + expect(approved).to.be.equal(spender); + } }); itEth('Can perform setApprovalForAll()', async ({helper}) => {