Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions docs/src/asset.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,45 @@ Non-fungible assets are encoded by hashing the `Asset` data into 32 bytes and pl
<img src={require('./img/asset/asset-storage.png').default} style={{width: '70%'}} alt="Asset storage"/>
</p>

### 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.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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.
Every asset in an account vault is stored as a key/value pair of two `Word`s (eight field elements).

The last sentence doesn't really make sense to me.


```
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:
Copy link
Copy Markdown
Contributor

@PhilippGackstatter PhilippGackstatter May 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The `asset_id_*` limbs distinguish different assets issued by the same faucet:
The `asset_id_*` limbs determine whether two assets issued by the same faucet are fungible (can be merged or splitted) - if the asset ID is the same, they are, if it's different, they are not.


- 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.
Comment on lines +75 to +76
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- 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.
- For fungible assets they are always `0`, because as the name implies, two fungible assets issued by the same faucet are always fungible with each other.
- 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. This way, the asset IDs of two assets issued by the same faucet have a very low chance of colliding and so the tx kernel never attempts to merge or split those.


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).
Comment on lines +78 to +83
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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 third element of the key, `faucet_id_suffix_with_metadata`, packs the faucet's account ID suffix together with a metadata byte. The lower 8 bits of every account-ID suffix are reserved zero by construction, leaving room for this asset metadata.
The asset metadata holds the per-asset callback flag:
- `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).

Too many low-level details, imo, and procedure names and file paths go stale fast, so I wouldn't include them.


The remaining 7 bits are reserved for future asset-level metadata.

#### Worked example
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#### Worked example
#### Example

Not sure what "worked" means.


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:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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:
A fungible asset with `amount = 10000`, callbacks enabled, issued by a faucet whose ID has `suffix = 447750849984126720` and `prefix = 12959558562786060576` is laid out as:

I wouldn't scope to just the asset vault, because assets also appear in other places (e.g. note assets - which we don't call a vault).


```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`.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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`.
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]`.

In principle the references are nice, but we usually don't include such references. If you'd like to include them, I'd recommend linking to docs.rs (with version set to latest), which would automatically provide the latest published view of the source.


### 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.
Expand Down
2 changes: 1 addition & 1 deletion docs/src/protocol_library.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,4 +163,4 @@ Asset procedures provide utilities for creating fungible and non-fungible assets
| Procedure | Description | Context |
| -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
| `create_fungible_asset` | Builds a fungible asset for the specified fungible faucet and amount.<br/><br/>**Inputs:** `[enable_callbacks, faucet_id_suffix, faucet_id_prefix, amount]`<br/>**Outputs:** `[ASSET_KEY, ASSET_VALUE]` | Any |
| `create_non_fungible_asset` | Builds a non-fungible asset for the specified non-fungible faucet and data hash.<br/><br/>**Inputs:** `[faucet_id_suffix, faucet_id_prefix, DATA_HASH]`<br/>**Outputs:** `[ASSET_KEY, ASSET_VALUE]` | Any |
| `create_non_fungible_asset` | Builds a non-fungible asset for the specified non-fungible faucet and data hash.<br/><br/>**Inputs:** `[enable_callbacks, faucet_id_suffix, faucet_id_prefix, DATA_HASH]`<br/>**Outputs:** `[ASSET_KEY, ASSET_VALUE]` | Any |
Loading