Token-Metadata Interface
Summary
Token-metadata is a very complex space, but at its base, all creators of
fungible and non-fungible tokens need a way to upload information about their token
on-chain. This proposal contains a spec for a simple token-metadata state
and instruction interface for SPL token mints. The interface can be implemented
by any program.
With a common interface, any wallet, dapp, or on-chain program can read the metadata,
and any tool that creates or modifies metadata will just work with any program
that implements the interface.
Motivation
Token creators on Solana need all sorts of functionality for their token-metadata,
and the Metaplex Token-Metadata program has been the one place for all metadata
needs, leading to a feature-rich program that still might not serve all needs.
At its base, token-metadata is a set of data fields associated to a particular token
mint, so we propose an interface that serves the simplest base case with some
compatibility with existing solutions.
With this proposal implemented, fungible and non-fungible token creators will
have two options:
- implement the interface in their own program, so they can eventually extend it
with new functionality or even other interfaces - use a reference program that implements the simplest case
Spec
Token-Metadata Struct
A program that implements the interface must write the following data fields
into a type-length-value entry into the account:
type Pubkey = [u8; 32];
type OptionalNonZeroPubkey = Pubkey; // if all zeroes, interpreted as `None`
type TlvDiscriminator = [u8; 8];
struct TokenMetadata {
discriminator: TlvDiscriminator,
length: u32,
update_authority: OptionalNonZeroPubkey,
mint: Pubkey,
name_len: u32,
name: [u8; 32],
symbol_len: u32,
symbol: [u8; 10],
uri_len: u32,
uri: [u8; 200],
}
This struct has some ABI-compatibility with the Metaplex
Metadata
struct.
The discriminator here is larger, at 8 bytes instead of 1, and is a different
value, but the following 318 bytes can be interpreted in the same way by wallets,
programs, and indexers.
The discriminator must be hashv(&["token-metadata-interface::state"])[0..8]
.
By storing the metadata in a TLV structure, a developer who implements this
interface in their program can freely add any other data fields in a different
TLV entry.
You can find more information about TLV / type-length-value structures at the
spl-type-length-value repo.
Instructions
Here are the instructions that must be implemented by a program conforming to
the interface.
Initialize Token Metadata
- Discriminator:
hashv(&["token-metadata-interface::initialize"])[0..8]
- Accounts:
0.[writable]
Metadata account-
[]
SPL mint account -
[signer]
Mint authority -
[]
Update authority
-
- Data:
0. name: String- symbol: String
- uri: String
The instruction processor must do the following:
- check that the mint account is an SPL mint
- check that the correct mint authority signed
- check that the name / symbol / URI fit in the limits of the struct
- check that the metadata account does not already have metadata written to it
- write all of the information into the metadata account
NOTE: This instruction only covers initialization and assumes that the provided
account is properly created, meaning that it has enough space for the data, and
enough lamports to be rent-exempt.
Update Token Metadata
- Discriminator:
hashv(&["token-metadata-interface::update"])[0..8]
- Accounts:
0.[writable]
Metadata account-
[signer]
Update authority
-
- Data:
0. name: String- symbol: String
- uri: String
The instruction processor must do the following:
- check that the metadata account is owned by the program and contains a valid
token-metadata TLV entry - check that the update authority signed
- check that the name / symbol / URI fit in the limits of the struct
- write the new information
NOTE: String
s are utf-8 encoded bytes, preceded by a little-endian u32
giving the number of bytes.
While these instructions aren’t strictly required to adhere to the interface,
they will integrate more nicely with other tooling.
For example, the JS SDK for token-interface can allow targeting any program id,
so if a program implements these instructions properly, then they can easily get
more usage.
Alternatives
As described at the start of this proposal, the space of token-metadata is vast
and comprises all sorts of functionality, including fees, programmability,
transferability, minting, etc.
This proposal is deliberately not specific to fungible or non-fungible tokens,
so it includes the base required for both, and nothing more.
For example, functionality for royalties could be implemented through a separate
interface.
(Optional) Program-Derived Address Convention
This base proposal only defines the functionality required to implement the interface,
and does not define any program-derived addresses for where the metadata should
be stored.
This approach is deliberate: by not requiring a particular address derivation,
it is possible for a token program to also implement the metadata interface!
On the other hand, since many metadata programs will likely not be token
programs, we include an optional convention for deriving a program address for
metadata as the following function call:
use solana_program::pubkey::Pubkey;
pub fn metadata_account_address(metadata_program_id: &Pubkey, mint: &Pubkey) -> Pubkey {
Pubkey::find_program_address(&[b"metadata", mint.as_ref()], metadata_program_id).0
}
Point for discussion: this is different from the Metaplex token-metadata derivation,
which repeats the metadata program id. This approach feels reasonable, given that
this interface already isn’t fully compatible with the Metaplex token-metadata accounts.
Client libraries need to parse Metaplex token-metadata accounts differently from
accounts that implement the token-metadata interface anyway, so special-casing
the address derivation for Metaplex seems reasonable.
On the flip-side, we can also completely omit this point and leave it for
another proposal regarding “token-metadata discoverability”, which may include an
interface for a token-metadata registry.
Further Work
This interface defines the minimum struct and instructions that a program must
implement in order to be considered a “token-metadata program”. It does not address
discoverability of token-metadata accounts.
For discoverability within the mint, spl-token-2022 will add a mint “extension”
to store the metadata account address.
As a proof-of-concept, spl-token-2022 will also implement this interface, and store the
metadata fields directly in the mint account.