wip
This commit is contained in:
parent
37a21ee6f6
commit
965fc7a148
17
README.md
17
README.md
|
@ -49,6 +49,16 @@ yarn workspaces run build
|
|||
yarn workspace @switchboard-xyz/switchboardv2-cli link
|
||||
```
|
||||
|
||||
### Rust Setup
|
||||
|
||||
The following command will build the anchor projects and update the program IDs
|
||||
|
||||
```
|
||||
anchor build
|
||||
node scripts/setup-example-programs
|
||||
anchor test
|
||||
```
|
||||
|
||||
### Python Setup
|
||||
|
||||
```
|
||||
|
@ -57,10 +67,13 @@ cd libraries/py
|
|||
poetry install
|
||||
```
|
||||
|
||||
### Build
|
||||
### Localnet Testing Setup
|
||||
|
||||
You may wish to run your own oracle for integration test. The following command will create a devnet Switchboard environment and output a `Switchboard.env` file to assist copying
|
||||
|
||||
```
|
||||
yarn workspaces run build
|
||||
sbv2 localnet:env --keypair ../payer-keypair.json
|
||||
chmod +x ./start-local-validator.sh && chmod +x ./start-oracle.sh
|
||||
```
|
||||
|
||||
## Test
|
||||
|
|
|
@ -169,11 +169,17 @@ OPTIONS
|
|||
|
||||
--queueKey=queueKey (required) public key of the queue to create aggregator for
|
||||
|
||||
--sourceCluster=devnet|mainnet-beta alternative solana cluster to copy source aggregator from
|
||||
|
||||
--varianceThreshold=varianceThreshold override source aggregator's varianceThreshold
|
||||
|
||||
EXAMPLE
|
||||
$ sbv2 aggregator:create:copy 8SXvChNYFhRq4EZuZvnhjrB3jJRQCv4k3P4W6hesH3Ee
|
||||
AY3vpUu6v49shWajeFjHjgikYfaBWNJgax8zoEouUDTs --keypair ../payer-keypair.json
|
||||
EXAMPLES
|
||||
$ sbv2 aggregator:create:copy GvDMxPzN1sCj7L26YDK2HnMRXEQmQ2aemov8YBtPS7vR --queueKey
|
||||
9WZ59yz95bd3XwJxDPVE2PjvVWmSy9WM1NgGD2Hqsohw --keypair ../payer-keypair.json
|
||||
$ sbv2 aggregator:create:copy GvDMxPzN1sCj7L26YDK2HnMRXEQmQ2aemov8YBtPS7vR --queueKey
|
||||
9WZ59yz95bd3XwJxDPVE2PjvVWmSy9WM1NgGD2Hqsohw --keypair ../payer-keypair.json --sourceCluster mainnet-beta
|
||||
$ sbv2 aggregator:create:copy FcSmdsdWks75YdyCGegRqXdt5BiNGQKxZywyzb8ckD7D --queueKey
|
||||
9WZ59yz95bd3XwJxDPVE2PjvVWmSy9WM1NgGD2Hqsohw --keypair ../payer-keypair.json --sourceCluster mainnet-beta
|
||||
```
|
||||
|
||||
_See code: [src/commands/aggregator/create/copy.ts](https://github.com/switchboard-xyz/switchboard-v2/blob/v0.1.18/src/commands/aggregator/create/copy.ts)_
|
||||
|
@ -1302,22 +1308,24 @@ USAGE
|
|||
$ sbv2 localnet:env
|
||||
|
||||
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
|
||||
|
||||
-s, --silent suppress cli prompts
|
||||
-o, --outputDir=outputDir output directory for scripts
|
||||
|
||||
-u, --rpcUrl=rpcUrl alternate RPC url
|
||||
-s, --silent suppress cli prompts
|
||||
|
||||
-v, --verbose log everything
|
||||
-u, --rpcUrl=rpcUrl alternate RPC url
|
||||
|
||||
--force overwrite output file if existing
|
||||
-v, --verbose log everything
|
||||
|
||||
--mainnetBeta WARNING: use mainnet-beta solana cluster
|
||||
--force overwrite output file if existing
|
||||
|
||||
--programId=programId alternative Switchboard program ID to interact with
|
||||
--mainnetBeta WARNING: use mainnet-beta solana cluster
|
||||
|
||||
--programId=programId alternative Switchboard program ID to interact with
|
||||
```
|
||||
|
||||
_See code: [src/commands/localnet/env.ts](https://github.com/switchboard-xyz/switchboard-v2/blob/v0.1.18/src/commands/localnet/env.ts)_
|
||||
|
@ -1635,22 +1643,24 @@ ARGUMENTS
|
|||
AGGREGATORKEY public key of the aggregator account to deserialize
|
||||
|
||||
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
|
||||
|
||||
-s, --silent suppress cli prompts
|
||||
-o, --oraclePubkeysData print the assigned oracles for the current round
|
||||
|
||||
-u, --rpcUrl=rpcUrl alternate RPC url
|
||||
-s, --silent suppress cli prompts
|
||||
|
||||
-v, --verbose log everything
|
||||
-u, --rpcUrl=rpcUrl alternate RPC url
|
||||
|
||||
--jobs output job definitions
|
||||
-v, --verbose log everything
|
||||
|
||||
--mainnetBeta WARNING: use mainnet-beta solana cluster
|
||||
--jobs output job definitions
|
||||
|
||||
--programId=programId alternative Switchboard program ID to interact with
|
||||
--mainnetBeta WARNING: use mainnet-beta solana cluster
|
||||
|
||||
--programId=programId alternative Switchboard program ID to interact with
|
||||
|
||||
ALIASES
|
||||
$ sbv2 aggregator:print
|
||||
|
@ -2145,6 +2155,8 @@ OPTIONS
|
|||
|
||||
-v, --verbose log everything
|
||||
|
||||
--enableBufferRelayers enable oracles to fulfill buffer relayer requests
|
||||
|
||||
--force overwrite output file if existing
|
||||
|
||||
--mainnetBeta WARNING: use mainnet-beta solana cluster
|
||||
|
@ -2157,6 +2169,8 @@ OPTIONS
|
|||
|
||||
--unpermissionedFeeds permit unpermissioned feeds
|
||||
|
||||
--unpermissionedVrf permit unpermissioned VRF accounts
|
||||
|
||||
ALIASES
|
||||
$ sbv2 custom:queue
|
||||
```
|
||||
|
|
|
@ -98,8 +98,8 @@
|
|||
"@oclif/plugin-warn-if-update-available": "^1.7.3",
|
||||
"@project-serum/anchor": "^0.24.2",
|
||||
"@solana/spl-token": "^0.1.8",
|
||||
"@solana/web3.js": "^1.41.10",
|
||||
"@switchboard-xyz/sbv2-utils": "^0.0.9",
|
||||
"@solana/web3.js": "1.39.1",
|
||||
"@switchboard-xyz/sbv2-utils": "^0.0.10",
|
||||
"@switchboard-xyz/switchboard-v2": "0.0.97",
|
||||
"assert": "^2.0.0",
|
||||
"big.js": "^6.1.1",
|
||||
|
|
|
@ -17,6 +17,7 @@ import {
|
|||
CrankAccount,
|
||||
JobAccount,
|
||||
LeaseAccount,
|
||||
loadSwitchboardProgram,
|
||||
OracleJob,
|
||||
OracleQueueAccount,
|
||||
PermissionAccount,
|
||||
|
@ -69,6 +70,11 @@ 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"],
|
||||
}),
|
||||
};
|
||||
|
||||
static args = [
|
||||
|
@ -81,7 +87,9 @@ export default class AggregatorCreateCopy extends BaseCommand {
|
|||
];
|
||||
|
||||
static examples = [
|
||||
"$ sbv2 aggregator:create:copy 8SXvChNYFhRq4EZuZvnhjrB3jJRQCv4k3P4W6hesH3Ee AY3vpUu6v49shWajeFjHjgikYfaBWNJgax8zoEouUDTs --keypair ../payer-keypair.json",
|
||||
"$ sbv2 aggregator:create:copy GvDMxPzN1sCj7L26YDK2HnMRXEQmQ2aemov8YBtPS7vR --queueKey 9WZ59yz95bd3XwJxDPVE2PjvVWmSy9WM1NgGD2Hqsohw --keypair ../payer-keypair.json",
|
||||
"$ sbv2 aggregator:create:copy GvDMxPzN1sCj7L26YDK2HnMRXEQmQ2aemov8YBtPS7vR --queueKey 9WZ59yz95bd3XwJxDPVE2PjvVWmSy9WM1NgGD2Hqsohw --keypair ../payer-keypair.json --sourceCluster mainnet-beta",
|
||||
"$ sbv2 aggregator:create:copy FcSmdsdWks75YdyCGegRqXdt5BiNGQKxZywyzb8ckD7D --queueKey 9WZ59yz95bd3XwJxDPVE2PjvVWmSy9WM1NgGD2Hqsohw --keypair ../payer-keypair.json --sourceCluster mainnet-beta",
|
||||
];
|
||||
|
||||
async run() {
|
||||
|
@ -90,6 +98,42 @@ export default class AggregatorCreateCopy extends BaseCommand {
|
|||
|
||||
const payerKeypair = programWallet(this.program);
|
||||
|
||||
const sourceProgram = !flags.sourceCluster
|
||||
? this.program
|
||||
: flags.sourceCluster === "devnet" ||
|
||||
flags.sourceCluster === "mainnet-beta"
|
||||
? await loadSwitchboardProgram(
|
||||
flags.sourceCluster,
|
||||
undefined,
|
||||
payerKeypair
|
||||
)
|
||||
: undefined;
|
||||
if (sourceProgram === undefined) {
|
||||
throw new Error(`Invalid sourceAggregatorCluster ${flags.sourceCluster}`);
|
||||
}
|
||||
const sourceAggregatorAccount = new AggregatorAccount({
|
||||
program: sourceProgram,
|
||||
publicKey: args.aggregatorSource,
|
||||
});
|
||||
|
||||
const sourceAggregator = await sourceAggregatorAccount.loadData();
|
||||
const sourceJobPubkeys: PublicKey[] = sourceAggregator.jobPubkeysData.slice(
|
||||
0,
|
||||
sourceAggregator.jobPubkeysSize
|
||||
);
|
||||
|
||||
const sourceJobAccounts = sourceJobPubkeys.map((publicKey) => {
|
||||
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
|
||||
);
|
||||
|
@ -105,28 +149,6 @@ export default class AggregatorCreateCopy extends BaseCommand {
|
|||
await tokenMint.getOrCreateAssociatedAccountInfo(payerKeypair.publicKey)
|
||||
).address;
|
||||
|
||||
const sourceAggregatorAccount = new AggregatorAccount({
|
||||
program: this.program,
|
||||
publicKey: args.aggregatorSource,
|
||||
});
|
||||
const sourceAggregator = await sourceAggregatorAccount.loadData();
|
||||
const sourceJobPubkeys: PublicKey[] = sourceAggregator.jobPubkeysData.slice(
|
||||
0,
|
||||
sourceAggregator.jobPubkeysSize
|
||||
);
|
||||
|
||||
const sourceJobAccounts = sourceJobPubkeys.map((publicKey) => {
|
||||
return new JobAccount({ program: this.program, 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 createAccountInstructions: (
|
||||
| TransactionInstruction
|
||||
| TransactionInstruction[]
|
||||
|
|
|
@ -18,6 +18,10 @@ export default class LocalnetEnvironment extends BaseCommand {
|
|||
description: "overwrite output file if existing",
|
||||
default: false,
|
||||
}),
|
||||
outputDir: flags.string({
|
||||
char: "o",
|
||||
description: "output directory for scripts",
|
||||
}),
|
||||
};
|
||||
|
||||
async run() {
|
||||
|
@ -25,24 +29,28 @@ export default class LocalnetEnvironment extends BaseCommand {
|
|||
const { flags } = this.parse(LocalnetEnvironment);
|
||||
const payerKeypair = programWallet(this.program);
|
||||
|
||||
const outputDir = flags.outputDir
|
||||
? path.join(process.cwd(), flags.outputDir)
|
||||
: process.cwd();
|
||||
|
||||
// TODO: Check paths and force flags
|
||||
if (!flags.force) {
|
||||
if (fs.existsSync(path.join(process.cwd(), "switchboard.env"))) {
|
||||
if (fs.existsSync(path.join(outputDir, "switchboard.env"))) {
|
||||
throw new Error(
|
||||
"switchboard.env already exists, use --force to overwrite"
|
||||
);
|
||||
}
|
||||
if (fs.existsSync(path.join(process.cwd(), "switchboard.json"))) {
|
||||
if (fs.existsSync(path.join(outputDir, "switchboard.json"))) {
|
||||
throw new Error(
|
||||
"switchboard.json already exists, use --force to overwrite"
|
||||
);
|
||||
}
|
||||
if (fs.existsSync(path.join(process.cwd(), "start-local-validator.sh"))) {
|
||||
if (fs.existsSync(path.join(outputDir, "start-local-validator.sh"))) {
|
||||
throw new Error(
|
||||
"start-local-validator.sh already exists, use --force to overwrite"
|
||||
);
|
||||
}
|
||||
if (fs.existsSync(path.join(process.cwd(), "start-oracle.sh"))) {
|
||||
if (fs.existsSync(path.join(outputDir, "start-oracle.sh"))) {
|
||||
throw new Error(
|
||||
"start-oracle.sh already exists, use --force to overwrite"
|
||||
);
|
||||
|
@ -70,7 +78,8 @@ export default class LocalnetEnvironment extends BaseCommand {
|
|||
new PublicKey(flags.programId)
|
||||
);
|
||||
// TODO: Add silent flag
|
||||
testEnvironment.writeAll(flags.keypair, process.cwd());
|
||||
fs.mkdirSync(outputDir, { recursive: true });
|
||||
testEnvironment.writeAll(flags.keypair, outputDir);
|
||||
}
|
||||
|
||||
async catch(error) {
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
/* eslint-disable unicorn/import-style */
|
||||
import { flags } from "@oclif/command";
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
import { prettyPrintAggregator } from "@switchboard-xyz/sbv2-utils";
|
||||
import {
|
||||
chalkString,
|
||||
prettyPrintAggregator,
|
||||
} from "@switchboard-xyz/sbv2-utils";
|
||||
import { AggregatorAccount } from "@switchboard-xyz/switchboard-v2";
|
||||
import BaseCommand from "../../BaseCommand";
|
||||
|
||||
|
@ -16,6 +19,10 @@ export default class AggregatorPrint extends BaseCommand {
|
|||
description: "output job definitions",
|
||||
default: false,
|
||||
}),
|
||||
oraclePubkeysData: flags.boolean({
|
||||
char: "o",
|
||||
description: "print the assigned oracles for the current round",
|
||||
}),
|
||||
};
|
||||
|
||||
static args = [
|
||||
|
@ -49,6 +56,19 @@ export default class AggregatorPrint extends BaseCommand {
|
|||
flags.jobs
|
||||
)
|
||||
);
|
||||
|
||||
if (flags.oraclePubkeysData) {
|
||||
this.logger.log(
|
||||
chalkString(
|
||||
"oraclePubkeyData",
|
||||
"\n" +
|
||||
(aggregator.currentRound.oraclePubkeysData as PublicKey[])
|
||||
.filter((pubkey) => !PublicKey.default.equals(pubkey))
|
||||
.map((pubkey) => pubkey.toString())
|
||||
.join("\n")
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async catch(error) {
|
||||
|
|
|
@ -4,17 +4,16 @@ import { flags } from "@oclif/command";
|
|||
import * as anchor from "@project-serum/anchor";
|
||||
import * as spl from "@solana/spl-token";
|
||||
import {
|
||||
AccountInfo,
|
||||
Keypair,
|
||||
SystemProgram,
|
||||
TransactionInstruction,
|
||||
} from "@solana/web3.js";
|
||||
import {
|
||||
chalkString,
|
||||
packAndSend,
|
||||
prettyPrintCrank,
|
||||
prettyPrintOracle,
|
||||
prettyPrintQueue,
|
||||
promiseWithTimeout,
|
||||
} from "@switchboard-xyz/sbv2-utils";
|
||||
import {
|
||||
CrankAccount,
|
||||
|
@ -30,7 +29,6 @@ import fs from "fs";
|
|||
import path from "path";
|
||||
import BaseCommand from "../../BaseCommand";
|
||||
import { sleep, verifyProgramHasPayer } from "../../utils";
|
||||
import { packAndSend } from "../../utils/transaction";
|
||||
|
||||
export default class QueueCreate extends BaseCommand {
|
||||
static description = "create a custom queue";
|
||||
|
@ -85,6 +83,14 @@ export default class QueueCreate extends BaseCommand {
|
|||
description: "permit unpermissioned feeds",
|
||||
default: false,
|
||||
}),
|
||||
unpermissionedVrf: flags.boolean({
|
||||
description: "permit unpermissioned VRF accounts",
|
||||
default: false,
|
||||
}),
|
||||
enableBufferRelayers: flags.boolean({
|
||||
description: "enable oracles to fulfill buffer relayer requests",
|
||||
default: false,
|
||||
}),
|
||||
outputFile: flags.string({
|
||||
char: "f",
|
||||
description: "output queue schema to a json file",
|
||||
|
@ -96,6 +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 outputPath =
|
||||
flags.outputFile === undefined
|
||||
|
@ -109,6 +116,9 @@ export default class QueueCreate extends BaseCommand {
|
|||
}
|
||||
|
||||
const authorityKeypair = await this.loadAuthority(flags.authority);
|
||||
if (!authorityKeypair.publicKey.equals(payerKeypair.publicKey)) {
|
||||
signers.push(authorityKeypair);
|
||||
}
|
||||
const [programStateAccount, stateBump] = ProgramStateAccount.fromSeed(
|
||||
this.program
|
||||
);
|
||||
|
@ -120,7 +130,7 @@ export default class QueueCreate extends BaseCommand {
|
|||
);
|
||||
|
||||
const ixns: (TransactionInstruction | TransactionInstruction[])[] = [];
|
||||
const signers: Keypair[] = [payerKeypair, authorityKeypair];
|
||||
// const signers: Keypair[] = [payerKeypair, authorityKeypair];
|
||||
|
||||
try {
|
||||
await programStateAccount.loadData();
|
||||
|
@ -215,6 +225,8 @@ export default class QueueCreate extends BaseCommand {
|
|||
minimumDelaySeconds: 5,
|
||||
queueSize: flags.queueSize,
|
||||
unpermissionedFeeds: flags.unpermissionedFeeds ?? false,
|
||||
unpermissionedVrf: flags.unpermissionedVrf ?? false,
|
||||
enableBufferRelayers: flags.enableBufferRelayers ?? false,
|
||||
})
|
||||
.accounts({
|
||||
oracleQueue: queueKeypair.publicKey,
|
||||
|
@ -345,35 +357,16 @@ export default class QueueCreate extends BaseCommand {
|
|||
})
|
||||
);
|
||||
|
||||
const createAccountSignatures = packAndSend(
|
||||
// console.log(`${signers.map((s) => s.publicKey.toString()).join("\n")}`);
|
||||
|
||||
const createAccountSignatures = await packAndSend(
|
||||
this.program,
|
||||
ixns,
|
||||
finalTransactions,
|
||||
[ixns, finalTransactions],
|
||||
signers,
|
||||
payerKeypair.publicKey
|
||||
);
|
||||
|
||||
let queueWs: number;
|
||||
const customQueuePromise = new Promise((resolve: (result: any) => void) => {
|
||||
queueWs = this.program.provider.connection.onAccountChange(
|
||||
queueAccount.publicKey,
|
||||
(accountInfo: AccountInfo<Buffer>, slot) => {
|
||||
const accountCoder = new anchor.BorshAccountsCoder(this.program.idl);
|
||||
resolve(
|
||||
accountCoder.decode("OracleQueueAccountData", accountInfo.data)
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
const queueData = await promiseWithTimeout(
|
||||
22_000,
|
||||
customQueuePromise
|
||||
).finally(() => {
|
||||
try {
|
||||
this.program.provider.connection.removeAccountChangeListener(queueWs);
|
||||
} catch {}
|
||||
});
|
||||
const queueData = await queueAccount.loadData();
|
||||
|
||||
if (outputPath) {
|
||||
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
||||
|
|
|
@ -22,15 +22,14 @@ export async function packAndSend(
|
|||
|
||||
const packedTransactions = packInstructions(ixnsBatch, feePayer, blockhash);
|
||||
const signedTransactions = signTransactions(packedTransactions, signers);
|
||||
const signedTxs = await (
|
||||
program.provider as anchor.AnchorProvider
|
||||
).wallet.signAllTransactions(signedTransactions);
|
||||
// const signedTxs = await (
|
||||
// program.provider as anchor.AnchorProvider
|
||||
// ).wallet.signAllTransactions(signedTransactions);
|
||||
|
||||
for (let k = 0; k < packedTransactions.length; k += 1) {
|
||||
const tx = signedTxs[k];
|
||||
const rawTx = tx.serialize();
|
||||
const tx = signedTransactions[k];
|
||||
signatures.push(
|
||||
program.provider.connection.sendRawTransaction(rawTx, {
|
||||
program.provider.connection.sendTransaction(tx, signers, {
|
||||
skipPreflight: true,
|
||||
maxRetries: 10,
|
||||
})
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
"@project-serum/anchor": "^0.24.2",
|
||||
"@saberhq/token-utils": "^1.12.68",
|
||||
"@solana/spl-token": "^0.1.8",
|
||||
"@solana/web3.js": "^1.37.1",
|
||||
"@solana/web3.js": "1.39.1",
|
||||
"@switchboard-xyz/switchboard-v2": "^0.0.97",
|
||||
"big.js": "^6.1.1",
|
||||
"chalk": "4",
|
||||
|
|
|
@ -1,326 +0,0 @@
|
|||
import * as anchor from "@project-serum/anchor";
|
||||
import * as spl from "@solana/spl-token";
|
||||
import {
|
||||
Keypair,
|
||||
PublicKey,
|
||||
SystemProgram,
|
||||
TransactionInstruction,
|
||||
} from "@solana/web3.js";
|
||||
import {
|
||||
AggregatorAccount,
|
||||
CrankAccount,
|
||||
JobAccount,
|
||||
LeaseAccount,
|
||||
OracleJob,
|
||||
OracleQueueAccount,
|
||||
PermissionAccount,
|
||||
ProgramStateAccount,
|
||||
SwitchboardDecimal,
|
||||
} from "@switchboard-xyz/switchboard-v2";
|
||||
import type Big from "big.js";
|
||||
|
||||
interface CopyAggregatorParameters {
|
||||
authority?: PublicKey;
|
||||
minOracles?: number;
|
||||
batchSize?: number;
|
||||
minJobs?: number;
|
||||
minUpdateDelay?: number;
|
||||
forceReportPeriod?: number;
|
||||
varianceThreshold?: Big;
|
||||
crankKey?: PublicKey;
|
||||
}
|
||||
|
||||
export async function copyAggregatorTxn(
|
||||
payerKeypair: Keypair,
|
||||
sourceAggregatorAccount: AggregatorAccount,
|
||||
targetQueue: OracleQueueAccount,
|
||||
params: CopyAggregatorParameters
|
||||
) {
|
||||
// load source environment
|
||||
const sourceAggregator = await sourceAggregatorAccount.loadData();
|
||||
const sourceJobPubkeys: PublicKey[] = sourceAggregator.jobPubkeysData.slice(
|
||||
0,
|
||||
sourceAggregator.jobPubkeysSize
|
||||
);
|
||||
const sourceJobAccounts = sourceJobPubkeys.map((publicKey) => {
|
||||
return new JobAccount({
|
||||
program: sourceAggregatorAccount.program,
|
||||
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 program = targetQueue.program;
|
||||
|
||||
const [programStateAccount, stateBump] =
|
||||
ProgramStateAccount.fromSeed(program);
|
||||
const programState = await programStateAccount.loadData();
|
||||
const queue = await targetQueue.loadData();
|
||||
|
||||
const tokenMint = await targetQueue.loadMint();
|
||||
const tokenWallet = (
|
||||
await tokenMint.getOrCreateAssociatedAccountInfo(payerKeypair.publicKey)
|
||||
).address;
|
||||
|
||||
const createAccountInstructions: (
|
||||
| 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 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,
|
||||
targetQueue.publicKey,
|
||||
aggregatorKeypair.publicKey
|
||||
);
|
||||
|
||||
const aggregatorAccount = new AggregatorAccount({
|
||||
program: this.program,
|
||||
publicKey: aggregatorKeypair.publicKey,
|
||||
});
|
||||
|
||||
// Create lease and push to crank
|
||||
const [leaseAccount, leaseBump] = LeaseAccount.fromSeed(
|
||||
this.program,
|
||||
targetQueue,
|
||||
aggregatorAccount
|
||||
);
|
||||
const leaseEscrow = await spl.Token.getAssociatedTokenAddress(
|
||||
spl.ASSOCIATED_TOKEN_PROGRAM_ID,
|
||||
spl.TOKEN_PROGRAM_ID,
|
||||
tokenMint.publicKey,
|
||||
leaseAccount.publicKey,
|
||||
true
|
||||
);
|
||||
|
||||
const jobPubkeys: Array<PublicKey> = [];
|
||||
const jobWallets: Array<PublicKey> = [];
|
||||
const walletBumps: Array<number> = [];
|
||||
for (const 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
|
||||
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: sourceAggregator.name,
|
||||
metadata: sourceAggregator.metadata,
|
||||
batchSize:
|
||||
params.batchSize ?? sourceAggregator.oracleRequestBatchSize,
|
||||
minOracleResults:
|
||||
params.minOracles ?? sourceAggregator.minOracleResults,
|
||||
minJobResults: params.minJobs ?? sourceAggregator.minJobResults,
|
||||
minUpdateDelaySeconds:
|
||||
params.minUpdateDelay ?? sourceAggregator.minUpdateDelaySeconds,
|
||||
varianceThreshold: params.varianceThreshold
|
||||
? SwitchboardDecimal.fromBig(params.varianceThreshold)
|
||||
: sourceAggregator.varianceThreshold,
|
||||
forceReportPeriod:
|
||||
params.forceReportPeriod ?? sourceAggregator.forceReportPeriod,
|
||||
stateBump,
|
||||
})
|
||||
.accounts({
|
||||
aggregator: aggregatorKeypair.publicKey,
|
||||
authority: payerKeypair.publicKey,
|
||||
queue: targetQueue.publicKey,
|
||||
authorWallet: tokenWallet,
|
||||
programState: programStateAccount.publicKey,
|
||||
})
|
||||
.instruction(),
|
||||
// create permissions
|
||||
await this.program.methods
|
||||
.permissionInit({})
|
||||
.accounts({
|
||||
permission: permissionAccount.publicKey,
|
||||
authority: queue.authority,
|
||||
granter: targetQueue.publicKey,
|
||||
grantee: aggregatorKeypair.publicKey,
|
||||
payer: payerKeypair.publicKey,
|
||||
systemProgram: SystemProgram.programId,
|
||||
})
|
||||
.instruction(),
|
||||
payerKeypair.publicKey.equals(queue.authority)
|
||||
? await this.program.methods
|
||||
.permissionSet({
|
||||
permission: { permitOracleQueueUsage: null },
|
||||
enable: true,
|
||||
})
|
||||
.accounts({
|
||||
permission: permissionAccount.publicKey,
|
||||
authority: queue.authority,
|
||||
})
|
||||
.instruction()
|
||||
: undefined,
|
||||
spl.Token.createAssociatedTokenAccountInstruction(
|
||||
spl.ASSOCIATED_TOKEN_PROGRAM_ID,
|
||||
spl.TOKEN_PROGRAM_ID,
|
||||
tokenMint.publicKey,
|
||||
leaseEscrow,
|
||||
leaseAccount.publicKey,
|
||||
payerKeypair.publicKey
|
||||
),
|
||||
await this.program.methods
|
||||
.leaseInit({
|
||||
loadAmount: new anchor.BN(0),
|
||||
stateBump,
|
||||
leaseBump,
|
||||
withdrawAuthority: payerKeypair.publicKey,
|
||||
walletBumps: Buffer.from([]),
|
||||
})
|
||||
.accounts({
|
||||
programState: programStateAccount.publicKey,
|
||||
lease: leaseAccount.publicKey,
|
||||
queue: targetQueue.publicKey,
|
||||
aggregator: aggregatorAccount.publicKey,
|
||||
systemProgram: SystemProgram.programId,
|
||||
funder: tokenWallet,
|
||||
payer: payerKeypair.publicKey,
|
||||
tokenProgram: spl.TOKEN_PROGRAM_ID,
|
||||
escrow: leaseEscrow,
|
||||
owner: payerKeypair.publicKey,
|
||||
mint: tokenMint.publicKey,
|
||||
})
|
||||
// .remainingAccounts(
|
||||
// jobPubkeys.concat(jobWallets).map((pubkey: PublicKey) => {
|
||||
// return { isSigner: false, isWritable: true, pubkey };
|
||||
// })
|
||||
// )
|
||||
.instruction(),
|
||||
params.crankKey
|
||||
? await this.program.methods
|
||||
.crankPush({
|
||||
stateBump,
|
||||
permissionBump,
|
||||
})
|
||||
.accounts({
|
||||
crank: new PublicKey(params.crankKey),
|
||||
aggregator: aggregatorAccount.publicKey,
|
||||
oracleQueue: targetQueue.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(params.crankKey),
|
||||
}).loadData()
|
||||
).dataBuffer,
|
||||
})
|
||||
.instruction()
|
||||
: undefined,
|
||||
].filter((item) => item)
|
||||
);
|
||||
|
||||
const finalInstructions: (
|
||||
| TransactionInstruction
|
||||
| TransactionInstruction[]
|
||||
)[] = [];
|
||||
|
||||
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();
|
||||
})
|
||||
))
|
||||
);
|
||||
return "";
|
||||
}
|
|
@ -6,7 +6,7 @@ export * from "./date";
|
|||
export * from "./errors";
|
||||
export * from "./nonce";
|
||||
export * from "./print";
|
||||
export * from "./solana";
|
||||
export * from "./state";
|
||||
export * from "./switchboard";
|
||||
export * from "./test";
|
||||
export * from "./transaction";
|
||||
|
|
|
@ -284,11 +284,16 @@ export async function prettyPrintQueue(
|
|||
outputString += chalk.underline(
|
||||
chalkString("\r\n## Oracles", " ".repeat(32), SPACING) + "\r\n"
|
||||
);
|
||||
data.queue.forEach(
|
||||
(row: PublicKey, index) =>
|
||||
(outputString +=
|
||||
chalkString(`# ${index + 1}`, row.toString(), SPACING) + "\r\n")
|
||||
);
|
||||
outputString += (data.queue as PublicKey[])
|
||||
.filter((pubkey) => !PublicKey.default.equals(pubkey))
|
||||
.map((pubkey) => pubkey.toString())
|
||||
.join("\n");
|
||||
|
||||
// (data.queue as PublicKey[]).forEach(
|
||||
// (row, index) =>
|
||||
// (outputString +=
|
||||
// chalkString(`# ${index + 1},`, row.toString(), SPACING) + "\r\n")
|
||||
// );
|
||||
}
|
||||
|
||||
return outputString;
|
||||
|
|
|
@ -0,0 +1,302 @@
|
|||
import * as anchor from "@project-serum/anchor";
|
||||
import * as spl from "@solana/spl-token";
|
||||
import {
|
||||
Keypair,
|
||||
PublicKey,
|
||||
SystemProgram,
|
||||
TransactionInstruction,
|
||||
} from "@solana/web3.js";
|
||||
import {
|
||||
CrankAccount,
|
||||
OracleAccount,
|
||||
OracleQueueAccount,
|
||||
PermissionAccount,
|
||||
ProgramStateAccount,
|
||||
programWallet,
|
||||
SwitchboardDecimal,
|
||||
} from "@switchboard-xyz/switchboard-v2";
|
||||
import Big from "big.js";
|
||||
import { chalkString } from "./print";
|
||||
import { packAndSend } from "./transaction";
|
||||
|
||||
export interface CreateQueueParams {
|
||||
authority?: PublicKey;
|
||||
name?: string;
|
||||
metadata?: string;
|
||||
minStake: anchor.BN;
|
||||
reward: anchor.BN;
|
||||
crankSize?: number;
|
||||
oracleTimeout?: number;
|
||||
numOracles?: number;
|
||||
unpermissionedFeeds?: boolean;
|
||||
unpermissionedVrf?: boolean;
|
||||
}
|
||||
|
||||
export interface CreateQueueResponse {
|
||||
queueAccount: OracleQueueAccount;
|
||||
crankPubkey: PublicKey;
|
||||
oracles: PublicKey[];
|
||||
}
|
||||
|
||||
export async function createQueue(
|
||||
program: anchor.Program,
|
||||
params: CreateQueueParams,
|
||||
queueSize = 500,
|
||||
authorityKeypair = programWallet(program)
|
||||
): Promise<CreateQueueResponse> {
|
||||
const payerKeypair = programWallet(program);
|
||||
|
||||
const [programStateAccount, stateBump] = ProgramStateAccount.fromSeed(
|
||||
this.program
|
||||
);
|
||||
const tokenMint = new spl.Token(
|
||||
this.program.provider.connection,
|
||||
spl.NATIVE_MINT,
|
||||
spl.TOKEN_PROGRAM_ID,
|
||||
payerKeypair
|
||||
);
|
||||
|
||||
const ixns: (TransactionInstruction | TransactionInstruction[])[] = [];
|
||||
const signers: Keypair[] = [payerKeypair, authorityKeypair];
|
||||
|
||||
try {
|
||||
await programStateAccount.loadData();
|
||||
} catch {
|
||||
const vaultKeypair = anchor.web3.Keypair.generate();
|
||||
ixns.push([
|
||||
SystemProgram.createAccount({
|
||||
fromPubkey: payerKeypair.publicKey,
|
||||
newAccountPubkey: vaultKeypair.publicKey,
|
||||
lamports:
|
||||
await this.program.provider.connection.getMinimumBalanceForRentExemption(
|
||||
spl.AccountLayout.span
|
||||
),
|
||||
space: spl.AccountLayout.span,
|
||||
programId: spl.TOKEN_PROGRAM_ID,
|
||||
}),
|
||||
spl.Token.createInitAccountInstruction(
|
||||
spl.TOKEN_PROGRAM_ID,
|
||||
tokenMint.publicKey,
|
||||
vaultKeypair.publicKey,
|
||||
payerKeypair.publicKey
|
||||
),
|
||||
await this.program.methods
|
||||
.programInit({
|
||||
stateBump,
|
||||
})
|
||||
.accounts({
|
||||
state: programStateAccount.publicKey,
|
||||
authority: payerKeypair.publicKey,
|
||||
tokenMint: tokenMint.publicKey,
|
||||
vault: vaultKeypair.publicKey,
|
||||
payer: payerKeypair.publicKey,
|
||||
systemProgram: SystemProgram.programId,
|
||||
tokenProgram: spl.TOKEN_PROGRAM_ID,
|
||||
daoMint: tokenMint.publicKey,
|
||||
})
|
||||
.instruction(),
|
||||
]);
|
||||
signers.push(vaultKeypair);
|
||||
}
|
||||
|
||||
const queueKeypair = anchor.web3.Keypair.generate();
|
||||
const queueBuffer = anchor.web3.Keypair.generate();
|
||||
const queueBufferSize = queueSize * 32 + 8;
|
||||
|
||||
const queueAccount = new OracleQueueAccount({
|
||||
program: this.program,
|
||||
publicKey: queueKeypair.publicKey,
|
||||
});
|
||||
|
||||
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 = params.crankSize ? params.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,
|
||||
});
|
||||
|
||||
ixns.push(
|
||||
anchor.web3.SystemProgram.createAccount({
|
||||
fromPubkey: payerKeypair.publicKey,
|
||||
newAccountPubkey: queueBuffer.publicKey,
|
||||
space: queueBufferSize,
|
||||
lamports:
|
||||
await this.program.provider.connection.getMinimumBalanceForRentExemption(
|
||||
queueBufferSize
|
||||
),
|
||||
programId: this.program.programId,
|
||||
}),
|
||||
await this.program.methods
|
||||
.oracleQueueInit({
|
||||
name: Buffer.from(params.name).slice(0, 32),
|
||||
metadata: Buffer.from("").slice(0, 64),
|
||||
reward: params.reward ? new anchor.BN(params.reward) : new anchor.BN(0),
|
||||
minStake: params.minStake
|
||||
? new anchor.BN(params.minStake)
|
||||
: new anchor.BN(0),
|
||||
// feedProbationPeriod: 0,
|
||||
oracleTimeout: params.oracleTimeout,
|
||||
slashingEnabled: false,
|
||||
varianceToleranceMultiplier: SwitchboardDecimal.fromBig(new Big(2)),
|
||||
authority: authorityKeypair.publicKey,
|
||||
// consecutiveFeedFailureLimit: new anchor.BN(1000),
|
||||
// consecutiveOracleFailureLimit: new anchor.BN(1000),
|
||||
minimumDelaySeconds: 5,
|
||||
queueSize: queueSize,
|
||||
unpermissionedFeeds: params.unpermissionedFeeds ?? false,
|
||||
unpermissionedVrf: params.unpermissionedVrf ?? false,
|
||||
})
|
||||
.accounts({
|
||||
oracleQueue: queueKeypair.publicKey,
|
||||
authority: authorityKeypair.publicKey,
|
||||
buffer: queueBuffer.publicKey,
|
||||
systemProgram: SystemProgram.programId,
|
||||
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: params.crankSize,
|
||||
})
|
||||
.accounts({
|
||||
crank: crankKeypair.publicKey,
|
||||
queue: queueKeypair.publicKey,
|
||||
buffer: crankBuffer.publicKey,
|
||||
systemProgram: SystemProgram.programId,
|
||||
payer: payerKeypair.publicKey,
|
||||
})
|
||||
.instruction()
|
||||
);
|
||||
signers.push(queueKeypair, queueBuffer, crankKeypair, crankBuffer);
|
||||
|
||||
const finalTransactions: (
|
||||
| TransactionInstruction
|
||||
| TransactionInstruction[]
|
||||
)[] = [];
|
||||
|
||||
const oracleAccounts = await Promise.all(
|
||||
Array.from(Array(params.numOracles).keys()).map(async (n) => {
|
||||
const name = `Oracle-${n + 1}`;
|
||||
const tokenWalletKeypair = anchor.web3.Keypair.generate();
|
||||
const [oracleAccount, oracleBump] = OracleAccount.fromSeed(
|
||||
this.program,
|
||||
queueAccount,
|
||||
tokenWalletKeypair.publicKey
|
||||
);
|
||||
|
||||
this.logger.debug(chalkString(name, oracleAccount.publicKey));
|
||||
|
||||
const [permissionAccount, permissionBump] = PermissionAccount.fromSeed(
|
||||
this.program,
|
||||
authorityKeypair.publicKey,
|
||||
queueAccount.publicKey,
|
||||
oracleAccount.publicKey
|
||||
);
|
||||
this.logger.debug(
|
||||
chalkString(`Permission-${n + 1}`, permissionAccount.publicKey)
|
||||
);
|
||||
|
||||
finalTransactions.push([
|
||||
SystemProgram.createAccount({
|
||||
fromPubkey: payerKeypair.publicKey,
|
||||
newAccountPubkey: tokenWalletKeypair.publicKey,
|
||||
lamports:
|
||||
await this.program.provider.connection.getMinimumBalanceForRentExemption(
|
||||
spl.AccountLayout.span
|
||||
),
|
||||
space: spl.AccountLayout.span,
|
||||
programId: spl.TOKEN_PROGRAM_ID,
|
||||
}),
|
||||
spl.Token.createInitAccountInstruction(
|
||||
spl.TOKEN_PROGRAM_ID,
|
||||
tokenMint.publicKey,
|
||||
tokenWalletKeypair.publicKey,
|
||||
programStateAccount.publicKey
|
||||
),
|
||||
await this.program.methods
|
||||
.oracleInit({
|
||||
name: Buffer.from(name).slice(0, 32),
|
||||
metadata: Buffer.from("").slice(0, 128),
|
||||
stateBump,
|
||||
oracleBump,
|
||||
})
|
||||
.accounts({
|
||||
oracle: oracleAccount.publicKey,
|
||||
oracleAuthority: authorityKeypair.publicKey,
|
||||
queue: queueKeypair.publicKey,
|
||||
wallet: tokenWalletKeypair.publicKey,
|
||||
programState: programStateAccount.publicKey,
|
||||
systemProgram: SystemProgram.programId,
|
||||
payer: payerKeypair.publicKey,
|
||||
})
|
||||
.instruction(),
|
||||
await this.program.methods
|
||||
.permissionInit({})
|
||||
.accounts({
|
||||
permission: permissionAccount.publicKey,
|
||||
authority: authorityKeypair.publicKey,
|
||||
granter: queueAccount.publicKey,
|
||||
grantee: oracleAccount.publicKey,
|
||||
payer: payerKeypair.publicKey,
|
||||
systemProgram: SystemProgram.programId,
|
||||
})
|
||||
.instruction(),
|
||||
await this.program.methods
|
||||
.permissionSet({
|
||||
permission: { permitOracleHeartbeat: null },
|
||||
enable: true,
|
||||
})
|
||||
.accounts({
|
||||
permission: permissionAccount.publicKey,
|
||||
authority: authorityKeypair.publicKey,
|
||||
})
|
||||
.instruction(),
|
||||
]);
|
||||
signers.push(tokenWalletKeypair);
|
||||
return {
|
||||
oracleAccount,
|
||||
name,
|
||||
permissionAccount,
|
||||
tokenWalletKeypair,
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
const createAccountSignatures = packAndSend(
|
||||
this.program,
|
||||
[ixns, finalTransactions],
|
||||
signers,
|
||||
payerKeypair.publicKey
|
||||
);
|
||||
|
||||
const result = await program.provider.connection.confirmTransaction(
|
||||
createAccountSignatures[-1]
|
||||
);
|
||||
|
||||
return {
|
||||
queueAccount,
|
||||
crankPubkey: crankAccount.publicKey,
|
||||
oracles: oracleAccounts.map((o) => o.oracleAccount.publicKey) ?? [],
|
||||
};
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
import type { Connection, SignatureResult } from "@solana/web3.js";
|
||||
|
||||
/** Watch a transaction and resolve when it is finalized */
|
||||
async function watchTransaction(
|
||||
txn: string,
|
||||
connection: Connection
|
||||
): Promise<void> {
|
||||
console.log(`https://explorer.solana.com/tx/${txn}?cluster=devnet`);
|
||||
connection.onSignature(txn, async (signatureResult: SignatureResult) => {
|
||||
const response = await connection.getTransaction(txn);
|
||||
console.log(JSON.stringify(response?.meta?.logMessages, undefined, 2));
|
||||
});
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
import * as anchor from "@project-serum/anchor";
|
||||
import {
|
||||
ConfirmOptions,
|
||||
Connection,
|
||||
Keypair,
|
||||
PublicKey,
|
||||
TransactionInstruction,
|
||||
TransactionSignature,
|
||||
} from "@solana/web3.js";
|
||||
import {
|
||||
packInstructions,
|
||||
signTransactions,
|
||||
} from "@switchboard-xyz/switchboard-v2";
|
||||
|
||||
export async function packAndSend(
|
||||
program: anchor.Program,
|
||||
ixnsBatches: (TransactionInstruction | TransactionInstruction[])[][],
|
||||
signers: Keypair[],
|
||||
feePayer: PublicKey
|
||||
): Promise<TransactionSignature[]> {
|
||||
const signatures: Promise<TransactionSignature>[] = [];
|
||||
|
||||
for await (const batch of ixnsBatches) {
|
||||
const { blockhash } =
|
||||
await program.provider.connection.getLatestBlockhash();
|
||||
|
||||
const packedTransactions = packInstructions(batch, feePayer, blockhash);
|
||||
const signedTransactions = signTransactions(packedTransactions, signers);
|
||||
const signedTxs = await (
|
||||
program.provider as anchor.AnchorProvider
|
||||
).wallet.signAllTransactions(signedTransactions);
|
||||
|
||||
for (let k = 0; k < packedTransactions.length; k += 1) {
|
||||
const tx = signedTxs[k];
|
||||
const rawTx = tx.serialize();
|
||||
// signatures.push(
|
||||
// program.provider.connection.sendRawTransaction(rawTx, {
|
||||
// skipPreflight: true,
|
||||
// maxRetries: 10,
|
||||
// })
|
||||
// );
|
||||
signatures.push(
|
||||
sendAndConfirmRawTransaction(program.provider.connection, rawTx, {
|
||||
skipPreflight: true,
|
||||
maxRetries: 10,
|
||||
commitment: "confirmed",
|
||||
})
|
||||
);
|
||||
// signatures.push(
|
||||
// program.provider.connection.sendTransaction(tx, signers, {
|
||||
// skipPreflight: true,
|
||||
// maxRetries: 10,
|
||||
// })
|
||||
// );
|
||||
}
|
||||
|
||||
await Promise.all(signatures);
|
||||
}
|
||||
|
||||
return Promise.all(signatures);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send and confirm a raw transaction
|
||||
*
|
||||
* If `commitment` option is not specified, defaults to 'max' commitment.
|
||||
*/
|
||||
export async function sendAndConfirmRawTransaction(
|
||||
connection: Connection,
|
||||
rawTransaction: Buffer,
|
||||
options: ConfirmOptions
|
||||
): Promise<TransactionSignature> {
|
||||
const sendOptions = options && {
|
||||
skipPreflight: options.skipPreflight,
|
||||
preflightCommitment: options.preflightCommitment || options.commitment,
|
||||
};
|
||||
const signature: TransactionSignature = await connection.sendRawTransaction(
|
||||
rawTransaction,
|
||||
sendOptions
|
||||
);
|
||||
const status = (
|
||||
await connection.confirmTransaction(
|
||||
signature as any,
|
||||
options.commitment || "max"
|
||||
)
|
||||
).value;
|
||||
|
||||
if (status.err) {
|
||||
throw new Error(
|
||||
`Raw transaction ${signature} failed (${JSON.stringify(status)})`
|
||||
);
|
||||
}
|
||||
|
||||
return signature;
|
||||
}
|
|
@ -11,7 +11,7 @@
|
|||
"scripts": {
|
||||
"docusaurus": "docusaurus",
|
||||
"start": "docusaurus start",
|
||||
"build": "For workspace website, run 'yarn docs:build' from the project root",
|
||||
"build": "echo \"For workspace anchor-vrf-parser, run 'yarn docs:build' from the project root\" && exit 0",
|
||||
"build:site": "docusaurus build --out-dir public",
|
||||
"swizzle": "docusaurus swizzle",
|
||||
"deploy": "docusaurus build --out-dir public && docusaurus deploy --out-dir public",
|
||||
|
|
42
yarn.lock
42
yarn.lock
|
@ -3862,6 +3862,26 @@
|
|||
superstruct "^0.14.2"
|
||||
tweetnacl "^1.0.0"
|
||||
|
||||
"@solana/web3.js@1.39.1":
|
||||
version "1.39.1"
|
||||
resolved "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.39.1.tgz#858ecd42ff2a5bcba3a4bb642a50194d77e2a578"
|
||||
integrity sha512-Q7XnWTAiU7n7GcoINDAAMLO7CJHpm5kPK46HKwJi2x0cusHQ3WFa7QEp6aPzH7tuf7yl/Kw1lYitcwTVOvqARA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.12.5"
|
||||
"@ethersproject/sha2" "^5.5.0"
|
||||
"@solana/buffer-layout" "^4.0.0"
|
||||
bn.js "^5.0.0"
|
||||
borsh "^0.7.0"
|
||||
bs58 "^4.0.1"
|
||||
buffer "6.0.1"
|
||||
cross-fetch "^3.1.4"
|
||||
jayson "^3.4.4"
|
||||
js-sha3 "^0.8.0"
|
||||
rpc-websockets "^7.4.2"
|
||||
secp256k1 "^4.0.2"
|
||||
superstruct "^0.14.2"
|
||||
tweetnacl "^1.0.0"
|
||||
|
||||
"@solana/web3.js@^0.86.1":
|
||||
version "0.86.4"
|
||||
resolved "https://registry.npmjs.org/@solana/web3.js/-/web3.js-0.86.4.tgz"
|
||||
|
@ -3927,28 +3947,6 @@
|
|||
superstruct "^0.14.2"
|
||||
tweetnacl "^1.0.0"
|
||||
|
||||
"@solana/web3.js@^1.41.10":
|
||||
version "1.41.10"
|
||||
resolved "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.41.10.tgz#fb1bf7d8ca25f126a2166fed1733fe357298a076"
|
||||
integrity sha512-2mPNoxGDt5jZ4MYA+aK7qKzvdXdN0niy7suYfkbrcgAWahJ/WSfPD2W0IvySDdLLfCQojSs7sdHIW+xsKV9dyQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.12.5"
|
||||
"@ethersproject/sha2" "^5.5.0"
|
||||
"@solana/buffer-layout" "^4.0.0"
|
||||
"@solana/buffer-layout-utils" "^0.2.0"
|
||||
bn.js "^5.0.0"
|
||||
borsh "^0.7.0"
|
||||
bs58 "^4.0.1"
|
||||
buffer "6.0.1"
|
||||
cross-fetch "^3.1.4"
|
||||
fast-stable-stringify "^1.0.0"
|
||||
jayson "^3.4.4"
|
||||
js-sha3 "^0.8.0"
|
||||
rpc-websockets "^7.4.2"
|
||||
secp256k1 "^4.0.2"
|
||||
superstruct "^0.14.2"
|
||||
tweetnacl "^1.0.0"
|
||||
|
||||
"@solana/web3.js@^1.42.0":
|
||||
version "1.42.0"
|
||||
resolved "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.42.0.tgz#296e4bbab1fbfc198b3e9c3d94016c3876eb6a2c"
|
||||
|
|
Loading…
Reference in New Issue