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.
Refresh Prices
Scope prices are pushed on-chain by permissionless “crank” transactions: anyone can sign and send a refresh, no admin authority required. The Scope SDK builds one instruction that refreshes the specified token slots for a feed. This is the instruction Multiply and flash-loan flows bundle in front of a leverage operation so reserves read fresh prices.
Import Dependencies Import the Scope SDK plus the Solana Kit transaction-building helpers and a keypair loader. import {
createSolanaRpc ,
createSolanaRpcSubscriptions ,
pipe ,
createTransactionMessage ,
setTransactionMessageFeePayerSigner ,
setTransactionMessageLifetimeUsingBlockhash ,
appendTransactionMessageInstructions ,
signTransactionMessageWithSigners ,
getSignatureFromTransaction ,
assertIsTransactionWithinSizeLimit ,
sendAndConfirmTransactionFactory ,
} from "@solana/kit" ;
import { parseKeypairFile } from "@kamino-finance/klend-sdk/dist/utils/signer" ;
import { Scope , SCOPE_MAINNET_HUBBLE_FEED } from "@kamino-finance/scope-sdk" ;
Set the keypair path, RPC endpoints, and the slot indices to refresh. const KEYPAIR_FILE = "/path/to/your/keypair.json" ;
const RPC_ENDPOINT = "https://api.mainnet-beta.solana.com" ;
const WS_ENDPOINT = "wss://api.mainnet-beta.solana.com" ;
// Slot indices to refresh (e.g. SOL, ETH, BTC on the Hubble feed).
const TOKENS = [ 0 , 1 , 2 ];
const signer = await parseKeypairFile ( KEYPAIR_FILE );
const rpc = createSolanaRpc ( RPC_ENDPOINT );
const rpcSubscriptions = createSolanaRpcSubscriptions ( WS_ENDPOINT );
const scope = new Scope ( "mainnet-beta" , rpc );
Build the Refresh Instruction refreshPriceListIx returns one instruction that refreshes the listed slots, or null if there is nothing to refresh.const ix = await scope . refreshPriceListIx (
{ config: SCOPE_MAINNET_HUBBLE_FEED . configuration },
TOKENS ,
);
if ( ! ix ) console . log ( `Nothing to refresh for tokens [ ${ TOKENS } ]` );
console . log ( `Built refresh ix: program ${ ix . programAddress } , ${ ix . accounts ?. length ?? 0 } accounts` );
The refresh is permissionless: any wallet can pay fees and send it. The Scope program writes the new dated prices to the feed’s OraclePrices account.
Build, Sign, and Send Transaction Compose the v0 transaction, sign, verify it fits the size limit, then send and confirm. const { value : blockhash } = await rpc
. getLatestBlockhash ({ commitment: "finalized" })
. send ();
const transactionMessage = pipe (
createTransactionMessage ({ version: 0 }),
( m ) => setTransactionMessageFeePayerSigner ( signer , m ),
( m ) => setTransactionMessageLifetimeUsingBlockhash ( blockhash , m ),
( m ) => appendTransactionMessageInstructions ([ ix ], m ),
);
const signedTransaction = await signTransactionMessageWithSigners ( transactionMessage );
assertIsTransactionWithinSizeLimit ( signedTransaction );
const signature = getSignatureFromTransaction ( signedTransaction );
await sendAndConfirmTransactionFactory ({ rpc , rpcSubscriptions })( signedTransaction , {
commitment: "confirmed" ,
skipPreflight: true ,
});
console . log ( "Refresh successful! Signature:" , signature );
The refresh is complete. The targeted slots now have fresh prices and timestamps on-chain.
Full Code Example
import {
createSolanaRpc ,
createSolanaRpcSubscriptions ,
pipe ,
createTransactionMessage ,
setTransactionMessageFeePayerSigner ,
setTransactionMessageLifetimeUsingBlockhash ,
appendTransactionMessageInstructions ,
signTransactionMessageWithSigners ,
getSignatureFromTransaction ,
assertIsTransactionWithinSizeLimit ,
sendAndConfirmTransactionFactory ,
} from "@solana/kit" ;
import { parseKeypairFile } from "@kamino-finance/klend-sdk/dist/utils/signer" ;
import { Scope , SCOPE_MAINNET_HUBBLE_FEED } from "@kamino-finance/scope-sdk" ;
const KEYPAIR_FILE = "/path/to/your/keypair.json" ;
const RPC_ENDPOINT = "https://api.mainnet-beta.solana.com" ;
const WS_ENDPOINT = "wss://api.mainnet-beta.solana.com" ;
const TOKENS = [ 0 , 1 , 2 ];
const signer = await parseKeypairFile ( KEYPAIR_FILE );
const rpc = createSolanaRpc ( RPC_ENDPOINT );
const rpcSubscriptions = createSolanaRpcSubscriptions ( WS_ENDPOINT );
const scope = new Scope ( "mainnet-beta" , rpc );
const ix = await scope . refreshPriceListIx (
{ config: SCOPE_MAINNET_HUBBLE_FEED . configuration },
TOKENS ,
);
if ( ! ix ) console . log ( `Nothing to refresh for tokens [ ${ TOKENS } ]` );
const { value : blockhash } = await rpc
. getLatestBlockhash ({ commitment: "finalized" })
. send ();
const transactionMessage = pipe (
createTransactionMessage ({ version: 0 }),
( m ) => setTransactionMessageFeePayerSigner ( signer , m ),
( m ) => setTransactionMessageLifetimeUsingBlockhash ( blockhash , m ),
( m ) => appendTransactionMessageInstructions ([ ix ], m ),
);
const signedTransaction = await signTransactionMessageWithSigners ( transactionMessage );
assertIsTransactionWithinSizeLimit ( signedTransaction );
const signature = getSignatureFromTransaction ( signedTransaction );
await sendAndConfirmTransactionFactory ({ rpc , rpcSubscriptions })( signedTransaction , {
commitment: "confirmed" ,
skipPreflight: true ,
});
console . log ( "Refresh successful! Signature:" , signature );
See all 54 lines