Skip to content

Token Metadata extension to form standard#2439

Open
afa7789 wants to merge 78 commits into0xMiden:nextfrom
afa7789:oz/metadata_extension
Open

Token Metadata extension to form standard#2439
afa7789 wants to merge 78 commits into0xMiden:nextfrom
afa7789:oz/metadata_extension

Conversation

@afa7789
Copy link
Contributor

@afa7789 afa7789 commented Feb 12, 2026

Unified metadata: One place for account/faucet metadata: token (symbol, decimals, max_supply), owner, name, and content URI. Slot names live under miden::standards::metadata::* (and ownable for owner).

Layout: Token metadata and owner in slots 0–1; name in 2 words (name_0, name_1); content URI in 6 words (content_uri_0..5). Same layout in Rust and MASM.

Faucets: Basic and network fungible faucets support optional name and content URI; both re-export metadata getters (get_name, get_content_uri, get_token_metadata, get_max_supply, get_decimals, get_token_symbol; network also get_owner).

Standalone Info: Non-faucet accounts can use the metadata Info component (name + content URI) for future use (e.g. NFTs).

Testing: Unit tests in miden-standards (metadata storage, recovery); integration tests in miden-testing (MASM getters, faucet + metadata).

@afa7789 afa7789 marked this pull request as ready for review February 12, 2026 20:12
@afa7789 afa7789 force-pushed the oz/metadata_extension branch 2 times, most recently from 55282e1 to b1dba96 Compare February 14, 2026 12:48
@onurinanc
Copy link
Contributor

@afa7789 In the context of token metadata discussion #2423 : Let's keep the contractURI as optional.

Basically, instead of:

 name: Option<TokenName>,
 contract_uri: Option<TokenContractURI>

We need to have:

 name: TokenName,
 contract_uri: Option<TokenContractURI>

@onurinanc
Copy link
Contributor

We should also consider adding the corresponding metadata and the new constructor to the network fungible faucets: https://github.com/afa7789/miden-base/blob/f7426116833b1f76da3195738ccb838a52880f80/crates/miden-standards/src/account/faucets/network_fungible.rs#L93-L101

@onurinanc
Copy link
Contributor

@afa7789 we should also add a flag and procedure to change max_supply. It's basically similar to have we have done in optional_set_contract_uri

@afa7789
Copy link
Contributor Author

afa7789 commented Feb 17, 2026

@afa7789 we should also add a flag and procedure to change max_supply. It's basically similar to have we have done in optional_set_contract_uri

In this branch ? pr ?

@onurinanc
Copy link
Contributor

@afa7789 we should also add a flag and procedure to change max_supply. It's basically similar to have we have done in optional_set_contract_uri

In this branch ? pr ?

Yes, it would be better if you can have this in this PR.

@afa7789 afa7789 changed the title Oz/metadata extension Token Metadata extension to form standard. Feb 19, 2026
@afa7789
Copy link
Contributor Author

afa7789 commented Feb 19, 2026

@bobbinth @mmagician this is ready for review :)

@mmagician mmagician added the pr-from-maintainers PRs that come from internal contributors or integration partners. They should be given priority label Feb 19, 2026
Copy link
Contributor

@bobbinth bobbinth left a comment

Choose a reason for hiding this comment

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

Not a review - but I left a couple of comments inline. The main one is about how we can handle returning large amounts of data from account interface procedures.

@onurinanc
Copy link
Contributor

@afa7789 Additionally, as this discussion #2423 (comment) has been concluded, you can update the PR with the following:

  • name (mandatory): up to 2 words, ~64 bytes (UTF-8)
  • description (optional): up to 6 words, ~192 bytes (UTF-8)
  • logo_uri (optional): up to 6 words, ~192 bytes (UTF-8)
  • external_link (optional): up to 6 words, ~192 bytes (UTF-8)

For description, logo_uri, and external_link:

  • configuration flags will be introduced to indicate whether the account was initialized with these fields.
  • mutable/immutable flags will also be added for these fields. (similar to how you did in content_uri)

@afa7789 afa7789 force-pushed the oz/metadata_extension branch 2 times, most recently from 5548bd5 to 609b355 Compare February 26, 2026 14:43
/// - Slot 12–17: logo_uri (6 Words)
/// - Slot 18–23: external_link (6 Words)
#[derive(Debug, Clone, Default)]
pub struct Info {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this not part of TokenMetadata directly?

It feels very closely related, so I would consider including it. Making it a separate component means users always need to remember to include it in their account next to TokenMetadata and need to decode both TokenMetadata and Info to get all the related data, so this pushes some complexity up the stack.

It would also be nice if we could set mutability flags directly via the mentioned TokenMetadataBuilder, e.g. TokenMetadataBuilder::new(...).description("abc").mutable_description().build().

Related, I think Info does not be an AccountComponent, since it does not have any code. This suggests it is a set of standard storage slots but not a full account component (a combination of functionality / code and storage). So in the same way as TokenMetadata is not an account component (but more like a standardized storage slot), we could make Info a reusable set of storage slots. I would then include it in TokenMetadata, which in turn is included in BasicFungibleFaucet (a proper account component). Notably, this does not prevent reusing Info for other purposes in the future (such as for NFTs).

Naming: I think this is more aptly described as TokenMetadata. This is more generic metadata than what is currently called TokenMetadata which is specific to fungible assets. So maybe it is better to rename the current TokenMetadata to FungibleTokenMetadata to free up that name for this.

@afa7789
Copy link
Contributor Author

afa7789 commented Mar 2, 2026

@PhilippGackstatter I did most of the mentioned things.
The exceptions are the Builder pattern and Generalized encoding, which will be left for the future.

@afa7789 afa7789 force-pushed the oz/metadata_extension branch 3 times, most recently from ebcbd2e to 671a9dc Compare March 5, 2026 04:05
@afa7789 afa7789 force-pushed the oz/metadata_extension branch from ecf267b to 2e50884 Compare March 5, 2026 14:17
afa7789 added 7 commits March 16, 2026 07:50
- Introduced FungibleTokenMetadataBuilder to simplify the creation of FungibleTokenMetadata instances.
- Updated all instances of FungibleTokenMetadata creation across the codebase to utilize the new builder pattern.
- Enhanced readability and maintainability by allowing optional fields to be set in a chainable manner.
- Adjusted tests and mock implementations to accommodate the new builder approach.
Copy link
Contributor

@bobbinth bobbinth left a comment

Choose a reason for hiding this comment

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

Looks good! Thank you! Not a full review - I mostly reviewed Rust code for now - but I left some comments inline. The main one is about code organization.

#[derive(Debug, Clone)]
pub struct AggLayerFaucet {
metadata: TokenMetadata,
metadata: FungibleTokenMetadata,
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems to be different from the pattern we use for NetworkFungibleFaucet and BasicFungibleFaucet. Specifically, here we put the metdata inside the AggLayerFaucet component, but there, metadata is an external component.

We should probably adjust this to make it work similarly to NetworkFungibleFaucet and BasicFungibleFaucet - but maybe that's for a follow-up PR (in this case, we can probably omit making changes to the AggLayerFaucet in this PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

but maybe that's for a follow-up PR (in this case, we can probably omit making changes to the AggLayerFaucet in this PR.

Okay, that's probably a good idea.

afa7789 added 3 commits March 18, 2026 10:30
- moved `TokenMetadata` and `TokenName` structs to manage token metadata including name, description, logo URI, and external link.
- Implemented `FixedWidthString` for encoding fixed-width UTF-8 strings into storage words.
- Added utility functions for reading and writing token metadata to account storage.
- Created a new module for string utilities and organized existing modules.
Copy link
Contributor

@PhilippGackstatter PhilippGackstatter left a comment

Choose a reason for hiding this comment

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

I find this PR hard to review due to the large amount of changes. I still think we should try to split this PR into smaller ones, e.g. one that introduces FixedWidthString and the types building on top of it, and another PR that makes the changes to TokenMetadata. Otherwise, I think we'll need more iterations for this PR due to more re-reviewing of code.

Comment on lines +228 to +234
fn felt_with_high_byte_set_returns_invalid_utf8() {
// Construct a Word where one felt has its 8th byte non-zero,
// which violates the 7-bytes-per-felt invariant.
// A value with byte[7] != 0: 2^56 exceeds the Goldilocks prime so we need a
// different approach — set a byte in positions 0..7 that decodes to invalid UTF-8.
// The length byte will claim len=0xFF (255) which exceeds the buffer, triggering the error.
let overflow_len = Felt::try_from(0xff_u64).unwrap();
Copy link
Contributor

Choose a reason for hiding this comment

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

The test name doesn't fit what it's doing, right? The test does sets the low byte in the felt to non-zero, but not the high byte.

This test is useful and we should keep it, but probably under a different name.

Separately, it would be useful to have a test that sets the high byte to non-zero and make sure it errors, which we could do like this:

// Construct a Word where one felt has its 8th byte non-zero,
// which violates the 7-bytes-per-felt invariant.
// The felt has bit 63 set, making it a valid felt but an invalid length prefix.
let high_byte_non_zero = Felt::try_from(2u64.pow(63)).unwrap();
let words = [
    Word::from([Felt::ZERO, high_byte_non_zero, Felt::ZERO, Felt::ZERO]),
    Word::from([Felt::ZERO, Felt::ZERO, Felt::ZERO, Felt::ZERO]),
];

Comment on lines +1 to +2
pub(super) const TOKEN_SYMBOL_TYPE: &str =
"miden::standards::fungible_faucets::metadata::token_symbol";
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd remove this module and move this definition to:

impl TokenSymbol {
    pub fn schema_type() -> SchemaType { ... }
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not sure about this, cause TokenSymbol lives in the miden-protocol part, so maybe better to not change it. And only work within miden-standards.

@afa7789
Copy link
Contributor Author

afa7789 commented Mar 18, 2026

@PhilippGackstatter
So should we do a:
PR 1 — oz/fixed-width-string (from next):

  • Create fresh from next
  • Manually bring in only utils/string.rs + utils/mod.rs
  • Zero deps on TokenMetadata
  • Very small, fast review

and another PR:
PR 2 — oz/token-metadata (from oz/fixed-width-string):

  • Create fresh from oz/fixed-width-string
  • Bring the TokenMetadata + MASM + faucet integration changes
  • Target this PR at oz/fixed-width-string branch (GitHub lets you do this)
  • When PR 1 merges → retarget PR 2 to next

And close this one? I will still do all the code suggestions here before doing that.

cc: @onurinanc @bobbinth

@bobbinth
Copy link
Contributor

PR 1 — oz/fixed-width-string (from next):

  • Create fresh from next
  • Manually bring in only utils/string.rs + utils/mod.rs
  • Zero deps on TokenMetadata
  • Very small, fast review

I would definitely do this. I think the FixedWidthString is basically ready to go and we can merge it very quickly and then update this PR with it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr-from-maintainers PRs that come from internal contributors or integration partners. They should be given priority

Projects

None yet

Development

Successfully merging this pull request may close these issues.