Skip to main content

Overview

Asset Configuration accounts store the NAV (Net Asset Value) pricing sources and settings for each token in the protocol. Every RWA and stablecoin must have an AssetConfig account before it can be used in trading pairs.

Account Structure

pub struct AssetConfig {
    pub mint_address: Pubkey,               // Token mint address
    pub nav_data: Vec<NavData>,             // Up to 5 NAV pricing sources
    pub price_difference_bps: u16,          // Maximum price divergence tolerance
    pub paused: bool,                       // Asset-level pause flag
    pub version: u8,                        // Configuration version
    pub asset_type: AssetType,              // Rwa or Stable
    pub used_in_pairs_count: u16,           // Number of pairs using this asset
    pub bump: u8,                           // PDA bump seed
}
PDA Seeds: ["asset", mint_address]

Asset Type

pub enum AssetType {
    Rwa,     // Real World Asset token
    Stable,  // Stablecoin token
}

Instructions

init_asset_config_account

Register a new token for use in the protocol.
pub fn init_asset_config_account(
    ctx: Context<InitAssetConfigAccount>,
    nav_data: Vec<NavData>,
    price_difference_bps: u16,
    asset_type: AssetType,
) -> Result<()>

Parameters

ParameterTypeDescription
nav_dataVec<NavData>1-5 NAV pricing sources
price_difference_bpsu16Maximum allowed price divergence (BPS)
asset_typeAssetTypeWhether token is RWA or Stablecoin

Required Accounts

#[derive(Accounts)]
pub struct InitAssetConfigAccount<'info> {
    pub global_config: Account<'info, GlobalConfig>,

    #[account(
        init,
        payer = admin,
        space = 8 + AssetConfig::space(nav_data.len()),
        seeds = [ASSET_PREFIX, mint.key().as_ref()],
        bump,
    )]
    pub asset_config: Account<'info, AssetConfig>,

    pub mint: InterfaceAccount<'info, Mint>,

    // Fee vault (created for stablecoins)
    #[account(
        init_if_needed,
        payer = admin,
        seeds = [FEE_VAULT_PREFIX, mint.key().as_ref()],
        bump,
        token::mint = mint,
        token::authority = program_authority,
    )]
    pub fee_vault: InterfaceAccount<'info, TokenAccount>,

    /// CHECK: PDA used as token authority
    #[account(
        seeds = [b"program_authority"],
        bump,
    )]
    pub program_authority: UncheckedAccount<'info>,

    #[account(
        mut,
        constraint = global_config.admin == admin.key()
            @ MultiliquidError::Unauthorized
    )]
    pub admin: Signer<'info>,

    pub system_program: Program<'info, System>,
    pub token_program: Interface<'info, TokenInterface>,
}

Behavior

  • Creates AssetConfig PDA for the token mint
  • Creates fee vault for fee collection (if stablecoin)
  • Validates NAV data (1-5 sources, decimals ≤ 9)
  • Sets paused to false by default
  • Sets used_in_pairs_count to 0

Access Control

Access: Admin only

Example

import { BN } from "@coral-xyz/anchor";

// NAV source: Read price from a fixed account address
const navData = [{
  u64FixedAddress: {
    navAccountAddress: priceOracleAccount,
    navPriceOffset: 0,
    priceDecimals: 6,
  }
}];

await program.methods
  .initAssetConfigAccount(
    navData,
    100,  // 1% max price divergence
    { rwa: {} }  // Asset type
  )
  .accounts({
    globalConfig,
    assetConfig,
    mint: rwaMint,
    feeVault,
    programAuthority,
    admin: adminWallet.publicKey,
    systemProgram: SystemProgram.programId,
    tokenProgram: TOKEN_PROGRAM_ID,
  })
  .rpc();

update_asset_config_account

Update NAV sources for an existing asset.
pub fn update_asset_config_account(
    ctx: Context<UpdateAssetConfigAccount>,
    nav_data: Vec<NavData>,
    price_difference_bps: u16,
    asset_type: AssetType,
) -> Result<()>

Parameters

ParameterTypeDescription
nav_dataVec<NavData>Updated NAV pricing sources (1-5)
price_difference_bpsu16Updated price divergence tolerance
asset_typeAssetTypeNew asset type (change blocked if asset is in use)

Required Accounts

#[derive(Accounts)]
pub struct UpdateAssetConfigAccount<'info> {
    pub global_config: Account<'info, GlobalConfig>,

    #[account(
        mut,
        seeds = [ASSET_PREFIX, mint.key().as_ref()],
        bump = asset_config.bump,
    )]
    pub asset_config: Account<'info, AssetConfig>,

    pub mint: InterfaceAccount<'info, Mint>,

    #[account(
        constraint = global_config.admin == admin.key()
            @ MultiliquidError::Unauthorized
    )]
    pub admin: Signer<'info>,
}

Behavior

  • Updates NAV sources and price divergence threshold
  • Can update asset_type, but change is blocked if used_in_pairs_count > 0
  • Does not affect pause state or usage count

Access Control

Access: Admin only

Example

// Add a second NAV source (Pyth oracle)
const updatedNavData = [
  {
    u64FixedAddress: {
      navAccountAddress: primaryOracle,
      navPriceOffset: 0,
      priceDecimals: 6,
    }
  },
  {
    pythPush: {
      pythPushAccountAddress: pythAccount,
      navPriceOffset: 8,
      decimalsOffset: 12,
    }
  }
];

await program.methods
  .updateAssetConfigAccount(
    updatedNavData,
    50,  // 0.5% max divergence
    { rwa: {} }  // Asset type
  )
  .accounts({
    globalConfig,
    assetConfig,
    mint: rwaMint,
    admin: adminWallet.publicKey,
  })
  .rpc();

set_paused_for_asset

Set the pause state for a specific asset.
pub fn set_paused_for_asset(
    ctx: Context<SetPausedForAsset>,
    paused: bool,
) -> Result<()>

Parameters

ParameterTypeDescription
pausedboolNew pause state

Required Accounts

#[derive(Accounts)]
pub struct SetPausedForAsset<'info> {
    pub global_config: Account<'info, GlobalConfig>,

    #[account(
        mut,
        seeds = [ASSET_PREFIX, mint.key().as_ref()],
        bump = asset_config.bump,
    )]
    pub asset_config: Account<'info, AssetConfig>,

    pub mint: InterfaceAccount<'info, Mint>,

    #[account(
        constraint = global_config.admin == admin.key()
            @ MultiliquidError::Unauthorized
    )]
    pub admin: Signer<'info>,
}

Behavior

  • Updates asset’s pause state
  • When paused, all swaps involving this asset are blocked
  • Does not affect other assets or global state

Access Control

Access: Admin only

Example

// Pause an asset
await program.methods
  .setPausedForAsset(true)
  .accounts({
    globalConfig,
    assetConfig,
    mint: rwaMint,
    admin: adminWallet.publicKey,
  })
  .rpc();

The program supports three types of NAV pricing sources:

U64FixedAddress

Read price from a fixed byte offset in an on-chain account.
NavData::U64FixedAddress {
    nav_account_address: Pubkey,  // Account containing price data
    nav_price_offset: u16,        // Byte offset to read price
    price_decimals: u8,           // Decimal places (0-9)
}
Use Cases:
  • Custom price oracle accounts
  • RWA issuer-published NAV accounts
  • Any account with a u64 price at known offset

Hardcoded

Static price value for stable-value assets.
NavData::Hardcoded {
    hardcoded_price: u64,   // Fixed price value
    price_decimals: u8,     // Decimal places (0-9)
}
Use Cases:
  • Dollar-pegged stablecoins (price = 1.0)
  • Assets with contractually fixed prices
  • Testing and development

PythPush

Pyth Network oracle integration.
NavData::PythPush {
    pyth_push_account_address: Pubkey,  // Pyth price account
    nav_price_offset: u16,              // Price location offset
    decimals_offset: u16,               // Exponent location offset
}
Use Cases:
  • Market-priced assets
  • Assets with Pyth price feeds
  • High-frequency price updates

View Price Source Details

Complete documentation on NAV pricing configuration and validation

Price Aggregation

When multiple NAV sources are configured:
  1. Read All Sources: Each source returns a price
  2. Normalize: All prices converted to 9 decimal places
  3. Validate Divergence: Check all prices within price_difference_bps
  4. Average: Return average of all valid prices
If any source diverges beyond threshold, the function returns 0, blocking swaps.

Error Codes

ErrorDescription
UnauthorizedCaller is not admin
MustProvideAtLeastOneNavDataNo NAV sources provided
Max5NavDataMore than 5 NAV sources
PriceDecimalsTooLargeDecimals exceed 9
InvalidNavNAV sources returned invalid or divergent data
OutOfRangeprice_difference_bps exceeds 9900

Next: Pair Management

Learn about trading pair creation and configuration