miden-protocol 0.17.0-rc.6

Core components of the Miden protocol
Documentation
use {AccountId, Asset, AssetId, AssetValue, AssetClass, AssetMetadata, AssetComposition, AssetAmount} from miden::protocol_utils::types

# ERRORS
# =================================================================================================

const ERR_VAULT_ASSET_METADATA_NOT_U32 = "asset metadata is not a u32"

const ERR_VAULT_ASSET_METADATA_UNKNOWN_VERSION = "unknown asset ID version"

const ERR_VAULT_ASSET_METADATA_NON_ZERO_RESERVED_BITS = "reserved asset metadata bits are non-zero"

const ERR_VAULT_ASSET_METADATA_UNKNOWN_COMPOSITION = "unknown asset metadata composition value"

# CONSTANTS
# =================================================================================================

# Specifies the maximum amount a fungible asset can represent.
#
# This is 2^63 - 2^31. See account_update.masm for more details.
pub const FUNGIBLE_ASSET_MAX_AMOUNT = 0x7fffffff80000000

# The number of elements in an asset, i.e. asset ID and value.
pub const ASSET_SIZE = 8

# The offset of the asset value in an asset stored in memory.
pub const ASSET_VALUE_MEMORY_OFFSET = 4

# Version 1 of the asset ID encoding.
const ASSET_VERSION_1 = 1

# The mask for the version bits in the asset metadata.
const VERSION_MASK = 0x0f # 0b1111

# The number of bits by which the composition is shifted in the asset metadata.
const COMPOSITION_SHIFT = 4

#! The mask for the composition bits in the asset metadata.
const COMPOSITION_MASK = 0x30 # 0b0011_0000

# The flag representing the AssetComposition::None composition.
pub const COMPOSITION_NONE = 0

# The flag representing the AssetComposition::Fungible composition.
pub const COMPOSITION_FUNGIBLE = 1

# The flag representing the AssetComposition::Custom composition.
pub const COMPOSITION_CUSTOM = 2

#! The composition value that is not valid.
const COMPOSITION_INVALID = 3

#! The u32 mask for the reserved bits in the asset metadata.
const METADATA_RESERVED_MASK = 0xffffffc0 # lower 8 bits: 0b1100_0000

# PROCEDURES
# =================================================================================================

#! Stores an asset ID and value into memory at the given pointer.
#!
#! The memory range pointer..pointer+8 will be overwritten.
#!
#! Inputs: [ptr, ASSET_ID, ASSET_VALUE]
#! Outputs: []
#!
#! Where:
#! - ptr is the memory address where the asset will be stored.
#! - ASSET_ID is the 4-element word representing the asset ID.
#! - ASSET_VALUE is the 4-element word representing the asset value.
pub proc store(dest: ptr<Asset>, asset: Asset)
    # store asset ID
    movdn.4 dup.4
    # => [ptr, ASSET_ID, ptr, ASSET_VALUE]

    mem_storew_le dropw
    # => [ptr, ASSET_VALUE]

    # store asset value
    add.ASSET_VALUE_MEMORY_OFFSET mem_storew_le dropw
    # => []
end

#! Loads an asset ID and value from memory given a pointer to the asset.
#!
#! Inputs: [ptr]
#! Outputs: [ASSET_ID, ASSET_VALUE]
#!
#! Where:
#! - ptr is the memory address of the asset.
#! - ASSET_ID is the 4-element word representing the asset ID.
#! - ASSET_VALUE is the 4-element word representing the asset value.
pub proc load(src: ptr<Asset>) -> Asset
    # load asset value
    padw dup.4 add.ASSET_VALUE_MEMORY_OFFSET mem_loadw_le
    # => [ASSET_VALUE, ptr]

    # load asset ID
    padw movup.8 mem_loadw_le
    # => [ASSET_ID, ASSET_VALUE]
end

#! Returns the faucet ID from an asset ID.
#!
#! WARNING: The faucet ID is not validated.
#!
#! Inputs:  [ASSET_ID]
#! Outputs: [faucet_id_suffix, faucet_id_prefix, ASSET_ID]
#!
#! Where:
#! - faucet_id is the account ID in the asset ID.
#! - ASSET_ID is the asset ID from which to extract the faucet ID.
pub proc id_to_faucet_id(asset_id: AssetId) -> (AccountId, AssetId)
    # => [asset_class_suffix, asset_class_prefix, faucet_id_suffix_and_metadata, faucet_id_prefix]
    dup.3 dup.3
    # => [faucet_id_suffix_and_metadata, faucet_id_prefix, ASSET_ID]

    exec.split_suffix_and_metadata drop
    # => [faucet_id_suffix, faucet_id_prefix, ASSET_ID]
end

#! Returns the faucet ID from an asset ID and consumes it.
#!
#! WARNING: The faucet ID is not validated.
#!
#! Inputs:  [ASSET_ID]
#! Outputs: [faucet_id_suffix, faucet_id_prefix]
#!
#! Where:
#! - faucet_id is the account ID in the asset ID.
#! - ASSET_ID is the asset ID from which to extract the faucet ID.
pub proc id_into_faucet_id(asset_id: AssetId) -> AccountId
    # => [asset_class_suffix, asset_class_prefix, faucet_id_suffix_and_metadata, faucet_id_prefix]
    drop drop
    # => [faucet_id_suffix_and_metadata, faucet_id_prefix]

    exec.split_suffix_and_metadata drop
    # => [faucet_id_suffix, faucet_id_prefix]
end

#! Returns the asset class from an asset ID.
#!
#! Inputs:  [ASSET_ID]
#! Outputs: [asset_class_suffix, asset_class_prefix, ASSET_ID]
#!
#! Where:
#! - asset_class is the asset class in the asset ID.
#! - ASSET_ID is the asset ID from which to extract the asset class.
pub proc id_to_asset_class(asset_id: AssetId) -> (AssetClass, AssetId)
    # => [asset_class_suffix, asset_class_prefix, faucet_id_suffix, faucet_id_prefix]
    dup.1 dup.1
    # => [asset_class_suffix, asset_class_prefix, ASSET_ID]
end

#! Returns the asset class from an asset ID and consumes it.
#!
#! Inputs:  [ASSET_ID]
#! Outputs: [asset_class_suffix, asset_class_prefix]
#!
#! Where:
#! - asset_class is the asset class in the asset ID.
#! - ASSET_ID is the asset ID from which to extract the asset class.
pub proc id_into_asset_class(asset_id: AssetId) -> AssetClass
    # => [asset_class_suffix, asset_class_prefix, faucet_id_suffix, faucet_id_prefix]
    movup.2 drop movup.2 drop
    # => [asset_class_suffix, asset_class_prefix]
end

#! Splits the merged faucet ID suffix and the asset metadata.
#!
#! Inputs:  [faucet_id_suffix_and_metadata]
#! Outputs: [asset_metadata, faucet_id_suffix]
#!
#! Where:
#! - faucet_id_suffix_and_metadata is the faucet ID suffix merged with the asset metadata.
#! - faucet_id_suffix is the suffix of the account ID.
#! - asset_metadata is the asset metadata.
pub proc split_suffix_and_metadata(faucet_id_suffix_and_metadata: felt) -> (AssetMetadata, felt)
    u32split
    # => [suffix_metadata_lo, suffix_metadata_hi]

    dup movdn.2
    # => [suffix_metadata_lo, suffix_metadata_hi, suffix_metadata_lo]

    # clear lower 8 bits of the lo part to get the actual ID suffix
    u32and.0xffffff00 swap
    # => [suffix_metadata_hi, suffix_metadata_lo', suffix_metadata_lo]

    # reassemble the ID suffix by multiplying the hi part with 2^32 and adding the lo part
    mul.0x0100000000 add
    # => [faucet_id_suffix, suffix_metadata_lo]

    # extract lower 8 bits of the lo part to get the metadata
    swap u32and.0xff
    # => [asset_metadata, faucet_id_suffix]
end

#! Validates that asset metadata is well formed and consumes it.
#!
#! WARNING: This procedure should not be exposed to or called from user code (e.g. miden::protocol
#! or miden::standards) as this would make the calling code only accept asset version 1.
#!
#! Inputs:  [asset_metadata]
#! Outputs: []
#!
#! Panics if:
#! - asset_metadata is not a valid u32
#! - encodes an unknown asset ID version.
#! - has reserved bits 6 or 7 set.
#! - encodes an unknown asset composition.
pub proc validate_metadata(asset_metadata: felt)
    # assert that the metadata fits in a u8
    u32split swap
    # => [hi, asset_metadata]
    eq.0 assert.err=ERR_VAULT_ASSET_METADATA_NOT_U32
    # => [asset_metadata]

    # the version defines how the rest of the metadata is decoded, so assert it first
    dup u32and.VERSION_MASK
    eq.ASSET_VERSION_1 assert.err=ERR_VAULT_ASSET_METADATA_UNKNOWN_VERSION
    # => [asset_metadata]

    # assert the reserved bits are all zero (bits 6..32)
    dup u32and.METADATA_RESERVED_MASK
    eq.0 assert.err=ERR_VAULT_ASSET_METADATA_NON_ZERO_RESERVED_BITS
    # => [asset_metadata]

    exec.metadata_into_composition
    # => [asset_composition]

    # assert the value is NOT the invalid composition, which implies it must be one of the valid
    # ones
    neq.COMPOSITION_INVALID assert.err=ERR_VAULT_ASSET_METADATA_UNKNOWN_COMPOSITION
    # => []
end

#! Extracts the asset composition from asset metadata.
#!
#! WARNING: asset_metadata is assumed to be a byte (in particular a valid u32)
#!
#! Inputs:  [asset_metadata]
#! Outputs: [asset_composition]
#!
#! Where:
#! - asset_metadata is the asset metadata.
#! - asset_composition is the composition value (see COMPOSITION_* constants).
proc metadata_into_composition(asset_metadata: AssetMetadata) -> AssetComposition
    # extract composition bits from the metadata
    u32and.COMPOSITION_MASK u32shr.COMPOSITION_SHIFT
    # => [asset_composition]
end

#! Returns the asset composition from an asset ID.
#!
#! Inputs:  [ASSET_ID]
#! Outputs: [asset_composition, ASSET_ID]
#!
#! Where:
#! - ASSET_ID is the asset ID from which to extract the composition.
#! - asset_composition is the composition value (see COMPOSITION_* constants).
pub proc id_to_composition(asset_id: AssetId) -> (AssetComposition, AssetId)
    # => [asset_class_suffix, asset_class_prefix, faucet_id_suffix_and_metadata, faucet_id_prefix]
    dup.2
    # => [faucet_id_suffix_and_metadata, ASSET_ID]

    exec.split_suffix_and_metadata swap drop
    # => [asset_metadata, ASSET_ID]

    exec.metadata_into_composition
    # => [asset_composition, ASSET_ID]
end

#! Returns the asset composition from an asset ID and consumes it.
#!
#! Inputs:  [ASSET_ID]
#! Outputs: [asset_composition]
#!
#! Where:
#! - ASSET_ID is the asset ID from which to extract the composition.
#! - asset_composition is the composition value (see COMPOSITION_* constants).
pub proc id_into_composition(asset_id: AssetId) -> AssetComposition
    # => [asset_class_suffix, asset_class_prefix, faucet_id_suffix_and_metadata, faucet_id_prefix]
    drop drop
    # => [faucet_id_suffix_and_metadata, faucet_id_prefix]

    exec.split_suffix_and_metadata
    # => [asset_metadata, faucet_id_suffix, faucet_id_prefix]

    movdn.2 drop drop
    # => [asset_metadata]

    exec.metadata_into_composition
    # => [asset_composition]
end

#! Returns the balance of the given fungible asset and consumes it.
#!
#! WARNING: Assumes that the given asset value is fungible and does NOT validate it.
#!
#! Inputs:  [ASSET_VALUE]
#! Outputs: [balance]
#!
#! Where:
#! - ASSET_VALUE is the fungible asset from which to extract the balance.
#! - balance is the amount of the fungible asset.
pub proc fungible_value_into_amount_unchecked(asset_value: AssetValue) -> AssetAmount
    movdn.3 drop drop drop
    # => [balance]
end