Skip to content

Commit

Permalink
feat: getnftinfo will return its metadata and creation time (#198)
Browse files Browse the repository at this point in the history
Signed-off-by: Mariusz Jasuwienas <[email protected]>
  • Loading branch information
arianejasuwienas committed Jan 30, 2025
1 parent e4a7e92 commit 012735b
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 8 deletions.
37 changes: 29 additions & 8 deletions contracts/HtsSystemContract.sol
Original file line number Diff line number Diff line change
Expand Up @@ -414,16 +414,15 @@ contract HtsSystemContract is IHederaTokenService {
returns (int64, NonFungibleTokenInfo memory) {
(int64 responseCode, TokenInfo memory tokenInfo) = getTokenInfo(token);
require(responseCode == HederaResponseCodes.SUCCESS, "getNonFungibleTokenInfo: failed to get token data");
NonFungibleTokenInfo memory nonFungibleTokenInfo;
(, NonFungibleTokenInfo memory nonFungibleTokenInfo) = IHederaTokenService(token).getNonFungibleTokenInfo(
token,
serialNumber
);
nonFungibleTokenInfo.tokenInfo = tokenInfo;
nonFungibleTokenInfo.serialNumber = serialNumber;
nonFungibleTokenInfo.spenderId = IERC721(token).getApproved(uint256(uint64(serialNumber)));
nonFungibleTokenInfo.ownerId = IERC721(token).ownerOf(uint256(uint64(serialNumber)));

// ToDo:
// nonFungibleTokenInfo.metadata = bytes(IERC721(token).tokenURI(uint256(uint64(serialNumber))));
// nonFungibleTokenInfo.creationTime = int64(0);

uint256 serial = uint256(uint64(serialNumber));
nonFungibleTokenInfo.spenderId = IERC721(token).getApproved(serial);
nonFungibleTokenInfo.ownerId = IERC721(token).ownerOf(serial);
return (responseCode, nonFungibleTokenInfo);
}

Expand Down Expand Up @@ -593,6 +592,15 @@ contract HtsSystemContract is IHederaTokenService {
_setApprovalForAll(from, to, approved);
return abi.encode(true);
}
if (selector == this.getNonFungibleTokenInfo.selector) {
require(msg.data.length >= 92, "getNonFungibleTokenInfo: Not enough calldata");
uint256 serialId = uint256(bytes32(msg.data[60:92]));
NonFungibleTokenInfo memory info;
(int64 creationTime, bytes memory metadata) = __nftInfo(serialId);
info.creationTime = creationTime;
info.metadata = metadata;
return abi.encode(HederaResponseCodes.SUCCESS, info);
}
if (selector == this._update.selector) {
require(msg.data.length >= 124, "update: Not enough calldata");
address from = address(bytes20(msg.data[40:60]));
Expand Down Expand Up @@ -788,6 +796,12 @@ contract HtsSystemContract is IHederaTokenService {
return bytes32(abi.encodePacked(selector, pad, serialId));
}

function __nftInfoSlot(uint32 serialId) internal virtual returns (bytes32) {
bytes4 selector = IHederaTokenService.getNonFungibleTokenInfo.selector;
uint192 pad = 0x0;
return bytes32(abi.encodePacked(selector, pad, serialId));
}

function _ownerOfSlot(uint32 serialId) internal virtual returns (bytes32) {
bytes4 selector = IERC721.ownerOf.selector;
uint192 pad = 0x0;
Expand Down Expand Up @@ -825,6 +839,13 @@ contract HtsSystemContract is IHederaTokenService {
uri = _uri;
}

function __nftInfo(uint256 serialId) private returns (int64, bytes memory) {
bytes32 slot = __nftInfoSlot(uint32(serialId));
bytes storage _nftInfo;
assembly { _nftInfo.slot := slot }
return abi.decode(_nftInfo, (int64, bytes));
}

function __ownerOf(uint256 serialId) private returns (address owner) {
bytes32 slot = _ownerOfSlot(uint32(serialId));
assembly { owner := sload(slot) }
Expand Down
11 changes: 11 additions & 0 deletions contracts/HtsSystemContractJson.sol
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,17 @@ contract HtsSystemContractJson is HtsSystemContract {
return slot;
}

function __nftInfoSlot(uint32 serialId) internal override virtual returns (bytes32) {
bytes32 slot = super.__nftInfoSlot(serialId);
if (_shouldFetch(slot)) {
string memory metadata = mirrorNode().getNftMetadata(address(this), serialId);
string memory createdTimestamp = mirrorNode().getNftCreatedTimestamp(address(this), serialId);
int64 creationTime = int64(vm.parseInt(vm.split(createdTimestamp, ".")[0]));
storeBytes(address(this), uint256(slot), abi.encode(creationTime, bytes(metadata)));
}
return slot;
}

function _ownerOfSlot(uint32 serialId) internal override virtual returns (bytes32) {
bytes32 slot = super._ownerOfSlot(serialId);
if (_shouldFetch(slot)) {
Expand Down
8 changes: 8 additions & 0 deletions contracts/MirrorNode.sol
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@ abstract contract MirrorNode {
return "";
}

function getNftCreatedTimestamp(address token, uint32 serial) external returns (string memory) {
string memory json = this.fetchNonFungibleToken(token, serial);
if (vm.keyExistsJson(json, ".created_timestamp")) {
return vm.parseJsonString(json, ".created_timestamp");
}
return "";
}

function getNftOwner(address token, uint32 serial) external returns (address) {
string memory json = this.fetchNonFungibleToken(token, serial);
if (vm.keyExistsJson(json, ".account_id")) {
Expand Down
7 changes: 7 additions & 0 deletions test/HTS.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -628,6 +628,13 @@ contract HTSTest is Test, TestSetup {
assertEq(nonFungibleTokenInfo.tokenInfo.fractionalFees.length, 0);
assertEq(nonFungibleTokenInfo.tokenInfo.royaltyFees.length, 0);
assertEq(nonFungibleTokenInfo.tokenInfo.ledgerId, testMode == TestMode.FFI ? "0x01" : "0x00");

// Additional information, not a part of the IERC721 interface.
assertEq(
string(nonFungibleTokenInfo.metadata),
"aHR0cHM6Ly92ZXJ5LWxvbmctc3RyaW5nLXdoaWNoLWV4Y2VlZHMtMzEtYnl0ZXMtYW5kLXJlcXVpcmVzLW11bHRpcGxlLXN0b3JhZ2Utc2xvdHMuY29tLzE="
);
assertEq(nonFungibleTokenInfo.creationTime, 1734948254);
}

function test_HTS_transferToken() external {
Expand Down

0 comments on commit 012735b

Please sign in to comment.