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 receiver price update account
feed_id : [ u8 ; 32 ], // Expected Pyth feed id
max_age_secs : u64 , // Maximum accepted price age
max_conf_bps : u16 , // Maximum accepted confidence ratio
}
How It Works :
Deserialize the configured Pyth receiver PriceUpdateV2 account
Verify the account’s feed id matches feed_id
Require the update to be no older than max_age_secs
Require confidence to be within max_conf_bps
Normalize the Pyth price and exponent into the program’s 9-decimal NAV format
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 ,
feedId: Array . from ( Buffer . from ( "40ac3329933a6b5b65cf31496018c5764ac0567316146f7d0de00095886b480d" , "hex" )),
maxAgeSecs: new BN ( 86_400 ),
maxConfBps: 100 ,
}
};
Pyth oracle integration requires the correct receiver account and feed id. Stale updates, excessive confidence ratios, or feed-id mismatches will reject the source.
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) / max_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.55% ✗ 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 ,
feedId: Array . from ( Buffer . from ( "40ac3329933a6b5b65cf31496018c5764ac0567316146f7d0de00095886b480d" , "hex" )),
maxAgeSecs: new BN ( 86_400 ),
maxConfBps: 100 ,
}
}
];
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 InvalidFeedIdPyth feed id is zeroed Provide the expected 32-byte feed id InvalidMaxAge / MaxAgeTooLargePyth max age is invalid Use a value from 1 second through 24 hours ConfBpsTooLargePyth max confidence setting exceeds the ceiling Use 1000 bps or lower MissingPythAccount / FeedIdMismatchPyth receiver account is missing or for the wrong feed Pass the configured Pyth account in remaining accounts ConfidenceTooLowPyth confidence ratio exceeds max_conf_bps Investigate oracle health or widen the configured threshold Price returns 0 Divergence exceeded Check price_difference_bps InvalidAccountPublicKeyRequired NAV source account was not passed Include each non-hardcoded source account in remaining accounts
Next: Architecture Overview Explore the program’s modular design and account structure