3 releases

0.0.3 Jan 27, 2022
0.0.2 Jan 26, 2022
0.0.1 Jan 26, 2022

#62 in #on-chain

ISC license

34KB
905 lines

List Program

The List Program creates lists of Solana account addresses. Developers can use these lists to index account data relevant to their programs without relying on an off-chain indexing solution.

Get Started

# Cargo.toml

[dependencies]
index-program = { version = "0.0.2" }

Mainnet: HPVqgVHeD9NPrJFSCd2UnpBudMYvorXYkzAqmy5naHTr

Devnet: HPVqgVHeD9NPrJFSCd2UnpBudMYvorXYkzAqmy5naHTr

Instructions

create_list

  • Initializes a new List account.
  • Errors if:
    • The list already exists for the given owner and namespace.

delete_list

  • Closes an List account.
  • Returns rent to owner.

push_element

  • Initializes a new Element account.
  • Appends the element to the index's data structure.
  • Errors if:
    • The list is already at max capacity.

pop_element

  • Closes an Element account.
  • Removes the element from the index's data structure.
  • Returns rent to owner.

State

List

  • Metadata for managing a list of addresses.
  • PDA is a hash of the owner's address and a custom namespace address.

Element

  • An address value with a position in a list.
  • PDA is a hash of the list's address and the pointer's position.

Examples

These examples are for Solana programs that need to create and manage their own on-chain lists. These examples show an Anchor program that has a singleton "authority account" for signing instructions on behalf of the program.

Create a list

Here is an example instruction create_my_list that creates an list owned by the program.

// create_my_list.rs

use {
    crate::state::*,
    anchor_lang::{prelude::*, solana_program::system_program},
    std::mem::size_of,
};

#[derive(Accounts)]
#[instruction(bump: u8)]
pub struct CreateMyList<'info> {
    #[account(
        mut,
        seeds = [SEED_AUTHORITY],
        bump = authority.bump,
        owner = crate::ID
    )]
    pub authority: Account<'info, Authority>,

    #[account(mut)]
    pub list: AccountInfo<'info>,

    #[account(address = list_program::ID)]
    pub list_program: Program<'info, list_program::program::ListProgram>,

    #[account(
        init,
        payer = signer,
        space = 8 + size_of<Namespace>()
    )]
    pub namespace: Account<'info, Namespace>

    #[account(mut)]
    pub signer: Signer<'info>,

    #[account(address = system_program::ID)]
    pub system_program: Program<'info, System>,
}

pub fn handler(ctx: Context<CreateMyIndex>, bump: u8) -> ProgramResult {
    // Get accounts.
    let authority = &ctx.accounts.authority;
    let list = &ctx.accounts.list;
    let list_program = &ctx.accounts.list_program;
    let namespace = &ctx.accounts.namespace;
    let signer = &ctx.accounts.signer;
    let system_program = &ctx.accounts.system_program;

    // Create a list owned by the program authority.
    list_program::cpi::create_index(
        CpiContext::new_with_signer(
            list_program.to_account_info(),
            list_program::cpi::accounts::CreateList {
                index: index.to_account_info(),
                namespace: namespace.to_account_info(),
                owner: authority.to_account_info(),
                payer: signer.to_account_info(),
                system_program: system_program.to_account_info(),
            },
            &[&[SEED_AUTHORITY, &[authority.bump]]],
        ),
        bump,
    )
}

Push an element

Here is an example instruction push_my_element that adds an element to a list owned by the program.

// create_my_pointer.rs

use {
    crate::state::*,
    anchor_lang::{prelude::*, solana_program::system_program},
    std::mem::size_of,
};

#[derive(Accounts)]
#[instruction(reference: Pubkey, bump: u8)]
pub struct CreateMyPointer<'info> {
    #[account(
        mut,
        seeds = [SEED_AUTHORITY],
        bump = authority.bump,
        owner = crate::ID
    )]
    pub authority: Account<'info, Authority>,

    #[account()]
    pub element: AccountInfo<'info>,

    #[account(
      mut,
      constraint = list.owner == authority.key(),
      constraint = list.namespace == namespace.key(),
      owner = list_program.key()
    )]
    pub list: AccountInfo<'info>,

    #[account(address = list_program::ID)]
    pub list_program: Program<'info, list_program::program::IndexProgram>,

    #[account()]
    pub namespace: Account<'info, Namespace>

    #[account(mut)]
    pub signer: Signer<'info>,

    #[account(address = system_program::ID)]
    pub system_program: Program<'info, System>,
}

pub fn handler(
  ctx: Context<CreateMyPointer>,
  reference: Pubkey,
  bump: u8,
) -> ProgramResult {
    // Get accounts.
    let authority = &ctx.accounts.authority;
    let element = &ctx.accounts.element;
    let list = &ctx.accounts.list;
    let list_program = &ctx.accounts.list_program;
    let signer = &ctx.accounts.signer;
    let system_program = &ctx.accounts.system_program;

    // Add the reference address into the list.
    list_program::cpi::push_element(
        CpiContext::new_with_signer(
            list_program.to_account_info(),
            list_program::cpi::accounts::PushElement {
                element: element.to_account_info(),
                list: list.to_account_info(),
                owner: authority.to_account_info(),
                payer: signer.to_account_info(),
                system_program: system_program.to_account_info(),
            },
            &[&[SEED_AUTHORITY, &[authority.bump]]],
        ),
        reference,
        bump,
    )
}

Dependencies

~17–26MB
~432K SLoC