Migrating to Switchboard, using Chainlink, on Solana
Pull vs Push
Chainlink on Solana is a push-based oracle, so part of this update will require a minor change to your front-end libraries so you can read Switchboard updates and keep them updated on-demand.
Add Switchboard SDKs
Switchboard provides two SDKs, one for using the SDK with Typescript, another in Rust for reading prices on-chain.
Rust SDK
Change the dependencies section of your Cargo.toml file from:
Chainlink provides a Typescript package, @chainlin/solana-sdk, for interacting with chainlink on the Solana blockchain. You can replace this package with the single Switchboard Typescript SDK, @switchboard-xyz/on-demand.
Since Chainlink uses the push model, they don't require a query for update data.
Switchboard
import { web3, AnchorProvider, Program } from"@coral-xyz/anchor";import { PullFeed, loadLookupTables, SB_ON_DEMAND_PID} from"@switchboard-xyz/on-demand";constconnection:Connection;constwallet:Wallet;// Get the Switchboard Programconstprovider=newAnchorProvider(connection, wallet, {});constidl= (awaitProgram.fetchIdl(programId, provider))!;constswitchboard=newProgram(idl, provider);// Replace with your feed pubkeyconstfeed=newPullFeed(switchboard,newPublicKey("6qmsMwtMmeqMgZEhyLv1Pe4wcqT5iKwJAWnmzmnKjf83"));// Fetch the update data const [pullIx,responses,success] =awaitfeed.fetchUpdateIx();
Step 3: Submitting Updates
When doing a transaction using Chainlink, you'd pass in some accounts related to the feeds you're using. With Switchboard, you can slip in an update instruction right before the instruction using your business logic and get the latest update for any data feed.
Chainlink
// ... // https://github.com/smartcontractkit/solana-starter-kit/blob/main/client.jsconstCHAINLINK_PROGRAM_ID="HEvSKofvBgfaexv23kMabbYqxasxU3mQ4ibBMEmJWHny";constDIVISOR=100000000;constCHAINLINK_FEED="99B2bTijsU6f1GCT73HmdR7HCFFjGMBcPZY6jZ96ynrR";// create an account to store the price dataconstpriceFeedAccount=anchor.web3.Keypair.generate();// ... anchor program setup ...// execute the txlet tx =awaitprogram.rpc.execute({ accounts: { decimal:priceFeedAccount.publicKey, user:provider.wallet.publicKey, chainlinkFeed:CHAINLINK_FEED, chainlinkProgram:CHAINLINK_PROGRAM_ID, systemProgram:anchor.web3.SystemProgram.programId }, options: { commitment:"confirmed" }, signers: [priceFeedAccount],});// Done!
Switchboard
// ... // Get the update instruction for switchboard and lookup tables to make the instruction lighterconst [pullIx,responses,success] =awaitfeedAccount.fetchUpdateIx({ crossbarClient: crossbar });constlookupTables=awaitloadLookupTables([...responses.map((x) =>x.oracle), feedAccount]);// Set priority fee for that the txconstpriorityFeeIx=web3.ComputeBudgetProgram.setComputeUnitPrice({ microLamports:100_000,});// Get the latest contextconst { context: { slot: minContextSlot }, value: { blockhash,lastValidBlockHeight },} =awaitconnection.getLatestBlockhashAndContext();// Get Transaction Message constmessage=newweb3.TransactionMessage({ payerKey: publicKey, recentBlockhash: blockhash, instructions: [addPriorityFee, pullIx, myIx],}).compileMessageV0(lookupTables);// Get Versioned Transactionconstvtx=newweb3.VersionedTransaction(message);constsigned=awaitwallet.signTransaction(vtx);// Send the transaction constsignature=awaitconnection.sendRawTransaction(signed.serialize(), { maxRetries:0, skipPreflight:true,});// Wait for confirmationawaitconnection.confirm({ signature, blockhash, lastValidBlockHeight,});
Summing it up
The end result will be the execution of the update, followed by the execution of myIx (and here that's the execute instruction).