This section provides the steps necessary to take your simulated feed definition and deploy it live to Solana/SVM, building on the design steps completed previously.
Prerequisites:
Successful completion of Section 1 (Designing and Simulating Your Feed).
A funded Solana Keypair file. Use the Solana CLI tool: Alternatively, you should note that each solana keypair file is essentially the representation of a private key in the format of an array with numbers like this: [66,46,178,32,49, ...]
solana-keygen new --outfile path/to/solana-keypair.json
Steps
Import Dependencies: Add the necessary @switchboard-xyz/on-demand dependencies to your index.ts file, which allow for Solana commands. Make sure you replace the index.ts which you'll want to deploy:
Get the Queue: Obtain an instance of the Switchboard queue. This is typically done after a successful simulation step. Remember, queues in the context of Switchboard are subnets of Oracles that respond to feed updates.
Call getDefaultQueue(solanaRPCUrl: string) to pull the Switchboard Mainnet Queue. Calling getDefaultDevnetQueue(solanaRPCUrl: string) will pull the Switchboard Devnet queue.
// Get the queue for the network you're deploying on
let queue = await getDefaultQueue(connection.rpcUrl);
[Optional] Store with Crossbar: For ease of use, and displaying your Jobs on the Explorer, you should store your feeds with CrossBar. This is a service that will store valid Job definitions on IPFS.
// Get the default crossbar server client
const crossbarClient = CrossbarClient.default();
// Upload jobs to Crossbar, which pins valid feeds on ipfs
// Feeds are associated with a specific queue, which is why we need to pass it in
const { feedHash } = await crossbarClient.store(queue.pubkey.toBase58(), jobs);
Load the Payer Keypair: Retrieve your Solana payer's loaded account. Ensure that it’s funded for pull feed creation to work.
// Get the payer keypair
const payer = await AnchorUtils.initKeypairFromFile(
"path/to/solana-keypair.json"
);
console.log("Using Payer:", payer.publicKey.toBase58(), "\n");
Generate a new pull feed object: This is just generating a new keypair associated with the PullFeedAccountData, and creating a wrapper object
Initialise the feed: Now, create the feed with all the required specs. Be mindful of parameters such as maximum variance and other values which determine the data feed.
// Get the initialization for the pull feeds
const ix = await pullFeed.initIx({
name: "BTC Price Feed", // the feed name (max 32 bytes)
queue: queue.pubkey, // the queue of oracles to bind to
maxVariance: 1.0, // the maximum variance allowed for the feed results
minResponses: 1, // minimum number of responses of jobs to allow
feedHash: Buffer.from(feedHash.slice(2), "hex"), // the feed hash
minSampleSize: 1, // The minimum number of samples required for setting feed value
maxStaleness: 60, // The maximum number of slots that can pass before a feed value is considered stale.
payer: payer.publicKey, // the payer of the feed
});
Create the Transaction and Simulation Check: Create the transaction and configure it so it can be sent to a Solana Validator. This example uses the asV0Tx, a convenience function that's not necessary (but can be useful) for building transactions with priority fees.
Congratulations, you have now created and fully deployed a feed using typescript on chain.
Final Result
The assembled contract should go through the steps we've built:
Configure the OracleJobs and Simulate
Pick the queue for the target network (Mainnet or Devnet)
Store with Crossbar so it's visible in the explorer
Build the PullFeed's initIx instruction
Build and send the transaction on Solana
index.ts
import {
CrossbarClient,
OracleJob
} from "@switchboard-xyz/common";
import {
AnchorUtils,
PullFeed,
getDefaultQueue,
asV0Tx,
} from "@switchboard-xyz/on-demand";
const jobs: OracleJob[] = [
OracleJob.create({
tasks: [
{
httpTask: {
url: "https://binance.com/api/v3/ticker/price",
},
},
{
jsonParseTask: {
path: "$[?(@.symbol == 'BTCUSDT')].price",
},
},
],
}),
];
console.log("Running simulation...\n");
// Print the jobs that are being run.
const jobJson = JSON.stringify({ jobs: jobs.map((job) => job.toJSON()) });
console.log(jobJson);
console.log();
// Serialize the jobs to base64 strings.
const serializedJobs = jobs.map((oracleJob) => {
const encoded = OracleJob.encodeDelimited(oracleJob).finish();
const base64 = Buffer.from(encoded).toString("base64");
return base64;
});
// Call the simulation server.
const response = await fetch("https://api.switchboard.xyz/api/simulate", {
method: "POST",
headers: [["Content-Type", "application/json"]],
body: JSON.stringify({ cluster: "Mainnet", jobs: serializedJobs }),
});
// Check response.
if (response.ok) {
const data = await response.json();
console.log(`Response is good (${response.status})`);
console.log(JSON.stringify(data, null, 2));
} else {
console.log(`Response is bad (${response.status})`);
throw await response.text();
}
console.log("Storing and creating the feed...\n");
// Get the queue for the network you're deploying on
let queue = await getDefaultQueue(solanaRpcUrl);
// Get the crossbar server client
const crossbarClient = CrossbarClient.default();
// Get the payer keypair
const payer = await AnchorUtils.initKeypairFromFile(
"path/to/solana-keypair.json"
);
console.log("Using Payer:", payer.publicKey.toBase58(), "\\n");
// Upload jobs to Crossbar, which pins valid feeds on ipfs
const { feedHash } = await crossbarClient.store(queue.pubkey.toBase58(), jobs);
const [pullFeed, feedKeypair] = PullFeed.generate(queue.program);
const initIx = await pullFeed.initIx({
name: "BTC Price Feed", // the feed name (max 32 bytes)
queue: queue.pubkey, // the queue of oracles to bind to
maxVariance: 1.0, // the maximum variance allowed for the feed results
minResponses: 1, // minimum number of responses of jobs to allow
feedHash: Buffer.from(feedHash.slice(2), "hex"), // the feed hash
minSampleSize: 1, // The minimum number of samples required for setting feed value
maxStaleness: 300, // The maximum number of slots that can pass before a feed value is considered stale.
payer: payer.publicKey, // the payer of the feed
});
const initTx = await asV0Tx({
connection: queue.program.provider.connection,
ixs: [initIx],
payer: payer.publicKey,
signers: [payer, feedKeypair],
computeUnitPrice: 200_000,
computeUnitLimitMultiple: 1.5,
});
// simulate the transaction
const simulateResult =
await queue.program.provider.connection.simulateTransaction(initTx, {
commitment: "processed",
});
console.log(simulateResult);
const initSig = await queue.program.provider.connection.sendTransaction(
initTx,
{
preflightCommitment: "processed",
skipPreflight: false,
}
);
console.log(`Feed ${feedKeypair.publicKey} initialized: ${initSig}`);
Example output:
If it's successful, you should see a successful simulation in the console. Just to verify that the account was initialised correctly, search for the printed key or signature on a Solana Explorer.