From 0dee6cde5d9d644dda5118b15da8ad371f991ef6 Mon Sep 17 00:00:00 2001
From: Brian Seong
Date: Fri, 1 May 2026 17:38:41 +0900
Subject: [PATCH 1/3] docs(protocol): document v0.14 asset two-word encoding
Adds an Encoding subsection to the Native asset section of asset.md,
covering ASSET_KEY/ASSET_VALUE layout for fungible and non-fungible
assets, the metadata-low-byte mechanic on faucet_id_suffix, and the
AssetCallbackFlag bit-pack. Includes a worked memory-layout example so
readers can verify their builds against AssetVaultKey::to_word().
Closes the readability gap that forced the Zoro team to dig through
miden-protocol Rust source to figure out the v0.14 layout (issue #270).
---
.../core-concepts/protocol/asset.md | 39 +++++++++++++++++++
1 file changed, 39 insertions(+)
diff --git a/versioned_docs/version-0.14/core-concepts/protocol/asset.md b/versioned_docs/version-0.14/core-concepts/protocol/asset.md
index 8cd17a02..333cd28c 100644
--- a/versioned_docs/version-0.14/core-concepts/protocol/asset.md
+++ b/versioned_docs/version-0.14/core-concepts/protocol/asset.md
@@ -60,6 +60,45 @@ Non-fungible assets are encoded by hashing the `Asset` data into 32 bytes and pl
+### Encoding
+
+Every asset in an account vault is stored as a key/value pair of two `Word`s (eight field elements). The same encoding is used by both the kernel and the standard library, and is the canonical layout when assets cross the MASM/Rust boundary.
+
+```
+ASSET_KEY = [asset_id_suffix, asset_id_prefix, faucet_id_suffix_with_metadata, faucet_id_prefix]
+ASSET_VALUE = [amount, 0, 0, 0] # fungible
+ASSET_VALUE = DATA_HASH # non-fungible
+```
+
+The `asset_id_*` limbs distinguish different assets issued by the same faucet:
+
+- For fungible assets they are always `0`. A faucet only ever issues one asset, so the faucet ID alone identifies it.
+- For non-fungible assets they are `DATA_HASH[0]` and `DATA_HASH[1]` — the first two field elements of the hash of the asset data.
+
+The third element of the key, `faucet_id_suffix_with_metadata`, packs the faucet's account-ID suffix together with a small metadata byte. The lower 8 bits of every account-ID suffix are reserved zero by construction (`validate_suffix` in `account/account_id/v0/mod.rs`), and `AssetVaultKey::to_word()` (`asset/vault/vault_key.rs`) bit-OR's the metadata byte into those reserved bits. There is no left-shift; the suffix is already aligned.
+
+Today only the lowest of those 8 bits is used, holding the per-asset callback flag from `AssetCallbackFlag` (`asset/asset_callbacks_flag.rs`):
+
+- `Disabled = 0` — the kernel skips faucet callbacks for this asset.
+- `Enabled = 1` — the kernel checks for and invokes the faucet's callback procedures (see [Callbacks](#callbacks) below).
+
+The remaining 7 bits are reserved for future asset-level metadata.
+
+#### Worked example
+
+A fungible asset with `amount = 10000`, callbacks enabled, issued by a faucet whose ID has `suffix = 447750849984126720` and `prefix = 12959558562786060576` lays out in vault memory as:
+
+```text
+[ 0, ← asset_id_suffix (zero for fungible)
+ 0, ← asset_id_prefix (zero for fungible)
+ 447750849984126721, ← faucet_id_suffix | callbacks_enabled (suffix ends in 0x00, OR'd with 0x01)
+ 12959558562786060576, ← faucet_id_prefix
+ 10000, ← amount
+ 0, 0, 0 ] ← ASSET_VALUE padding
+```
+
+For a non-fungible asset the last four elements are replaced with `DATA_HASH` and the first two elements of the key are filled from `DATA_HASH[0..2]`. See `FungibleAsset::to_value_word()` and `NonFungibleAsset::to_value_word()` in `asset/fungible.rs` and `asset/nonfungible.rs`.
+
### Burning
Assets in Miden can be burned through various methods, such as rendering them unspendable by storing them in an unconsumable note, or sending them back to their original faucet for burning using it's dedicated function.
From 4e6a739b242f324f2c0b108d13eb6c3a0b798bd8 Mon Sep 17 00:00:00 2001
From: Brian Seong
Date: Fri, 1 May 2026 17:38:52 +0900
Subject: [PATCH 2/3] docs(migration): link asset encoding spec instead of
inlining formula
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The v0.14 migration page's ASSET_KEY summary used a (faucet_id_suffix
<< 8) | callbacks_enabled formula that's misleading — the bits are
OR'd into reserved low-byte space, not left-shifted. Trim the formula
and link to the canonical Encoding section in core-concepts/protocol/
asset.md. The migration page's job is the v0.13 -> v0.14 delta, not
redefining the spec.
---
docs/builder/migration/05-asset-vault-faucet.md | 9 ++++-----
.../builder/migration/05-asset-vault-faucet.md | 9 ++++-----
2 files changed, 8 insertions(+), 10 deletions(-)
diff --git a/docs/builder/migration/05-asset-vault-faucet.md b/docs/builder/migration/05-asset-vault-faucet.md
index e79bba8c..6918976f 100644
--- a/docs/builder/migration/05-asset-vault-faucet.md
+++ b/docs/builder/migration/05-asset-vault-faucet.md
@@ -16,12 +16,11 @@ Assets are now represented as two words (`ASSET_KEY` + `ASSET_VALUE`) instead of
### Summary
-The single 4-felt `ASSET` word has been split into two words:
+The single 4-felt `ASSET` word has been split into two words: `ASSET_KEY` (identity + faucet + callback flag) and `ASSET_VALUE` (amount or data hash). Every kernel procedure and standard-library helper that previously accepted or returned `ASSET` now works with the `ASSET_KEY, ASSET_VALUE` pair.
-- **`ASSET_KEY`** = `[asset_id_suffix, asset_id_prefix, (faucet_id_suffix << 8) | callbacks_enabled, faucet_id_prefix]`
-- **`ASSET_VALUE`** = `[amount, 0, 0, 0]` for fungible assets, or `DATA_HASH` for non-fungible assets.
-
-Every kernel procedure and standard-library helper that previously accepted or returned `ASSET` now works with the `ASSET_KEY, ASSET_VALUE` pair.
+:::note Canonical layout
+The full field-by-field layout — including how the per-asset callback flag is packed into the reserved low byte of `faucet_id_suffix` — is documented in [Asset encoding](../../core-concepts/protocol/asset.md#encoding). This page covers the v0.13 → v0.14 delta only.
+:::
### Affected Code
diff --git a/versioned_docs/version-0.14/builder/migration/05-asset-vault-faucet.md b/versioned_docs/version-0.14/builder/migration/05-asset-vault-faucet.md
index e79bba8c..6918976f 100644
--- a/versioned_docs/version-0.14/builder/migration/05-asset-vault-faucet.md
+++ b/versioned_docs/version-0.14/builder/migration/05-asset-vault-faucet.md
@@ -16,12 +16,11 @@ Assets are now represented as two words (`ASSET_KEY` + `ASSET_VALUE`) instead of
### Summary
-The single 4-felt `ASSET` word has been split into two words:
+The single 4-felt `ASSET` word has been split into two words: `ASSET_KEY` (identity + faucet + callback flag) and `ASSET_VALUE` (amount or data hash). Every kernel procedure and standard-library helper that previously accepted or returned `ASSET` now works with the `ASSET_KEY, ASSET_VALUE` pair.
-- **`ASSET_KEY`** = `[asset_id_suffix, asset_id_prefix, (faucet_id_suffix << 8) | callbacks_enabled, faucet_id_prefix]`
-- **`ASSET_VALUE`** = `[amount, 0, 0, 0]` for fungible assets, or `DATA_HASH` for non-fungible assets.
-
-Every kernel procedure and standard-library helper that previously accepted or returned `ASSET` now works with the `ASSET_KEY, ASSET_VALUE` pair.
+:::note Canonical layout
+The full field-by-field layout — including how the per-asset callback flag is packed into the reserved low byte of `faucet_id_suffix` — is documented in [Asset encoding](../../core-concepts/protocol/asset.md#encoding). This page covers the v0.13 → v0.14 delta only.
+:::
### Affected Code
From bf75a5b67d8c521a6a7323cc6bc07b22a8a6264f Mon Sep 17 00:00:00 2001
From: Brian Seong
Date: Tue, 5 May 2026 15:24:33 +0900
Subject: [PATCH 3/3] docs(migration): show non-fungible asset callback flag
order
---
.../builder/migration/05-asset-vault-faucet.md | 18 +++++++++++++-----
.../builder/migration/05-asset-vault-faucet.md | 18 +++++++++++++-----
2 files changed, 26 insertions(+), 10 deletions(-)
diff --git a/docs/builder/migration/05-asset-vault-faucet.md b/docs/builder/migration/05-asset-vault-faucet.md
index 6918976f..10772cb8 100644
--- a/docs/builder/migration/05-asset-vault-faucet.md
+++ b/docs/builder/migration/05-asset-vault-faucet.md
@@ -73,15 +73,23 @@ The `asset::build_fungible_asset` and `asset::build_non_fungible_asset` procedur
### Affected Code
-**MASM (fungible asset creation):**
+**MASM (asset creation):**
```masm
-# Before (0.13): stack = [faucet_id_prefix, faucet_id_suffix, amount, ...]
+# Before (0.13): fungible stack = [faucet_id_prefix, faucet_id_suffix, amount, ...]
exec.asset::build_fungible_asset
# -> [ASSET, ...]
-# After (0.14): stack = [enable_callbacks, faucet_id_suffix, faucet_id_prefix, amount, ...]
+# Before (0.13): non-fungible stack = [faucet_id_prefix, faucet_id_suffix, DATA_HASH, ...]
+exec.asset::build_non_fungible_asset
+# -> [ASSET, ...]
+
+# After (0.14): fungible stack = [enable_callbacks, faucet_id_suffix, faucet_id_prefix, amount, ...]
exec.asset::create_fungible_asset
# -> [ASSET_KEY, ASSET_VALUE, ...]
+
+# After (0.14): non-fungible stack = [enable_callbacks, faucet_id_suffix, faucet_id_prefix, DATA_HASH, ...]
+exec.asset::create_non_fungible_asset
+# -> [ASSET_KEY, ASSET_VALUE, ...]
```
**Rust:**
@@ -100,7 +108,7 @@ let asset = FungibleAsset::new(faucet_id, amount)?;
1. Rename all `exec.asset::build_fungible_asset` calls to `exec.asset::create_fungible_asset`.
2. Rename all `exec.asset::build_non_fungible_asset` calls to `exec.asset::create_non_fungible_asset`.
3. Add the `enable_callbacks` flag as the new top-of-stack element.
-4. Note the changed argument order: `[enable_callbacks, faucet_id_suffix, faucet_id_prefix, amount]`.
+4. Note the changed argument order: `[enable_callbacks, faucet_id_suffix, faucet_id_prefix, amount]` for fungible assets and `[enable_callbacks, faucet_id_suffix, faucet_id_prefix, DATA_HASH]` for non-fungible assets.
5. Update consumers to expect `[ASSET_KEY, ASSET_VALUE]` on the stack instead of a single `[ASSET]`.
### Common Errors
@@ -109,7 +117,7 @@ let asset = FungibleAsset::new(faucet_id, amount)?;
| --- | --- | --- |
| `unknown procedure asset::build_fungible_asset` | Procedure renamed | Use `asset::create_fungible_asset`. |
| `unknown procedure asset::build_non_fungible_asset` | Procedure renamed | Use `asset::create_non_fungible_asset`. |
-| `FailedAssertion` in `create_fungible_asset` | Missing `enable_callbacks` flag or wrong argument order | Push `[enable_callbacks, faucet_id_suffix, faucet_id_prefix, amount]`. |
+| `FailedAssertion` in `create_*_asset` | Missing `enable_callbacks` flag or wrong argument order | Push `[enable_callbacks, faucet_id_suffix, faucet_id_prefix, amount]` for fungible assets, or `[enable_callbacks, faucet_id_suffix, faucet_id_prefix, DATA_HASH]` for non-fungible assets. |
---
diff --git a/versioned_docs/version-0.14/builder/migration/05-asset-vault-faucet.md b/versioned_docs/version-0.14/builder/migration/05-asset-vault-faucet.md
index 6918976f..10772cb8 100644
--- a/versioned_docs/version-0.14/builder/migration/05-asset-vault-faucet.md
+++ b/versioned_docs/version-0.14/builder/migration/05-asset-vault-faucet.md
@@ -73,15 +73,23 @@ The `asset::build_fungible_asset` and `asset::build_non_fungible_asset` procedur
### Affected Code
-**MASM (fungible asset creation):**
+**MASM (asset creation):**
```masm
-# Before (0.13): stack = [faucet_id_prefix, faucet_id_suffix, amount, ...]
+# Before (0.13): fungible stack = [faucet_id_prefix, faucet_id_suffix, amount, ...]
exec.asset::build_fungible_asset
# -> [ASSET, ...]
-# After (0.14): stack = [enable_callbacks, faucet_id_suffix, faucet_id_prefix, amount, ...]
+# Before (0.13): non-fungible stack = [faucet_id_prefix, faucet_id_suffix, DATA_HASH, ...]
+exec.asset::build_non_fungible_asset
+# -> [ASSET, ...]
+
+# After (0.14): fungible stack = [enable_callbacks, faucet_id_suffix, faucet_id_prefix, amount, ...]
exec.asset::create_fungible_asset
# -> [ASSET_KEY, ASSET_VALUE, ...]
+
+# After (0.14): non-fungible stack = [enable_callbacks, faucet_id_suffix, faucet_id_prefix, DATA_HASH, ...]
+exec.asset::create_non_fungible_asset
+# -> [ASSET_KEY, ASSET_VALUE, ...]
```
**Rust:**
@@ -100,7 +108,7 @@ let asset = FungibleAsset::new(faucet_id, amount)?;
1. Rename all `exec.asset::build_fungible_asset` calls to `exec.asset::create_fungible_asset`.
2. Rename all `exec.asset::build_non_fungible_asset` calls to `exec.asset::create_non_fungible_asset`.
3. Add the `enable_callbacks` flag as the new top-of-stack element.
-4. Note the changed argument order: `[enable_callbacks, faucet_id_suffix, faucet_id_prefix, amount]`.
+4. Note the changed argument order: `[enable_callbacks, faucet_id_suffix, faucet_id_prefix, amount]` for fungible assets and `[enable_callbacks, faucet_id_suffix, faucet_id_prefix, DATA_HASH]` for non-fungible assets.
5. Update consumers to expect `[ASSET_KEY, ASSET_VALUE]` on the stack instead of a single `[ASSET]`.
### Common Errors
@@ -109,7 +117,7 @@ let asset = FungibleAsset::new(faucet_id, amount)?;
| --- | --- | --- |
| `unknown procedure asset::build_fungible_asset` | Procedure renamed | Use `asset::create_fungible_asset`. |
| `unknown procedure asset::build_non_fungible_asset` | Procedure renamed | Use `asset::create_non_fungible_asset`. |
-| `FailedAssertion` in `create_fungible_asset` | Missing `enable_callbacks` flag or wrong argument order | Push `[enable_callbacks, faucet_id_suffix, faucet_id_prefix, amount]`. |
+| `FailedAssertion` in `create_*_asset` | Missing `enable_callbacks` flag or wrong argument order | Push `[enable_callbacks, faucet_id_suffix, faucet_id_prefix, amount]` for fungible assets, or `[enable_callbacks, faucet_id_suffix, faucet_id_prefix, DATA_HASH]` for non-fungible assets. |
---