sRFC 00016: Generalized Ownable Indexable Assets

Generalized Ownable Indexable Assets

Summary

This is a standard protocol to enables RPCs to track assets that programs to create, replace, update, and delete for users. Indexers will be able to interpret “owned” program data by executing a pre-determined simulated view function on the program.

Motivation

We want to support forking of commonly used protocols, so that big enterprises can have full control and maintainability over their contracts, while also retaining the ability to have their program’s data indexed & shown in wallets.

Centralized control of commonly used protocols leads to catastrophic failure scenarios when the trusted operator is compromised. Short term work-arounds in previous upheavals, e.g. the genesis of OpenBook, have failed to produce meaningful abstraction patterns on Solana. As developers on Solana begin to realize the risks of centralized protocols, they may want to fork even the SPL Token program.

However they will quickly run into issues getting adoption into marketplaces, dApps, and wallets. This is both a social and technical problem. We hope that this standard for indexing will solve the technical problem, and thus reduce the tension in the social problem of adoption.

Specification

Require the usage of CPI events. See sRFC #00013.

We propose that programs control ownable assets for wallets using the following 4 CRUD event structs.
The payload of these events is used to manage an Asset which has an ID of type Pubkey, and consists of an ordered array of Pubkeys.

// Inform indexers that a new Asset Group was created for an authority
pub struct CrudCreate {
  asset_id: Pubkey,
  authority: Pubkey,
  pubkeys: Vec<Pubkey>,
  data: Vec<u8>
}

// Inform indexers to change both authority & pubkeys
pub struct CrudReplaceKeys {
  asset_id: Pubkey,
  authority: Pubkey,
  pubkeys: Vec<Pubkey>
}

// Inform indexers to update the bytes for an asset group
pub struct CrudUpdateBytees {
  asset_id: Pubkey,
  owner: Pubkey
}

// Inform indexers to delete the asset 
pub struct CrudDestroy {
  asset_id: Pubkey
}

Indexers will store assets issued by programs, so that you will always be able to query /getAssetsForOwner { program_id, wallet } and have it return a list of Asset { id: Pubkey, pubkeys: Vec<Pubkey>, data: Vec<u8>}.

Optionally, indexers can ask the program for a human readable interpretation of the Asset’s data by simulating view functions on the program that it belongs to. This is done by sending the getAssetData anchor instruction with no arguments, and deserializing the return_value as a JSON.

Programs that comply with this spec will have similar implementation as below:

#[program]
pub mod my_program {
  ...

  pub fn get_asset_data(ctx: Context<GetAssetData>, data: Vec<u8>) -> Ok(()) {
    let asset_id_account = &ctx.accounts.asset_id.to_account_info();
    let asset_accounts = &ctx.remaining_accounts.to_vec();
    // interpret asset data
    let my_json_bytes = // serialize asset data here
    set_return_data(my_json_bytes)
    Ok(())
  }
}

#[derive(Accounts)]
pub struct GetAssetData<'info> {
  /// CHECK:
  pub asset_id: AccountInfo<'info>,
  /// CHECK:
  pub authority: AccountInfo<'info>,
}

This will allow indexers to serve JSON data from each Asset that the program has issued.

Implementation:
TBD

1 Like

Does this concept require indexers to all be running an implementation of the Digital Asset Standard API (set forth by Metaplex)?
Or would there be a more generalized implementation of this endpoint/view function calling getAssetsForOwner and getAssetData that gets baked into RPC nodes/indexers?

1 Like

There would be a more generalized version of the indexer that supports those two RPC calls (getAssetsForOwner and getAssetData).