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.
NAV Data Types
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 :
Read account data at nav_account_address
Read u64 at byte offset nav_price_offset
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 :
Return hardcoded_price directly
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 :
Read Pyth price account data
Read price value at nav_price_offset
Read exponent at decimals_offset
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 Decimals Raw Value Normalized (9 decimals) USD Value 6 1_050000 1_050_000_000 $1.05 8 105_000000 1_050_000_000 $1.05 9 1_050000000 1_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 Type Recommended Source Rationale Dollar-pegged stablecoin Hardcoded No oracle dependency, fixed $1.00 NAV-accruing RWA U64FixedAddress Issuer publishes official NAV Market-priced asset PythPush Real-time market data Critical assets Multiple sources Redundancy and validation
Divergence Thresholds
Scenario Recommended BPS Notes Single source 0 No divergence possible Similar sources (same feed) 10-50 Account for timing Different feeds 100-200 Allow for feed differences Volatile assets 200-500 Wider 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
Error Cause Solution InvalidNavDataInvalid configuration Check decimals ≤ 9, 1-5 sources PriceDivergenceSources disagree Investigate oracle health Price returns 0 Divergence exceeded Check price_difference_bps Account read failure Invalid offset Verify nav_price_offset
Next: Architecture Overview Explore the program’s modular design and account structure