TON SBT Contract Product & Technical requirements
Product description
So-called soulbound NFT’s are such NFT’s that are only owned by single person and should not be sold or transferred.
Original idea was described in this article from Vitalik Buterin http://vitalik.ca/general/2022/01/26/soulbound.html
Basic solution actually is pretty simple: we can simply block ability to transfer NFT, but this leads to two problems:
- Sometimes users want to move their assets from one wallet to other
- Users can sell access to the wallet instead of the NFT itself
We don’t belive that there is a good solution for the second problem, but first one is actually pretty big for TON since in TON user can have infinite number of addresses for a single wallet.
To solve first problem we suggest new approach of key-bound NFT’s.
This NFT’s store public key of their owner and allow transfers only signed with corresponding private key.
This way actual owner of NFT always can set owner address of NFT to any desired one.
Technical description
Solution should follow TON NFT Standard and extend reference NFT contract implementation with some changes:
- NFT item contract should store public key of the owner
- Public key along with owner address should be passed when minting new NFT in collection
- NFT should only accept transfer operations if
custom_payloadexists & is a cell with sign of the transfer signed by private key corresponding to stored public key - NFT should have
GETmethods to return public key of the owner
Transfer process
According to the standard TLB schema for transfer message is:
transfer#5fcc3d14 query_id:uint64 new_owner:MsgAddress response_destination:MsgAddress custom_payload:(Maybe ^Cell) forward_amount:(VarUInteger 16) forward_payload:(Either Cell ^Cell) = InternalMsgBody;
We suggest soulbound NFT’s to accept transfer messages only when custom_payload contains a Cell with sign of schema below:
transfer#5fcc3d14 query_id:uint64 new_owner:MsgAddress response_destination:MsgAddress custom_payload_bit:(## 1) { custom_payload_bit = 1 } forward_amount:(VarUInteger 16) forward_payload:(Either Cell ^Cell) = InternalMsgBody;
In this way contract should only read first reference from message body (which is the custom_payload & contains the sign) and check that sign of the rest of the body is the same as sign from the reference.
In case of transfer to zero address - this should be considered as burning NFT and both current owner address should be set to zero address & public key should be set to empty one.
Development requirements
- Custom NFT & Collection contracts for SBT support (collection may support both normal and soulbound tokens)
- Custom Soulbound Single-NFT contract
- Solution should be covered with tests
- Solution should follow style of Getgems contracts (http://github.com/getgems-io/nft-contracts). Including: tests, code for interacting with contract, code for setting it up.
- Solution should contain some docs & examples for deploying & interacting with contract