> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ton.org/llms.txt
> Use this file to discover all available pages before exploring further.

<AgentInstructions>

## Submitting Feedback

If you encounter incorrect, outdated, or confusing documentation on this page, submit feedback:

POST https://docs.ton.org/feedback

```json
{
  "path": "/standard/tokens/nft/sbt",
  "feedback": "Description of the issue"
}
```

Only submit feedback when you have something specific and actionable to report.

</AgentInstructions>

# SBT: How it works

export const Aside = ({type = "note", title = "", icon = "", iconType = "regular", children}) => {
  const asideVariants = ["note", "tip", "caution", "danger"];
  const asideComponents = {
    note: {
      outerStyle: "border-sky-500/20 bg-sky-50/50 dark:border-sky-500/30 dark:bg-sky-500/10",
      innerStyle: "text-sky-900 dark:text-sky-200",
      calloutType: "note",
      icon: <svg width="14" height="14" viewBox="0 0 14 14" fill="currentColor" xmlns="http://www.w3.org/2000/svg" className="w-4 h-4 text-sky-500" aria-label="Note">
          <path fill-rule="evenodd" clip-rule="evenodd" d="M7 1.3C10.14 1.3 12.7 3.86 12.7 7C12.7 10.14 10.14 12.7 7 12.7C5.48908 12.6974 4.0408 12.096 2.97241 11.0276C1.90403 9.9592 1.30264 8.51092 1.3 7C1.3 3.86 3.86 1.3 7 1.3ZM7 0C3.14 0 0 3.14 0 7C0 10.86 3.14 14 7 14C10.86 14 14 10.86 14 7C14 3.14 10.86 0 7 0ZM8 3H6V8H8V3ZM8 9H6V11H8V9Z"></path>
        </svg>
    },
    tip: {
      outerStyle: "border-emerald-500/20 bg-emerald-50/50 dark:border-emerald-500/30 dark:bg-emerald-500/10",
      innerStyle: "text-emerald-900 dark:text-emerald-200",
      calloutType: "tip",
      icon: <svg width="11" height="14" viewBox="0 0 11 14" fill="currentColor" xmlns="http://www.w3.org/2000/svg" className="text-emerald-600 dark:text-emerald-400/80 w-3.5 h-auto" aria-label="Tip">
          <path d="M3.12794 12.4232C3.12794 12.5954 3.1776 12.7634 3.27244 12.907L3.74114 13.6095C3.88471 13.8248 4.21067 14 4.46964 14H6.15606C6.41415 14 6.74017 13.825 6.88373 13.6095L7.3508 12.9073C7.43114 12.7859 7.49705 12.569 7.49705 12.4232L7.50055 11.3513H3.12521L3.12794 12.4232ZM5.31288 0C2.52414 0.00875889 0.5 2.26889 0.5 4.78826C0.5 6.00188 0.949566 7.10829 1.69119 7.95492C2.14321 8.47011 2.84901 9.54727 3.11919 10.4557C3.12005 10.4625 3.12175 10.4698 3.12261 10.4771H7.50342C7.50427 10.4698 7.50598 10.463 7.50684 10.4557C7.77688 9.54727 8.48281 8.47011 8.93484 7.95492C9.67728 7.13181 10.1258 6.02703 10.1258 4.78826C10.1258 2.15486 7.9709 0.000106649 5.31288 0ZM7.94902 7.11267C7.52078 7.60079 6.99082 8.37878 6.6077 9.18794H4.02051C3.63739 8.37878 3.10743 7.60079 2.67947 7.11294C2.11997 6.47551 1.8126 5.63599 1.8126 4.78826C1.8126 3.09829 3.12794 1.31944 5.28827 1.3126C7.2435 1.3126 8.81315 2.88226 8.81315 4.78826C8.81315 5.63599 8.50688 6.47551 7.94902 7.11267ZM4.87534 2.18767C3.66939 2.18767 2.68767 3.16939 2.68767 4.37534C2.68767 4.61719 2.88336 4.81288 3.12521 4.81288C3.36705 4.81288 3.56274 4.61599 3.56274 4.37534C3.56274 3.6515 4.1515 3.06274 4.87534 3.06274C5.11719 3.06274 5.31288 2.86727 5.31288 2.62548C5.31288 2.38369 5.11599 2.18767 4.87534 2.18767Z"></path>
        </svg>
    },
    caution: {
      outerStyle: "border-amber-500/20 bg-amber-50/50 dark:border-amber-500/30 dark:bg-amber-500/10",
      innerStyle: "text-amber-900 dark:text-amber-200",
      calloutType: "warning",
      icon: <svg className="flex-none w-5 h-5 text-amber-400 dark:text-amber-300/80" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" aria-label="Warning">
          <path stroke-linecap="round" stroke-linejoin="round" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path>
        </svg>
    },
    danger: {
      outerStyle: "border-red-500/20 bg-red-50/50 dark:border-red-500/30 dark:bg-red-500/10",
      innerStyle: "text-red-900 dark:text-red-200",
      calloutType: "danger",
      icon: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor" className="text-red-600 dark:text-red-400/80 w-4 h-4" aria-label="Danger">
          <path d="M17.1 292c-12.9-22.3-12.9-49.7 0-72L105.4 67.1c12.9-22.3 36.6-36 62.4-36l176.6 0c25.7 0 49.5 13.7 62.4 36L494.9 220c12.9 22.3 12.9 49.7 0 72L406.6 444.9c-12.9 22.3-36.6 36-62.4 36l-176.6 0c-25.7 0-49.5-13.7-62.4-36L17.1 292zm41.6-48c-4.3 7.4-4.3 16.6 0 24l88.3 152.9c4.3 7.4 12.2 12 20.8 12l176.6 0c8.6 0 16.5-4.6 20.8-12L453.4 268c4.3-7.4 4.3-16.6 0-24L365.1 91.1c-4.3-7.4-12.2-12-20.8-12l-176.6 0c-8.6 0-16.5 4.6-20.8 12L58.6 244zM256 128c13.3 0 24 10.7 24 24l0 112c0 13.3-10.7 24-24 24s-24-10.7-24-24l0-112c0-13.3 10.7-24 24-24zM224 352a32 32 0 1 1 64 0 32 32 0 1 1 -64 0z"></path>
        </svg>
    }
  };
  let variant = type;
  let gotInvalidVariant = false;
  if (!asideVariants.includes(type)) {
    gotInvalidVariant = true;
    variant = "danger";
  }
  const iconVariants = ["regular", "solid", "light", "thin", "sharp-solid", "duotone", "brands"];
  if (!iconVariants.includes(iconType)) {
    iconType = "regular";
  }
  return <>
      <div className={`callout my-4 px-5 py-4 overflow-hidden rounded-2xl flex gap-3 border ${asideComponents[variant].outerStyle}`} data-callout-type={asideComponents[variant].calloutType}>
        <div className="mt-0.5 w-4" data-component-part="callout-icon">
          {}
          {icon === "" ? asideComponents[variant].icon : <Icon icon={icon} iconType={iconType} size={14} />}
        </div>
        <div className={`text-sm prose min-w-0 w-full ${asideComponents[variant].innerStyle}`} data-component-part="callout-content">
          {gotInvalidVariant ? <p>
              <span className="font-bold">
                Invalid <code>type</code> passed!
              </span>
              <br />
              <span className="font-bold">Received: </span>
              {type}
              <br />
              <span className="font-bold">Expected one of: </span>
              {asideVariants.join(", ")}
            </p> : <>
              {title && <p className="font-bold">{title}</p>}
              {children}
            </>}
        </div>
      </div>
    </>;
};

Soul-Bound Tokens (SBTs) represent non-transferable digital credentials in the TON ecosystem. Unlike standard NFTs, SBTs are permanently bound to their owner and cannot be transferred to another address after minting. The canonical specification is defined in [TEP-85](https://github.com/ton-blockchain/TEPs/blob/c5bfe285ef91810fab02c5352593f5a1455458bf/text/0085-sbt-standard.md).

The SBT standard provides a general interaction scheme while leaving the specific implementation of related contracts to developers.

## Contract data storage

The SBT standard defines what data must be stored in the contract. Each SBT contract must store the following fields:

| Field                | Type         | Description                                           |
| -------------------- | ------------ | ----------------------------------------------------- |
| `index`              | `uint256`    | SBT identifier                                        |
| `collection_address` | `MsgAddress` | Collection address                                    |
| `owner`              | `MsgAddress` | Owner address                                         |
| `content`            | `Cell`       | SBT content/metadata                                  |
| `authority`          | `MsgAddress` | Authority address that can revoke the SBT             |
| `revoked_at`         | `uint64`     | Revocation time in Unix format, or `0` if not revoked |

<Aside type="note">
  The standard defines **what** must be stored in the contract, but does not specify **how** exactly this data should be structured in storage. Developers are free to choose their own implementation.
</Aside>

## Message layouts

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

* prove ownership: sending proof of SBT ownership to a destination 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.

<Aside type="note">
  All message schemes include the `query_id` field. This field is primarily used for off-chain parsing and linking request-response pairs. The protocol itself does not require it for on-chain logic.
</Aside>

## Bound to single owner

The `owner` field is set during minting and remains immutable. The following sections describe the key operations and their message flows.

## Prove ownership

```mermaid theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
sequenceDiagram
  participant U as Owner
  participant S as SBT
  participant D as Destination

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

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

### Prove ownership message (inbound to SBT)

```tlb title="TL-B" theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
;; Inbound message to SBT
prove_ownership#04ded148 query_id:uint64 destination:MsgAddress
  forward_payload:^Cell with_content:Bool = InternalMsgBody;
```

| Name              | Type         | Description                                               |
| ----------------- | ------------ | --------------------------------------------------------- |
| `query_id`        | `uint64`     | Arbitrary identifier to link request and response.        |
| `destination`     | `MsgAddress` | Address of the destination contract to receive the proof. |
| `forward_payload` | `Cell`       | Arbitrary data forwarded to the destination contract.     |
| `with_content`    | `Bool`       | If `true`, attach SBT `content` to the proof.             |

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

```tlb title="TL-B" theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
;; SBT response to the destination 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`     | Matches the `query_id` from the request.           |
| `item_id`    | `uint256`    | Identifier of the SBT item.                        |
| `owner`      | `MsgAddress` | Current owner address.                             |
| `data`       | `Cell`       | Equals `forward_payload` from the request.         |
| `revoked_at` | `uint64`     | Revoke time if SBT is revoked, `0` otherwise.      |
| `content`    | `Maybe Cell` | SBT content if requested with `with_content=true`. |

The transaction is rejected if the sender is not the `owner`.

## Request current owner

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

```mermaid theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
sequenceDiagram
  participant I as Initiator
  participant S as SBT
  participant D as Destination

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

### Request owner message (inbound to SBT)

```tlb title="TL-B" theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
;; Inbound message to SBT
request_owner#d0c3bfea query_id:uint64 destination:MsgAddress
  forward_payload:^Cell with_content:Bool = InternalMsgBody;
```

| Name              | Type         | Description                                              |
| ----------------- | ------------ | -------------------------------------------------------- |
| `query_id`        | `uint64`     | Arbitrary identifier to link request and response.       |
| `destination`     | `MsgAddress` | Address of the destination contract to receive the info. |
| `forward_payload` | `Cell`       | Arbitrary data forwarded to the destination contract.    |
| `with_content`    | `Bool`       | If `true`, attach SBT `content` in the response.         |

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

```tlb title="TL-B" theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
;; SBT response to the destination 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`     | Matches the `query_id` from the request.   |
| `item_id`    | `uint256`    | Identifier of the SBT item.                |
| `initiator`  | `MsgAddress` | Address of the requester.                  |
| `owner`      | `MsgAddress` | Current owner address.                     |
| `data`       | `Cell`       | Equals `forward_payload` from the request. |
| `revoked_at` | `uint64`     | Revoke time if revoked, `0` otherwise.     |
| `content`    | `Maybe Cell` | SBT content if requested.                  |

## 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.

```mermaid theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
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
```

### Destroy message (inbound to SBT)

```tlb title="TL-B" theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
;; Internal message to SBT
destroy#1f04537a query_id:uint64 = InternalMsgBody;
```

| Name       | Type     | Description                                        |
| ---------- | -------- | -------------------------------------------------- |
| `query_id` | `uint64` | Arbitrary identifier to link request and response. |

### Excesses message (SBT -> sender)

```tlb title="TL-B" theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
;; Excess returned to the sender
excesses#d53276db query_id:uint64 = InternalMsgBody;
```

| Name       | Type     | Description                              |
| ---------- | -------- | ---------------------------------------- |
| `query_id` | `uint64` | Matches the `query_id` from the request. |

The transaction is rejected if the sender is not the `owner`.

## Revoke SBT

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

```mermaid theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
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
```

### Revoke message (inbound to SBT)

```tlb title="TL-B" theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
;; Inbound message to SBT
revoke#6f89f5e3 query_id:uint64 = InternalMsgBody;
```

| Name       | Type     | Description                                 |
| ---------- | -------- | ------------------------------------------- |
| `query_id` | `uint64` | Arbitrary identifier for off-chain parsing. |

The transaction is rejected if:

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