Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -331,8 +331,7 @@
{
"group": "SBT",
"pages": [
"standard/tokens/sbt/overview",
"standard/tokens/sbt/how-works"
"standard/tokens/sbt/how-it-works"
]
},
"standard/tokens/airdrop"
Expand Down
2 changes: 1 addition & 1 deletion guidebook/from-ethereum.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -118,5 +118,5 @@ This section showcases match between some of the Ethereum standards and proposal
| Token metadata | ERC-4955 (Not exactly, but close match) | [Token Data Standard (TEP-0064)](/standard/tokens/metadata) |
| NFT royalty standard | EIP-2981 | [NFT Royalty Standard (TEP-0066)](/standard/tokens/nft/comparison) |
| DNS-like registry | ENS (EIP-137) | [DNS Standard (TEP-0081)](/services/dns) |
| Soulbound / account-bound token concept | EIP-4973 | [SBT Standard (TEP-0085)](/standard/tokens/sbt/overview) |
| Soulbound / account-bound token concept | EIP-4973 | [SBT Standard (TEP-0085)](/standard/tokens/nft/comparison#sbt) |
| Wallet connection protocol | WalletConnect / EIP-1193 | [TonConnect (TEP-0115)](/ecosystem/ton-connect) |
10 changes: 10 additions & 0 deletions standard/tokens/nft/comparison.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,16 @@ Short overview:

An SBT inherits the uniqueness and metadata model of NFTs but disables transfer operations, binding the token permanently to the recipient's address after mint. This makes SBTs well‑suited for identity and credentials: attendance records, participation proofs, and non‑transferable achievements. It also has on-chain API to proof ownership of SBT item.

#### Core functionality

SBTs provide several key operations that enable secure credential management:

- **Ownership binding** - the owner is set at mint time and never changes, ensuring permanent association with the recipient.
- **Ownership proof** - allows the owner to request the SBT to send a proof to a target contract confirming ownership, with optional content attachment.
- **Owner information requests** - any party can request current owner information and optional content from the SBT.
- **SBT destruction** - the owner can destroy the SBT contract, clearing ownership and authority fields.
- **Revocation mechanism** - the authority can mark the SBT as revoked, preventing further use while maintaining historical records.

## Single NFT (no collection)

A Single NFT is an item contract deployed without an associated collection. It keeps the same ownership semantics but omits shared collection metadata and indexing.
Expand Down
2 changes: 1 addition & 1 deletion standard/tokens/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ The TON blockchain supports three distinct categories of digital tokens, each de

- [Fungible tokens](/standard/tokens/jettons/overview) (Jettons) - this is a web3 way to create new currency on your own
- [Non-Fungible Tokens](/standard/tokens/nft/overview) (NFTs) - like Jettons, but each token is unique and represent distinct entity
- [Soul-bound Tokens](/standard/tokens/sbt/overview) (SBTs) - like NFTs, but not-transferable, bound to a single owner
- [Soul-bound Tokens](/standard/tokens/nft/comparison#sbt) (SBTs) - like NFTs, but not-transferable, bound to a single owner

## Comparison table

Expand Down
213 changes: 213 additions & 0 deletions standard/tokens/sbt/how-it-works.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
---
title: "SBT: How it works"
sidebarTitle: "How it works"
---

import { Aside } from '/snippets/aside.jsx';

This article describes the basic ideas and processes behind the implementation of SBT (Soul-Bound Token) in the TON Blockchain.

SBT is a variation of an NFT Item, but has some differences.

<Aside type="note">
It is important to keep in mind that SBT standards provide only a general scheme of interaction, leaving the specific implementation of related contracts to developers.
</Aside>

<Aside type="caution">
In all schemes below you will see the query id field. Nowadays the field is deprecated, and protocols themselves don't need it. It is mostly used for easier off-chain parsing and other web2 processing.
</Aside>

## Message flows

Interactions with SBT contracts, which are most often encountered by users and developers, are:

- prove ownership: sending proof of SBT ownership to a target contract;
- request current owner: requesting current owner information from SBT;
- destroy SBT: destroying the SBT contract and returning remaining balance;
- revoke SBT: marking the SBT as revoked by authority.

## Bound to single owner

The `owner` of SBT is set at the minting time and never changes. Below is a simple explanation of the key operations and their message flows.

## Prove ownership
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like this should go into "Internal messages" subsection


This message flow allows the `owner` to ask the SBT to send a proof to a target contract confirming that they own this SBT. You may include arbitrary `forward_payload` and optionally attach `content`.

### Prove ownership message (inbound to SBT)

```tlb title="TL-B"
;; Inbound message to SBT
prove_ownership#04ded148 query_id:uint64 dest:MsgAddress
forward_payload:^Cell with_content:Bool = InternalMsgBody;
```

| Name | Type | Description |
| ----------------- | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `query_id` | `uint64` | Links the request `prove_ownership` and the response `ownership_proof` to each other. To ensure this process works correctly, always use a unique query ID. |
| `dest` | `MsgAddress` | Address of the target contract to receive the proof. |
| `forward_payload` | `Cell` | Arbitrary data forwarded to the target contract. |
| `with_content` | `Bool` | If `true`, attach SBT `content`. |

### Ownership proof message (SBT -> target contract)

```tlb title="TL-B"
;; SBT response to the target contract (if checks pass)
ownership_proof#0524c7ae query_id:uint64 item_id:uint256 owner:MsgAddress
data:^Cell revoked_at:uint64 content:(Maybe ^Cell) = InternalMsgBody;
```

| Name | Type | Description |
| ------------ | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `query_id` | `uint64` | Links the request `prove_ownership` and this `ownership_proof` response to each other. To ensure this process works correctly, always use a unique query ID. |
| `item_id` | `uint256` | Identifier of the SBT item. |
| `owner` | `MsgAddress` | Current owner address. |
| `data` | `Cell` | Custom data forwarded to the target contract, equal to `forward_payload`. |
| `revoked_at` | `uint64` | Revoke time if SBT is revoked, `0` otherwise. |
| `content` | `maybe Cell` | SBT content if it was requested with `with_content=true`. |
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
| `content` | `maybe Cell` | SBT content if it was requested with `with_content=true`. |
| `content` | `Maybe Cell` | SBT content if it was requested with `with_content=true`. |


Rejected transaction if the sender is not the `owner`.

```mermaid
sequenceDiagram
participant U as Owner
participant S as SBT
participant D as dest (target contract)

U->>S: prove_ownership
alt owner
S->>D: ownership_proof
else not owner
S-->>U: reject
end
```

## Request current owner

This message flow allows any initiator to ask the SBT to send the current `owner` (and optionally the `content`) to a target contract.

### Request owner message (inbound to SBT)

```tlb title="TL-B"
;; Inbound message to SBT
request_owner#d0c3bfea query_id:uint64 dest:MsgAddress
forward_payload:^Cell with_content:Bool = InternalMsgBody;
```

| Name | Type | Description |
| ----------------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `query_id` | `uint64` | Links the request `request_owner` and the response `owner_info` to each other. To ensure this process works correctly, always use a unique query ID. |
| `dest` | `MsgAddress` | Address of the target contract to receive the response. |
| `forward_payload` | `Cell` | Arbitrary data forwarded to the target contract. |
| `with_content` | `Bool` | If `true`, attach SBT `content` in the response. |

### Owner info message (SBT -> target contract)

```tlb title="TL-B"
;; SBT response to the target contract
owner_info#0dd607e3 query_id:uint64 item_id:uint256 initiator:MsgAddress owner:MsgAddress
data:^Cell revoked_at:uint64 content:(Maybe ^Cell) = InternalMsgBody;
```

| Name | Type | Description |
| ------------ | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `query_id` | `uint64` | Links the request `request_owner` and this `owner_info` response to each other. To ensure this process works correctly, always use a unique query ID. |
| `item_id` | `uint256` | Identifier of the SBT item. |
| `initiator` | `MsgAddress` | Address of the requester. |
| `owner` | `MsgAddress` | Current owner address. |
| `data` | `Cell` | Custom data forwarded to the target, equal to `forward_payload`. |
| `revoked_at` | `uint64` | Revoke time if revoked, `0` otherwise. |
| `content` | `maybe Cell` | SBT content if it was requested. |

### Full pipeline

```mermaid
sequenceDiagram
participant I as Initiator
participant S as SBT
participant D as dest (target contract)

I->>S: request_owner
S->>D: owner_info
```

## Destroy

This message flow allows the `owner` to destroy the SBT contract. This clears the `owner` and `authority` fields, and sends remaining balance back to the sender via an `excesses` message.

### Destroy message (inbound to SBT)

```tlb title="TL-B"
;; Internal message to SBT
destroy#1f04537a query_id:uint64 = InternalMsgBody;
```

| Name | Type | Description |
| ---------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| `query_id` | `uint64` | Links the request `destroy` and the response `excesses` to each other. To ensure this process works correctly, always use a unique query ID. |

### Excesses message (SBT -> sender)

```tlb title="TL-B"
;; Excess returned to the sender
excesses#d53276db query_id:uint64 = InternalMsgBody;
```

| Name | Type | Description |
| ---------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `query_id` | `uint64` | Links the request `destroy` and this `excesses` response to each other. To ensure this process works correctly, always use a unique query ID. |

Rejected transaction if the sender is not the `owner`.

### Full pipeline

```mermaid
sequenceDiagram
participant U as Owner
participant S as SBT

U->>S: destroy(query_id)
alt owner
S->>S: owner = null, authority = null
S-->>U: excesses(query_id)
else not owner
S-->>U: reject
end
```

## Revoke SBT

This message flow allows the `authority` to mark the SBT as revoked. Revoking twice is disallowed.

### Revoke message (inbound to SBT)

```tlb title="TL-B"
;; Inbound message to SBT
revoke#6f89f5e3 query_id:uint64 = InternalMsgBody;
```

| Name | Type | Description |
| ---------- | -------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `query_id` | `uint64` | Identifies the `revoke` request for off-chain parsing. To ensure this process works correctly, always use a unique query ID. |

Rejected transaction if:

- the sender is not the `authority`;
- the SBT was already revoked.

```mermaid
sequenceDiagram
participant A as Authority
participant S as SBT

A->>S: revoke(query_id)
alt A == authority and not revoked
S->>S: revoked_at = now
else not authority or already revoked
S-->>A: reject
end
```

## See more:

- SBT Standard [TEP-85](https://github.com/ton-blockchain/TEPs/blob/c5bfe285ef91810fab02c5352593f5a1455458bf/text/0085-sbt-standard.md)
Empty file removed standard/tokens/sbt/how-works.mdx
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why empty and not deleted?

Empty file.
25 changes: 0 additions & 25 deletions standard/tokens/sbt/overview.mdx

This file was deleted.