Non-Fungible Token on-chain indexes (TIP-4.3)
Requires: TIP-4.1 Requires: TIP-6.1
Abstract
Using the Index contract code, you can find all your NFT with one dApp query. This makes blockchain application less dependent on different off-chain parsers and indexers
On-chain Indexes solves easy and fast searching any data in blockchain. This document shows standard for basic query. Any developer can get an idea of this solution and realize his own on-chain index.
Motivation
A standard interface allows search all Collection and all NFT by owner using base dApp functionality
Specification
The keywords “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119
Contracts
Collection
- TIP4.1 contract that minted tokenNFT
- TIP4.1 contract that store token informationIndexBasis
- contract, that helps to find all collections by the code hash of whichIndex
- contract, that helps to find:- All user tokens in current collection using owner address and collection address
- All user tokens in all collections using owner address
code of IndexBasis
and Index
contracts and code hash of contracts is fixed and CANNOT BE CHANGED
IIndexBasis
pragma ton-solidity >= 0.58.0;
interface IIndexBasis {
function getInfo() external view responsible returns (address collection);
function destruct(address gasReceiver) external;
}
IndexBasis
pragma ton-solidity >= 0.58.0;
import 'IIndexBasis.sol';
/**
* Errors
* 101 - Method for collection only
**/
contract IndexBasis is IIndexBasis {
address static _collection;
modifier onlyCollection() {
require(msg.sender == _collection, 101, "Method for collection only");
tvm.accept();
_;
}
constructor() public onlyCollection {}
function getInfo() override public view responsible returns (address collection) {
return {value: 0, flag: 64} _collection;
}
function destruct(address gasReceiver) override public onlyCollection {
selfdestruct(gasReceiver);
}
}
Code hash of IndexBasis
compiled by TVMCompiler v0.58.2
and TVM-linker v0.14.51
without salting is 2359f897c9527073b1c95140c670089aa5ab825f5fd1bd453db803fbab47def2
IIndexBasis.getInfo()
function getInfo() external view responsible returns (address collection);
collection
(address
) - collection token contract address
IIndexBasis.destruct()
function destruct(address gasReceiver) external;
gasReceiver
(address
) - address of contract that receives all remaining contract balance afterselfdestruct()
call
IIndex
pragma ton-solidity >= 0.58.0;
interface IIndex {
function getInfo() external view responsible returns (
address collection,
address owner,
address nft
);
function destruct(address gasReceiver) external;
}
Index
pragma ton-solidity >= 0.58.0;
import 'IIndex.sol';
/**
* Errors
* 101 - Method for NFT only
* 102 - Salt doesn't contain any value
**/
contract Index is IIndex {
address static _nft;
address _collection;
address _owner;
constructor(address collection) public {
optional(TvmCell) salt = tvm.codeSalt(tvm.code());
require(salt.hasValue(), 102, "Salt doesn't contain any value");
(, address collection_, address owner) = salt
.get()
.toSlice()
.decode(string, address, address);
require(msg.sender == _nft);
tvm.accept();
_collection = collection_;
_owner = owner;
if (collection_.value == 0) {
_collection = collection;
}
}
function getInfo() override public view responsible returns (
address collection,
address owner,
address nft
) {
return {value: 0, flag: 64} (
_collection,
_owner,
_nft
);
}
function destruct(address gasReceiver) override public {
require(msg.sender == _nft, 101, "Method for NFT only");
selfdestruct(gasReceiver);
}
}
Code hash of Index
compiled by TVMCompiler v0.58.2
and TVM-linker v0.14.51
without salting is 61e5f39a693dc133ea8faf3e80fac069250161b0bced3790c20ae234ce6fd866
Index.getInfo()
function getInfo() external view responsible returns (
address collection,
address owner,
address nft
);
collection
(address
) - collection token contract addressowner
(address
) - token owner contract addressnft
(address
) - token contract address
IIndexBasis.destruct()
function destruct(address gasReceiver) external;
gasReceiver
(address
) - address of contract that receives all remaining contract balance afterselfdestruct()
call
Collection
- TIP-4.1
Collection
contract must implement theTIP4_3Collection
interface and TIP-6.1 interfaces - TIP-4.1
Collection
contract must deployIndexBasis
contract after deployment with code salt - TIP-4.1
Collection
contract mustdestuct()
IndexBasis
contracts before collection destruction
TIP4_3Collection
pragma ton-solidity >= 0.58.0;
interface TIP4_3Collection {
function indexBasisCode() external view responsible returns (TvmCell code);
function indexBasisCodeHash() external view responsible returns (uint256 hash);
function indexCode() external view responsible returns (TvmCell code);
function indexCodeHash() external view responsible returns (uint256 hash);
function resolveIndexBasis() external view responsible returns (address indexBasis);
}
NOTE The TIP-6.1 identifier for this interface is 0x4387BBFB
TIP4_3Collection.indexBasisCode()
function indexBasisCode() external view responsible returns (TvmCell code);
code
(TvmCell
) - basis index contract code
TIP4_3Collection.indexBasisCodeHash()
function indexBasisCodeHash() external view responsible returns (uint256 hash);
hash
(uint256
) - basis index contract code hash
TIP4_3Collection.indexCode()
function indexCode() external view responsible returns (TvmCell code);
code
(TvmCell
) - index contract code
TIP4_3Collection.indexCodeHash()
function indexCodeHash() external view responsible returns (uint256 hash);
hash
(uint256
) - index contract code hash
TIP4_3Collection.indexBasis()
function resolveIndexBasis() external view responsible returns (address indexBasis);
indexBasis
(address
) - basis index contract address
Code salt parameters
stamp
(string
) - stamp that determine type of index.stamp = "nft";
for all NFT indexes
Example of IndexBasis
deployment
function deployIndexBasis(TvmCell codeIndex, address collection, uint128 value) private pure {
string stamp = "nft";
TvmBuilder salt;
salt.store(stamp);
TvmCell code = tvm.setCodeSalt(codeIndex, salt.toCell());
TvmCell stateInit = tvm.buildStateInit({
contr: IndexBasis,
varInit: {_collection: collection},
code: code
});
new IndexBasis{stateInit: stateInit, value: value}();
}
NFT
- TIP-4.1
Collection
contract must implement theTIP4_3NFT
interface and TIP-6.1 interfaces - TIP-4.1
NFT
contract must deploy at least twoIndex
contract after deployment with different code salt- With zero collection address
collection = "0:0000000000000000000000000000000000000000000000000000000000000000"
in code salt - With non-zero collection address
collection = "0:3bd8…"
in code salt
- With zero collection address
- TIP-4.1
NFT
contract mustdestuct()
Index
before NFT burning - TIP-4.1
NFT
contract mustdestuct()
oldIndex
contacts and deploy newIndex
contracts if owner changed
TIP4_3NFT
pragma ton-solidity >= 0.58.0;
interface TIP4_3NFT {
function indexCode() external view responsible returns (TvmCell code);
function indexCodeHash() external view responsible returns (uint256 hash);
function resolveIndex(address collection, address owner) external view responsible returns (address index);
}
NOTE The TIP-6.1 identifier for this interface is 0x4DF6250B
TIP4_3NFT.indexCode()
function indexCode() external view responsible returns (TvmCell code);
code
(TvmCell
) - index contract code
TIP4_3NFT.indexCodeHash()
function indexCodeHash() external view responsible returns (uint256 hash);
hash
(uint256
) - basis index contract code hash
TIP4_3NFT.resolveIndex()
function resolveIndex(address collection, address owner) external view responsible returns (address index);
collection
(address
) - collection token contract addressowner
(address
) - token owner contract addressindex
(address
) - index contract address
Code salt parameters
stamp
(string
) - stamp that determine type of index.stamp = "nft";
for all NFT indexescollection
(address
) - collection token contract addressowner
(address
) - token owner contract address
Example of Index
deployment
function deployIndex(TvmCell codeIndex, address nft, address collection, address owner, uint128 value) private pure {
string stamp = "nft";
TvmBuilder salt;
salt.store(stamp, collection, owner);
TvmCell code = tvm.setCodeSalt(codeIndex, salt.toCell());
TvmCell stateInit = tvm.buildStateInit({
contr: Index,
varInit: {_nft: nft},
code: code
});
new Index{stateInit: stateInit, value: value}();
}
Example of dApp query for search by index
query {
accounts(
filter: {
code_hash: {
eq: "207dc560c5956de1a2c1479356f8f3ee70a59767db2bf4788b1d61ad42cdad82"
}
}
){
id
}
}
Part of response example
{
"data": {
"accounts": [
{
"id": "0:000001b0422f6a7069786fa9a27aa7bb8042f58e1df01dfebc51dcb2baa5eeae"
},
{
"id": "0:00022772794253c1bf8cb4fa59d6161d574033c13d881f3eea14675b911e61b0"
}
]
}
}