Skip to content

solana-program/program-metadata

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Program Metadata

Attach custom data to any program.

Overview

The Program Metadata provides the ability to attach metadata information to any program. The information is represented by a PDA account with a pre-defined derivation, e.g., it can be used to add the IDL of a program, with a PDA derived using the "idl" string. This string that identifies the metadata PDA account is called a seed.

There are two types of metadata accounts:

  • canonical: these are metadata accounts created by the program upgrade authority. They are derived from [program key, seed].
  • non-canonical (a.k.a. third-party): these are metadata account created by any authority. They are derived from [program key, authority key, seed].

While there can only be a single canonical metadata account for a pair (program, seed), there can be any number of non-canonical metadata accounts. The rationale is to allow anyone to add additional metadata to any program, but also provide a mechanism to differentiate metadata information added by the program upgrade authority. The metadata is either saved on chain in an account or it can be saved to a URL or another account.

Quick Start

Upload an IDL or security.txt file to your program in one command:

# Upload IDL (as upgrade authority using the default CLI keypair)
npx @solana-program/program-metadata write idl <program-id> ./idl.json

# Upload metadata with additional information about your program similar to security.txt
npx @solana-program/program-metadata write security <program-id> ./security.json

At the moment the Solana explorer only reads Codama IDLs that are uploaded as canonical metadata accounts. But soon it will also support security files and Anchor IDLs.

Usage

The CLI supports both canonical (program upgrade authority) and non-canonical (third-party) metadata accounts, using a seed-based approach (e.g. "idl", "security").

Installation

You can run the CLI directly with npx (no install required):

npx @solana-program/program-metadata <command> [options]

Or install globally:

npm install -g @solana-program/program-metadata
program-metadata <command> [options]

See all the commands:

npx @solana-program/program-metadata --help

Commands

Create a Metadata Account

Create a new metadata account for a program (either creates or updates if it already exists):

npx @solana-program/program-metadata write <seed> <program-id> <file> [options]
  • <seed>: e.g. "idl", "security" as standard or anything else you want to use for other data
  • <program-id>: The program's address
  • <file>: Path to the metadata or IDL file (JSON, YAML, TOML, etc.)
  • <url>: Optionally point to a URL containing the metadata
  • <account>: Optionally point to an account address that contains the metadata. When using this option you also need to set offset and account length: --account-offset and --account-length

Fetch Metadata

Download metadata to a file or print to stdout:

npx @solana-program/program-metadata fetch <seed> <program-id> [options]
  • --output <file>: Save to file
  • --raw: Output raw data in hex

Authority and Account Management

By default your keypair that creates the metadata account will be its authority. You can change the authority by using the set-authority command. This can be useful when you don't want to update the metadata using the program authority going forward or if you use a multisig to create the metadata account for example. (Multisig instructions see further down) For canonical metadata accounts the upgrade authority can always claim back the metadata account if it is not immutable. For non-canonical metadata accounts the authority can not be changed because the PDA is derived from that authority.

  • Set a new authority:
    npx @solana-program/program-metadata set-authority <seed> <program-id> --new-authority <pubkey> Note that the program upgrade authority can always claim back the metadata account if it is not immutable.
  • Remove authority:
    npx @solana-program/program-metadata remove-authority <seed> <program-id> This will leave only the upgrade authority as the authority of the metadata account.
  • Make metadata immutable:
    npx @solana-program/program-metadata set-immutable <seed> <program-id> This will make the metadata account immutable and cannot be updated anymore, even for the update authority.
  • Close metadata account:
    npx @solana-program/program-metadata close <seed> <program-id> This will close the account and you can reclaim the rent.

Buffer Management

Using a buffer account you can split the metadata update into the uploading of the data part and then assign the buffer to the program in a later transaction.

  • Create/update/fetch/close buffer accounts:
    npx @solana-program/program-metadata create-buffer|update-buffer|fetch-buffer|close-buffer ...
  • List all buffer accounts for an authority:
    npx @solana-program/program-metadata list-buffers [authority]
  • Update a metadata account with a buffer:
    npx @solana-program/program-metadata write <seed> <program-id> --buffer <buffer-address>

Options

  • --keypair <path>: Path to keypair file (defaults to Solana config)
  • --payer <path>: Path to keypair file of transaction fee and storage payer (defaults to keypair)
  • --priority-fees <number>: Priority fees in micro-lamports per compute unit (default: 100000)
  • --rpc <string>: Custom RPC URL
  • --export [address]: Export transactions instead of running them. Optionally specify an override authority address.
  • --export-encoding <encoding>: How to encode exported transactions. Choices: none, utf8, base58, base64 (default: base64)
  • -h, --help: Show help for command

Squads Multisig

You can also use the program metadata program as a multisig. All commands in the CLI can also be exported as transactions in various formats using the --export flag. For updating a metadata account of a program that is managed by a Squads multisig you need to create a buffer first and then export a transaction that you can then import and sign in Squads or any other multisig of your choosing.

  1. Transfer the program authority to the squad using the squads dashboard
  2. Create the buffer account and transfer ownership to the squad
npx @solana-program/program-metadata create-buffer ./target/idl/let_me_buy.json
npx @solana-program/program-metadata set-buffer-authority <buffer-address> --new-authority <multisig-address>
  1. Export the transaction as base58 and then import it into your multisig under developers/txBuilder/createTransaction/addInstruction/ImportAsBase58
npx @solana-program/program-metadata write idl <program-address> --buffer <buffer-address> --export <multisig-address> --export-encoding base58 --close-buffer <your-address-to-get-the-buffer-rent-back>
  1. Sign the transaction in your multisig and send it

Examples

Upload IDL as canonical (upgrade authority):

npx @solana-program/program-metadata write idl <program-id> ./idl.json --keypair <authority-keypair>

Upload metadata as third-party (non-canonical):

npx @solana-program/program-metadata write idl <program-id> ./metadata.json --non-canonical <your-pubkey>

Fetch canonical metadata:

npx @solana-program/program-metadata fetch idl <program-id> --output ./idl.json

Fetch non-canonical metadata:

npx @solana-program/program-metadata fetch idl <program-id> --non-canonical <pubkey> --output ./idl.json

Close a metadata account:

npx @solana-program/program-metadata close idl <program-id>

Security.txt File Format

You can also use the program metadata program to upload a security.txt file to your program without having it as part of the binary file like the original security.txt format. This is useful to show name, description and icon of your program in the Solana Explorer and also gives security researchers a place to report issues and contact you. You can also add a link to your web app which makes it easier for users to find and interact with your program.

For that you just create a json file containing the security.txt data and upload it to the program metadata account using "security" as seed instead of "idl".

{
  "name": "MyProgramName",
  "logo": "https://upload.wikimedia.org/wikipedia/en/b/b9/Solana_logo.png",
  "description": "Example program for meta data",
  "notification": "On the first of january we will release a new version! Please update your SDKS!!!!",
  "sdk": "https://github.com/solana-program/program-metadata",
  "project_url": "https://github.com/solana-developers/",
  "contacts": [
    "email:[email protected]",
    "discord:MyProgram#1234",
    "twitter:@MyProgram"
  ],
  "policy": "https://example.com/security-policy",
  "preferred_languages": ["en", "de"],
  "encryption": "https://example.com/pgp-key",
  "source_code": "https://github.com/solana-developers/",
  "source_release": "v0.1.0",
  "source_revision": "abc123def456",
  "auditors": ["Audit Firm A", "Security Researcher B"],
  "acknowledgements": "https://example.com/security-acknowledgements",
  "expiry": "2024-12-31",
  "version": "0.1.0"
}

Then use the same commands as for the IDL to upload the security.txt file:

npx @solana-program/program-metadata write security <program-id> ./security.json

Fetching IDL or Security Metadata with the JavaScript SDK

Instead of the CLI you can also use the client JavaScript SDK. Then you can fetch the on-chain IDL or security.txt metadata for any program using JavaScript. This is useful if you want to integrate metadata fetching into your own tools or scripts.

Note: The SDK requires a Solana RPC object from @solana/kit (not a @solana/web3.js Connection).

Example: Fetching the IDL

import { fetchAndParseMetadataContent } from '@solana-program/program-metadata';
import { createSolanaRpc } from '@solana/kit';

const rpc = createSolanaRpc('https://api.mainnet-beta.solana.com'); // or devnet/testnet
const programId = 'YOUR_PROGRAM_ID';

async function main() {
  const idl = await fetchAndParseMetadataContent(rpc, programId, 'idl');
  console.log(idl);
}

main();

Example: Fetching security.txt

const security = await fetchAndParseMetadataContent(rpc, programId, 'security');
console.log(security);

You can use any seed (e.g., 'idl', 'security', or your own custom seed) to fetch the corresponding metadata for a program.

How to run it:

npm i @solana/kit
npm i @solana-program/program-metadata
node fetch-metadata.mjs

You can use the Program Metadata program id for example if you want to test it out: ProgM6JCCvbYkfKqJYHePx4xxSUSqJp7rh8Lyv7nk7S

How the data is formatted and saved

By default the metadata is zlib compressed and encoded in utf8 and saved on chain in an account. To save space you can also point the metadata in the account to a URL using the --url flag or to another account using the --account <address> flag. When using the --account flag you can also specify the offset in the account where the data starts using the --account-offset <number> and --account-length <number> flags.

Like this you can for example have multiple programs point to the same metadata account or you can save your IDL in your github repository and let the metadata account just point to it.

  • Seeds: The <seed> argument is a string like "idl" or "security" that determines the type of metadata. It is used to derive the address of the metadata account for your program. Use different seeds for different types of metadata. You can attach any data to programs that you like. If you have a certain standard in mind please open a discussion on this repository. The program could for example also enable versioned IDLs or you could think of adding attestations to programs to make them more trustworthy. Something like an auditedBy metadata could be interesting for example.
  • Canonical vs. Non-Canonical: By default, the upgrade authority creates canonical metadata. Use --non-canonical <pubkey> to create third-party metadata accounts. This could for example be useful for already frozen programs which do not have access to their upgrade authority anymore.
  • File Types: The CLI auto-detects JSON, YAML, or TOML.
  • Compression: By default all metadata is compressed in the zlib format to save on chain space. You can override this by using the --compression flag and change it to none or gzip.
  • Encoding: By default all metadata is encoded in utf8. You can override this by using the --encoding flag and change it to none, base58 or base64.

Building

To build the program locally, first install the required packages using:

pnpm install

and then run:

pnpm programs:build

Testing

The repository includes two types of tests: program tests and JS client tests.

To run the program tests:

pnpm programs:test

To run the JS tests:

pnpm clients:js:test

License

The code is licensed under the Apache License Version 2.0

About

Attach custom data to any program

Resources

License

Security policy

Stars

Watchers

Forks

Packages

No packages published

Contributors 5