updated feed-walkthrough example

This commit is contained in:
Conner Gallagher 2022-09-23 13:43:17 -06:00
parent a8c045614b
commit e0514925d3
4 changed files with 126 additions and 40 deletions

View File

@ -0,0 +1,59 @@
# Switchboard-V2 Feed Walkthrough
This example will walk you through
- creating a personal oracle queue with a crank
- add a SOL/USD data feed onto the crank
- spin up a docker environment to run your own oracle
- fulfill your update request on-chain
## Usage
```bash
ts-node src/main [PAYER_KEYPAIR_PATH]
```
where **PAYER_KEYPAIR_PATH** is the location of your Solana keypair, defaulting
to `~/.config/solana/id.json` if not provided
When prompted, run the docker compose script in a new shell to start your local
oracle then confirm the prompt to turn the crank and request an update on-chain.
The oracle is ready to fulfill updates when it sees the following logs:
```bash
{"timestamp":"2022-09-23T19:24:11.874Z","level":"info","message":"Loaded 1000 nonce accounts"}
{"timestamp":"2022-09-23T19:24:11.885Z","level":"info","message":"started health check handler"}
{"timestamp":"2022-09-23T19:24:11.886Z","level":"info","message":"Heartbeat routine started with an interval of 15 seconds."}
{"timestamp":"2022-09-23T19:24:11.887Z","level":"info","message":"Watching event: AggregatorOpenRoundEvent ..."}
{"timestamp":"2022-09-23T19:24:11.893Z","level":"info","message":"Watching event: VrfRequestRandomnessEvent ..."}
{"timestamp":"2022-09-23T19:24:11.894Z","level":"info","message":"Using default performance monitoring"}
```
Example Output:
```bash
$ ts-node src/main
######## Switchboard Setup ########
Program State BYM81n8HvTJuqZU1PmTVcwZ9G8uoji7FKM6EaPkwphPt
Oracle Queue AVbBmSeKJppRcphaPY1fPbFQW48Eg851G4XfqyTPMZNF
Crank 6fNsrJhaB2MPpwpcxW7AL5zyoiq7Gyz2mM6q3aVz7xxh
Oracle CmTr9FSeuhMPBLEPa3o2M71RwRnBz6LMcsfzHaW721Ak
Permission 2pC5ESkVKGx4yowGrVB21f6eXaaMRQY5cBazfqn1bAQs
Aggregator (SOL/USD) FLixyyJVzfCF4PmDG2VcFm1LUBu1aBTXox3oCWNVU88m
Permission EVerqanwRrHRvtPXDRdFHPc7VnXuyEPRr9XA5udpFA4E
Lease FC6SfAEuoB1SoZAnCqkMyyYnSfLSy8KfPUFH9SASBUzU
Job (FTX) BbNzfRQjTYiCZVfvK1qpQkkon3kP2tbvaCHfzsyjeBU3
✔ Switchboard setup complete
######## Start the Oracle ########
Run the following command in a new shell
ORACLE_KEY=CmTr9FSeuhMPBLEPa3o2M71RwRnBz6LMcsfzHaW721Ak PAYER_KEYPAIR=/Users/switchboard/.config/solana/id.json RPC_URL=https://api.devnet.solana.com docker-compose up
Select 'Y' when the docker container displays Starting listener... [y/n]: y
✔ Crank turned
######## Aggregator Result ########
Result: 30.91
✔ Aggregator succesfully updated!
```

View File

@ -0,0 +1,17 @@
version: "3.3"
services:
oracle:
image: "switchboardlabs/node:dev-v2-09-13-22"
network_mode: host
restart: always
secrets:
- PAYER_SECRETS
environment:
- LIVE=1
- CLUSTER=devnet
- HEARTBEAT_INTERVAL=15 # Seconds
- ORACLE_KEY=${ORACLE_KEY}
- RPC_URL=${RPC_URL}
secrets:
PAYER_SECRETS:
file: ${PAYER_KEYPAIR}

View File

@ -1,5 +1,6 @@
import * as anchor from "@project-serum/anchor";
import * as spl from "@solana/spl-token-v2";
import type { PublicKey } from "@solana/web3.js";
import { clusterApiUrl, Connection, Keypair } from "@solana/web3.js";
import { IOracleJob, OracleJob } from "@switchboard-xyz/common";
import {
@ -17,23 +18,54 @@ import {
} from "@switchboard-xyz/switchboard-v2";
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";
import { getKeypair, sleep, toAccountString } from "./utils";
dotenv.config();
export const toAccountString = (
label: string,
publicKey: PublicKey | string | undefined
): string => {
if (typeof publicKey === "string") {
return `${chalk.blue(label.padEnd(24, " "))} ${chalk.yellow(publicKey)}`;
}
if (!publicKey) {
return "";
}
return `${chalk.blue(label.padEnd(24, " "))} ${chalk.yellow(
publicKey.toString()
)}`;
};
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(
`failed to load authority keypair from ${keypairPath}, try providing a path to your keypair with the script 'ts-node src/main KEYPAIR_PATH'`
);
}
const keypairString = fs.readFileSync(keypairPath, "utf8");
const keypairBuffer = new Uint8Array(JSON.parse(keypairString));
const walletKeypair = Keypair.fromSecretKey(keypairBuffer);
return walletKeypair;
};
async function main() {
// get payer keypair
let authority: Keypair;
if (process.env.PAYER_KEYPAIR) {
authority = getKeypair(process.env.PAYER_KEYPAIR);
let payerKeypairPath: string;
if (process.argv.length > 2 && process.argv[2]) {
payerKeypairPath = process.argv[2];
} else if (process.env.PAYER_KEYPAIR) {
payerKeypairPath = process.env.PAYER_KEYPAIR;
} else {
// attempt to load default keypair
const homeDir = os.homedir();
authority = getKeypair(path.join(homeDir, ".config/solana/id.json"));
payerKeypairPath = path.join(os.homedir(), ".config/solana/id.json");
}
const authority = getKeypair(payerKeypairPath);
// get cluster
let cluster: "mainnet-beta" | "devnet" | "localnet";
@ -195,7 +227,7 @@ async function main() {
console.log(chalk.yellow("######## Start the Oracle ########"));
console.log(chalk.blue("Run the following command in a new shell\r\n"));
console.log(
` ORACLE_KEY=${oracleAccount.publicKey} PAYER_KEYPAIR=${authority} RPC_URL=${rpcUrl} docker-compose up\r\n`
` ORACLE_KEY=${oracleAccount.publicKey} PAYER_KEYPAIR=${payerKeypairPath} RPC_URL=${rpcUrl} docker-compose up\r\n`
);
if (
!readlineSync.keyInYN(
@ -217,7 +249,7 @@ async function main() {
const crank = await crankAccount.loadData();
const queue = await queueAccount.loadData();
const crankTurnSignature = await crankAccount.popTxn({
const crankTurnSignature = await crankAccount.pop({
payoutWallet: tokenAccount,
queuePubkey: queueAccount.publicKey,
queueAuthority: queue.authority,
@ -248,7 +280,15 @@ async function main() {
console.log(chalk.yellow("######## Aggregator Result ########"));
await sleep(5000);
try {
const result = await aggregatorAccount.getLatestValue();
let result = await aggregatorAccount.getLatestValue();
if (result === null) {
// wait a bit longer
await sleep(2500);
result = await aggregatorAccount.getLatestValue();
if (result === null) {
throw new Error(`Aggregator currently holds no value.`);
}
}
console.log(`${chalk.blue("Result:")} ${chalk.green(result)}\r\n`);
console.log(chalk.green("\u2714 Aggregator succesfully updated!"));
} catch (error: any) {

View File

@ -1,30 +0,0 @@
import type { PublicKey } from "@solana/web3.js";
import { Keypair } from "@solana/web3.js";
import chalk from "chalk";
import fs from "fs";
export const toAccountString = (
label: string,
publicKey: PublicKey | string | undefined
): string => {
if (typeof publicKey === "string") {
return `${chalk.blue(label.padEnd(24, " "))} ${chalk.yellow(publicKey)}`;
}
if (!publicKey) return "";
return `${chalk.blue(label.padEnd(24, " "))} ${chalk.yellow(
publicKey.toString()
)}`;
};
export const sleep = (ms: number): Promise<any> =>
new Promise((s) => setTimeout(s, ms));
export const getKeypair = (path: string): Keypair => {
if (!fs.existsSync(path)) {
throw new Error(`failed to load authority keypair from ${path}`);
}
const keypairString = fs.readFileSync(path, "utf8");
const keypairBuffer = new Uint8Array(JSON.parse(keypairString));
const walletKeypair = Keypair.fromSecretKey(keypairBuffer);
return walletKeypair;
};