updated feed-walkthrough example

This commit is contained in:
Conner Gallagher 2023-02-27 09:54:28 -07:00
parent 16ec4aa199
commit 37adcf80b8
3 changed files with 1225 additions and 245 deletions

File diff suppressed because it is too large Load Diff

View File

@ -18,8 +18,9 @@
},
"dependencies": {
"@solana/spl-token-v2": "npm:@solana/spl-token@^0.2.0",
"@solana/web3.js": "^1.67.2",
"@switchboard-xyz/common": "^2.1.29",
"@solana/web3.js": "^1.73.3",
"@switchboard-xyz/common": "^2.1.31",
"@switchboard-xyz/oracle": "^2.1.4",
"@switchboard-xyz/solana.js": "file:../solana.js",
"chalk": "^4.1.2",
"dotenv": "^16.0.1",

View File

@ -1,23 +1,22 @@
import type { PublicKey } from "@solana/web3.js";
import { clusterApiUrl, Connection, Keypair } from "@solana/web3.js";
import { OracleJob } from "@switchboard-xyz/common";
import { OracleJob, sleep } from "@switchboard-xyz/common";
import { NodeOracle } from "@switchboard-xyz/oracle";
import {
AggregatorAccount,
SwitchboardNetwork,
SwitchboardProgram,
QueueAccount,
TransactionObject,
PermissionAccount,
LeaseAccount,
types,
} from "@switchboard-xyz/solana.js";
import chalk from "chalk";
import dotenv from "dotenv";
import fs from "fs";
import os from "os";
import path from "path";
import readlineSync from "readline-sync";
dotenv.config();
let oracle: NodeOracle | undefined = undefined;
export const toAccountString = (
label: string,
publicKey: PublicKey | string | undefined
@ -33,9 +32,6 @@ export const toAccountString = (
)}`;
};
export const sleep = (ms: number): Promise<any> =>
new Promise((s) => setTimeout(s, ms));
export const getKeypair = (keypairPath: string): Keypair => {
if (!fs.existsSync(keypairPath)) {
throw new Error(
@ -60,13 +56,15 @@ async function main() {
}
const authority = getKeypair(payerKeypairPath);
if ((process.env.CLUSTER ?? "").startsWith("mainnet")) {
throw new Error(`This script should not be used on mainnet`);
}
// get cluster
let cluster: "mainnet-beta" | "devnet" | "localnet";
let cluster: "devnet" | "localnet";
if (
process.env.CLUSTER &&
(process.env.CLUSTER === "mainnet-beta" ||
process.env.CLUSTER === "devnet" ||
process.env.CLUSTER === "localnet")
(process.env.CLUSTER === "devnet" || process.env.CLUSTER === "localnet")
) {
cluster = process.env.CLUSTER;
} else {
@ -84,196 +82,107 @@ async function main() {
const program = await SwitchboardProgram.load(
cluster === "localnet" ? "devnet" : cluster,
new Connection(rpcUrl),
new Connection(rpcUrl, "confirmed"),
authority
);
console.log(program.cluster);
console.log(chalk.yellow("######## Switchboard Setup ########"));
// create our token wallet for the wrapped SOL mint
const tokenAccount = await program.mint.getOrCreateAssociatedUser(
program.walletPubkey
);
const [network, signatures] = await SwitchboardNetwork.create(program, {
name: "Queue-1",
slashingEnabled: false,
reward: 0,
minStake: 0,
cranks: [{ name: "Crank", maxRows: 10 }],
oracles: [
{
name: "Oracle",
enable: true,
},
],
});
// Oracle Queue
const [queueAccount, queueInit] = await QueueAccount.createInstructions(
program,
program.walletPubkey,
{
name: "Queue-1",
slashingEnabled: false,
reward: 0,
minStake: 0,
}
);
const queueAccount = network.queue.account;
console.log(toAccountString("Oracle Queue", queueAccount.publicKey));
// Crank
const [crankAccount, crankInit] = await queueAccount.createCrankInstructions(
program.walletPubkey,
{
name: "Crank",
maxRows: 10,
}
);
const crankAccount = network.cranks[0].account;
console.log(toAccountString("Crank", crankAccount.publicKey));
// Oracle
const [oracleAccount, oracleInit] =
await queueAccount.createOracleInstructions(program.walletPubkey, {
name: "Oracle",
enable: true,
queueAuthority: authority,
});
const oracleAccount = network.oracles[0].account;
console.log(toAccountString("Oracle", oracleAccount.publicKey));
// Aggregator
const [aggregatorAccount, aggregatorInit] =
await queueAccount.createFeedInstructions(program.walletPubkey, {
name: "SOL_USD",
queueAuthority: authority,
batchSize: 1,
minRequiredOracleResults: 1,
minRequiredJobResults: 1,
minUpdateDelaySeconds: 10,
fundAmount: 0.5,
enable: true,
jobs: [
{
weight: 2,
data: OracleJob.encodeDelimited(
OracleJob.fromObject({
tasks: [
{
httpTask: {
url: `https://ftx.us/api/markets/SOL_USD`,
},
const [aggregatorAccount] = await queueAccount.createFeed({
name: "SOL_USD",
batchSize: 1,
minRequiredOracleResults: 1,
minRequiredJobResults: 1,
minUpdateDelaySeconds: 10,
fundAmount: 0.5,
queueAuthority: authority,
enable: true,
crankPubkey: crankAccount.publicKey,
jobs: [
{
weight: 2,
data: OracleJob.encodeDelimited(
OracleJob.fromObject({
tasks: [
{
valueTask: {
value: 10,
},
{
jsonParseTask: { path: "$.result.price" },
},
],
})
).finish(),
},
],
});
console.log(toAccountString("Aggregator", aggregatorAccount.publicKey));
const [permissionAccount, permissionBump] = PermissionAccount.fromSeed(
program,
authority.publicKey,
queueAccount.publicKey,
aggregatorAccount.publicKey
);
const [leaseAccount, leaseBump] = LeaseAccount.fromSeed(
program,
queueAccount.publicKey,
aggregatorAccount.publicKey
);
const leaseEscrow = program.mint.getAssociatedAddress(leaseAccount.publicKey);
const crankPushIxn = types.crankPush(
program,
{
params: {
stateBump: program.programState.bump,
permissionBump: permissionBump,
notifiRef: null,
},
],
})
).finish(),
},
},
{
crank: crankAccount.publicKey,
aggregator: aggregatorAccount.publicKey,
oracleQueue: queueAccount.publicKey,
queueAuthority: authority.publicKey,
permission: permissionAccount.publicKey,
lease: leaseAccount.publicKey,
escrow: leaseEscrow,
programState: program.programState.publicKey,
dataBuffer: crankAccount.dataBuffer!.publicKey,
}
);
const packedTxns = TransactionObject.pack([
queueInit,
crankInit,
...oracleInit,
...aggregatorInit,
new TransactionObject(authority.publicKey, [crankPushIxn], []),
]);
const txnSignatures = await program.signAndSendAll(packedTxns, {
skipPreflight: true,
],
});
console.log(JSON.stringify(txnSignatures, undefined, 2));
const aggregator = await aggregatorAccount.loadData();
await oracleAccount.heartbeat();
console.log(toAccountString("Aggregator", aggregatorAccount.publicKey));
console.log(chalk.green("\u2714 Switchboard setup complete"));
// Run the oracle
console.log(chalk.yellow("######## Start the Oracle ########"));
console.log(chalk.blue("Run the following command in a new shell\r\n"));
oracle = await NodeOracle.fromReleaseChannel({
releaseChannel: "testnet",
chain: "solana",
network: program.cluster === "mainnet-beta" ? "mainnet" : program.cluster,
rpcUrl: program.connection.rpcEndpoint,
oracleKey: oracleAccount.publicKey.toBase58(),
secretPath: payerKeypairPath,
envVariables: {
VERBOSE: "1",
DEBUG: "1",
DISABLE_NONE_QUEUE: "1",
},
});
await oracle.startAndAwait();
await sleep(1000); // wait 1 extra second for oracle to heartbeat
console.log(chalk.green("\u2714 Oracle ready"));
console.log(chalk.yellow("######## Calling OpenRound ########"));
const [newAggregatorState] =
await aggregatorAccount.openRoundAndAwaitResult();
console.log(
` ORACLE_KEY=${oracleAccount.publicKey} PAYER_KEYPAIR=${payerKeypairPath} RPC_URL=${rpcUrl} docker-compose up\r\n`
);
if (
!readlineSync.keyInYN(
`Select 'Y' when the docker container displays ${chalk.underline(
"Starting listener..."
)}`
)
) {
console.log(chalk.red("\u2716 User exited..."));
return;
}
console.log("");
const confirmedRoundPromise = aggregatorAccount.nextRound();
// Turn the Crank
async function turnCrank(retryCount: number): Promise<number> {
try {
const readyPubkeys = await crankAccount.peakNextReady(5);
if (readyPubkeys) {
await crankAccount.pop({
payoutWallet: tokenAccount,
readyPubkeys,
nonce: 0,
});
console.log(chalk.green("\u2714 Crank turned"));
return 0;
} else {
console.log(chalk.red("\u2716 No feeds ready, exiting"));
return --retryCount;
}
} catch {
return --retryCount;
}
}
// Might need time for accounts to propagate
let retryCount = 3;
while (retryCount) {
await sleep(3000);
retryCount = await turnCrank(retryCount);
}
// Read Aggregators latest result
console.log(chalk.yellow("######## Aggregator Result ########"));
const confirmedRound = await confirmedRoundPromise;
console.log(
`${chalk.blue("Result:")} ${chalk.green(confirmedRound.result.toBig())}\r\n`
`${chalk.blue("Result:")} ${chalk.green(
AggregatorAccount.decodeLatestValue(newAggregatorState).toString()
)}\r\n`
);
console.log(chalk.green("\u2714 Aggregator succesfully updated!"));
if (oracle) {
oracle.stop();
oracle = undefined;
}
}
main().then(
() => process.exit(),
() => {
oracle?.stop();
process.exit();
},
(error) => {
oracle?.stop();
console.error("Failed to create a private feed");
console.error(error);
process.exit(-1);