---
title: "6. SBOR: How Radix Encodes Data"
url: "https://radix.wiki/developers/transactions/06-sbor"
updated: 2026-11-14
license: CC-BY-4.0
license_url: "https://creativecommons.org/licenses/by/4.0/"
version: "1.0.0"
---

# 6. SBOR: How Radix Encodes Data

|  |  |
| --- | --- |
| Series | [Transactions track](/developers/transactions), part 6 |
| Name | Originally "Scrypto Binary-Friendly Object Representation" |
| Variants | Scrypto SBOR (prefix `0x5c`), Manifest SBOR (`0x4d`), Basic SBOR (`0x5b`) |
| Integers | Fixed width, little-endian; lengths in LEB128 |
| Source | [sbor crate](https://github.com/radixdlt/radixdlt-scrypto/blob/main/sbor/src), radixdlt-scrypto |
| Tested with | `sbor` and `radix-common` 1.4.0, 14 November 2026 |

## What is SBOR?

SBOR is the format in which Radix represents data everywhere: in transactions, in component state stored on the ledger, in calls between components inside the [Radix Engine](/contents/tech/core-protocols/radix-engine), and in the JSON the Gateway API returns. It defines a value model, a binary encoding of it, and a type model that attaches names and validation to values ([SBOR](https://docs.radixdlt.com/docs/what-is-sbor)).

A developer rarely writes SBOR by hand: `#[derive(ScryptoSbor)]` generates the encoding, and a [manifest](/developers/transactions/01-manifest-language)'s argument syntax is a text form of it. It matters when reading raw payloads, decoding events or state outside Rust, or working out why a manifest argument does not match a method's signature.

## The value model

Every SBOR value has a **value kind**, written as one byte before the value. Sixteen core kinds are shared by every variant of SBOR: `Bool` (`0x01`); signed integers `I8` to `I128` (`0x02`–`0x06`); unsigned `U8` to `U128` (`0x07`–`0x0b`); `String` (`0x0c`); and four composites, `Array` (`0x20`), `Tuple` (`0x21`), `Enum` (`0x22`) and `Map` (`0x23`) ([SBOR Value Model](https://docs.radixdlt.com/docs/sbor-value-model)). There are no floats, for the same reason Scrypto has none: every validator must compute the same result.

A Rust struct is a `Tuple` and a Rust enum is an `Enum`: a variant index followed by its fields as a tuple. Field and variant names are not in the value. Each element of an `Array` or `Map` must share one value kind, which in Rust means the element type must implement `Categorize`.

Each variant adds custom kinds from `0x80` up ([Scrypto](https://github.com/radixdlt/radixdlt-scrypto/blob/main/radix-common/src/data/scrypto/custom_value_kind.rs), [Manifest](https://github.com/radixdlt/radixdlt-scrypto/blob/main/radix-common/src/data/manifest/custom_value_kind.rs)):

| Scrypto SBOR | Manifest SBOR |
| --- | --- |
| `Reference` `0x80`, `Own` `0x90`, `Decimal` `0xa0`, `PreciseDecimal` `0xb0`, `NonFungibleLocalId` `0xc0` | `Address` `0x80`, `Bucket` `0x81`, `Proof` `0x82`, `Expression` `0x83`, `Blob` `0x84`, `Decimal` `0x85`, `PreciseDecimal` `0x86`, `NonFungibleLocalId` `0x87`, `AddressReservation` `0x88` |

The split follows what each side can refer to. Inside the engine a value can own an object or reference a global one. A manifest refers instead to the buckets and proofs it has created, to blobs attached to the transaction, and to expressions such as `ENTIRE_WORKTOP`; the transaction processor translates these into Scrypto values when it makes each call ([Manifest Value Syntax](https://docs.radixdlt.com/docs/manifest-value-syntax)).

## How is a value encoded in bytes?

A payload starts with a one-byte prefix naming the variant, then the value: its kind byte, then its body. These encodings were produced with the 1.4.0 crates:

```
scrypto_encode(&7u8)                  5c 07 07
scrypto_encode(&"hi".to_string())     5c 0c 02 68 69
scrypto_encode(&vec![1u32, 2])        5c 20 09 02 01 00 00 00 02 00 00 00
scrypto_encode(&Side::Buy)            5c 22 00 00
scrypto_encode(&Side::Sell(3))        5c 22 01 01 07 03
```

Reading the vector: `0x20` for an array, `0x09` once for its element kind `U32`, `0x02` for the count, then each element's four bytes without a kind byte. Lengths and counts are LEB128 varints. Integers are fixed-width and **little-endian**: 1 as a `U32` is `01 00 00 00`. The value-model table in the official docs labels the integer encodings big-endian, which the bytes above and the crate's encoder both contradict ([integer.rs](https://github.com/radixdlt/radixdlt-scrypto/blob/main/sbor/src/codec/integer.rs)).

A struct, and the same struct encoded for a manifest:

```
#[derive(ScryptoSbor, ManifestSbor)]
struct Order { price: Decimal, qty: u32, memo: Option }

let o = Order { price: dec!(1), qty: 2, memo: None };

scrypto_encode(&o)    5c 21 03  a0 00 00 64 a7 b3 b6 e0 0d 00 … 00  09 02 00 00 00  22 00 00
manifest_encode(&o)   4d 21 03  85 00 00 64 a7 b3 b6 e0 0d 00 … 00  09 02 00 00 00  22 00 00
```

The two payloads differ in two bytes: the prefix, `0x5c` or `0x4d`, and the `Decimal`'s kind, `0xa0` or `0x85`. The `Decimal` 1 is its internal 192-bit integer, 1018, in 24 little-endian bytes, and `None` is enum variant 0 with no fields. Because names are not encoded, the Scrypto payload decoded without error as the tuple `(Decimal, u32, Option)`. Decoding it as Manifest SBOR failed at the first byte with `UnexpectedPayloadPrefix { expected: 77, actual: 92 }`, which is `0x4d` against `0x5c`.

The same prefix starts every transaction. A V2 transaction built in [Rust](/developers/transactions/07-rust-transaction-building) began `4d 22 0c`: Manifest SBOR, an enum, variant 12, which the transaction crate assigns to `NotarizedTransactionV2`.

## How do I write SBOR values in a manifest?

Manifest arguments are Manifest SBOR values in text. Decompiling a manifest built with the Rust builder gives the canonical form of each kind:

```
CALL_METHOD
    Address("component_tdx_2_1cptxxxxxxxxxfaucetxxxxxxxxx000527798379xxxxxxxxxyulkzl")
    "some_method"
    Bucket("xrd")
    5u8
    -3i64
    "text"
    true
    Array(1u16, 2u16)
    Enum(Decimal("1"))
    Enum()
    Tuple(PreciseDecimal("0.5"), NonFungibleLocalId("#7#"))
    NonFungibleLocalId("")
    Map("k" => 1u32)
;
```

Integers carry a type suffix: `5` alone fails in the lexer with `InvalidIntegerType`. A struct argument is written as a `Tuple` of its fields in order. An enum is written `Enum(fields…)` with its variant index; the decompiler printed `Some(dec!(1))` as `Enum(Decimal("1"))` and `None` as `Enum()`, though the compiler also accepts `Some(…)` and `None`. The engine's own enums have named aliases, such as `Enum()`, listed in the manifest compiler's [manifest_enums.rs](https://github.com/radixdlt/radixdlt-scrypto/blob/main/radix-transactions/src/manifest/manifest_enums.rs); an enum from your own blueprint takes the numeric index ([Manifest Value Syntax](https://docs.radixdlt.com/docs/manifest-value-syntax)).

A non-fungible local ID is written by form: `#7#` for an integer, `` for a string, `[0a1b]` for bytes and `{…}` for a RUID.

## Schemas and JSON

The type model gives a value its names. A package's **schema**, generated from its Rust types at build time and stored with it on the ledger, says that the tuple at a given position is an `Order` whose first field is `price`. The engine uses it to check that call arguments and stored state match the declared types, and the Gateway uses it to name fields when it returns state ([SBOR Type Model](https://docs.radixdlt.com/docs/sbor-type-model)).

The Gateway's JSON form is **Programmatic JSON**, the stable one of SBOR's several JSON representations: each value is an object with a `kind`, plus `type_name` and `field_name` where a schema supplies them ([Programmatic JSON](https://docs.radixdlt.com/docs/sbor-programmatic-json)). The Stokenet faucet component's state, from `/state/entity/details` on 14 November 2026:

```
{
  "kind": "Tuple",
  "type_name": "Faucet",
  "fields": [
    { "kind": "Own", "type_name": "Vault", "field_name": "vault",
      "value": "internal_vault_tdx_2_1tz9uaalv8g3ahmwep2trlyj2m3zn7rstm9pwessa3k56me2fuywfep" },
    { "kind": "Own", "type_name": "KeyValueStore", "field_name": "transactions",
      "value": "internal_keyvaluestore_tdx_2_1krn7clzr3qmq2zhwr77mdenksxswf00yeh8tn3vyzesg4kr3xxslpp" }
  ]
}
```

The faucet's two fields are owned objects, so their values are the addresses of the internal vault and key-value store it owns.

## Next Steps

- [Building Transactions in Rust](/developers/transactions/07-rust-transaction-building) – produce and submit a Manifest SBOR payload
- [Data Types and the Runtime API](/developers/scrypto/12-data-types-and-runtime) – the Scrypto types behind these value kinds

## External Links

- [SBOR](https://docs.radixdlt.com/docs/what-is-sbor) – Radix documentation overview
- [SBOR Value Model](https://docs.radixdlt.com/docs/sbor-value-model) and [SBOR Type Model](https://docs.radixdlt.com/docs/sbor-type-model)
- [Manifest Value Syntax](https://docs.radixdlt.com/docs/manifest-value-syntax)
- [sbor crate source](https://github.com/radixdlt/radixdlt-scrypto/blob/main/sbor/src) – radixdlt-scrypto repository
- [SBOR ez mode](https://xstelea.github.io/radix-web3.js/sbor) – radix-web3.js schema builder for TypeScript
