Skip to main content

Overview

Price sources (NavData) provide USD-denominated pricing for all assets in the Multiliquid Program. Each asset can have 1-5 pricing sources, which are aggregated and validated during swap execution. 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 (as u64)
    price_decimals: u8,           // Decimal places (0-9)
}
How It Works:
  1. Read account data at nav_account_address
  2. Read u64 at byte offset nav_price_offset
  3. Interpret value with price_decimals decimal places
Use Cases:
  • Custom price oracle accounts
  • RWA issuer-published NAV accounts
  • Any account storing price as u64 at known offset
Example Configuration:
// Read price from offset 0 in a custom oracle account
const navData = {
  u64FixedAddress: {
    navAccountAddress: oracleAccount,
    navPriceOffset: 0,      // Read from start of account data
    priceDecimals: 6,       // Price has 6 decimal places
  }
};

// Example: Price of 1050000 with 6 decimals = $1.05

Hardcoded

Static price value for stable-value assets.
NavData::Hardcoded {
    hardcoded_price: u64,   // Fixed price value
    price_decimals: u8,     // Decimal places (0-9)
}
How It Works:
  1. Return hardcoded_price directly
  2. Interpret with price_decimals decimal places
Use Cases:
  • Dollar-pegged stablecoins (USDC, USDT)
  • Assets with contractually fixed prices
  • Testing and development environments
Example Configuration:
// Stablecoin pegged to $1.00
const navData = {
  hardcoded: {
    hardcodedPrice: new BN(1_000000),  // 1.000000
    priceDecimals: 6,
  }
};

// Example: Price of 1000000 with 6 decimals = $1.00
Hardcoded prices are ideal for dollar-pegged stablecoins that maintain a 1:1 USD value. They have zero external dependencies and minimal gas costs.

PythPush

Pyth Network oracle integration for market prices.
NavData::PythPush {
    pyth_push_account_address: Pubkey,  // Pyth price account
    nav_price_offset: u16,              // Price location offset
    decimals_offset: u16,               // Exponent location offset
}
How It Works:
  1. Read Pyth price account data
  2. Read price value at nav_price_offset
  3. Read exponent at decimals_offset
  4. Calculate final price: price × 10^exponent
Use Cases:
  • Market-priced assets with Pyth feeds
  • Real-time price updates
  • Cross-chain price consistency
Example Configuration:
// Pyth oracle for an RWA with market price
const navData = {
  pythPush: {
    pythPushAccountAddress: pythPriceAccount,
    navPriceOffset: 8,      // Pyth price struct offset
    decimalsOffset: 12,     // Pyth exponent offset
  }
};
Pyth oracle integration requires correct offset configuration matching the Pyth price account structure. Incorrect offsets will result in invalid prices.

Price Aggregation

When multiple NAV sources are configured for an asset, the program performs price aggregation:

Aggregation Process

┌─────────────────────────────────────────────────────────┐
│                  Price Aggregation                       │
├─────────────────────────────────────────────────────────┤
│  1. Read all NAV sources                                │
│     Source 1: $1.0500 (6 decimals)                     │
│     Source 2: $1.0510 (8 decimals)                     │
│     Source 3: $1.0495 (6 decimals)                     │
│                                                         │
│  2. Normalize to 9 decimals                            │
│     Source 1: 1_050_000_000                            │
│     Source 2: 1_051_000_000                            │
│     Source 3: 1_049_500_000                            │
│                                                         │
│  3. Validate divergence (e.g., 100 BPS = 1%)          │
│     Max: 1_051_000_000                                 │
│     Min: 1_049_500_000                                 │
│     Diff: 0.14% ✓ (within threshold)                   │
│                                                         │
│  4. Calculate average                                   │
│     Average: 1_050_166_666 (9 decimals)               │
│     = $1.050166666                                     │
└─────────────────────────────────────────────────────────┘

Divergence Validation

The price_difference_bps setting controls maximum allowed price divergence:
// In AssetConfig
pub price_difference_bps: u16,  // Maximum divergence in basis points
Calculation:
divergence_bps = (max_price - min_price) / min_price × 10000
Behavior:
  • If divergence_bps > price_difference_bps: Return 0 (block swaps)
  • If within threshold: Return average price
Example:
price_difference_bps = 100 (1%)

Source 1: $1.05
Source 2: $1.06
Divergence: 0.95% ✓ Allowed

Source 1: $1.05
Source 2: $1.10
Divergence: 4.76% ✗ Blocked (returns 0)

Decimal Normalization

All prices are normalized to 9 decimal places internally:
Source DecimalsRaw ValueNormalized (9 decimals)USD Value
61_0500001_050_000_000$1.05
8105_0000001_050_000_000$1.05
91_0500000001_050_000_000$1.05
Normalization Formula:
normalized = raw_value × 10^(9 - source_decimals)

Configuration Examples

Single Source: Dollar-Pegged Stablecoin

// USDC with fixed $1.00 price
const usdcNavData = [{
  hardcoded: {
    hardcodedPrice: new BN(1_000000),
    priceDecimals: 6,
  }
}];

await program.methods
  .initAssetConfigAccount(
    usdcNavData,
    0,  // No divergence check needed (single source)
    { stable: {} }
  )
  .accounts({ /* ... */ })
  .rpc();

Single Source: RWA with Custom Oracle

// RWA with price published to custom account
const rwaNavData = [{
  u64FixedAddress: {
    navAccountAddress: issuerOracleAccount,
    navPriceOffset: 0,
    priceDecimals: 6,
  }
}];

await program.methods
  .initAssetConfigAccount(
    rwaNavData,
    0,  // Single source
    { rwa: {} }
  )
  .accounts({ /* ... */ })
  .rpc();

Multiple Sources: RWA with Redundancy

// RWA with primary oracle + Pyth backup
const rwaNavData = [
  {
    u64FixedAddress: {
      navAccountAddress: primaryOracle,
      navPriceOffset: 0,
      priceDecimals: 6,
    }
  },
  {
    pythPush: {
      pythPushAccountAddress: pythAccount,
      navPriceOffset: 8,
      decimalsOffset: 12,
    }
  }
];

await program.methods
  .initAssetConfigAccount(
    rwaNavData,
    100,  // Allow 1% divergence between sources
    { rwa: {} }
  )
  .accounts({ /* ... */ })
  .rpc();

Reading NAV Accounts in Swaps

When executing swaps, NAV source accounts must be passed as remaining accounts:
// Collect all NAV source accounts for both assets
const navAccounts = [
  // RWA NAV sources
  { pubkey: rwaOracleAccount, isWritable: false, isSigner: false },
  // Stablecoin NAV sources (if not hardcoded)
  // ... additional accounts as needed
];

await program.methods
  .swap(amount, minOut, null, { stableToAsset: {} }, { exactIn: {} })
  .accounts({ /* ... */ })
  .remainingAccounts(navAccounts)
  .rpc();
Hardcoded NAV sources don’t require external accounts. Only U64FixedAddress and PythPush sources need their accounts passed.

Best Practices

Choosing NAV Sources

Asset TypeRecommended SourceRationale
Dollar-pegged stablecoinHardcodedNo oracle dependency, fixed $1.00
NAV-accruing RWAU64FixedAddressIssuer publishes official NAV
Market-priced assetPythPushReal-time market data
Critical assetsMultiple sourcesRedundancy and validation

Divergence Thresholds

ScenarioRecommended BPSNotes
Single source0No divergence possible
Similar sources (same feed)10-50Account for timing
Different feeds100-200Allow for feed differences
Volatile assets200-500Wider tolerance needed

Monitoring

  • Track NAV source health and availability
  • Alert on divergence events (swaps blocked)
  • Monitor Pyth oracle staleness
  • Verify issuer oracle updates regularly

Error Handling

ErrorCauseSolution
InvalidNavDataInvalid configurationCheck decimals ≤ 9, 1-5 sources
PriceDivergenceSources disagreeInvestigate oracle health
Price returns 0Divergence exceededCheck price_difference_bps
Account read failureInvalid offsetVerify nav_price_offset

Next: Architecture Overview

Explore the program’s modular design and account structure