Skip to content

Implement standard TokenMetadata storage slot #2344

@PhilippGackstatter

Description

@PhilippGackstatter

Motivation

We are using the basic fungible faucet's token metadata in a few places, i.e. itself, NetworkFungibleFaucet and the agglayer faucet. To deduplicate the conversion from that metadata into the storage layout and to have a way to abstract over the concrete faucets, it would be good to introduce a TokenMetadata type, that is:

Implementation

/// The metadata of a fungible faucet's token.
///
/// ## Storage Layout
///
/// - [`Self::metadata_slot`]: `[token_supply, max_supply, decimals, token_symbol]`, where:
///   - `token_supply` is the current supply of the token.
///   - `max_supply` is the maximum supply of the token.
///   - `decimals` are the decimals of the token.
///   - `token_symbol` is the [`TokenSymbol`] encoded to a [`Felt`].
#[derive(Clone, Copy)]
pub struct TokenMetadata {
    token_supply: Felt,
    max_supply: Felt,
    decimals: u8,
    symbol: TokenSymbol,
}

It is basically a standardized storage slot, and so it can implement conversion from and to StorageSlot:

impl TokenMetadata {
    /// TODO
    pub fn metadata_slot() -> &'static StorageSlotName {
        todo!("return miden::standards::fungible_faucets::metadata like BasicFungibleFaucet does")
    }
}

impl From<TokenMetadata> for Word {
    fn from(metadata: TokenMetadata) -> Self {
        Word::new([
            metadata.token_supply,
            metadata.max_supply,
            Felt::from(metadata.decimals),
            Felt::from(metadata.symbol),
        ])
    }
}

impl From<TokenMetadata> for StorageSlot {
    fn from(metadata: TokenMetadata) -> Self {
        StorageSlot::new(
            TokenMetadata::metadata_slot().clone(),
            StorageSlotContent::Value(Word::from(metadata)),
        )
    }
}

impl TryFrom<&StorageSlot> for TokenMetadata {
    type Error = todo!();

    fn try_from(value: &StorageSlot) -> Result<Self, Self::Error> {
        todo!("extract content if name is TokenMetadata::metadata_slot() and decode word")
    }
}

impl TryFrom<&AccountStorage> for TokenMetadata {
    type Error = todo!();

    fn try_from(value: &AccountStorage) -> Result<Self, Self::Error> {
        todo!("calls TokenMetadata::try_from(storage_slot) on the appropriate slot")
    }
}

It would also have most of the methods that BasicFungibleFaucet has now, e.g. new to enforce basic constraints, getters for the fields and the standard metadata_slot name.

Usage

Then we can redefine the faucet types more cleanly as:

pub struct BasicFungibleFaucet {
    metadata: TokenMetadata
}

pub struct NetworkFungibleFaucet {
    metadata: TokenMetadata,
    owner_account_id: AccountId,
}

And in create_agglayer_faucet_component, construct a TokenMetadata and convert it into a storage slot without duplicating the details of how it is laid out.

Abstraction

This type basically allows abstracting over BasicFungibleFaucet and NetworkFungibleFaucet since both have the same slot. So, we can get the current token supply from both kinds of faucets by reconstructing TokenMetadata::try_from(account_storage) and getting the token_supply.

Context

Follow-up from #2335.
Motivation: 1, 2

Metadata

Metadata

Assignees

Labels

good first issueGood for newcomersrustIssues that affect or pull requests that update Rust code

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions