TON SBT Contract Product & Technical requirements

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_payload exists & is a cell with sign of the transfer signed by private key corresponding to stored public key
  • NFT should have GET methods 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



Report Page