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, 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).
A developer rarely writes SBOR by hand: #[derive(ScryptoSbor)] generates the encoding, and a manifest'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). 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, Manifest):
| 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).
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 03Reading 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).
A struct, and the same struct encoded for a manifest:
#[derive(ScryptoSbor, ManifestSbor)]
struct Order { price: Decimal, qty: u32, memo: Option<String> }
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 00The 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<String>). 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 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<U16>(1u16, 2u16)
Enum<1u8>(Decimal("1"))
Enum<0u8>()
Tuple(PreciseDecimal("0.5"), NonFungibleLocalId("#7#"))
NonFungibleLocalId("<abc>")
Map<String, U32>("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<n u8>(fields…) with its variant index; the decompiler printed Some(dec!(1)) as Enum<1u8>(Decimal("1")) and None as Enum<0u8>(), though the compiler also accepts Some(…) and None. The engine's own enums have named aliases, such as Enum<DefaultDepositRule::Reject>(), listed in the manifest compiler's manifest_enums.rs; an enum from your own blueprint takes the numeric index (Manifest Value Syntax).
A non-fungible local ID is written by form: #7# for an integer, <abc> 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).
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). 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 – produce and submit a Manifest SBOR payload
- Data Types and the Runtime API – the Scrypto types behind these value kinds
External Links
- SBOR – Radix documentation overview
- SBOR Value Model and SBOR Type Model
- Manifest Value Syntax
- sbor crate source – radixdlt-scrypto repository
- SBOR ez mode – radix-web3.js schema builder for TypeScript
