Merge branch 'fix-create-agg-cli' into main

This commit is contained in:
Conner Gallagher 2022-05-30 22:42:40 -06:00
commit 7d56377292
24 changed files with 1421 additions and 668 deletions

View File

@ -13,7 +13,6 @@ npm install -g @switchboard-xyz/switchboardv2-cli
<!-- commands -->
* [`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

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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<Buffer>(),
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<PublicKey> = [];
const jobWallets: Array<PublicKey> = [];
const walletBumps: Array<number> = [];
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<Buffer>(),
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<Buffer>, 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)
);
}

View File

@ -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<Buffer>(),
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<Buffer>, 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) {

View File

@ -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);
}

View File

@ -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");
}
}

View File

@ -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`)}`
);

View File

@ -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

View File

@ -1,4 +1,3 @@
/* eslint-disable unicorn/import-style */
import { flags } from "@oclif/command";
import { PublicKey } from "@solana/web3.js";
import {

View File

@ -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`)}`
);

View File

@ -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 {

View File

@ -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);
};

View File

@ -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;

View File

@ -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"
}
}
}

View File

@ -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

View File

@ -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",

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -187,7 +187,7 @@ You will need to collect the following environment variables to inject into the
</td>
</tr>
<tr>
<td>GRAFANA_PASSWORD</td>
<td>GRAFANA_ADMIN_PASSWORD</td>
<td>
<b>
<u>Optional</u>

View File

@ -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

View File

@ -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

View File

@ -207,7 +207,7 @@ You will need to collect the following environment variables to inject into the
</td>
</tr>
<tr>
<td>GRAFANA_PASSWORD</td>
<td>GRAFANA_ADMIN_PASSWORD</td>
<td>
<b>
<u>Optional</u>
@ -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

View File

@ -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"