diff --git a/docs.json b/docs.json index 8f089ab9..060d517e 100644 --- a/docs.json +++ b/docs.json @@ -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" diff --git a/guidebook/from-ethereum.mdx b/guidebook/from-ethereum.mdx index a1b672a0..b6c78c3e 100644 --- a/guidebook/from-ethereum.mdx +++ b/guidebook/from-ethereum.mdx @@ -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) | diff --git a/standard/tokens/nft/comparison.mdx b/standard/tokens/nft/comparison.mdx index b7d49576..85558c74 100644 --- a/standard/tokens/nft/comparison.mdx +++ b/standard/tokens/nft/comparison.mdx @@ -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. diff --git a/standard/tokens/overview.mdx b/standard/tokens/overview.mdx index 36e11fbc..caf8433e 100644 --- a/standard/tokens/overview.mdx +++ b/standard/tokens/overview.mdx @@ -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 diff --git a/standard/tokens/sbt/how-it-works.mdx b/standard/tokens/sbt/how-it-works.mdx new file mode 100644 index 00000000..0bd8b7aa --- /dev/null +++ b/standard/tokens/sbt/how-it-works.mdx @@ -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. + + + + + +## 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 + +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`. | + +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) diff --git a/standard/tokens/sbt/how-works.mdx b/standard/tokens/sbt/how-works.mdx deleted file mode 100644 index e69de29b..00000000 diff --git a/standard/tokens/sbt/overview.mdx b/standard/tokens/sbt/overview.mdx deleted file mode 100644 index c3ca505f..00000000 --- a/standard/tokens/sbt/overview.mdx +++ /dev/null @@ -1,25 +0,0 @@ ---- -title: "SBT: Soul-bound token" -sidebarTitle: "Overview" ---- - -Soul-bound Tokens are non-transferable [NFTs](/standard/tokens/nft/overview). - -## Why -Soul-bound Tokens represent a revolutionary concept in digital identity and credential management within the TON blockchain. These tokens embody permanence and non-transferability, creating an immutable link between specific credentials and their holders' blockchain addresses. The term "soul-bound" references the permanent binding of these tokens to their recipient's account. - -Everyone can be confident that each SBT has exactly one owner, enabling protocols to grant privileges to that owner via the SBT, which expands the protocol's capabilities. - -The technical foundation of SBTs builds upon existing NFT infrastructure while implementing transfer restrictions. SBTs inherit the uniqueness and metadata capabilities of NFTs but incorporate smart contract logic that prevents transfer operations. This design ensures that tokens remain permanently bound to their original recipients while maintaining all the verification and authenticity benefits of blockchain technology. - -The defining characteristic of SBTs lies in their non-transferable nature. Once issued to a specific address, these tokens cannot be moved, sold, or transferred to any other address. - -## Use cases: - -- Event participation record -- Attendance certificate -- Community contribution - -## Deep Dive -- [SBTs working principles](/standard/tokens/sbt/how-works) -- SBT Standard [TEP-85](https://github.com/ton-blockchain/TEPs/blob/0d7989fba6f2d9cb08811bf47263a9b314dc5296/text/0085-sbt-standard.md) \ No newline at end of file