Skip to main content

Documentation Index

Fetch the complete documentation index at: https://kamino.com/docs/llms.txt

Use this file to discover all available pages before exploring further.

Reading Scope Prices

Scope serves prices two ways. On-chain, the OraclePrices account holds a fixed 512-slot array of dated prices and the SDK reads it directly. Off-chain, the Kamino REST endpoint serves the same prices pre-aggregated and keyed by mint, with no RPC or SDK required.

Read All Prices

Direct on-chain read of every populated slot in a feed.
import { createSolanaRpc } from '@solana/kit';
import { Scope, SCOPE_MAINNET_HUBBLE_FEED } from '@kamino-finance/scope-sdk';
import Decimal from 'decimal.js';

const RPC_ENDPOINT = 'https://api.mainnet-beta.solana.com';

const rpc = createSolanaRpc(RPC_ENDPOINT);
const scope = new Scope('mainnet-beta', rpc);

const oraclePrices = await scope.getSingleOraclePrices({
  config: SCOPE_MAINNET_HUBBLE_FEED.configuration,
});

// Fixed-size array; a slot is unset when its price value is 0.
const populated = oraclePrices.prices
  .map((p, index) => ({ index, p }))
  .filter(({ p }) => p.price.value.toString() !== '0');

console.log(`Feed ${SCOPE_MAINNET_HUBBLE_FEED.oraclePrices}: ${populated.length} populated of ${oraclePrices.prices.length}`);

populated.slice(0, 10).forEach(({ index, p }) => {
  const price = new Decimal(p.price.value.toString()).div(new Decimal(10).pow(Number(p.price.exp)));
  const updated = new Date(Number(p.unixTimestamp) * 1000).toISOString();
  console.log(`  [${index}] ${price.toString()} (updated ${updated})`);
});
View CodeA price is value / 10^exp; freshness comes from lastUpdatedSlot and unixTimestamp on the same dated price. getSingleOraclePrices accepts the feed by its configuration account, its OraclePrices pubkey, or its PDA seed.

Get a Single Token Price

A token’s USD price is resolved by a “scope chain”: up to 4 slot indices, padded with 65535. One index means a direct price; several means a derived price (for example TOKEN/SOL then SOL/USD).
import { createSolanaRpc } from '@solana/kit';
import { Scope, SCOPE_MAINNET_HUBBLE_FEED } from '@kamino-finance/scope-sdk';

const RPC_ENDPOINT = 'https://api.mainnet-beta.solana.com';
const CHAIN = [0, 65535, 65535, 65535]; // SOL/USD, slot 0, single-hop

const rpc = createSolanaRpc(RPC_ENDPOINT);
const scope = new Scope('mainnet-beta', rpc);

const oraclePrices = await scope.getSingleOraclePrices({
  config: SCOPE_MAINNET_HUBBLE_FEED.configuration,
});
if (!Scope.isScopeChainValid(CHAIN)) console.log('Chain is not valid:', CHAIN);

const { price, timestamp } = await scope.getPriceFromChain(CHAIN, oraclePrices);
const ageSec = Math.floor(Date.now() / 1000) - timestamp.toNumber();

console.log(`Price: ${price.toString()}`);
console.log(`Updated: ${new Date(timestamp.toNumber() * 1000).toISOString()} (${ageSec}s ago)`);
View CodeThe calling program supplies the scope chain. A klend reserve, for example, stores it on its token config; Scope just resolves it against the feed’s price list. Sentinel slots (65535) are skipped.

Types

Returned by getPriceFromChain. A resolved price plus its freshness.
type ScopeDatedPrice = {
  price: Decimal;
  timestamp: BN;
};

Additional Resources

API Examples

SDK Repository