From d58ef1ce993236eab621f83b7ba5d70406cb39e2 Mon Sep 17 00:00:00 2001 From: Conner Gallagher Date: Mon, 30 May 2022 22:39:57 -0600 Subject: [PATCH] fix broken aggregator:create commands - cleaned up transaction packing logic dep revert package.json revert --- cli/README.md | 116 ++- cli/src/BaseCommand.ts | 14 +- cli/src/commands/aggregator/add/job.ts | 3 + cli/src/commands/aggregator/create/copy.ts | 292 +++---- cli/src/commands/aggregator/create/index.ts | 348 +++++++- cli/src/commands/aggregator/create/json.ts | 15 +- cli/src/commands/job/create/index.ts | 84 ++ cli/src/commands/oracle/create.ts | 18 +- cli/src/commands/permission/set.ts | 2 + cli/src/commands/print/aggregator.ts | 1 - cli/src/commands/queue/add/crank.ts | 107 ++- cli/src/commands/queue/create.ts | 110 +-- cli/src/utils/keypair.ts | 7 +- libraries/sbv2-utils/src/transaction.ts | 5 +- libraries/ts/package.json | 10 +- libraries/ts/src/sbv2.ts | 7 +- package.json | 2 +- website/api/_tasks.md | 902 +++++++++++++------- website/docs-1/oracle/gcp/automated.mdx | 2 +- website/docs-1/oracle/gcp/environment.mdx | 2 +- website/docs-1/oracle/gcp/grafana.mdx | 2 +- website/docs-1/oracle/gcp/manual.mdx | 2 +- website/docs/oracle/operator/gcp.mdx | 8 +- yarn.lock | 30 +- 24 files changed, 1421 insertions(+), 668 deletions(-) create mode 100644 cli/src/commands/job/create/index.ts diff --git a/cli/README.md b/cli/README.md index b8bbbd3..0ef4fa1 100644 --- a/cli/README.md +++ b/cli/README.md @@ -13,7 +13,6 @@ npm install -g @switchboard-xyz/switchboardv2-cli * [`sbv2 aggregator:add:job AGGREGATORKEY`](#sbv2-aggregatoraddjob-aggregatorkey) -* [`sbv2 aggregator:create QUEUEKEY`](#sbv2-aggregatorcreate-queuekey) * [`sbv2 aggregator:create:copy AGGREGATORSOURCE`](#sbv2-aggregatorcreatecopy-aggregatorsource) * [`sbv2 aggregator:create:json DEFINITIONFILE`](#sbv2-aggregatorcreatejson-definitionfile) * [`sbv2 aggregator:lock AGGREGATORKEY`](#sbv2-aggregatorlock-aggregatorkey) @@ -36,6 +35,7 @@ npm install -g @switchboard-xyz/switchboardv2-cli * [`sbv2 crank:push CRANKKEY AGGREGATORKEY`](#sbv2-crankpush-crankkey-aggregatorkey) * [`sbv2 crank:turn CRANKKEY`](#sbv2-crankturn-crankkey) * [`sbv2 help [COMMAND]`](#sbv2-help-command) +* [`sbv2 job:create JOBDEFINITION`](#sbv2-jobcreate-jobdefinition) * [`sbv2 job:create:copy JOBSOURCE`](#sbv2-jobcreatecopy-jobsource) * [`sbv2 job:create:json DEFINITIONFILE`](#sbv2-jobcreatejson-definitionfile) * [`sbv2 job:create:template TEMPLATE ID`](#sbv2-jobcreatetemplate-template-id) @@ -125,57 +125,6 @@ EXAMPLE _See code: [src/commands/aggregator/add/job.ts](https://github.com/switchboard-xyz/switchboard-v2/blob/v0.1.26/src/commands/aggregator/add/job.ts)_ -## `sbv2 aggregator:create QUEUEKEY` - -create an aggregator account - -``` -USAGE - $ sbv2 aggregator:create QUEUEKEY - -ARGUMENTS - QUEUEKEY public key of the oracle queue account to create aggregator for - -OPTIONS - -a, --authority=authority alternate keypair that is the authority for the aggregator - -h, --help show CLI help - -j, --job=job filesystem path to job definition file - - -k, --keypair=keypair keypair that will pay for onchain transactions. defaults to new account - authority if no alternate authority provided - - -s, --silent suppress cli prompts - - -u, --rpcUrl=rpcUrl alternate RPC url - - -v, --verbose log everything - - --batchSize=batchSize number of oracles requested for each open round call - - --force skip job confirmation - - --forceReportPeriod=forceReportPeriod Number of seconds for which, even if the variance threshold is not passed, - accept new responses from oracles. - - --mainnetBeta WARNING: use mainnet-beta solana cluster - - --minJobs=minJobs number of jobs that must respond before an oracle responds - - --minOracles=minOracles number of oracles that must respond before a value is accepted on-chain - - --newQueue=newQueue public key of the new oracle queue - - --programId=programId alternative Switchboard program ID to interact with - - --updateInterval=updateInterval set an aggregator's minimum update delay - - --varianceThreshold=varianceThreshold percentage change between a previous accepted result and the next round before - an oracle reports a value on-chain. Used to conserve lease cost during low - volatility -``` - -_See code: [src/commands/aggregator/create/index.ts](https://github.com/switchboard-xyz/switchboard-v2/blob/v0.1.26/src/commands/aggregator/create/index.ts)_ - ## `sbv2 aggregator:create:copy AGGREGATORSOURCE` copy an aggregator account to a new oracle queue @@ -203,8 +152,12 @@ OPTIONS --batchSize=batchSize override source aggregator's oracleRequestBatchSize + --copyJobs create copy of job accounts instead of referincing existing job account + --crankKey=crankKey public key of the crank to push aggregator to + --enable set permissions to PERMIT_ORACLE_QUEUE_USAGE + --force skip job confirmation --forceReportPeriod=forceReportPeriod override source aggregator's forceReportPeriod @@ -219,9 +172,9 @@ OPTIONS --programId=programId alternative Switchboard program ID to interact with - --queueKey=queueKey (required) public key of the queue to create aggregator for + --queueAuthority=queueAuthority alternative keypair to use for queue authority - --sourceCluster=devnet|mainnet-beta alternative solana cluster to copy source aggregator from + --queueKey=queueKey (required) public key of the queue to create aggregator for --varianceThreshold=varianceThreshold override source aggregator's varianceThreshold @@ -959,6 +912,39 @@ OPTIONS _See code: [@oclif/plugin-help](https://github.com/oclif/plugin-help/blob/v5.1.12/src/commands/help.ts)_ +## `sbv2 job:create JOBDEFINITION` + +create a buffer relayer account + +``` +USAGE + $ sbv2 job:create JOBDEFINITION + +ARGUMENTS + JOBDEFINITION filesystem path to job definition + +OPTIONS + -a, --authority=authority alternate keypair that will be the aggregator authority + -h, --help show CLI help + + -k, --keypair=keypair keypair that will pay for onchain transactions. defaults to new account authority if no + alternate authority provided + + -n, --name=name name of the buffer account + + -s, --silent suppress cli prompts + + -u, --rpcUrl=rpcUrl alternate RPC url + + -v, --verbose log everything + + --mainnetBeta WARNING: use mainnet-beta solana cluster + + --programId=programId alternative Switchboard program ID to interact with +``` + +_See code: [src/commands/job/create/index.ts](https://github.com/switchboard-xyz/switchboard-v2/blob/v0.1.26/src/commands/job/create/index.ts)_ + ## `sbv2 job:create:copy JOBSOURCE` copy a job account @@ -2151,24 +2137,26 @@ ARGUMENTS QUEUEKEY public key of the oracle queue to create a crank on OPTIONS - -h, --help show CLI help + -h, --help show CLI help - -k, --keypair=keypair keypair that will pay for onchain transactions. defaults to new account authority if no - alternate authority provided + -k, --keypair=keypair keypair that will pay for onchain transactions. defaults to new account authority if + no alternate authority provided - -n, --name=name name of the crank for easier identification + -n, --name=name name of the crank for easier identification - -r, --maxRows=maxRows maximum number of rows a crank can support + -r, --maxRows=maxRows maximum number of rows a crank can support - -s, --silent suppress cli prompts + -s, --silent suppress cli prompts - -u, --rpcUrl=rpcUrl alternate RPC url + -u, --rpcUrl=rpcUrl alternate RPC url - -v, --verbose log everything + -v, --verbose log everything - --mainnetBeta WARNING: use mainnet-beta solana cluster + --mainnetBeta WARNING: use mainnet-beta solana cluster - --programId=programId alternative Switchboard program ID to interact with + --programId=programId alternative Switchboard program ID to interact with + + --queueAuthority=queueAuthority alternative keypair to use for queue authority EXAMPLE $ sbv2 queue:add:crank 5aYuxRdcB9GpWrEXVMBQp2R5uf94uoBiFdMEBwcmHuU4 -k ../authority-keypair.json -n crank-1 diff --git a/cli/src/BaseCommand.ts b/cli/src/BaseCommand.ts index 881134f..3f54814 100644 --- a/cli/src/BaseCommand.ts +++ b/cli/src/BaseCommand.ts @@ -1,5 +1,3 @@ -/* eslint-disable unicorn/no-process-exit */ -/* eslint-disable no-process-exit */ import Command, { flags } from "@oclif/command"; import { Input } from "@oclif/parser"; import * as anchor from "@project-serum/anchor"; @@ -146,6 +144,7 @@ abstract class BaseCommand extends Command { if (this.verbose) { this.logger.log("verbose logging enabled"); } + this.logger.debug(chalk.underline(chalk.blue("## Config".padEnd(16)))); this.logger.debug( `${chalk.yellow("cluster:")} ${chalk.blue(this.cluster)}` @@ -166,10 +165,12 @@ abstract class BaseCommand extends Command { if (message) { logger.info(chalk.red(`${FAILED_ICON}${message}`)); } + if (error.message) { const messageLines = error.message.split("\n"); logger.error(messageLines[0]); } + if (this.verbose) { console.error(error); } @@ -209,7 +210,6 @@ abstract class BaseCommand extends Command { loadConfig(): void { const configPath = path.join(this.config.configDir, "config.json"); if (fs.existsSync(configPath)) { - // eslint-disable-next-line @typescript-eslint/no-unused-vars const userConfig: CliConfig = JSON.parse( fs.readFileSync(configPath, "utf-8") ); @@ -240,6 +240,7 @@ abstract class BaseCommand extends Command { this.saveConfig(newConfig); break; } + case "mainnet-rpc": { const newConfig = { ...this.cliConfig, @@ -251,6 +252,7 @@ abstract class BaseCommand extends Command { this.saveConfig(newConfig); break; } + default: { this.logger.warn("not implemented yet"); } @@ -274,15 +276,17 @@ abstract class BaseCommand extends Command { // Converts a string to a tokenAmount // If a decimal is found, it will be normalized using 9 decimal places getTokenAmount(value: string, decimals = 9): anchor.BN { - if (isNaN(Number(value))) { - throw new Error("tokenAmount must be an integer or decimal"); + if (Number.isNaN(Number(value))) { + throw new TypeError("tokenAmount must be an integer or decimal"); } + if (value.split(".").length > 1) { const float = new Big(value); const scale = BigUtils.safePow(new Big(10), decimals); const tokenAmount = BigUtils.safeMul(float, scale); return new anchor.BN(tokenAmount.toFixed(0)); } + return new anchor.BN(value); } } diff --git a/cli/src/commands/aggregator/add/job.ts b/cli/src/commands/aggregator/add/job.ts index e3d2a63..b65182f 100644 --- a/cli/src/commands/aggregator/add/job.ts +++ b/cli/src/commands/aggregator/add/job.ts @@ -81,12 +81,14 @@ export default class AggregatorAddJob extends BaseCommand { if (flags.jobDefinition) { this.jobDefinition = JSON.parse(flags.jobDefinition, pubKeyReviver); } + if (flags.jobKey) { this.jobAccount = new JobAccount({ program: this.program, publicKey: new PublicKey(flags.jobKey), }); } + if (!this.jobDefinition && !this.jobAccount) { throw new Error("need to provide --jobDefinition or --jobKey"); } @@ -95,6 +97,7 @@ export default class AggregatorAddJob extends BaseCommand { if (fs.existsSync(flags.outputFile) && !flags.force) { throw new OutputFileExistsNoForce(flags.outputFile); } + this.outputFile = flags.outputFile; } } diff --git a/cli/src/commands/aggregator/create/copy.ts b/cli/src/commands/aggregator/create/copy.ts index 30ba9cc..cdc3f7e 100644 --- a/cli/src/commands/aggregator/create/copy.ts +++ b/cli/src/commands/aggregator/create/copy.ts @@ -1,3 +1,4 @@ +/* eslint-disable unicorn/no-array-push-push */ import { flags } from "@oclif/command"; import * as anchor from "@project-serum/anchor"; import * as spl from "@solana/spl-token"; @@ -9,6 +10,7 @@ import { TransactionInstruction, } from "@solana/web3.js"; import { + packAndSend, prettyPrintAggregator, promiseWithTimeout, } from "@switchboard-xyz/sbv2-utils"; @@ -17,7 +19,6 @@ import { CrankAccount, JobAccount, LeaseAccount, - loadSwitchboardProgram, OracleJob, OracleQueueAccount, PermissionAccount, @@ -28,8 +29,9 @@ import { import Big from "big.js"; import BaseCommand from "../../../BaseCommand"; import { verifyProgramHasPayer } from "../../../utils"; -import { packAndSend } from "../../../utils/transaction"; +// TODO: Fix command so it accepts a feed authority flag +// TODO: Add flag that skips job creation export default class AggregatorCreateCopy extends BaseCommand { static description = "copy an aggregator account to a new oracle queue"; @@ -70,11 +72,21 @@ export default class AggregatorCreateCopy extends BaseCommand { description: "public key of the crank to push aggregator to", required: false, }), - sourceCluster: flags.string({ - description: "alternative solana cluster to copy source aggregator from", - required: false, - options: ["devnet", "mainnet-beta"], + enable: flags.boolean({ + description: "set permissions to PERMIT_ORACLE_QUEUE_USAGE", }), + queueAuthority: flags.string({ + description: "alternative keypair to use for queue authority", + }), + copyJobs: flags.boolean({ + description: + "create copy of job accounts instead of referincing existing job account", + }), + // sourceCluster: flags.string({ + // description: "alternative solana cluster to copy source aggregator from", + // required: false, + // options: ["devnet", "mainnet-beta"], + // }), }; static args = [ @@ -97,20 +109,24 @@ export default class AggregatorCreateCopy extends BaseCommand { const { args, flags } = this.parse(AggregatorCreateCopy); const payerKeypair = programWallet(this.program); + const feedAuthority = await this.loadAuthority(flags.authority); + const queueAuthority = await this.loadAuthority(flags.queueAuthority); - const sourceProgram = !flags.sourceCluster - ? this.program - : flags.sourceCluster === "devnet" || - flags.sourceCluster === "mainnet-beta" - ? await loadSwitchboardProgram( - flags.sourceCluster, - undefined, - payerKeypair - ) - : undefined; + // const sourceProgram = !flags.sourceCluster + // ? this.program + // : flags.sourceCluster === "devnet" || + // flags.sourceCluster === "mainnet-beta" + // ? await loadSwitchboardProgram( + // flags.sourceCluster, + // undefined, + // payerKeypair + // ) + // : undefined; + const sourceProgram = this.program; if (sourceProgram === undefined) { - throw new Error(`Invalid sourceAggregatorCluster ${flags.sourceCluster}`); + throw new Error(`Invalid sourceAggregatorCluster`); } + const sourceAggregatorAccount = new AggregatorAccount({ program: sourceProgram, publicKey: args.aggregatorSource, @@ -126,14 +142,6 @@ export default class AggregatorCreateCopy extends BaseCommand { return new JobAccount({ program: sourceProgram, publicKey: publicKey }); }); - const sourceJobs = await Promise.all( - sourceJobAccounts.map(async (jobAccount) => { - const data = await jobAccount.loadData(); - const job = OracleJob.decodeDelimited(data.data); - return { job, data }; - }) - ); - const [programStateAccount, stateBump] = ProgramStateAccount.fromSeed( this.program ); @@ -153,67 +161,17 @@ export default class AggregatorCreateCopy extends BaseCommand { | TransactionInstruction | TransactionInstruction[] )[] = []; - const createAccountSigners: Keypair[] = [payerKeypair]; - - const jobAccounts = await Promise.all( - sourceJobs.map(async ({ job, data }) => { - const jobKeypair = Keypair.generate(); - createAccountSigners.push(jobKeypair); - - const jobData = Buffer.from( - OracleJob.encodeDelimited( - OracleJob.create({ - tasks: job.tasks, - }) - ).finish() - ); - const size = - 280 + jobData.length + (data.variables?.join("")?.length ?? 0); - - createAccountInstructions.push([ - SystemProgram.createAccount({ - fromPubkey: payerKeypair.publicKey, - newAccountPubkey: jobKeypair.publicKey, - space: size, - lamports: - await this.program.provider.connection.getMinimumBalanceForRentExemption( - size - ), - programId: this.program.programId, - }), - await this.program.methods - .jobInit({ - name: Buffer.from(data.name), - data: jobData, - variables: - data.variables?.map((item) => Buffer.from("")) ?? - new Array(), - authorWallet: payerKeypair.publicKey, - stateBump, - }) - .accounts({ - job: jobKeypair.publicKey, - authorWallet: tokenWallet, - authority: payerKeypair.publicKey, - programState: programStateAccount.publicKey, - }) - // .signers([jobKeypair]) - .instruction(), - ]); - - return new JobAccount({ - program: this.program, - publicKey: jobKeypair.publicKey, - }); - }) - ); + const createAccountSigners: Keypair[] = [ + payerKeypair, + feedAuthority, + queueAuthority, + ]; + // Create Aggregator & Permissions const aggregatorKeypair = Keypair.generate(); this.logger.debug(`Aggregator: ${aggregatorKeypair.publicKey}`); createAccountSigners.push(aggregatorKeypair); const aggregatorSize = this.program.account.aggregatorAccountData.size; - const permissionAccountSize = - this.program.account.permissionAccountData.size; const [permissionAccount, permissionBump] = PermissionAccount.fromSeed( this.program, queue.authority, @@ -240,23 +198,6 @@ export default class AggregatorCreateCopy extends BaseCommand { true ); - const jobPubkeys: Array = []; - const jobWallets: Array = []; - const walletBumps: Array = []; - for (let idx in jobAccounts) { - const [jobWallet, bump] = anchor.utils.publicKey.findProgramAddressSync( - [ - payerKeypair.publicKey.toBuffer(), - spl.TOKEN_PROGRAM_ID.toBuffer(), - tokenMint.publicKey.toBuffer(), - ], - spl.ASSOCIATED_TOKEN_PROGRAM_ID - ); - jobPubkeys.push(jobAccounts[idx].publicKey); - jobWallets.push(jobWallet); - walletBumps.push(bump); - } - createAccountInstructions.push( [ // allocate aggregator space @@ -291,7 +232,7 @@ export default class AggregatorCreateCopy extends BaseCommand { }) .accounts({ aggregator: aggregatorKeypair.publicKey, - authority: payerKeypair.publicKey, + authority: feedAuthority.publicKey, queue: queueAccount.publicKey, authorWallet: tokenWallet, programState: programStateAccount.publicKey, @@ -309,18 +250,23 @@ export default class AggregatorCreateCopy extends BaseCommand { systemProgram: SystemProgram.programId, }) .instruction(), - payerKeypair.publicKey.equals(queue.authority) + flags.enable && queueAuthority.publicKey.equals(queue.authority) ? await this.program.methods .permissionSet({ - permission: { permitOracleQueueUsage: null }, + permission: { permitOracleQueueUsage: undefined }, enable: true, }) .accounts({ permission: permissionAccount.publicKey, - authority: queue.authority, + authority: queueAuthority.publicKey, }) .instruction() : undefined, + ].filter((item) => item) + ); + + createAccountInstructions.push( + [ spl.Token.createAssociatedTokenAccountInstruction( spl.ASSOCIATED_TOKEN_PROGRAM_ID, spl.TOKEN_PROGRAM_ID, @@ -334,7 +280,7 @@ export default class AggregatorCreateCopy extends BaseCommand { loadAmount: new anchor.BN(0), stateBump, leaseBump, - withdrawAuthority: payerKeypair.publicKey, + withdrawAuthority: feedAuthority.publicKey, walletBumps: Buffer.from([]), }) .accounts({ @@ -364,7 +310,7 @@ export default class AggregatorCreateCopy extends BaseCommand { }) .accounts({ crank: new PublicKey(flags.crankKey), - aggregator: aggregatorAccount.publicKey, + aggregator: aggregatorKeypair.publicKey, oracleQueue: queueAccount.publicKey, queueAuthority: queue.authority, permission: permissionAccount.publicKey, @@ -383,54 +329,116 @@ export default class AggregatorCreateCopy extends BaseCommand { ].filter((item) => item) ); - const finalInstructions: ( - | TransactionInstruction - | TransactionInstruction[] - )[] = []; + const createJobIxns = flags.copyJobs + ? // create job account copies + await Promise.all( + sourceJobAccounts.map(async (jobAccount) => { + const jobKeypair = Keypair.generate(); + createAccountSigners.push(jobKeypair); // add signers - finalInstructions.push( - ...(await Promise.all( - jobAccounts.map(async (jobAccount) => { - return this.program.methods - .aggregatorAddJob({ - weight: 1, - }) - .accounts({ - aggregator: aggregatorKeypair.publicKey, - authority: payerKeypair.publicKey, - job: jobAccount.publicKey, - }) - .instruction(); - }) - )) - ); + const job = await jobAccount.loadData(); + const data = await jobAccount.loadData(); + const jobData = Buffer.from( + OracleJob.encodeDelimited( + OracleJob.create({ + tasks: job.tasks, + }) + ).finish() + ); + + const size = + 280 + jobData.length + (data.variables?.join("")?.length ?? 0); + + return [ + SystemProgram.createAccount({ + fromPubkey: payerKeypair.publicKey, + newAccountPubkey: jobKeypair.publicKey, + space: size, + lamports: + await this.program.provider.connection.getMinimumBalanceForRentExemption( + size + ), + programId: this.program.programId, + }), + await this.program.methods + .jobInit({ + name: Buffer.from(data.name), + data: jobData, + variables: + data.variables?.map((item) => Buffer.from("")) ?? + new Array(), + authorWallet: payerKeypair.publicKey, + stateBump, + }) + .accounts({ + job: jobKeypair.publicKey, + authorWallet: tokenWallet, + authority: feedAuthority.publicKey, + programState: programStateAccount.publicKey, + }) + .signers([feedAuthority]) + .instruction(), + await this.program.methods + .aggregatorAddJob({ + weight: 1, + }) + .accounts({ + aggregator: aggregatorKeypair.publicKey, + authority: feedAuthority.publicKey, + job: jobAccount.publicKey, + }) + .instruction(), + ]; + }) + ) + : // add job by pubkey + await Promise.all( + sourceJobAccounts.map(async (jobAccount) => { + const addJobIxn = await this.program.methods + .aggregatorAddJob({ + weight: 1, + }) + .accounts({ + aggregator: aggregatorKeypair.publicKey, + authority: feedAuthority.publicKey, + job: jobAccount.publicKey, + }) + .instruction(); + return addJobIxn; + }) + ); const createAccountSignatures = packAndSend( this.program, - createAccountInstructions, - finalInstructions, + [createAccountInstructions, createJobIxns], createAccountSigners, payerKeypair.publicKey - ); + ).catch((error) => { + throw error; + }); let aggInitWs: number; - const aggInitPromise = new Promise((resolve: (result: boolean) => void) => { + const aggInitPromise = new Promise((resolve: (result: any) => void) => { aggInitWs = this.program.provider.connection.onAccountChange( - aggregatorAccount.publicKey, + aggregatorKeypair.publicKey, (accountInfo: AccountInfo, slot) => { - resolve(true); + const aggData = new anchor.BorshAccountsCoder( + this.program.idl + ).decode("AggregatorAccountData", accountInfo.data); + resolve(aggData); } ); }); - const awaitResult = await promiseWithTimeout( - 22_000, - aggInitPromise - ).finally(() => { - try { - this.program.provider.connection.removeAccountChangeListener(aggInitWs); - } catch {} - }); + const result = await promiseWithTimeout(45_000, aggInitPromise).finally( + () => { + try { + this.program.provider.connection.removeAccountChangeListener( + aggInitWs + ); + } catch {} + } + ); if (this.silent) { console.log(aggregatorAccount.publicKey.toString()); @@ -438,13 +446,7 @@ export default class AggregatorCreateCopy extends BaseCommand { } this.logger.info( - await prettyPrintAggregator( - aggregatorAccount, - undefined, - true, - true, - true - ) + await prettyPrintAggregator(aggregatorAccount, result, true, true, true) ); } diff --git a/cli/src/commands/aggregator/create/index.ts b/cli/src/commands/aggregator/create/index.ts index c46851c..2c98b31 100644 --- a/cli/src/commands/aggregator/create/index.ts +++ b/cli/src/commands/aggregator/create/index.ts @@ -1,16 +1,41 @@ import { flags } from "@oclif/command"; -import { PublicKey } from "@solana/web3.js"; +import * as anchor from "@project-serum/anchor"; +import * as spl from "@solana/spl-token"; import { + AccountInfo, + Keypair, + PublicKey, + SystemProgram, + TransactionInstruction, +} from "@solana/web3.js"; +import { + packAndSend, + prettyPrintAggregator, + promiseWithTimeout, + sleep, +} from "@switchboard-xyz/sbv2-utils"; +import { + AggregatorAccount, + CrankAccount, + JobAccount, + LeaseAccount, OracleJob, OracleQueueAccount, + PermissionAccount, + ProgramStateAccount, programWallet, + SwitchboardDecimal, } from "@switchboard-xyz/switchboard-v2"; +import Big from "big.js"; import fs from "fs"; import path from "path"; import BaseCommand from "../../../BaseCommand"; import { verifyProgramHasPayer } from "../../../utils"; +// TODO: Finish export default class AggregatorCreate extends BaseCommand { + static hidden = true; + static description = "create an aggregator account"; static flags = { @@ -20,7 +45,20 @@ export default class AggregatorCreate extends BaseCommand { char: "a", description: "alternate keypair that is the authority for the aggregator", }), - forceReportPeriod: flags.string({ + crankKey: flags.string({ + description: "public key of the crank to join", + }), + enable: flags.boolean({ + description: "set permissions to PERMIT_ORACLE_QUEUE_USAGE", + }), + queueAuthority: flags.string({ + description: "alternative keypair to use for queue authority", + }), + name: flags.string({ + char: "n", + description: "name of the aggregator", + }), + forceReportPeriod: flags.integer({ description: "Number of seconds for which, even if the variance threshold is not passed, accept new responses from oracles.", }), @@ -34,9 +72,6 @@ export default class AggregatorCreate extends BaseCommand { description: "number of oracles that must respond before a value is accepted on-chain", }), - newQueue: flags.string({ - description: "public key of the new oracle queue", - }), updateInterval: flags.string({ description: "set an aggregator's minimum update delay", }), @@ -66,38 +101,307 @@ export default class AggregatorCreate extends BaseCommand { const { args, flags } = this.parse(AggregatorCreate); const payerKeypair = programWallet(this.program); + const feedAuthority = await this.loadAuthority(flags.authority); + const queueAuthority = await this.loadAuthority(flags.queueAuthority); + const [programStateAccount, stateBump] = ProgramStateAccount.fromSeed( + this.program + ); const queueAccount = new OracleQueueAccount({ program: this.program, publicKey: args.queueKey, }); + const queue = await queueAccount.loadData(); const switchTokenMint = await queueAccount.loadMint(); - const payerTokenWallet = ( + const tokenWallet = ( await switchTokenMint.getOrCreateAssociatedAccountInfo( payerKeypair.publicKey ) ).address; - const jobs = flags.job.map((jobDefinition) => { - const jobJson = JSON.parse( - fs.readFileSync( - jobDefinition.startsWith("/") - ? jobDefinition - : path.join(process.cwd(), jobDefinition), - "utf8" + const createAccountInstructions: ( + | TransactionInstruction + | TransactionInstruction[] + )[] = []; + const createAccountSigners: Keypair[] = [ + payerKeypair, + feedAuthority, + queueAuthority, + ]; + + // Create Job Accounts + const createJobs = flags.job + ? await Promise.all( + flags.job.map( + async ( + jobDefinition + ): Promise<[TransactionInstruction, Keypair]> => { + const jobJson = JSON.parse( + fs.readFileSync( + jobDefinition.startsWith("/") + ? jobDefinition + : path.join(process.cwd(), jobDefinition), + "utf8" + ) + ); + if (!jobJson || !("tasks" in jobJson)) { + throw new Error("job definition missing tasks"); + } + + const jobKeypair = anchor.web3.Keypair.generate(); + + const data = Buffer.from( + OracleJob.encodeDelimited( + OracleJob.create({ + tasks: jobJson.tasks, + }) + ).finish() + ); + + const createJobIxn = await this.program.methods + .jobInit({ + name: Buffer.from("").slice(0, 32), + data: data, + variables: new Array(), + authorWallet: payerKeypair.publicKey, + stateBump, + }) + .accounts({ + job: jobKeypair.publicKey, + authorWallet: tokenWallet, + authority: feedAuthority.publicKey, + programState: programStateAccount.publicKey, + }) + .signers([feedAuthority]) + .instruction(); + + return [createJobIxn, jobKeypair]; + } + ) ) - ); - if (!jobJson || !("tasks" in jobJson)) { - throw new Error("job definition missing tasks"); - } - const data = Buffer.from( - OracleJob.encodeDelimited( - OracleJob.create({ - tasks: jobJson.tasks, + : []; + createAccountInstructions.push(createJobs.map((index) => index[0])); + createAccountSigners.push(...createJobs.map((index) => index[1])); + const jobAccounts = createJobs + .map((index) => index[1]) + .map((jobKeypair) => { + return new JobAccount({ + program: this.program, + publicKey: jobKeypair.publicKey, + keypair: jobKeypair, + }); + }); + + // Create Aggregator Account + const aggregatorKeypair = anchor.web3.Keypair.generate(); + const aggregatorSize = this.program.account.aggregatorAccountData.size; + const aggregatorAccount = new AggregatorAccount({ + program: this.program, + publicKey: aggregatorKeypair.publicKey, + }); + const [permissionAccount, permissionBump] = PermissionAccount.fromSeed( + this.program, + queue.authority, + queueAccount.publicKey, + aggregatorKeypair.publicKey + ); + createAccountInstructions.push( + [ + SystemProgram.createAccount({ + fromPubkey: payerKeypair.publicKey, + newAccountPubkey: aggregatorKeypair.publicKey, + space: aggregatorSize, + lamports: + await this.program.provider.connection.getMinimumBalanceForRentExemption( + aggregatorSize + ), + programId: this.program.programId, + }), + // create aggregator + await this.program.methods + .aggregatorInit({ + name: Buffer.from(flags.name ?? "").slice(0, 32), + metadata: Buffer.from("").slice(0, 64), + batchSize: flags.batchSize ?? 1, + minOracleResults: flags.minOracles ?? 1, + minJobResults: flags.minJobs ?? 1, + minUpdateDelaySeconds: flags.updateInterval ?? 30, + varianceThreshold: flags.varianceThreshold + ? SwitchboardDecimal.fromBig(new Big(flags.varianceThreshold)) + : SwitchboardDecimal.fromBig(new Big(0)), + forceReportPeriod: flags.forceReportPeriod ?? 0, + stateBump, }) - ).finish() + .accounts({ + aggregator: aggregatorKeypair.publicKey, + authority: feedAuthority.publicKey, + queue: queueAccount.publicKey, + authorWallet: tokenWallet, + programState: programStateAccount.publicKey, + }) + .instruction(), + await this.program.methods + .permissionInit({}) + .accounts({ + permission: permissionAccount.publicKey, + authority: queue.authority, + granter: queueAccount.publicKey, + grantee: aggregatorKeypair.publicKey, + payer: payerKeypair.publicKey, + systemProgram: SystemProgram.programId, + }) + .instruction(), + flags.enable && queueAuthority.publicKey.equals(queue.authority) + ? await this.program.methods + .permissionSet({ + permission: { permitOracleQueueUsage: undefined }, + enable: true, + }) + .accounts({ + permission: permissionAccount.publicKey, + authority: queueAuthority.publicKey, + }) + .instruction() + : undefined, + ].filter((item) => item) + ); + createAccountSigners.push(aggregatorKeypair); + + // Create Lease Account + // Add to crank if applicable + const [leaseAccount, leaseBump] = LeaseAccount.fromSeed( + this.program, + queueAccount, + aggregatorAccount + ); + const leaseEscrow = await spl.Token.getAssociatedTokenAddress( + spl.ASSOCIATED_TOKEN_PROGRAM_ID, + spl.TOKEN_PROGRAM_ID, + switchTokenMint.publicKey, + leaseAccount.publicKey, + true + ); + createAccountInstructions.push( + [ + spl.Token.createAssociatedTokenAccountInstruction( + spl.ASSOCIATED_TOKEN_PROGRAM_ID, + spl.TOKEN_PROGRAM_ID, + switchTokenMint.publicKey, + leaseEscrow, + leaseAccount.publicKey, + payerKeypair.publicKey + ), + await this.program.methods + .leaseInit({ + loadAmount: new anchor.BN(0), + stateBump, + leaseBump, + withdrawAuthority: feedAuthority.publicKey, + walletBumps: Buffer.from([]), + }) + .accounts({ + programState: programStateAccount.publicKey, + lease: leaseAccount.publicKey, + queue: queueAccount.publicKey, + aggregator: aggregatorAccount.publicKey, + systemProgram: SystemProgram.programId, + funder: tokenWallet, + payer: payerKeypair.publicKey, + tokenProgram: spl.TOKEN_PROGRAM_ID, + escrow: leaseEscrow, + owner: payerKeypair.publicKey, + mint: switchTokenMint.publicKey, + }) + // .remainingAccounts( + // jobPubkeys.concat(jobWallets).map((pubkey: PublicKey) => { + // return { isSigner: false, isWritable: true, pubkey }; + // }) + // ) + .instruction(), + flags.crankKey + ? await this.program.methods + .crankPush({ + stateBump, + permissionBump, + }) + .accounts({ + crank: new PublicKey(flags.crankKey), + aggregator: aggregatorKeypair.publicKey, + oracleQueue: queueAccount.publicKey, + queueAuthority: queue.authority, + permission: permissionAccount.publicKey, + lease: leaseAccount.publicKey, + escrow: leaseEscrow, + programState: programStateAccount.publicKey, + dataBuffer: ( + await new CrankAccount({ + program: this.program, + publicKey: new PublicKey(flags.crankKey), + }).loadData() + ).dataBuffer, + }) + .instruction() + : undefined, + ].filter((item) => item) + ); + + // Add Job Accounts + const addJobIxns = await Promise.all( + jobAccounts.map(async (job) => { + return this.program.methods + .aggregatorAddJob({ + weight: 1, + }) + .accounts({ + aggregator: aggregatorKeypair.publicKey, + authority: feedAuthority.publicKey, + job: job.publicKey, + }) + .instruction(); + }) + ); + + const createAccountSignatures = packAndSend( + this.program, + [createAccountInstructions, addJobIxns], + createAccountSigners, + payerKeypair.publicKey + ).catch((error) => { + throw error; + }); + + let aggInitWs: number; + const aggInitPromise = new Promise((resolve: (result: any) => void) => { + aggInitWs = this.program.provider.connection.onAccountChange( + aggregatorKeypair.publicKey, + (accountInfo: AccountInfo, slot) => { + const aggData = new anchor.BorshAccountsCoder( + this.program.idl + ).decode("AggregatorAccountData", accountInfo.data); + resolve(aggData); + } ); }); + + const result = await promiseWithTimeout(45_000, aggInitPromise).finally( + () => { + try { + this.program.provider.connection.removeAccountChangeListener( + aggInitWs + ); + } catch {} + } + ); + + if (this.silent) { + console.log(aggregatorAccount.publicKey.toString()); + } + + await sleep(2500); + + this.logger.info( + await prettyPrintAggregator(aggregatorAccount, result, true, true, true) + ); } async catch(error) { diff --git a/cli/src/commands/aggregator/create/json.ts b/cli/src/commands/aggregator/create/json.ts index 94b729f..46043a8 100644 --- a/cli/src/commands/aggregator/create/json.ts +++ b/cli/src/commands/aggregator/create/json.ts @@ -70,17 +70,16 @@ export default class JsonCreateAggregator extends BaseCommand { if (!fs.existsSync(definitionFile)) { throw new Error("input file does not exist"); } - let aggregatorDefinition: fromAggregatorJSON = JSON.parse( + + const aggregatorDefinition: fromAggregatorJSON = JSON.parse( fs.readFileSync(definitionFile, "utf-8"), pubKeyReviver ); - if (flags.outputFile) { - if (fs.existsSync(flags.outputFile) && !flags.force) { - throw new Error( - "output file exists. Run the command with '--force' to overwrite it" - ); - } + if (flags.outputFile && fs.existsSync(flags.outputFile) && !flags.force) { + throw new Error( + "output file exists. Run the command with '--force' to overwrite it" + ); } let authority = programWallet(this.program); @@ -91,6 +90,7 @@ export default class JsonCreateAggregator extends BaseCommand { if (!aggregatorDefinition.queuePublicKey && !flags.queueKey) { throw new Error("you must provide a --queueKey to create aggregator for"); } + const queueAccount = new OracleQueueAccount({ program: this.program, publicKey: aggregatorDefinition.queuePublicKey @@ -173,6 +173,7 @@ export default class JsonCreateAggregator extends BaseCommand { jobs.push(account); } } + for await (const job of jobs) { await aggregatorAccount.addJob(job, authority); } diff --git a/cli/src/commands/job/create/index.ts b/cli/src/commands/job/create/index.ts new file mode 100644 index 0000000..d9b482e --- /dev/null +++ b/cli/src/commands/job/create/index.ts @@ -0,0 +1,84 @@ +import { flags } from "@oclif/command"; +import { prettyPrintJob } from "@switchboard-xyz/sbv2-utils"; +import { + JobAccount, + OracleJob, + programWallet, +} from "@switchboard-xyz/switchboard-v2"; +import fs from "fs"; +import path from "path"; +import BaseCommand from "../../../BaseCommand"; +import { verifyProgramHasPayer } from "../../../utils"; + +export default class JobCreate extends BaseCommand { + static description = "create a buffer relayer account"; + + static flags = { + ...BaseCommand.flags, + authority: flags.string({ + char: "a", + description: "alternate keypair that will be the aggregator authority", + }), + name: flags.string({ + char: "n", + description: "name of the buffer account", + }), + }; + + static args = [ + { + name: "jobDefinition", + required: true, + description: "filesystem path to job definition", + }, + ]; + + async run() { + verifyProgramHasPayer(this.program); + const { args, flags } = this.parse(JobCreate); + const payerKeypair = programWallet(this.program); + const authority = await this.loadAuthority(flags.authority); + + const jobDefinitionPath = args.jobDefinition.startsWith("/") + ? args.jobDefinition + : path.join(process.cwd(), args.jobDefinition); + if (!fs.existsSync(jobDefinitionPath)) { + throw new Error(`jobDefinitionPath does not exist, ${jobDefinitionPath}`); + } + + const oracleJob = OracleJob.create( + JSON.parse(fs.readFileSync(jobDefinitionPath, "utf-8")) + ); + if (!("tasks" in oracleJob)) { + throw new Error("Must provide tasks in job definition"); + } + + const data = Buffer.from( + OracleJob.encodeDelimited( + OracleJob.create({ + tasks: oracleJob.tasks, + }) + ).finish() + ); + console.log(`DATA: [${data.join(",")}]`); + const jobAccount = await JobAccount.create(this.program, { + authority: authority.publicKey, + name: flags.name ? Buffer.from(flags.name) : Buffer.from(""), + data, + }); + + if (this.silent) { + this.logger.info(jobAccount.publicKey.toString()); + return; + } + + const job = await jobAccount.loadData(); + this.logger.info(await prettyPrintJob(jobAccount, job)); + + console.log(`DATA: [${job.data.join(",")}]`); + } + + async catch(error) { + super.catch(error, "failed to create buffer relayer account"); + } +} diff --git a/cli/src/commands/oracle/create.ts b/cli/src/commands/oracle/create.ts index 8765f50..8aee833 100644 --- a/cli/src/commands/oracle/create.ts +++ b/cli/src/commands/oracle/create.ts @@ -64,11 +64,19 @@ export default class OracleCreate extends BaseCommand { const { args, flags } = this.parse(OracleCreate); verifyProgramHasPayer(this.program); const payerKeypair = programWallet(this.program); + const signers: Keypair[] = [payerKeypair]; const authorityKeypair = await this.loadAuthority(flags.authority); + // if (!payerKeypair.publicKey.equals(authorityKeypair.publicKey)) { + // signers.push(authorityKeypair); + // } + const queueAuthority: Keypair = flags.queueAuthority ? await loadKeypair(flags.queueAuthority) : payerKeypair; + if (!payerKeypair.publicKey.equals(queueAuthority.publicKey)) { + signers.push(queueAuthority); + } const queueAccount = new OracleQueueAccount({ program: this.program, @@ -82,6 +90,7 @@ export default class OracleCreate extends BaseCommand { ); const tokenWalletKeypair = anchor.web3.Keypair.generate(); + signers.push(tokenWalletKeypair); const [oracleAccount, oracleBump] = OracleAccount.fromSeed( this.program, queueAccount, @@ -92,7 +101,7 @@ export default class OracleCreate extends BaseCommand { const [permissionAccount, permissionBump] = PermissionAccount.fromSeed( this.program, - authorityKeypair.publicKey, + queue.authority, queueAccount.publicKey, oracleAccount.publicKey ); @@ -137,7 +146,7 @@ export default class OracleCreate extends BaseCommand { .permissionInit({}) .accounts({ permission: permissionAccount.publicKey, - authority: authorityKeypair.publicKey, + authority: queue.authority, granter: queueAccount.publicKey, grantee: oracleAccount.publicKey, payer: payerKeypair.publicKey, @@ -152,9 +161,11 @@ export default class OracleCreate extends BaseCommand { `Invalid queue authority, received ${queueAuthority.publicKey}, expected ${queue.authority}` ); } + createOracleTxn.add( await this.program.methods .permissionSet({ + // eslint-disable-next-line unicorn/no-null permission: { permitOracleHeartbeat: null }, enable: true, }) @@ -168,7 +179,7 @@ export default class OracleCreate extends BaseCommand { const signature = await this.program.provider.sendAndConfirm( createOracleTxn, - [payerKeypair, authorityKeypair, tokenWalletKeypair] + signers ); const oracleData = await oracleAccount.loadData(); @@ -176,6 +187,7 @@ export default class OracleCreate extends BaseCommand { console.log(oracleAccount.publicKey.toString()); return; } + this.logger.log( `${chalk.green(`${CHECK_ICON}Oracle account created successfully`)}` ); diff --git a/cli/src/commands/permission/set.ts b/cli/src/commands/permission/set.ts index 4a319f1..7805fa4 100644 --- a/cli/src/commands/permission/set.ts +++ b/cli/src/commands/permission/set.ts @@ -57,12 +57,14 @@ export default class PermissionSet extends BaseCommand { authorityKey = data.authority; break; } + default: { throw new Error( `Granter should be a OracleQueueAccount, received ${granterAccountType}` ); } } + const authority = await this.loadAuthority(flags.authority, authorityKey); // check and load grantees account type, and assign permissions based on type diff --git a/cli/src/commands/print/aggregator.ts b/cli/src/commands/print/aggregator.ts index 0415bb9..a162bce 100644 --- a/cli/src/commands/print/aggregator.ts +++ b/cli/src/commands/print/aggregator.ts @@ -1,4 +1,3 @@ -/* eslint-disable unicorn/import-style */ import { flags } from "@oclif/command"; import { PublicKey } from "@solana/web3.js"; import { diff --git a/cli/src/commands/queue/add/crank.ts b/cli/src/commands/queue/add/crank.ts index b36b6e3..f85ddef 100644 --- a/cli/src/commands/queue/add/crank.ts +++ b/cli/src/commands/queue/add/crank.ts @@ -1,19 +1,20 @@ import { flags } from "@oclif/command"; -import { Keypair, PublicKey } from "@solana/web3.js"; -import { OracleQueueAccount } from "@switchboard-xyz/switchboard-v2"; +import * as anchor from "@project-serum/anchor"; +import { PublicKey, SystemProgram } from "@solana/web3.js"; +import { + prettyPrintCrank, + verifyProgramHasPayer, +} from "@switchboard-xyz/sbv2-utils"; +import { + CrankAccount, + OracleQueueAccount, + programWallet, +} from "@switchboard-xyz/switchboard-v2"; import chalk from "chalk"; -import { fromCrankJSON } from "../../../accounts"; -import { CrankClass } from "../../../accounts/crank/crank"; import BaseCommand from "../../../BaseCommand"; import { CHECK_ICON } from "../../../utils"; export default class QueueAddCrank extends BaseCommand { - queueAccount: OracleQueueAccount; - - crankDefinition: fromCrankJSON; - - queueAuthority: Keypair | undefined = undefined; - static description = "add a crank to an existing oracle queue"; static flags = { @@ -26,10 +27,9 @@ export default class QueueAddCrank extends BaseCommand { char: "r", description: "maximum number of rows a crank can support", }), - // authority: flags.string({ - // char: "a", - // description: "alternate keypair that is the authority for oracle queue", - // }), + queueAuthority: flags.string({ + description: "alternative keypair to use for queue authority", + }), }; static args = [ @@ -46,41 +46,72 @@ export default class QueueAddCrank extends BaseCommand { // "$ sbv2 queue:add:crank 5aYuxRdcB9GpWrEXVMBQp2R5uf94uoBiFdMEBwcmHuU4 -k ../payer-keypair.json -a ../authority-keypair.json", ]; - async init() { - await super.init(); + async run() { const { args, flags } = this.parse(QueueAddCrank); - - this.queueAccount = new OracleQueueAccount({ - program: this.program, - publicKey: args.queueKey, - }); - - // TODO: Not implemented yet - // if (flags.authority) { - // this.queueAuthority = await loadKeypair(flags.authority); - // } + verifyProgramHasPayer(this.program); + const payerKeypair = programWallet(this.program); if (flags.maxRows < 0) { throw new Error("max rows must be a positive number"); } - this.crankDefinition = { - name: flags.name || "", - maxRows: flags.maxRows || undefined, - }; - } + const maxRows = flags.maxRows; - async run() { - const crank = await CrankClass.fromJSON( - this.context, - this.crankDefinition, - this.queueAccount + const queueAccount = new OracleQueueAccount({ + program: this.program, + publicKey: args.queueKey, + }); + const queue = await queueAccount.loadData(); + + const queueAuthority = await this.loadAuthority( + flags.queueAuthority, + queue.authority ); + const crankKeypair = anchor.web3.Keypair.generate(); + const bufferKeypair = anchor.web3.Keypair.generate(); + const crankSize = this.program.account.crankAccountData.size; + const bufferSize = maxRows * 40 + 8; + const signature = await this.program.methods + .crankInit({ + name: (flags.name ? Buffer.from(flags.name) : Buffer.from("")).slice( + 0, + 32 + ), + metadata: Buffer.from("").slice(0, 64), + crankSize: maxRows, + }) + .accounts({ + crank: crankKeypair.publicKey, + queue: queueAccount.publicKey, + buffer: bufferKeypair.publicKey, + systemProgram: SystemProgram.programId, + payer: payerKeypair.publicKey, + }) + .signers([crankKeypair, bufferKeypair]) + .preInstructions([ + anchor.web3.SystemProgram.createAccount({ + fromPubkey: payerKeypair.publicKey, + newAccountPubkey: bufferKeypair.publicKey, + space: bufferSize, + lamports: + await this.program.provider.connection.getMinimumBalanceForRentExemption( + bufferSize + ), + programId: this.program.programId, + }), + ]) + .rpc(); + + const crankAccount = new CrankAccount({ + program: this.program, + publicKey: crankKeypair.publicKey, + }); + if (this.silent) { - console.log(crank.account.publicKey.toString()); + console.log(crankKeypair.publicKey.toString()); } else { - this.logger.log(crank.prettyPrint()); + this.logger.log(await prettyPrintCrank(crankAccount)); this.logger.log( `${chalk.green(`${CHECK_ICON}Crank created successfully\r\n`)}` ); diff --git a/cli/src/commands/queue/create.ts b/cli/src/commands/queue/create.ts index 429572a..6829813 100644 --- a/cli/src/commands/queue/create.ts +++ b/cli/src/commands/queue/create.ts @@ -1,4 +1,4 @@ -/* eslint-disable unicorn/prevent-abbreviations */ +/* eslint-disable complexity */ /* eslint-disable unicorn/new-for-builtins */ import { flags } from "@oclif/command"; import * as anchor from "@project-serum/anchor"; @@ -102,7 +102,7 @@ export default class QueueCreate extends BaseCommand { verifyProgramHasPayer(this.program); const { flags, args } = this.parse(QueueCreate); const payerKeypair = programWallet(this.program); - const signers: Keypair[] = []; + const signers: Keypair[] = [payerKeypair]; const outputPath = flags.outputFile === undefined @@ -119,6 +119,7 @@ export default class QueueCreate extends BaseCommand { if (!authorityKeypair.publicKey.equals(payerKeypair.publicKey)) { signers.push(authorityKeypair); } + const [programStateAccount, stateBump] = ProgramStateAccount.fromSeed( this.program ); @@ -186,18 +187,6 @@ export default class QueueCreate extends BaseCommand { this.logger.debug(chalkString("OracleQueue", queueKeypair.publicKey)); this.logger.debug(chalkString("OracleBuffer", queueBuffer.publicKey)); - const crankKeypair = anchor.web3.Keypair.generate(); - const crankBuffer = anchor.web3.Keypair.generate(); - const crankSize = flags.crankSize ? flags.crankSize * 40 + 8 : 0; - - this.logger.debug(chalkString("CrankAccount", crankKeypair.publicKey)); - this.logger.debug(chalkString("CrankBuffer", crankBuffer.publicKey)); - - const crankAccount = new CrankAccount({ - program: this.program, - publicKey: crankKeypair.publicKey, - }); - setupQueueTxns.push( anchor.web3.SystemProgram.createAccount({ fromPubkey: payerKeypair.publicKey, @@ -238,33 +227,51 @@ export default class QueueCreate extends BaseCommand { payer: payerKeypair.publicKey, mint: tokenMint.publicKey, }) - .instruction(), - anchor.web3.SystemProgram.createAccount({ - fromPubkey: payerKeypair.publicKey, - newAccountPubkey: crankBuffer.publicKey, - space: crankSize, - lamports: - await this.program.provider.connection.getMinimumBalanceForRentExemption( - crankSize - ), - programId: this.program.programId, - }), - await this.program.methods - .crankInit({ - name: Buffer.from("Crank").slice(0, 32), - metadata: Buffer.from("").slice(0, 64), - crankSize: flags.crankSize, - }) - .accounts({ - crank: crankKeypair.publicKey, - queue: queueKeypair.publicKey, - buffer: crankBuffer.publicKey, - systemProgram: SystemProgram.programId, - payer: payerKeypair.publicKey, - }) .instruction() ); - signers.push(queueKeypair, queueBuffer, crankKeypair, crankBuffer); + signers.push(queueKeypair, queueBuffer); + + let crankAccount: CrankAccount | undefined; + if (flags.crankSize) { + const crankKeypair = anchor.web3.Keypair.generate(); + const crankBuffer = anchor.web3.Keypair.generate(); + const crankSize = flags.crankSize ? flags.crankSize * 40 + 8 : 0; + + this.logger.debug(chalkString("CrankAccount", crankKeypair.publicKey)); + this.logger.debug(chalkString("CrankBuffer", crankBuffer.publicKey)); + crankAccount = new CrankAccount({ + program: this.program, + publicKey: crankKeypair.publicKey, + }); + signers.push(crankKeypair, crankBuffer); + + setupQueueTxns.push( + anchor.web3.SystemProgram.createAccount({ + fromPubkey: payerKeypair.publicKey, + newAccountPubkey: crankBuffer.publicKey, + space: crankSize, + lamports: + await this.program.provider.connection.getMinimumBalanceForRentExemption( + crankSize + ), + programId: this.program.programId, + }), + await this.program.methods + .crankInit({ + name: Buffer.from("Crank").slice(0, 32), + metadata: Buffer.from("").slice(0, 64), + crankSize: flags.crankSize, + }) + .accounts({ + crank: crankKeypair.publicKey, + queue: queueKeypair.publicKey, + buffer: crankBuffer.publicKey, + systemProgram: SystemProgram.programId, + payer: payerKeypair.publicKey, + }) + .instruction() + ); + } const finalTransactions: ( | TransactionInstruction @@ -272,7 +279,7 @@ export default class QueueCreate extends BaseCommand { )[] = []; const oracleAccounts = await Promise.all( - Array.from(Array(flags.numOracles).keys()).map(async (n) => { + [...Array(flags.numOracles).keys()].map(async (n) => { const name = `Oracle-${n + 1}`; const tokenWalletKeypair = anchor.web3.Keypair.generate(); const [oracleAccount, oracleBump] = OracleAccount.fromSeed( @@ -340,6 +347,7 @@ export default class QueueCreate extends BaseCommand { .instruction(), await this.program.methods .permissionSet({ + // eslint-disable-next-line unicorn/no-null permission: { permitOracleHeartbeat: null }, enable: true, }) @@ -386,16 +394,20 @@ export default class QueueCreate extends BaseCommand { name: flags.name, queueAccount: queueKeypair.publicKey.toString(), queueSize: flags.queueSize, - queueReward: Number.parseInt(flags.reward), - minStake: Number.parseInt(flags.minStake), + queueReward: Number.parseInt(flags.reward, 10), + minStake: Number.parseInt(flags.minStake, 10), oracleTimeout: flags.oracleTimeout, - crankAccounts: [ - { - name: "Crank", - crankAccount: crankKeypair.publicKey.toString(), - maxRows: flags.crankSize, - }, - ], + // crankAccount === undefined ? undefined : + crankAccounts: + crankAccount === undefined + ? undefined + : [ + { + name: "Crank", + crankAccount: crankAccount.publicKey.toString(), + maxRows: flags.crankSize, + }, + ], oracleAccounts: [ oracleAccounts.map((oracle) => { return { diff --git a/cli/src/utils/keypair.ts b/cli/src/utils/keypair.ts index 958a372..56e9921 100644 --- a/cli/src/utils/keypair.ts +++ b/cli/src/utils/keypair.ts @@ -60,7 +60,9 @@ export const loadGoogleSecretKeypair = async ( const client = new SecretManagerServiceClient(); const [accessResponse] = await client.accessSecretVersion({ - name: secretPath, + name: secretPath.includes("/versions/") + ? secretPath + : `${secretPath}/versions/latest`, }); const secrets = accessResponse?.payload.data; @@ -91,6 +93,7 @@ export const loadKeypair = async ( const keypair = loadKeypairFs(keypairPath); return keypair; } catch {} + try { const keypair = await loadGoogleSecretKeypair(keypairPath); return keypair; @@ -100,6 +103,7 @@ export const loadKeypair = async ( } else if ("secretPath" in keypairPath) { return loadGoogleSecretKeypair(keypairPath.secretPath); } + throw new InvalidKeypairProvided(keypairPath); }; @@ -140,5 +144,6 @@ export const loadKeypairWithDescriptor = async ( }; return [keypair, descriptor]; } + throw new InvalidKeypairProvided(keypairPath); }; diff --git a/libraries/sbv2-utils/src/transaction.ts b/libraries/sbv2-utils/src/transaction.ts index aad7c0c..1c7127f 100644 --- a/libraries/sbv2-utils/src/transaction.ts +++ b/libraries/sbv2-utils/src/transaction.ts @@ -31,11 +31,12 @@ export async function packAndSend( ).wallet.signAllTransactions(signedTransactions); for (let k = 0; k < packedTransactions.length; k += 1) { - const tx = signedTxs[k]; - const rawTx = tx!.serialize(); + const tx = signedTxs[k]!; + const rawTx = tx.serialize(); signatures.push( sendAndConfirmRawTransaction(program.provider.connection, rawTx, { maxRetries: 10, + commitment: "processed", }) .then((sig) => { return sig; diff --git a/libraries/ts/package.json b/libraries/ts/package.json index a480fab..b562d96 100644 --- a/libraries/ts/package.json +++ b/libraries/ts/package.json @@ -25,7 +25,7 @@ "scripts": { "docgen": "yarn build && npx typedoc", "build:protos": "pbjs -t static-module -o src/protos/index.js ../protos/*.proto && pbts -o src/protos/index.d.ts src/protos/index.js", - "build:cjs:protos": "shx mkdir -p lib/cjs/protos; pbjs -t static-module -w commonjs -o lib/cjs/protos/index.js ../protos/*.proto && pbts -o lib/cjs/protos/index.d.ts lib/cjs/protos/index.js", + "build:cjs:protos": "shx mkdir -p lib/cjs/protos; pbjs -t static-module -o lib/cjs/protos/index.js ../protos/*.proto && pbts -o lib/cjs/protos/index.d.ts lib/cjs/protos/index.js", "build:esm:protos": "shx mkdir -p lib/esm/protos; pbjs -t static-module -o lib/esm/protos/index.js ../protos/*.proto && pbts -o lib/esm/protos/index.d.ts lib/esm/protos/index.js", "build:cjs": "shx rm -rf lib/cjs && tsc -p tsconfig.cjs.json && shx echo '{\"type\": \"commonjs\"}' > lib/cjs/package.json && yarn build:cjs:protos", "build:esm": "shx rm -rf lib/esm && tsc -p tsconfig.esm.json && shx echo '{\"type\": \"module\"}' > lib/esm/package.json && yarn build:esm:protos", @@ -43,6 +43,7 @@ "@project-serum/anchor": "^0.24.2", "@solana/spl-governance": "^0.0.34", "@switchboard-xyz/eslint-config": "^0.1.1", + "@switchboard-xyz/switchboard-api": "^0.2.201", "assert": "^2.0.0", "big.js": "^6.1.1", "bs58": "^4.0.1", @@ -65,12 +66,5 @@ "shx": "^0.3.4", "typedoc": "^0.22.13", "typescript": "^4.2.4" - }, - "eslintConfig": { - "extends": "@switchboard-xyz", - "rules": { - "no-return-await": "off", - "quotes": "off" - } } } diff --git a/libraries/ts/src/sbv2.ts b/libraries/ts/src/sbv2.ts index 17059b0..b20b03a 100644 --- a/libraries/ts/src/sbv2.ts +++ b/libraries/ts/src/sbv2.ts @@ -18,11 +18,12 @@ import { TransactionInstruction, TransactionSignature, } from "@solana/web3.js"; +// eslint-disable-next-line import/extensions +// import { OracleJob } from "./protos/index.js"; +import { OracleJob } from "@switchboard-xyz/switchboard-api"; +import assert from "assert"; import Big from "big.js"; import * as crypto from "crypto"; -// eslint-disable-next-line import/extensions -import { OracleJob } from "./protos/index.js"; -const assert = require("assert"); /** * Switchboard Devnet Program ID diff --git a/package.json b/package.json index 7e1e7e8..67fd529 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "docs:build:sbv2-utils": "yarn workspace @switchboard-xyz/sbv2-utils docgen", "docs:build:cli": "node ./tools/scripts/generate-cli-docs.js", "docs:build:site": "yarn workspace website build:site", - "docs:build:tasks": "protoc --proto_path=../switchboard-core/protos --doc_out=website/api --doc_opt=markdown,_tasks.md ../switchboard-core/protos/job_schemas.proto", + "docs:build:tasks": "protoc --proto_path=./libraries/protos --doc_out=website/api --doc_opt=markdown,_tasks.md ./libraries/protos/job_schemas.proto", "docs:build": "run-s docs:build:ts docs:build:sbv2-lite docs:build:sbv2-utils docs:build:cli docs:build:site", "docs:deploy": "yarn workspace website deploy", "gen:idl": "rawrtools gen:anchor SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f -o website/idl -p /idl", diff --git a/website/api/_tasks.md b/website/api/_tasks.md index 31cac34..a335d56 100644 --- a/website/api/_tasks.md +++ b/website/api/_tasks.md @@ -1,564 +1,848 @@ # Protocol Documentation - ## Table of Contents - [job_schemas.proto](#job_schemas.proto) - - - [JobPosting](#.JobPosting) - - [JobResult](#.JobResult) - - [OracleJob](#.OracleJob) - - [OracleJob.AddTask](#.OracleJob.AddTask) - - [OracleJob.AnchorFetchTask](#.OracleJob.AnchorFetchTask) - - [OracleJob.ConditionalTask](#.OracleJob.ConditionalTask) - - [OracleJob.DefiKingdomsTask](#.OracleJob.DefiKingdomsTask) - - [OracleJob.DefiKingdomsTask.Token](#.OracleJob.DefiKingdomsTask.Token) - - [OracleJob.DivideTask](#.OracleJob.DivideTask) - - [OracleJob.HttpTask](#.OracleJob.HttpTask) - - [OracleJob.HttpTask.Header](#.OracleJob.HttpTask.Header) - - [OracleJob.JsonParseTask](#.OracleJob.JsonParseTask) - - [OracleJob.JupiterSwapTask](#.OracleJob.JupiterSwapTask) - - [OracleJob.LendingRateTask](#.OracleJob.LendingRateTask) - - [OracleJob.LpExchangeRateTask](#.OracleJob.LpExchangeRateTask) - - [OracleJob.LpTokenPriceTask](#.OracleJob.LpTokenPriceTask) - - [OracleJob.MangoPerpMarketTask](#.OracleJob.MangoPerpMarketTask) - - [OracleJob.MaxTask](#.OracleJob.MaxTask) - - [OracleJob.MeanTask](#.OracleJob.MeanTask) - - [OracleJob.MedianTask](#.OracleJob.MedianTask) - - [OracleJob.MultiplyTask](#.OracleJob.MultiplyTask) - - [OracleJob.OracleTask](#.OracleJob.OracleTask) - - [OracleJob.PerpMarketTask](#.OracleJob.PerpMarketTask) - - [OracleJob.PowTask](#.OracleJob.PowTask) - - [OracleJob.RegexExtractTask](#.OracleJob.RegexExtractTask) - - [OracleJob.SerumSwapTask](#.OracleJob.SerumSwapTask) - - [OracleJob.SplStakePoolTask](#.OracleJob.SplStakePoolTask) - - [OracleJob.SplTokenParseTask](#.OracleJob.SplTokenParseTask) - - [OracleJob.SubtractTask](#.OracleJob.SubtractTask) - - [OracleJob.SushiswapExchangeRateTask](#.OracleJob.SushiswapExchangeRateTask) - - [OracleJob.Task](#.OracleJob.Task) - - [OracleJob.TpsTask](#.OracleJob.TpsTask) - - [OracleJob.TwapTask](#.OracleJob.TwapTask) - - [OracleJob.UniswapExchangeRateTask](#.OracleJob.UniswapExchangeRateTask) - - [OracleJob.ValueTask](#.OracleJob.ValueTask) - - [OracleJob.WebsocketTask](#.OracleJob.WebsocketTask) - - [OracleJob.XStepPriceTask](#.OracleJob.XStepPriceTask) - - - [OracleJob.HttpTask.Method](#.OracleJob.HttpTask.Method) - - [OracleJob.JsonParseTask.AggregationMethod](#.OracleJob.JsonParseTask.AggregationMethod) - - [OracleJob.LendingRateTask.Field](#.OracleJob.LendingRateTask.Field) - + - [JobPosting](#.JobPosting) + - [JobResult](#.JobResult) + - [OracleJob](#.OracleJob) + - [OracleJob.AddTask](#.OracleJob.AddTask) + - [OracleJob.AnchorFetchTask](#.OracleJob.AnchorFetchTask) + - [OracleJob.CacheTask](#.OracleJob.CacheTask) + - [OracleJob.ConditionalTask](#.OracleJob.ConditionalTask) + - [OracleJob.DefiKingdomsTask](#.OracleJob.DefiKingdomsTask) + - [OracleJob.DefiKingdomsTask.Token](#.OracleJob.DefiKingdomsTask.Token) + - [OracleJob.DivideTask](#.OracleJob.DivideTask) + - [OracleJob.HttpTask](#.OracleJob.HttpTask) + - [OracleJob.HttpTask.Header](#.OracleJob.HttpTask.Header) + - [OracleJob.JsonParseTask](#.OracleJob.JsonParseTask) + - [OracleJob.JupiterSwapTask](#.OracleJob.JupiterSwapTask) + - [OracleJob.LendingRateTask](#.OracleJob.LendingRateTask) + - [OracleJob.LpExchangeRateTask](#.OracleJob.LpExchangeRateTask) + - [OracleJob.LpTokenPriceTask](#.OracleJob.LpTokenPriceTask) + - [OracleJob.MangoPerpMarketTask](#.OracleJob.MangoPerpMarketTask) + - [OracleJob.MaxTask](#.OracleJob.MaxTask) + - [OracleJob.MeanTask](#.OracleJob.MeanTask) + - [OracleJob.MedianTask](#.OracleJob.MedianTask) + - [OracleJob.MultiplyTask](#.OracleJob.MultiplyTask) + - [OracleJob.OracleTask](#.OracleJob.OracleTask) + - [OracleJob.PancakeswapExchangeRateTask](#.OracleJob.PancakeswapExchangeRateTask) + - [OracleJob.PerpMarketTask](#.OracleJob.PerpMarketTask) + - [OracleJob.PowTask](#.OracleJob.PowTask) + - [OracleJob.RegexExtractTask](#.OracleJob.RegexExtractTask) + - [OracleJob.SerumSwapTask](#.OracleJob.SerumSwapTask) + - [OracleJob.SplStakePoolTask](#.OracleJob.SplStakePoolTask) + - [OracleJob.SplTokenParseTask](#.OracleJob.SplTokenParseTask) + - [OracleJob.SubtractTask](#.OracleJob.SubtractTask) + - [OracleJob.SushiswapExchangeRateTask](#.OracleJob.SushiswapExchangeRateTask) + - [OracleJob.SysclockOffsetTask](#.OracleJob.SysclockOffsetTask) + - [OracleJob.Task](#.OracleJob.Task) + - [OracleJob.TpsTask](#.OracleJob.TpsTask) + - [OracleJob.TwapTask](#.OracleJob.TwapTask) + - [OracleJob.UniswapExchangeRateTask](#.OracleJob.UniswapExchangeRateTask) + - [OracleJob.ValueTask](#.OracleJob.ValueTask) + - [OracleJob.WebsocketTask](#.OracleJob.WebsocketTask) + - [OracleJob.XStepPriceTask](#.OracleJob.XStepPriceTask) + + - [OracleJob.CacheTask.Method](#.OracleJob.CacheTask.Method) + - [OracleJob.HttpTask.Method](#.OracleJob.HttpTask.Method) + - [OracleJob.JsonParseTask.AggregationMethod](#.OracleJob.JsonParseTask.AggregationMethod) + - [OracleJob.LendingRateTask.Field](#.OracleJob.LendingRateTask.Field) + - [Scalar Value Types](#scalar-value-types) - + +

Top

## job_schemas.proto + + ### JobPosting - The schema Oracle nodes receive when they are notified to fulfill a job. -| Field | Type | Label | Description | -| ----------------------- | ----------------- | -------- | ------------------------------------------------ | -| aggregator_state_pubkey | [bytes](#bytes) | optional | Pubkey of the aggregator to fulfill the job for. | -| node_pubkeys | [bytes](#bytes) | repeated | The pubkey of the nodes this job is assigned to. | -| slot | [uint64](#uint64) | optional | Slot number of the job posting. | + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| aggregator_state_pubkey | [bytes](#bytes) | optional | Pubkey of the aggregator to fulfill the job for. | +| node_pubkeys | [bytes](#bytes) | repeated | The pubkey of the nodes this job is assigned to. | +| slot | [uint64](#uint64) | optional | Slot number of the job posting. | + + + + + ### JobResult - This schema Oracle nodes respond with when fulfilling a job. -| Field | Type | Label | Description | -| ----------- | --------------- | -------- | ----------------------------------------------------------------- | -| node_pubkey | [bytes](#bytes) | optional | The public key of the responding node. | -| result | double | optional | The median value of the jobs the node has fulfilled successfully. | -| error | [bool](#bool) | optional | True if the node failed to decide on an answer to the job. | + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| node_pubkey | [bytes](#bytes) | optional | The public key of the responding node. | +| result | [double](#double) | optional | The median value of the jobs the node has fulfilled successfully. | +| error | [bool](#bool) | optional | True if the node failed to decide on an answer to the job. | + + + + + ### OracleJob +Represnts a list of tasks to be performed by a switchboard oracle. -Represents a list of tasks to be performed by a switchboard oracle. -| Field | Type | Label | Description | -| ----- | --------------------------------- | -------- | ------------------------------------------------- | +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | | tasks | [OracleJob.Task](#OracleJob.Task) | repeated | The chain of tasks to perform for this OracleJob. | + + + + + ### OracleJob.AddTask - This task will add a numerical input by a scalar value or by another aggregate. -| Field | Type | Label | Description | -| ----------------- | ----------------------- | -------- | -------------------------------------------------------------------------------- | -| scalar | double | optional | Specifies a scalar to add by. | -| aggregator_pubkey | string | optional | Specifies an aggregator to add by. | -| job | [OracleJob](#OracleJob) | optional | A job whose result is computed before adding our numerical input by that result. | + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| scalar | [double](#double) | optional | Specifies a scalar to add by. | +| aggregator_pubkey | [string](#string) | optional | Specifies an aggregator to add by. | +| job | [OracleJob](#OracleJob) | optional | A job whose result is computed before adding our numerical input by that result. | + + + + + ### OracleJob.AnchorFetchTask - Load a parse an Anchor based solana account. -| Field | Type | Label | Description | -| --------------- | ------ | -------- | --------------------------------------- | -| program_id | string | optional | Owning program of the account to parse. | -| account_address | string | optional | The account to parse. | + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| program_id | [string](#string) | optional | Owning program of the account to parse. | +| account_address | [string](#string) | optional | The account to parse. | + + + + + + + + +### OracleJob.CacheTask + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| name | [string](#string) | optional | | +| method | [OracleJob.CacheTask.Method](#OracleJob.CacheTask.Method) | optional | | + + + + + ### OracleJob.ConditionalTask - This task will run the `attempt` subtasks in an effort to produce a valid numerical result. If `attempt` fails to produce an acceptable result, `on_failure` subtasks will be run instead. -| Field | Type | Label | Description | -| ---------- | --------------------------------- | -------- | ----------------------------------------------------------------------------------------------------- | -| attempt | [OracleJob.Task](#OracleJob.Task) | repeated | A list of subtasks to process in an attempt to produce a valid numerical result. | + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| attempt | [OracleJob.Task](#OracleJob.Task) | repeated | A list of subtasks to process in an attempt to produce a valid numerical result. | | on_failure | [OracleJob.Task](#OracleJob.Task) | repeated | A list of subtasks that will be run if `attempt` subtasks are unable to produce an acceptable result. | + + + + + ### OracleJob.DefiKingdomsTask -| Field | Type | Label | Description | -| --------- | --------------------------------------------------------------------- | -------- | ----------- | -| provider | string | optional | | -| in_token | [OracleJob.DefiKingdomsTask.Token](#OracleJob.DefiKingdomsTask.Token) | optional | | -| out_token | [OracleJob.DefiKingdomsTask.Token](#OracleJob.DefiKingdomsTask.Token) | optional | | + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| provider | [string](#string) | optional | | +| in_token | [OracleJob.DefiKingdomsTask.Token](#OracleJob.DefiKingdomsTask.Token) | optional | | +| out_token | [OracleJob.DefiKingdomsTask.Token](#OracleJob.DefiKingdomsTask.Token) | optional | | + + + + + ### OracleJob.DefiKingdomsTask.Token -| Field | Type | Label | Description | -| -------- | ------ | -------- | ----------- | -| address | string | optional | | -| decimals | int32 | optional | | + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| address | [string](#string) | optional | | +| decimals | [int32](#int32) | optional | | + + + + + ### OracleJob.DivideTask - This task will divide a numerical input by a scalar value or by another aggregate. -| Field | Type | Label | Description | -| ----------------- | ----------------------- | -------- | ---------------------------------------------------------------------------------- | -| scalar | double | optional | Specifies a basic scalar denominator to divide by. | -| aggregator_pubkey | string | optional | Specifies another aggregator result to divide by. | -| job | [OracleJob](#OracleJob) | optional | A job whose result is computed before dividing our numerical input by that result. | + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| scalar | [double](#double) | optional | Specifies a basic scalar denominator to divide by. | +| aggregator_pubkey | [string](#string) | optional | Specifies another aggregator resut to divide by. | +| job | [OracleJob](#OracleJob) | optional | A job whose result is computed before dividing our numerical input by that result. | + + + + + ### OracleJob.HttpTask - -The adapter will report the text body of a successful HTTP request to the specified url, +The adapter will report the text body of a successful HTTP request to the specified url, or return an error if the response status code is greater than or equal to 400. @return string representation of it's output. -| Field | Type | Label | Description | -| ------- | ------------------------------------------------------- | -------- | ----------------------------------------------------------- | -| url | string | optional | A string containing the URL to direct this HTTP request to. | -| method | [OracleJob.HttpTask.Method](#OracleJob.HttpTask.Method) | optional | The type of HTTP request to make. | -| headers | [OracleJob.HttpTask.Header](#OracleJob.HttpTask.Header) | repeated | A list of headers to add to this HttpTask. | -| body | string | optional | A stringified body (if any) to add to this HttpTask. | + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| url | [string](#string) | optional | A string containing the URL to direct this HTTP request to. | +| method | [OracleJob.HttpTask.Method](#OracleJob.HttpTask.Method) | optional | The type of HTTP request to make. | +| headers | [OracleJob.HttpTask.Header](#OracleJob.HttpTask.Header) | repeated | A list of headers to add to this HttpTask. | +| body | [string](#string) | optional | A stringified body (if any) to add to this HttpTask. | + + + + + ### OracleJob.HttpTask.Header - An object that represents a header to add to an HTTP request. -| Field | Type | Label | Description | -| ----- | ------ | -------- | ----------- | -| key | string | optional | | -| value | string | optional | | + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| key | [string](#string) | optional | | +| value | [string](#string) | optional | | + + + + + ### OracleJob.JsonParseTask - The adapter walks the path specified and returns the value found at that result. If returning -JSON data from the HttpGet or HttpPost adapters, you must use this adapter to parse the +JSON data from the HttpGet or HttpPost adapters, you must use this adapter to parse the response. -| Field | Type | Label | Description | -| ------------------ | --------------------------------------------------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------- | -| path | string | optional | JSONPath formatted path to the element. https://t.ly/uLtw https://www.npmjs.com/package/jsonpath-plus | + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| path | [string](#string) | optional | JSONPath formatted path to the element. https://t.ly/uLtw https://www.npmjs.com/package/jsonpath-plus | | aggregation_method | [OracleJob.JsonParseTask.AggregationMethod](#OracleJob.JsonParseTask.AggregationMethod) | optional | The technique that will be used to aggregate the results if walking the specified path returns multiple numerical results. | + + + + + ### OracleJob.JupiterSwapTask -| Field | Type | Label | Description | -| ----------------- | ------ | -------- | ----------- | -| in_token_address | string | optional | | -| out_token_address | string | optional | | -| base_amount | double | optional | | + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| in_token_address | [string](#string) | optional | | +| out_token_address | [string](#string) | optional | | +| base_amount | [double](#double) | optional | | + + + + + ### OracleJob.LendingRateTask - Fetch the lending rates for various Solana protocols -| Field | Type | Label | Description | -| ---------- | ------------------------------------------------------------------- | -------- | ------------------------------------------------------------- | -| protocol | string | optional | 01, apricot, francium, jet, larix, mango, port, solend, tulip | -| asset_mint | string | optional | A token mint address supported by the chosen protocol | -| field | [OracleJob.LendingRateTask.Field](#OracleJob.LendingRateTask.Field) | optional | | + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| protocol | [string](#string) | optional | 01, apricot, francium, jet, larix, mango, port, solend, tulip | +| asset_mint | [string](#string) | optional | A token mint address supported by the chosen protocol | +| field | [OracleJob.LendingRateTask.Field](#OracleJob.LendingRateTask.Field) | optional | | + + + + + ### OracleJob.LpExchangeRateTask - Fetch the current swap price for a given liquidity pool -| Field | Type | Label | Description | -| ---------------------------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------- | -| in_token_address | string | optional | Not Used | -| out_token_address | string | optional | Not Used | -| mercurial_pool_address | string | optional | Mercurial finance pool address. A full list can be found here: https://github.com/mercurial-finance/stable-swap-n-pool-js | -| saber_pool_address | string | optional | Saber pool address. A full list can be found here: https://github.com/saber-hq/saber-registry-dist | -| orca_pool_token_mint_address | string | optional | Orca pool address. A full list can be found here: https://www.orca.so/pools | -| raydium_pool_address | string | optional | The Raydium liquidity pool ammId. A full list can be found here: https://sdk.raydium.io/liquidity/mainnet.json | + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| in_token_address | [string](#string) | optional | Not Used | +| out_token_address | [string](#string) | optional | Not Used | +| mercurial_pool_address | [string](#string) | optional | Mercurial finance pool address. A full list can be found here: https://github.com/mercurial-finance/stable-swap-n-pool-js | +| saber_pool_address | [string](#string) | optional | Saber pool address. A full list can be found here: https://github.com/saber-hq/saber-registry-dist | +| orca_pool_token_mint_address | [string](#string) | optional | Orca pool address. A full list can be found here: https://www.orca.so/pools | +| raydium_pool_address | [string](#string) | optional | The Raydium liquidity pool ammId. A full list can be found here: https://sdk.raydium.io/liquidity/mainnet.json | + + + + + ### OracleJob.LpTokenPriceTask - Fetch LP token price info from a number of supported exchanges. -| Field | Type | Label | Description | -| ---------------------- | ----------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| mercurial_pool_address | string | optional | Mercurial finance pool address. A full list can be found here: https://github.com/mercurial-finance/stable-swap-n-pool-js | -| saber_pool_address | string | optional | Saber pool address. A full list can be found here: https://github.com/saber-hq/saber-registry-dist | -| orca_pool_address | string | optional | Orca pool address. A full list can be found here: https://www.orca.so/pools | -| raydium_pool_address | string | optional | The Raydium liquidity pool ammId. A full list can be found here: https://sdk.raydium.io/liquidity/mainnet.json | -| price_feed_addresses | string | repeated | A list of Switchboard aggregator accounts used to calculate the fair LP price. This ensures the price is based on the previous round to mitigate flash loan price manipulation. | -| price_feed_jobs | [OracleJob](#OracleJob) | repeated | | -| use_fair_price | [bool](#bool) | optional | If enabled and price_feed_addresses provided, the oracle will calculate the fair LP price based on the liquidity pool reserves. See our blog post for more information: https://switchboardxyz.medium.com/fair-lp-token-oracles-94a457c50239 | + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| mercurial_pool_address | [string](#string) | optional | Mercurial finance pool address. A full list can be found here: https://github.com/mercurial-finance/stable-swap-n-pool-js | +| saber_pool_address | [string](#string) | optional | Saber pool address. A full list can be found here: https://github.com/saber-hq/saber-registry-dist | +| orca_pool_address | [string](#string) | optional | Orca pool address. A full list can be found here: https://www.orca.so/pools | +| raydium_pool_address | [string](#string) | optional | The Raydium liquidity pool ammId. A full list can be found here: https://sdk.raydium.io/liquidity/mainnet.json | +| price_feed_addresses | [string](#string) | repeated | A list of Switchboard aggregator accounts used to calculate the fair LP price. This ensures the price is based on the previous round to mitigate flash loan price manipulation. | +| price_feed_jobs | [OracleJob](#OracleJob) | repeated | | +| use_fair_price | [bool](#bool) | optional | If enabled and price_feed_addresses provided, the oracle will calculate the fair LP price based on the liquidity pool reserves. See our blog post for more information: https://switchboardxyz.medium.com/fair-lp-token-oracles-94a457c50239 | + + + + + ### OracleJob.MangoPerpMarketTask - Fetch the current price for a Mango perpetual market -| Field | Type | Label | Description | -| ------------------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| perp_market_address | string | optional | Mainnet address for a mango perpetual market. A full list can be found here: https://github.com/blockworks-foundation/mango-client-v3/blob/main/src/ids.json | + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| perp_market_address | [string](#string) | optional | Mainnet address for a mango perpetual market. A full list can be found here: https://github.com/blockworks-foundation/mango-client-v3/blob/main/src/ids.json | + + + + + ### OracleJob.MaxTask - Returns the maximum value of all the results returned by the provided subtasks and subjobs. -| Field | Type | Label | Description | -| ----- | --------------------------------- | -------- | ------------------------------------------------------------------ | + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | | tasks | [OracleJob.Task](#OracleJob.Task) | repeated | A list of subtasks to process and produce a list of result values. | -| jobs | [OracleJob](#OracleJob) | repeated | A list of subjobs to process and produce a list of result values. | +| jobs | [OracleJob](#OracleJob) | repeated | A list of subjobs to process and produce a list of result values. | + + + + + ### OracleJob.MeanTask - Returns the mean of all the results returned by the provided subtasks and subjobs. -| Field | Type | Label | Description | -| ----- | --------------------------------- | -------- | ------------------------------------------------------------------ | + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | | tasks | [OracleJob.Task](#OracleJob.Task) | repeated | A list of subtasks to process and produce a list of result values. | -| jobs | [OracleJob](#OracleJob) | repeated | A list of subjobs to process and produce a list of result values. | +| jobs | [OracleJob](#OracleJob) | repeated | A list of subjobs to process and produce a list of result values. | + + + + + ### OracleJob.MedianTask - -Returns the median of all the results returned by the provided subtasks and subjobs. Nested +Returns the median of all the results returned by the provided subtasks and subjobs. Nested tasks must return a Number. -| Field | Type | Label | Description | -| ----------------------- | --------------------------------- | -------- | ------------------------------------------------------------------ | -| tasks | [OracleJob.Task](#OracleJob.Task) | repeated | A list of subtasks to process and produce a list of result values. | -| jobs | [OracleJob](#OracleJob) | repeated | A list of subjobs to process and produce a list of result values. | -| min_successful_required | int32 | optional | | + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| tasks | [OracleJob.Task](#OracleJob.Task) | repeated | A list of subtasks to process and produce a list of result values. | +| jobs | [OracleJob](#OracleJob) | repeated | A list of subjobs to process and produce a list of result values. | +| min_successful_required | [int32](#int32) | optional | | + + + + + ### OracleJob.MultiplyTask - This task will multiply a numerical input by a scalar value or by another aggregate. -| Field | Type | Label | Description | -| ----------------- | ----------------------- | -------- | ------------------------------------------------------------------------------------- | -| scalar | double | optional | Specifies a scalar to multiply by. | -| aggregator_pubkey | string | optional | Specifies an aggregator to multiply by. | -| job | [OracleJob](#OracleJob) | optional | A job whose result is computed before multiplying our numerical input by that result. | + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| scalar | [double](#double) | optional | Specifies a scalar to multiply by. | +| aggregator_pubkey | [string](#string) | optional | Specifies an aggregator to multiply by. | +| job | [OracleJob](#OracleJob) | optional | A job whose result is computed before multiplying our numerical input by that result. | + + + + + ### OracleJob.OracleTask - Fetch the current price of a Solana oracle protocol -| Field | Type | Label | Description | -| -------------------------------- | ------ | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| switchboard_address | string | optional | Mainnet address of a Switchboard V2 feed. Switchboard is decentralized and allows anyone to build their own feed. A small subset of feeds is available here: https://switchboard.xyz/explorer | -| pyth_address | string | optional | Mainnet address for a Pyth feed. A full list can be found here: https://pyth.network/markets/ | -| chainlink_address | string | optional | Devnet address for a Chainlink feed. A full list can be found here: https://docs.chain.link/docs/solana/data-feeds-solana | -| pyth_allowed_confidence_interval | double | optional | Value (as a percentage) that the lower bound confidence interval is of the actual value. Confidence intervals that are larger that this threshold are rejected. | + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| switchboard_address | [string](#string) | optional | Mainnet address of a Switchboard V2 feed. Switchboard is decentralized and allows anyone to build their own feed. A small subset of feeds is available here: https://switchboard.xyz/explorer | +| pyth_address | [string](#string) | optional | Mainnet address for a Pyth feed. A full list can be found here: https://pyth.network/markets/ | +| chainlink_address | [string](#string) | optional | Devnet address for a Chainlink feed. A full list can be found here: https://docs.chain.link/docs/solana/data-feeds-solana | +| pyth_allowed_confidence_interval | [double](#double) | optional | Value (as a percentage) that the lower bound confidence interval is of the actual value. Confidence intervals that are larger that this treshold are rejected. | + + + + + + + + +### OracleJob.PancakeswapExchangeRateTask + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| in_token_address | [string](#string) | optional | | +| out_token_address | [string](#string) | optional | | +| in_token_amount | [double](#double) | optional | | +| slippage | [double](#double) | optional | | +| provider | [string](#string) | optional | | + + + + + ### OracleJob.PerpMarketTask - Fetch the current price of a perpetual market -| Field | Type | Label | Description | -| -------------------- | ------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| mango_market_address | string | optional | Market address for a mango perpetual market. A full list can be found here: https://github.com/blockworks-foundation/mango-client-v3/blob/main/src/ids.json | -| drift_market_address | string | optional | Market address for a drift perpetual market. A full list can be found here: https://github.com/drift-labs/protocol-v1/blob/master/sdk/src/constants/markets.ts | -| zeta_market_address | string | optional | Market address for a zeta perpetual market. | -| zo_market_address | string | optional | Market address for a 01 protocol perpetual market. | + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| mango_market_address | [string](#string) | optional | Market address for a mango perpetual market. A full list can be found here: https://github.com/blockworks-foundation/mango-client-v3/blob/main/src/ids.json | +| drift_market_address | [string](#string) | optional | Market address for a drift perpetual market. A full list can be found here: https://github.com/drift-labs/protocol-v1/blob/master/sdk/src/constants/markets.ts | +| zeta_market_address | [string](#string) | optional | Market address for a zeta perpetual market. | +| zo_market_address | [string](#string) | optional | Market address for a 01 protocol perpetual market. | + + + + + ### OracleJob.PowTask - Take the power of the working value. -| Field | Type | Label | Description | -| ----------------- | ------ | -------- | ---------------------------------------------------------------- | -| scalar | double | optional | Take the working value to the exponent of value. | -| aggregator_pubkey | string | optional | Take the working value to the exponent of the aggregators value. | + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| scalar | [double](#double) | optional | Take the working value to the exponent of value. | +| aggregator_pubkey | [string](#string) | optional | Take the working value to the exponent of the aggregators value. | + + + + + ### OracleJob.RegexExtractTask - Find a pattern within a string of a previous task and extract a group number. -| Field | Type | Label | Description | -| ------------ | ------ | -------- | ------------------------ | -| pattern | string | optional | Regex pattern to find. | -| group_number | int32 | optional | Group number to extract. | + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| pattern | [string](#string) | optional | Regex pattern to find. | +| group_number | [int32](#int32) | optional | Group number to extract. | + + + + + ### OracleJob.SerumSwapTask - Fetch the latest swap price on Serum's orderbook -| Field | Type | Label | Description | -| ------------------ | ------ | -------- | -------------------------------------- | -| serum_pool_address | string | optional | The serum pool to fetch swap price for | + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| serum_pool_address | [string](#string) | optional | The serum pool to fetch swap price for | + + + + + ### OracleJob.SplStakePoolTask -| Field | Type | Label | Description | -| ------ | ------ | -------- | ----------------------------------- | -| pubkey | string | optional | The pubkey of the SPL Stake Pool.`` | + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| pubkey | [string](#string) | optional | The pubkey of the SPL Stake Pool.`` | + + + + + ### OracleJob.SplTokenParseTask -| Field | Type | Label | Description | -| --------------------- | ------ | -------- | ----------- | -| token_account_address | string | optional | | -| mint_address | string | optional | | + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| token_account_address | [string](#string) | optional | | +| mint_address | [string](#string) | optional | | + + + + + ### OracleJob.SubtractTask - This task will subtract a numerical input by a scalar value or by another aggregate. -| Field | Type | Label | Description | -| ----------------- | ----------------------- | -------- | ------------------------------------------------------------------------------------- | -| scalar | double | optional | Specifies a scalar to subtract by. | -| aggregator_pubkey | string | optional | Specifies an aggregator to subtract by. | -| job | [OracleJob](#OracleJob) | optional | A job whose result is computed before subtracting our numerical input by that result. | + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| scalar | [double](#double) | optional | Specifies a scalar to subtract by. | +| aggregator_pubkey | [string](#string) | optional | Specifies an aggregator to subtract by. | +| job | [OracleJob](#OracleJob) | optional | A job whose result is computed before subtracting our numerical input by that result. | + + + + + ### OracleJob.SushiswapExchangeRateTask -| Field | Type | Label | Description | -| ----------------- | ------ | -------- | ----------- | -| in_token_address | string | optional | | -| out_token_address | string | optional | | -| in_token_amount | uint32 | optional | | -| slippage | double | optional | | -| provider | string | optional | | + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| in_token_address | [string](#string) | optional | | +| out_token_address | [string](#string) | optional | | +| in_token_amount | [double](#double) | optional | | +| slippage | [double](#double) | optional | | +| provider | [string](#string) | optional | | + + + + + + + + +### OracleJob.SysclockOffsetTask + + + + + + ### OracleJob.Task -| Field | Type | Label | Description | -| ---------------------------- | --------------------------------------------------------------------------- | -------- | ----------- | -| http_task | [OracleJob.HttpTask](#OracleJob.HttpTask) | optional | | -| json_parse_task | [OracleJob.JsonParseTask](#OracleJob.JsonParseTask) | optional | | -| median_task | [OracleJob.MedianTask](#OracleJob.MedianTask) | optional | | -| mean_task | [OracleJob.MeanTask](#OracleJob.MeanTask) | optional | | -| websocket_task | [OracleJob.WebsocketTask](#OracleJob.WebsocketTask) | optional | | -| divide_task | [OracleJob.DivideTask](#OracleJob.DivideTask) | optional | | -| multiply_task | [OracleJob.MultiplyTask](#OracleJob.MultiplyTask) | optional | | -| lp_token_price_task | [OracleJob.LpTokenPriceTask](#OracleJob.LpTokenPriceTask) | optional | | -| lp_exchange_rate_task | [OracleJob.LpExchangeRateTask](#OracleJob.LpExchangeRateTask) | optional | | -| conditional_task | [OracleJob.ConditionalTask](#OracleJob.ConditionalTask) | optional | | -| value_task | [OracleJob.ValueTask](#OracleJob.ValueTask) | optional | | -| max_task | [OracleJob.MaxTask](#OracleJob.MaxTask) | optional | | -| regex_extract_task | [OracleJob.RegexExtractTask](#OracleJob.RegexExtractTask) | optional | | -| xstep_price_task | [OracleJob.XStepPriceTask](#OracleJob.XStepPriceTask) | optional | | -| add_task | [OracleJob.AddTask](#OracleJob.AddTask) | optional | | -| subtract_task | [OracleJob.SubtractTask](#OracleJob.SubtractTask) | optional | | -| twap_task | [OracleJob.TwapTask](#OracleJob.TwapTask) | optional | | -| serum_swap_task | [OracleJob.SerumSwapTask](#OracleJob.SerumSwapTask) | optional | | -| pow_task | [OracleJob.PowTask](#OracleJob.PowTask) | optional | | -| lending_rate_task | [OracleJob.LendingRateTask](#OracleJob.LendingRateTask) | optional | | -| mango_perp_market_task | [OracleJob.MangoPerpMarketTask](#OracleJob.MangoPerpMarketTask) | optional | | -| jupiter_swap_task | [OracleJob.JupiterSwapTask](#OracleJob.JupiterSwapTask) | optional | | -| perp_market_task | [OracleJob.PerpMarketTask](#OracleJob.PerpMarketTask) | optional | | -| oracle_task | [OracleJob.OracleTask](#OracleJob.OracleTask) | optional | | -| anchor_fetch_task | [OracleJob.AnchorFetchTask](#OracleJob.AnchorFetchTask) | optional | | -| defi_kingdoms_task | [OracleJob.DefiKingdomsTask](#OracleJob.DefiKingdomsTask) | optional | | -| tps_task | [OracleJob.TpsTask](#OracleJob.TpsTask) | optional | | -| spl_stake_pool_task | [OracleJob.SplStakePoolTask](#OracleJob.SplStakePoolTask) | optional | | -| spl_token_parse_task | [OracleJob.SplTokenParseTask](#OracleJob.SplTokenParseTask) | optional | | -| uniswap_exchange_rate_task | [OracleJob.UniswapExchangeRateTask](#OracleJob.UniswapExchangeRateTask) | optional | | -| sushiswap_exchange_rate_task | [OracleJob.SushiswapExchangeRateTask](#OracleJob.SushiswapExchangeRateTask) | optional | | + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| http_task | [OracleJob.HttpTask](#OracleJob.HttpTask) | optional | | +| json_parse_task | [OracleJob.JsonParseTask](#OracleJob.JsonParseTask) | optional | | +| median_task | [OracleJob.MedianTask](#OracleJob.MedianTask) | optional | | +| mean_task | [OracleJob.MeanTask](#OracleJob.MeanTask) | optional | | +| websocket_task | [OracleJob.WebsocketTask](#OracleJob.WebsocketTask) | optional | | +| divide_task | [OracleJob.DivideTask](#OracleJob.DivideTask) | optional | | +| multiply_task | [OracleJob.MultiplyTask](#OracleJob.MultiplyTask) | optional | | +| lp_token_price_task | [OracleJob.LpTokenPriceTask](#OracleJob.LpTokenPriceTask) | optional | | +| lp_exchange_rate_task | [OracleJob.LpExchangeRateTask](#OracleJob.LpExchangeRateTask) | optional | | +| conditional_task | [OracleJob.ConditionalTask](#OracleJob.ConditionalTask) | optional | | +| value_task | [OracleJob.ValueTask](#OracleJob.ValueTask) | optional | | +| max_task | [OracleJob.MaxTask](#OracleJob.MaxTask) | optional | | +| regex_extract_task | [OracleJob.RegexExtractTask](#OracleJob.RegexExtractTask) | optional | | +| xstep_price_task | [OracleJob.XStepPriceTask](#OracleJob.XStepPriceTask) | optional | | +| add_task | [OracleJob.AddTask](#OracleJob.AddTask) | optional | | +| subtract_task | [OracleJob.SubtractTask](#OracleJob.SubtractTask) | optional | | +| twap_task | [OracleJob.TwapTask](#OracleJob.TwapTask) | optional | | +| serum_swap_task | [OracleJob.SerumSwapTask](#OracleJob.SerumSwapTask) | optional | | +| pow_task | [OracleJob.PowTask](#OracleJob.PowTask) | optional | | +| lending_rate_task | [OracleJob.LendingRateTask](#OracleJob.LendingRateTask) | optional | | +| mango_perp_market_task | [OracleJob.MangoPerpMarketTask](#OracleJob.MangoPerpMarketTask) | optional | | +| jupiter_swap_task | [OracleJob.JupiterSwapTask](#OracleJob.JupiterSwapTask) | optional | | +| perp_market_task | [OracleJob.PerpMarketTask](#OracleJob.PerpMarketTask) | optional | | +| oracle_task | [OracleJob.OracleTask](#OracleJob.OracleTask) | optional | | +| anchor_fetch_task | [OracleJob.AnchorFetchTask](#OracleJob.AnchorFetchTask) | optional | | +| defi_kingdoms_task | [OracleJob.DefiKingdomsTask](#OracleJob.DefiKingdomsTask) | optional | | +| tps_task | [OracleJob.TpsTask](#OracleJob.TpsTask) | optional | | +| spl_stake_pool_task | [OracleJob.SplStakePoolTask](#OracleJob.SplStakePoolTask) | optional | | +| spl_token_parse_task | [OracleJob.SplTokenParseTask](#OracleJob.SplTokenParseTask) | optional | | +| uniswap_exchange_rate_task | [OracleJob.UniswapExchangeRateTask](#OracleJob.UniswapExchangeRateTask) | optional | | +| sushiswap_exchange_rate_task | [OracleJob.SushiswapExchangeRateTask](#OracleJob.SushiswapExchangeRateTask) | optional | | +| pancakeswap_exchange_rate_task | [OracleJob.PancakeswapExchangeRateTask](#OracleJob.PancakeswapExchangeRateTask) | optional | | +| cache_task | [OracleJob.CacheTask](#OracleJob.CacheTask) | optional | | +| sysclock_offset_task | [OracleJob.SysclockOffsetTask](#OracleJob.SysclockOffsetTask) | optional | | + + + + + ### OracleJob.TpsTask + + + + + + ### OracleJob.TwapTask - Takes a twap over a set period for a certain aggregator. -| Field | Type | Label | Description | -| -------------------------- | ------------- | -------- | -------------------------------------------------------------------- | -| aggregator_pubkey | string | optional | The target aggregator for the TWAP. | -| period | int32 | optional | Period, in seconds, the twap should account for | -| weight_by_propagation_time | [bool](#bool) | optional | Weight samples by their propagation time | -| min_samples | uint32 | optional | Minimum number of samples in the history to calculate a valid result | -| ending_unix_timestamp | int32 | optional | Ending unix timestamp to collect values up to | + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| aggregator_pubkey | [string](#string) | optional | The target aggregator for the TWAP. | +| period | [int32](#int32) | optional | Period, in seconds, the twap should account for | +| weight_by_propagation_time | [bool](#bool) | optional | Weight samples by their propagation time | +| min_samples | [uint32](#uint32) | optional | Minimum number of samples in the history to calculate a valid result | +| ending_unix_timestamp | [int32](#int32) | optional | Ending unix timestamp to collect values up to | + + + + + ### OracleJob.UniswapExchangeRateTask -| Field | Type | Label | Description | -| ----------------- | ------ | -------- | ----------- | -| in_token_address | string | optional | | -| out_token_address | string | optional | | -| in_token_amount | uint32 | optional | | -| slippage | double | optional | | -| provider | string | optional | | + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| in_token_address | [string](#string) | optional | | +| out_token_address | [string](#string) | optional | | +| in_token_amount | [double](#double) | optional | | +| slippage | [double](#double) | optional | | +| provider | [string](#string) | optional | | + + + + + ### OracleJob.ValueTask - Returns a specified value. -| Field | Type | Label | Description | -| ----------------- | ------ | -------- | ----------------------------------------------- | -| value | double | optional | The value that will be returned from this task. | -| aggregator_pubkey | string | optional | Specifies an aggregator to pull the value of. | + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| value | [double](#double) | optional | The value that will be returned from this task. | +| aggregator_pubkey | [string](#string) | optional | Specifies an aggregatorr to pull the value of. | + + + + + ### OracleJob.WebsocketTask - Opens and maintains a websocket for light speed data retrieval. -| Field | Type | Label | Description | -| -------------------- | ------ | -------- | --------------------------------------------------------------------------------------------------------------------------------- | -| url | string | optional | The websocket url. | -| subscription | string | optional | The websocket message to notify of a new subscription. | -| max_data_age_seconds | int32 | optional | Minimum amount of time required between when the horses are taking out. | -| filter | string | optional | Incoming message JSONPath filter. Example: "$[?(@.channel == 'ticker' && @.market == 'BTC/USD')]" | + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| url | [string](#string) | optional | The websocket url. | +| subscription | [string](#string) | optional | The websocket message to notify of a new subscription. | +| max_data_age_seconds | [int32](#int32) | optional | Minimum amount of time required between when the horses are taking out. | +| filter | [string](#string) | optional | Incoming message JSONPath filter. Example: "$[?(@.channel == 'ticker' && @.market == 'BTC/USD')]" | + + + + + ### OracleJob.XStepPriceTask -| Field | Type | Label | Description | -| ---------------------- | --------------------------------------------- | -------- | ---------------------------------------------------------------------- | -| step_job | [OracleJob.MedianTask](#OracleJob.MedianTask) | optional | median task containing the job definitions to fetch the STEP/USD price | -| step_aggregator_pubkey | string | optional | existing aggregator pubkey for STEP/USD | + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| step_job | [OracleJob.MedianTask](#OracleJob.MedianTask) | optional | median task containing the job definitions to fetch the STEP/USD price | +| step_aggregator_pubkey | [string](#string) | optional | existing aggregator pubkey for STEP/USD | + + + + + + + + + + +### OracleJob.CacheTask.Method + + +| Name | Number | Description | +| ---- | ------ | ----------- | +| METHOD_GET | 0 | | +| METHOD_SET | 1 | | + + ### OracleJob.HttpTask.Method - An enumeration representing the types of HTTP requests available to make. -| Name | Number | Description | -| -------------- | ------ | -------------------------------------------- | -| METHOD_UNKNOWN | 0 | Unset HTTP method will default to METHOD_GET | -| METHOD_GET | 1 | Perform an HTTP 'GET' request. | -| METHOD_POST | 2 | Perform an HTTP 'POST' request. | +| Name | Number | Description | +| ---- | ------ | ----------- | +| METHOD_UNKOWN | 0 | Unset HTTP method will default to METHOD_GET | +| METHOD_GET | 1 | Perform an HTTP 'GET' request. | +| METHOD_POST | 2 | Perform an HTTP 'POST' request. | + + ### OracleJob.JsonParseTask.AggregationMethod - The methods of combining a list of numerical results. -| Name | Number | Description | -| ---- | ------ | -------------------------------------- | -| NONE | 0 | | -| MIN | 1 | Grab the minimum value of the results. | -| MAX | 2 | Grab the maximum value of the results. | -| SUM | 3 | Sum up all of the results. | +| Name | Number | Description | +| ---- | ------ | ----------- | +| NONE | 0 | | +| MIN | 1 | Grab the minimum value of the results. | +| MAX | 2 | Grab the maximum value of the results. | +| SUM | 3 | Sum up all of the results. | + + ### OracleJob.LendingRateTask.Field -| Name | Number | Description | -| ------------------ | ------ | -------------------- | -| FIELD_DEPOSIT_RATE | 0 | deposit lending rate | -| FIELD_BORROW_RATE | 1 | borrow lending rate | + +| Name | Number | Description | +| ---- | ------ | ----------- | +| FIELD_DEPOSIT_RATE | 0 | deposit lending rate | +| FIELD_BORROW_RATE | 1 | borrow lending rate | + + + + + + + + + ## Scalar Value Types -| .proto Type | Notes | C++ | Java | Python | Go | C# | PHP | Ruby | -| ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- | ------ | ---------- | ----------- | ------- | ---------- | -------------- | ------------------------------ | -| double | | double | double | float | float64 | double | float | Float | -| float | | float | float | float | float32 | float | float | Float | -| int32 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) | -| int64 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead. | int64 | long | int/long | int64 | long | integer/string | Bignum | -| uint32 | Uses variable-length encoding. | uint32 | int | int/long | uint32 | uint | integer | Bignum or Fixnum (as required) | -| uint64 | Uses variable-length encoding. | uint64 | long | int/long | uint64 | ulong | integer/string | Bignum or Fixnum (as required) | -| sint32 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) | -| sint64 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s. | int64 | long | int/long | int64 | long | integer/string | Bignum | -| fixed32 | Always four bytes. More efficient than uint32 if values are often greater than 2^28. | uint32 | int | int | uint32 | uint | integer | Bignum or Fixnum (as required) | -| fixed64 | Always eight bytes. More efficient than uint64 if values are often greater than 2^56. | uint64 | long | int/long | uint64 | ulong | integer/string | Bignum | -| sfixed32 | Always four bytes. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) | -| sfixed64 | Always eight bytes. | int64 | long | int/long | int64 | long | integer/string | Bignum | -| bool | | bool | boolean | boolean | bool | bool | boolean | TrueClass/FalseClass | -| string | A string must always contain UTF-8 encoded or 7-bit ASCII text. | string | String | str/unicode | string | string | string | String (UTF-8) | -| bytes | May contain any arbitrary sequence of bytes. | string | ByteString | str | []byte | ByteString | string | String (ASCII-8BIT) | +| .proto Type | Notes | C++ | Java | Python | Go | C# | PHP | Ruby | +| ----------- | ----- | --- | ---- | ------ | -- | -- | --- | ---- | +| double | | double | double | float | float64 | double | float | Float | +| float | | float | float | float | float32 | float | float | Float | +| int32 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) | +| int64 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead. | int64 | long | int/long | int64 | long | integer/string | Bignum | +| uint32 | Uses variable-length encoding. | uint32 | int | int/long | uint32 | uint | integer | Bignum or Fixnum (as required) | +| uint64 | Uses variable-length encoding. | uint64 | long | int/long | uint64 | ulong | integer/string | Bignum or Fixnum (as required) | +| sint32 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) | +| sint64 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s. | int64 | long | int/long | int64 | long | integer/string | Bignum | +| fixed32 | Always four bytes. More efficient than uint32 if values are often greater than 2^28. | uint32 | int | int | uint32 | uint | integer | Bignum or Fixnum (as required) | +| fixed64 | Always eight bytes. More efficient than uint64 if values are often greater than 2^56. | uint64 | long | int/long | uint64 | ulong | integer/string | Bignum | +| sfixed32 | Always four bytes. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) | +| sfixed64 | Always eight bytes. | int64 | long | int/long | int64 | long | integer/string | Bignum | +| bool | | bool | boolean | boolean | bool | bool | boolean | TrueClass/FalseClass | +| string | A string must always contain UTF-8 encoded or 7-bit ASCII text. | string | String | str/unicode | string | string | string | String (UTF-8) | +| bytes | May contain any arbitrary sequence of bytes. | string | ByteString | str | []byte | ByteString | string | String (ASCII-8BIT) | + diff --git a/website/docs-1/oracle/gcp/automated.mdx b/website/docs-1/oracle/gcp/automated.mdx index 69c746c..68ac420 100644 --- a/website/docs-1/oracle/gcp/automated.mdx +++ b/website/docs-1/oracle/gcp/automated.mdx @@ -41,6 +41,6 @@ You will need to manually add: - BACKUP_MAINNET_RPC - ORACLE_KEY - GRAFANA_HOSTNAME -- GRAFANA_PASSWORD +- GRAFANA_ADMIN_PASSWORD - GRAFANA_TLS_CRT - GRAFANA_TLS_KEY diff --git a/website/docs-1/oracle/gcp/environment.mdx b/website/docs-1/oracle/gcp/environment.mdx index 7e6cc55..f45de14 100644 --- a/website/docs-1/oracle/gcp/environment.mdx +++ b/website/docs-1/oracle/gcp/environment.mdx @@ -187,7 +187,7 @@ You will need to collect the following environment variables to inject into the - GRAFANA_PASSWORD + GRAFANA_ADMIN_PASSWORD Optional diff --git a/website/docs-1/oracle/gcp/grafana.mdx b/website/docs-1/oracle/gcp/grafana.mdx index 322b0ae..0d06cb5 100644 --- a/website/docs-1/oracle/gcp/grafana.mdx +++ b/website/docs-1/oracle/gcp/grafana.mdx @@ -32,6 +32,6 @@ When the necessary keys and CSR are generated, head over to [gethttpsforfree.com Your `PROJECTNAME.env` file should now contain - GRAFANA_HOSTNAME -- GRAFANA_PASSWORD (You can set this to whatever value you want) +- GRAFANA_ADMIN_PASSWORD (You can set this to whatever value you want) - GRAFANA_TLS_CRT - GRAFANA_TLS_KEY diff --git a/website/docs-1/oracle/gcp/manual.mdx b/website/docs-1/oracle/gcp/manual.mdx index 117e846..6103e51 100644 --- a/website/docs-1/oracle/gcp/manual.mdx +++ b/website/docs-1/oracle/gcp/manual.mdx @@ -180,6 +180,6 @@ You will need to manually add: - BACKUP_MAINNET_RPC - ORACLE_KEY - GRAFANA_HOSTNAME -- GRAFANA_PASSWORD +- GRAFANA_ADMIN_PASSWORD - GRAFANA_TLS_CRT - GRAFANA_TLS_KEY diff --git a/website/docs/oracle/operator/gcp.mdx b/website/docs/oracle/operator/gcp.mdx index f3b1064..2c62ceb 100644 --- a/website/docs/oracle/operator/gcp.mdx +++ b/website/docs/oracle/operator/gcp.mdx @@ -207,7 +207,7 @@ You will need to collect the following environment variables to inject into the - GRAFANA_PASSWORD + GRAFANA_ADMIN_PASSWORD Optional @@ -287,7 +287,7 @@ You will need to manually add: - BACKUP_MAINNET_RPC - ORACLE_KEY - GRAFANA_HOSTNAME -- GRAFANA_PASSWORD +- GRAFANA_ADMIN_PASSWORD - GRAFANA_TLS_CRT - GRAFANA_TLS_KEY @@ -469,7 +469,7 @@ You will need to manually add: - BACKUP_MAINNET_RPC - ORACLE_KEY - GRAFANA_HOSTNAME -- GRAFANA_PASSWORD +- GRAFANA_ADMIN_PASSWORD - GRAFANA_TLS_CRT - GRAFANA_TLS_KEY @@ -502,7 +502,7 @@ When the necessary keys and CSR are generated, head over to [gethttpsforfree.com Your `PROJECTNAME.env` file should now contain - GRAFANA_HOSTNAME -- GRAFANA_PASSWORD (You can set this to whatever value you want) +- GRAFANA_ADMIN_PASSWORD (You can set this to whatever value you want) - GRAFANA_TLS_CRT - GRAFANA_TLS_KEY diff --git a/yarn.lock b/yarn.lock index a341b8f..0ce301f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4668,6 +4668,18 @@ "@svgr/plugin-jsx" "^6.2.1" "@svgr/plugin-svgo" "^6.2.0" +"@switchboard-xyz/switchboard-api@^0.2.201": + version "0.2.201" + resolved "https://registry.npmjs.org/@switchboard-xyz/switchboard-api/-/switchboard-api-0.2.201.tgz#d082206d521d24dbcdeb06a77e6637a56ab883eb" + integrity sha512-hlxgeYmO6dbOEcmQzT1SqRxdiCFyVOMpyW4HFPgmPKT0+wSVkjsLc+BKkMGYPDaO0sWMLTJrj0FGhhTsrqd8Mg== + dependencies: + "@solana/web3.js" "^1.17.0" + form-data "^4.0.0" + protobufjs "^6.10.2" + rpc-websockets "^7.4.12" + typedoc "^0.22.15" + ws "^7.4.6" + "@szmarczak/http-timer@^1.1.2": version "1.1.2" resolved "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz" @@ -6884,7 +6896,7 @@ combine-promises@^1.1.0: resolved "https://registry.npmjs.org/combine-promises/-/combine-promises-1.1.0.tgz" integrity sha512-ZI9jvcLDxqwaXEixOhArm3r7ReIivsXkpbyEWyeOhzz1QS0iSgBPnWvEqvIQtYyamGCYA88gFhmUrs9hrrQ0pg== -combined-stream@^1.0.6, combined-stream@~1.0.6: +combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: version "1.0.8" resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== @@ -9169,6 +9181,15 @@ fork-ts-checker-webpack-plugin@^6.5.0: semver "^7.3.2" tapable "^1.0.0" +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + form-data@~2.3.2: version "2.3.3" resolved "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz" @@ -14600,7 +14621,7 @@ rimraf@^3.0.0, rimraf@^3.0.2: dependencies: glob "^7.1.3" -rpc-websockets@^7.4.2: +rpc-websockets@^7.4.12, rpc-websockets@^7.4.2: version "7.4.18" resolved "https://registry.npmjs.org/rpc-websockets/-/rpc-websockets-7.4.18.tgz" integrity sha512-bVu+4qM5CkGVlTqJa6FaAxLbb5uRnyH4te7yjFvoCzbnif7PT4BcvXtNTprHlNvsH+/StB81zUQicxMrUrIomA== @@ -16894,6 +16915,11 @@ ws@^7.0.0, ws@^7.3.1, ws@^7.4.5: resolved "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz" integrity sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A== +ws@^7.4.6: + version "7.5.8" + resolved "https://registry.npmjs.org/ws/-/ws-7.5.8.tgz#ac2729881ab9e7cbaf8787fe3469a48c5c7f636a" + integrity sha512-ri1Id1WinAX5Jqn9HejiGb8crfRio0Qgu8+MtL36rlTA6RLsMdWt1Az/19A2Qij6uSHUMphEFaTKa4WG+UNHNw== + ws@^8.4.2, ws@^8.5.0: version "8.5.0" resolved "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz"