programs: added sbv2-vrf-example CLI script
deps deps wip wip
This commit is contained in:
parent
cce9392789
commit
62a3ad9d5c
|
@ -4,5 +4,6 @@
|
|||
},
|
||||
"files.eol": "\n",
|
||||
"editor.formatOnSave": true,
|
||||
"editor.wordWrap": "on"
|
||||
"editor.wordWrap": "on",
|
||||
"typescript.tsdk": "node_modules/typescript/lib"
|
||||
}
|
||||
|
|
|
@ -196,9 +196,7 @@ export default class AggregatorCreateCopy extends BaseCommand {
|
|||
const leaseEscrow = await spl.getAssociatedTokenAddress(
|
||||
mint.address,
|
||||
leaseAccount.publicKey,
|
||||
true,
|
||||
spl.TOKEN_PROGRAM_ID,
|
||||
spl.ASSOCIATED_TOKEN_PROGRAM_ID
|
||||
true
|
||||
);
|
||||
|
||||
createAccountInstructions.push(
|
||||
|
@ -310,6 +308,8 @@ export default class AggregatorCreateCopy extends BaseCommand {
|
|||
.crankPush({
|
||||
stateBump,
|
||||
permissionBump,
|
||||
nofitiRef: null,
|
||||
notifiRef: null,
|
||||
})
|
||||
.accounts({
|
||||
crank: new PublicKey(flags.crankKey),
|
||||
|
|
|
@ -341,6 +341,8 @@ export default class AggregatorCreate extends BaseCommand {
|
|||
.crankPush({
|
||||
stateBump,
|
||||
permissionBump,
|
||||
nofitiRef: null,
|
||||
notifiRef: null,
|
||||
})
|
||||
.accounts({
|
||||
crank: new PublicKey(flags.crankKey),
|
||||
|
|
|
@ -6,6 +6,8 @@ import BaseCommand from "../../BaseCommand";
|
|||
export default class VrfPrint extends BaseCommand {
|
||||
outputFile?: string;
|
||||
|
||||
static enableJsonFlag = true;
|
||||
|
||||
static description = "Print the deserialized Switchboard VRF account";
|
||||
|
||||
static aliases = ["vrf:print"];
|
||||
|
|
|
@ -38,7 +38,7 @@ export default class VrfVerify extends BaseCommand {
|
|||
const vrf = await vrfAccount.loadData();
|
||||
|
||||
const status = toVrfStatusString(vrf.status);
|
||||
if (status !== "statusVerifying") {
|
||||
if (status !== "StatusVerifying") {
|
||||
throw new Error(`Vrf not ready to be verified, current status ${status}`);
|
||||
}
|
||||
|
||||
|
|
|
@ -73,10 +73,18 @@ export const toPermissionString = (
|
|||
}
|
||||
};
|
||||
|
||||
export const toVrfStatusString = (status: Record<string, unknown>): string => {
|
||||
if (status === undefined) {
|
||||
return "";
|
||||
}
|
||||
export type VrfStatusString =
|
||||
| ""
|
||||
| "StatusNone"
|
||||
| "StatusRequesting"
|
||||
| "StatusVerifying"
|
||||
| "StatusVerified"
|
||||
| "StatusCallbackSuccess"
|
||||
| "StatusVerifyFailure";
|
||||
|
||||
export const toVrfStatusString = (
|
||||
status: Record<string, unknown>
|
||||
): VrfStatusString => {
|
||||
if ("statusNone" in status) {
|
||||
return "StatusNone";
|
||||
}
|
||||
|
@ -95,7 +103,7 @@ export const toVrfStatusString = (status: Record<string, unknown>): string => {
|
|||
if ("statusVerifyFailure" in status) {
|
||||
return "StatusVerifyFailure";
|
||||
}
|
||||
return "Unknown";
|
||||
return "";
|
||||
};
|
||||
|
||||
export async function prettyPrintProgramState(
|
||||
|
@ -582,10 +590,14 @@ export async function prettyPrintVrf(
|
|||
"latestResult",
|
||||
JSON.stringify(
|
||||
{
|
||||
producer: data.builders[0]?.producer.toString() ?? "",
|
||||
status: toVrfStatusString(data.builders[0]?.status) ?? "",
|
||||
verified: data.builders[0]?.verified ?? "",
|
||||
txRemaining: data.builders[0]?.txRemaining ?? "",
|
||||
producer: data.builders[0]?.producer.toString() ?? "",
|
||||
reprProof: `[${data.builders[0].reprProof.map((value) =>
|
||||
value.toString()
|
||||
)}]`,
|
||||
reprProofHex: Buffer.from(data.builders[0].reprProof).toString("hex"),
|
||||
currentRound: {
|
||||
result: `[${data.currentRound.result.map((value) =>
|
||||
value.toString()
|
||||
|
@ -593,6 +605,7 @@ export async function prettyPrintVrf(
|
|||
alpha: `[${data.currentRound.alpha.map((value) =>
|
||||
value.toString()
|
||||
)}]`,
|
||||
alphaHex: Buffer.from(data.currentRound.alpha).toString("hex"),
|
||||
requestSlot: data.currentRound?.requestSlot?.toString() ?? "",
|
||||
requestTimestamp: anchorBNtoDateTimeString(
|
||||
data.currentRound.requestTimestamp
|
||||
|
|
|
@ -0,0 +1,763 @@
|
|||
#!/usr/bin/env ts-node-esm
|
||||
/* eslint-disable @typescript-eslint/no-loop-func */
|
||||
/* eslint-disable @typescript-eslint/no-unused-expressions */
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
import * as anchor from "@project-serum/anchor";
|
||||
import * as spl from "@solana/spl-token";
|
||||
import type {
|
||||
Connection,
|
||||
ParsedTransactionWithMeta,
|
||||
PublicKey,
|
||||
} from "@solana/web3.js";
|
||||
import * as sbv2Utils from "@switchboard-xyz/sbv2-utils";
|
||||
import { toVrfStatusString } from "@switchboard-xyz/sbv2-utils";
|
||||
import * as sbv2 from "@switchboard-xyz/switchboard-v2";
|
||||
import chalk from "chalk";
|
||||
const yargs = require("yargs");
|
||||
const { hideBin } = require("yargs/helpers");
|
||||
// import yargs from "yargs";
|
||||
// import { hideBin } from "yargs/helpers";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { AnchorVrfParser, IDL } from "../../target/types/anchor_vrf_parser";
|
||||
import { VrfClient } from "./client/accounts";
|
||||
import { PROGRAM_ID } from "./client/programId";
|
||||
|
||||
const DEFAULT_MAINNET_RPC = "https://ssc-dao.genesysgo.net";
|
||||
const DEFAULT_DEVNET_RPC = "https://devnet.genesysgo.net";
|
||||
const DEFAULT_LOCALNET_RPC = "http://localhost:8899";
|
||||
|
||||
const DEFAULT_COMMITMENT = "confirmed";
|
||||
|
||||
const VRF_REQUEST_AMOUNT = BigInt(2_000_000);
|
||||
|
||||
interface RequestRandomnessResult {
|
||||
success: boolean;
|
||||
status: string;
|
||||
counter: number;
|
||||
txRemaining: number;
|
||||
producer: string;
|
||||
alpha: string;
|
||||
alphaHex: string;
|
||||
proof?: string;
|
||||
proofHex?: string;
|
||||
proofBase64?: string;
|
||||
result: string;
|
||||
stage?: number;
|
||||
txs?: ParsedTransactionWithMeta[] | string[] | any[];
|
||||
}
|
||||
|
||||
yargs(hideBin(process.argv))
|
||||
.scriptName("sbv2-vrf-example")
|
||||
.command(
|
||||
"create [queueKey]",
|
||||
"create a VRF client",
|
||||
(y: any) => {
|
||||
return y
|
||||
.positional("queueKey", {
|
||||
type: "string",
|
||||
describe: "publicKey of the oracle queue to create a VRF for",
|
||||
default: "F8ce7MsckeZAbAGmxjJNetxYXQa9mKr9nnrC3qKubyYy",
|
||||
})
|
||||
.option("option", {
|
||||
description: "test",
|
||||
});
|
||||
},
|
||||
async function (argv: any) {
|
||||
const { queueKey, rpcUrl, cluster } = argv;
|
||||
|
||||
const { vrfClientProgram, switchboardProgram, payer, provider } =
|
||||
await loadCli(rpcUrl, cluster);
|
||||
|
||||
const vrfSecret = anchor.web3.Keypair.generate();
|
||||
|
||||
const [vrfClientKey, vrfClientBump] =
|
||||
anchor.utils.publicKey.findProgramAddressSync(
|
||||
[
|
||||
Buffer.from("STATE"),
|
||||
vrfSecret.publicKey.toBytes(),
|
||||
payer.publicKey.toBytes(),
|
||||
],
|
||||
vrfClientProgram.programId
|
||||
);
|
||||
|
||||
const vrfIxCoder = new anchor.BorshInstructionCoder(vrfClientProgram.idl);
|
||||
const vrfClientCallback = {
|
||||
programId: vrfClientProgram.programId,
|
||||
accounts: [
|
||||
// ensure all accounts in updateResult are populated
|
||||
{ pubkey: vrfClientKey, isSigner: false, isWritable: true },
|
||||
{ pubkey: vrfSecret.publicKey, isSigner: false, isWritable: false },
|
||||
],
|
||||
ixData: vrfIxCoder.encode("updateResult", ""), // pass any params for instruction here
|
||||
};
|
||||
|
||||
const queueAccount = new sbv2.OracleQueueAccount({
|
||||
program: switchboardProgram,
|
||||
publicKey: new anchor.web3.PublicKey(queueKey),
|
||||
});
|
||||
const { unpermissionedVrfEnabled, authority, dataBuffer } =
|
||||
await queueAccount.loadData();
|
||||
|
||||
// Create Switchboard VRF and Permission account
|
||||
const vrfAccount = await sbv2.VrfAccount.create(switchboardProgram, {
|
||||
queue: queueAccount,
|
||||
callback: vrfClientCallback,
|
||||
authority: vrfClientKey, // vrf authority
|
||||
keypair: vrfSecret,
|
||||
});
|
||||
await sbv2Utils.sleep(2000);
|
||||
const { escrow } = await vrfAccount.loadData();
|
||||
console.log(sbv2Utils.chalkString("VRF Account", vrfAccount.publicKey));
|
||||
|
||||
const permissionAccount = await sbv2.PermissionAccount.create(
|
||||
switchboardProgram,
|
||||
{
|
||||
authority,
|
||||
granter: queueAccount.publicKey,
|
||||
grantee: vrfAccount.publicKey,
|
||||
}
|
||||
);
|
||||
// console.log(`Created Permission Account: ${permissionAccount.publicKey}`);
|
||||
|
||||
// If queue requires permissions to use VRF, check the correct authority was provided
|
||||
if (!unpermissionedVrfEnabled) {
|
||||
if (!payer.publicKey.equals(authority)) {
|
||||
throw new Error(
|
||||
`queue requires PERMIT_VRF_REQUESTS and wrong queue authority provided`
|
||||
);
|
||||
}
|
||||
|
||||
await permissionAccount.set({
|
||||
authority: payer,
|
||||
permission: sbv2.SwitchboardPermission.PERMIT_VRF_REQUESTS,
|
||||
enable: true,
|
||||
});
|
||||
}
|
||||
|
||||
// Create VRF Client account
|
||||
await vrfClientProgram.methods
|
||||
.initState({
|
||||
maxResult: new anchor.BN(1),
|
||||
})
|
||||
.accounts({
|
||||
state: vrfClientKey,
|
||||
vrf: vrfAccount.publicKey,
|
||||
payer: payer.publicKey,
|
||||
authority: payer.publicKey,
|
||||
systemProgram: anchor.web3.SystemProgram.programId,
|
||||
})
|
||||
.rpc();
|
||||
|
||||
console.log(sbv2Utils.chalkString("VRF Client", vrfClientKey));
|
||||
|
||||
console.log(`\nRun the following command to request randomness`);
|
||||
console.log(
|
||||
`\t${chalk.yellow(
|
||||
"npx sbv2-vrf-example request",
|
||||
vrfClientKey.toString()
|
||||
)}`
|
||||
);
|
||||
}
|
||||
)
|
||||
.command(
|
||||
"request [vrfClientKey]",
|
||||
"request randomness for a VRF client",
|
||||
(y: any) => {
|
||||
return y.positional("vrfClientKey", {
|
||||
type: "string",
|
||||
describe: "publicKey of the VRF client to request randomness for",
|
||||
demand: true,
|
||||
});
|
||||
},
|
||||
async function (argv: any) {
|
||||
const { vrfClientKey, rpcUrl, cluster } = argv;
|
||||
if (!vrfClientKey) {
|
||||
throw new Error(`Must provide vrfClientKey arguement`);
|
||||
}
|
||||
|
||||
const { vrfClientProgram, switchboardProgram, payer, provider } =
|
||||
await loadCli(rpcUrl as string, cluster as string);
|
||||
|
||||
const vrfClient = await VrfClient.fetch(
|
||||
provider.connection,
|
||||
new anchor.web3.PublicKey(vrfClientKey)
|
||||
);
|
||||
if (!vrfClient) {
|
||||
throw new Error(
|
||||
`Failed to fetch VrfClient for account ${vrfClientKey}`
|
||||
);
|
||||
}
|
||||
|
||||
const vrfAccount = new sbv2.VrfAccount({
|
||||
program: switchboardProgram,
|
||||
publicKey: vrfClient.vrf,
|
||||
});
|
||||
const vrfData = await vrfAccount.loadData();
|
||||
|
||||
const vrfEscrow = await spl.getAccount(
|
||||
provider.connection,
|
||||
vrfData.escrow,
|
||||
DEFAULT_COMMITMENT,
|
||||
spl.TOKEN_PROGRAM_ID
|
||||
);
|
||||
|
||||
const queueAccount = new sbv2.OracleQueueAccount({
|
||||
program: switchboardProgram,
|
||||
publicKey: vrfData.oracleQueue,
|
||||
});
|
||||
const queueData = await queueAccount.loadData();
|
||||
const mint = await queueAccount.loadMint();
|
||||
|
||||
const payerTokenAccount = await spl.getOrCreateAssociatedTokenAccount(
|
||||
provider.connection,
|
||||
payer,
|
||||
mint.address,
|
||||
payer.publicKey,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
spl.TOKEN_PROGRAM_ID,
|
||||
spl.ASSOCIATED_TOKEN_PROGRAM_ID
|
||||
);
|
||||
|
||||
const tokensNeeded = VRF_REQUEST_AMOUNT - vrfEscrow.amount;
|
||||
|
||||
if (tokensNeeded > 0 && payerTokenAccount.amount < tokensNeeded) {
|
||||
throw new Error(
|
||||
`Payer token account has insufficient funds, need 2_000_000, current ${payerTokenAccount.amount}`
|
||||
);
|
||||
}
|
||||
|
||||
const [programStateAccount, programStateBump] =
|
||||
sbv2.ProgramStateAccount.fromSeed(switchboardProgram);
|
||||
|
||||
const [permissionAccount, permissionBump] =
|
||||
sbv2.PermissionAccount.fromSeed(
|
||||
switchboardProgram,
|
||||
queueData.authority,
|
||||
queueAccount.publicKey,
|
||||
vrfAccount.publicKey
|
||||
);
|
||||
|
||||
let ws: number | undefined = undefined;
|
||||
const waitForResultPromise = new Promise(
|
||||
(
|
||||
resolve: (result: anchor.BN) => void,
|
||||
reject: (reason: string) => void
|
||||
) => {
|
||||
ws = vrfClientProgram.provider.connection.onAccountChange(
|
||||
new anchor.web3.PublicKey(vrfClientKey),
|
||||
async (
|
||||
accountInfo: anchor.web3.AccountInfo<Buffer>,
|
||||
context: anchor.web3.Context
|
||||
) => {
|
||||
const clientState = VrfClient.decode(accountInfo.data);
|
||||
if (clientState.result.gt(new anchor.BN(0))) {
|
||||
resolve(clientState.result);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
).then(async (result) => {
|
||||
console.log(
|
||||
sbv2Utils.chalkString("Client Result", result.toString(10))
|
||||
);
|
||||
if (ws) {
|
||||
await vrfClientProgram.provider.connection.removeAccountChangeListener(
|
||||
ws
|
||||
);
|
||||
}
|
||||
ws = undefined;
|
||||
return result;
|
||||
});
|
||||
|
||||
const requestTxnPromise = vrfClientProgram.methods
|
||||
.requestResult({
|
||||
switchboardStateBump: programStateBump,
|
||||
permissionBump,
|
||||
})
|
||||
.accounts({
|
||||
state: vrfClientKey,
|
||||
authority: payer.publicKey,
|
||||
switchboardProgram: switchboardProgram.programId,
|
||||
vrf: vrfAccount.publicKey,
|
||||
oracleQueue: queueAccount.publicKey,
|
||||
queueAuthority: queueData.authority,
|
||||
dataBuffer: queueData.dataBuffer,
|
||||
permission: permissionAccount.publicKey,
|
||||
escrow: vrfData.escrow,
|
||||
payerWallet: payerTokenAccount.address,
|
||||
payerAuthority: payer.publicKey,
|
||||
recentBlockhashes: anchor.web3.SYSVAR_RECENT_BLOCKHASHES_PUBKEY,
|
||||
programState: programStateAccount.publicKey,
|
||||
tokenProgram: spl.TOKEN_PROGRAM_ID,
|
||||
})
|
||||
.signers([payer, payer])
|
||||
.rpc()
|
||||
.then((txnSignature) => {
|
||||
console.log(
|
||||
`${chalk.yellow("Randomness Requested")}\t${txnSignature}`
|
||||
);
|
||||
});
|
||||
|
||||
let result: anchor.BN;
|
||||
try {
|
||||
result = await sbv2Utils.promiseWithTimeout(
|
||||
45_000,
|
||||
waitForResultPromise,
|
||||
new Error(`Timed out waiting for VRF Client callback`)
|
||||
);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
} finally {
|
||||
if (ws) {
|
||||
await vrfClientProgram.provider.connection.removeAccountChangeListener(
|
||||
ws
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
}
|
||||
)
|
||||
.command(
|
||||
"loop [vrfClientKey] [numLoops]",
|
||||
"request randomness for a VRF client N times and record the results",
|
||||
(y: any) => {
|
||||
return y
|
||||
.positional("vrfClientKey", {
|
||||
type: "string",
|
||||
describe: "publicKey of the VRF client to request randomness for",
|
||||
demand: true,
|
||||
})
|
||||
.positional("numLoops", {
|
||||
type: "number",
|
||||
describe: "number of times to request randomness",
|
||||
default: 100,
|
||||
});
|
||||
},
|
||||
async function (argv: any) {
|
||||
const { vrfClientKey, rpcUrl, cluster, numLoops } = argv;
|
||||
if (!vrfClientKey) {
|
||||
throw new Error(`Must provide vrfClientKey arguement`);
|
||||
}
|
||||
|
||||
const startTime = Math.floor(Date.now() / 1000);
|
||||
|
||||
const vrfClientPubkey = new anchor.web3.PublicKey(vrfClientKey);
|
||||
|
||||
const { vrfClientProgram, switchboardProgram, payer, provider } =
|
||||
await loadCli(rpcUrl as string, cluster as string);
|
||||
|
||||
const vrfClient = await VrfClient.fetch(
|
||||
provider.connection,
|
||||
vrfClientPubkey
|
||||
);
|
||||
if (!vrfClient) {
|
||||
throw new Error(
|
||||
`Failed to fetch VrfClient for account ${vrfClientKey}`
|
||||
);
|
||||
}
|
||||
|
||||
const vrfAccount = new sbv2.VrfAccount({
|
||||
program: switchboardProgram,
|
||||
publicKey: vrfClient.vrf,
|
||||
});
|
||||
const vrfData = await vrfAccount.loadData();
|
||||
|
||||
const vrfEscrow = await spl.getAccount(
|
||||
provider.connection,
|
||||
vrfData.escrow,
|
||||
DEFAULT_COMMITMENT,
|
||||
spl.TOKEN_PROGRAM_ID
|
||||
);
|
||||
|
||||
const queueAccount = new sbv2.OracleQueueAccount({
|
||||
program: switchboardProgram,
|
||||
publicKey: vrfData.oracleQueue,
|
||||
});
|
||||
const queueData = await queueAccount.loadData();
|
||||
const mint = await queueAccount.loadMint();
|
||||
|
||||
const payerTokenAccount = await spl.getOrCreateAssociatedTokenAccount(
|
||||
provider.connection,
|
||||
payer,
|
||||
mint.address,
|
||||
payer.publicKey,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
spl.TOKEN_PROGRAM_ID,
|
||||
spl.ASSOCIATED_TOKEN_PROGRAM_ID
|
||||
);
|
||||
|
||||
const tokensNeeded = VRF_REQUEST_AMOUNT - vrfEscrow.amount;
|
||||
|
||||
if (tokensNeeded > 0 && payerTokenAccount.amount < tokensNeeded) {
|
||||
throw new Error(
|
||||
`Payer token account has insufficient funds, need 2_000_000, current ${payerTokenAccount.amount}`
|
||||
);
|
||||
}
|
||||
|
||||
const [programStateAccount, programStateBump] =
|
||||
sbv2.ProgramStateAccount.fromSeed(switchboardProgram);
|
||||
|
||||
const [permissionAccount, permissionBump] =
|
||||
sbv2.PermissionAccount.fromSeed(
|
||||
switchboardProgram,
|
||||
queueData.authority,
|
||||
queueAccount.publicKey,
|
||||
vrfAccount.publicKey
|
||||
);
|
||||
|
||||
const requestInstruction = await vrfClientProgram.methods
|
||||
.requestResult({
|
||||
switchboardStateBump: programStateBump,
|
||||
permissionBump,
|
||||
})
|
||||
.accounts({
|
||||
state: vrfClientPubkey,
|
||||
authority: payer.publicKey,
|
||||
switchboardProgram: switchboardProgram.programId,
|
||||
vrf: vrfAccount.publicKey,
|
||||
oracleQueue: queueAccount.publicKey,
|
||||
queueAuthority: queueData.authority,
|
||||
dataBuffer: queueData.dataBuffer,
|
||||
permission: permissionAccount.publicKey,
|
||||
escrow: vrfData.escrow,
|
||||
payerWallet: payerTokenAccount.address,
|
||||
payerAuthority: payer.publicKey,
|
||||
recentBlockhashes: anchor.web3.SYSVAR_RECENT_BLOCKHASHES_PUBKEY,
|
||||
programState: programStateAccount.publicKey,
|
||||
tokenProgram: spl.TOKEN_PROGRAM_ID,
|
||||
})
|
||||
.signers([payer])
|
||||
.instruction();
|
||||
|
||||
const results: RequestRandomnessResult[] = [];
|
||||
let successes = 0;
|
||||
let failures = 0;
|
||||
try {
|
||||
for await (const i of [...Array(numLoops).keys()]) {
|
||||
writeStatus(i, numLoops, successes, failures);
|
||||
let success = false;
|
||||
try {
|
||||
while (true) {
|
||||
try {
|
||||
await provider.sendAndConfirm(
|
||||
new anchor.web3.Transaction().add(requestInstruction),
|
||||
[payer]
|
||||
);
|
||||
// console.log("sent");
|
||||
break;
|
||||
} catch (error) {
|
||||
if (!error.message.includes("0x17b5")) {
|
||||
throw error;
|
||||
}
|
||||
await sbv2Utils.sleep(2500);
|
||||
}
|
||||
}
|
||||
|
||||
await awaitCallback(provider.connection, vrfClientPubkey, 45_000)
|
||||
.then(() => {
|
||||
success = true;
|
||||
successes++;
|
||||
})
|
||||
.catch(async (error) => {
|
||||
failures++;
|
||||
const vrf = await vrfAccount.loadData();
|
||||
|
||||
clearStatus();
|
||||
console.error(error.message);
|
||||
writeVrfState(vrf);
|
||||
writeStatus(i, numLoops, successes, failures);
|
||||
});
|
||||
} catch (error) {}
|
||||
|
||||
const vrf = await vrfAccount.loadData();
|
||||
const vrfStatus = sbv2Utils.toVrfStatusString(vrf.status);
|
||||
const result: RequestRandomnessResult = {
|
||||
success: vrfStatus === "StatusCallbackSuccess",
|
||||
counter: vrf.counter.toString(10),
|
||||
producer: vrf.builders[0].producer.toString(),
|
||||
status: vrfStatus,
|
||||
txRemaining: vrf.builders[0].txRemaining,
|
||||
alpha: `[${vrf.currentRound.alpha.slice(0, 32).toString()}]`,
|
||||
alphaHex: Buffer.from(vrf.currentRound.alpha.slice(0, 32)).toString(
|
||||
"hex"
|
||||
),
|
||||
proof: `[${vrf.builders[0].reprProof.slice(0, 80).toString()}]`,
|
||||
proofHex: Buffer.from(
|
||||
vrf.builders[0].reprProof.slice(0, 80)
|
||||
).toString("hex"),
|
||||
proofBase64: Buffer.from(
|
||||
vrf.builders[0].reprProof.slice(0, 80)
|
||||
).toString("base64"),
|
||||
result: `[${vrf.currentRound.result.toString()}]`,
|
||||
stage: vrf.builders[0].stage,
|
||||
txs:
|
||||
vrfStatus === "StatusCallbackSuccess"
|
||||
? undefined
|
||||
: await fetchTransactions(
|
||||
vrfAccount.program.provider.connection,
|
||||
vrfAccount.publicKey,
|
||||
15
|
||||
),
|
||||
};
|
||||
results.push(result);
|
||||
|
||||
saveResults(startTime, vrfClientKey, results);
|
||||
|
||||
clearStatus();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`GLOBAL: ${error}`);
|
||||
}
|
||||
|
||||
writeStatus(numLoops, numLoops, successes, failures);
|
||||
|
||||
saveResults(startTime, vrfClientKey, results);
|
||||
|
||||
process.exit(0);
|
||||
}
|
||||
)
|
||||
.options({
|
||||
cluster: {
|
||||
type: "string",
|
||||
alias: "c",
|
||||
describe: "Solana cluster to interact with",
|
||||
options: ["devnet", "mainnet-beta", "localnet"],
|
||||
default: "devnet",
|
||||
demand: false,
|
||||
},
|
||||
rpcUrl: {
|
||||
type: "string",
|
||||
alias: "u",
|
||||
describe: "Alternative RPC URL",
|
||||
},
|
||||
})
|
||||
.help().argv;
|
||||
|
||||
function getRpcUrl(cluster: string): string {
|
||||
switch (cluster) {
|
||||
case "mainnet-beta":
|
||||
return DEFAULT_MAINNET_RPC;
|
||||
case "devnet":
|
||||
return DEFAULT_DEVNET_RPC;
|
||||
case "localnet":
|
||||
return DEFAULT_LOCALNET_RPC;
|
||||
default:
|
||||
throw new Error(`Failed to find RPC_URL for cluster ${cluster}`);
|
||||
}
|
||||
}
|
||||
|
||||
function writeVrfState(vrf: any) {
|
||||
console.log(`Status: ${toVrfStatusString(vrf.builders[0]?.status) ?? ""}`);
|
||||
console.log(`TxRemaining: ${vrf.builders[0]?.txRemaining ?? ""}`);
|
||||
console.log(
|
||||
`Alpha: [${vrf.currentRound.alpha
|
||||
.slice(0, 32)
|
||||
.map((value) => value.toString())}]`
|
||||
);
|
||||
console.log(
|
||||
`AlphaHex: ${Buffer.from(vrf.currentRound.alpha.slice(0, 32)).toString(
|
||||
"hex"
|
||||
)}`
|
||||
);
|
||||
console.log(
|
||||
`Proof: [${vrf.builders[0].reprProof
|
||||
.slice(0, 80)
|
||||
.map((value) => value.toString())}]`
|
||||
);
|
||||
console.log(
|
||||
`ProofHex: ${Buffer.from(vrf.builders[0].reprProof.slice(0, 80)).toString(
|
||||
"hex"
|
||||
)}`
|
||||
);
|
||||
console.log(
|
||||
`ProofBase64: ${Buffer.from(
|
||||
vrf.builders[0].reprProof.slice(0, 80)
|
||||
).toString("base64")}`
|
||||
);
|
||||
console.log(`Stage: ${vrf.builders[0].stage}`);
|
||||
}
|
||||
|
||||
function saveResults(
|
||||
timestamp: number,
|
||||
vrfClientKey: anchor.web3.PublicKey,
|
||||
results: RequestRandomnessResult[]
|
||||
) {
|
||||
if (results.length) {
|
||||
fs.writeFileSync(
|
||||
path.join(process.cwd(), `${vrfClientKey}_${timestamp}.json`),
|
||||
JSON.stringify(results, undefined, 2),
|
||||
"utf-8"
|
||||
);
|
||||
}
|
||||
|
||||
const errorResults = results.filter((result) => !result.success);
|
||||
|
||||
if (errorResults.length) {
|
||||
fs.writeFileSync(
|
||||
path.join(process.cwd(), `${vrfClientKey}_ERRORS_${timestamp}.json`),
|
||||
JSON.stringify(errorResults, undefined, 2),
|
||||
"utf-8"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async function loadCli(
|
||||
rpcUrl: string,
|
||||
cluster: string
|
||||
): Promise<{
|
||||
vrfClientProgram: anchor.Program<AnchorVrfParser>;
|
||||
switchboardProgram: anchor.Program;
|
||||
payer: anchor.web3.Keypair;
|
||||
provider: anchor.AnchorProvider;
|
||||
}> {
|
||||
if (cluster !== "mainnet-beta" && cluster !== "devnet") {
|
||||
throw new Error(
|
||||
`cluster must be mainnet-beta or devnet, cluster = ${cluster}`
|
||||
);
|
||||
}
|
||||
|
||||
process.env.ANCHOR_WALLET = sbv2Utils.getAnchorWalletPath();
|
||||
const url = rpcUrl || getRpcUrl(cluster);
|
||||
const envProvider = anchor.AnchorProvider.local(url);
|
||||
const provider = new anchor.AnchorProvider(
|
||||
new anchor.web3.Connection(url, {
|
||||
commitment: DEFAULT_COMMITMENT,
|
||||
}),
|
||||
envProvider.wallet,
|
||||
{
|
||||
commitment: DEFAULT_COMMITMENT,
|
||||
}
|
||||
);
|
||||
|
||||
const switchboardProgram = await sbv2.loadSwitchboardProgram(
|
||||
cluster,
|
||||
provider.connection,
|
||||
(provider.wallet as sbv2.AnchorWallet).payer,
|
||||
{
|
||||
commitment: DEFAULT_COMMITMENT,
|
||||
}
|
||||
);
|
||||
const payer = sbv2.programWallet(switchboardProgram);
|
||||
|
||||
// load VRF Client program
|
||||
const vrfClientProgram = new anchor.Program(
|
||||
IDL,
|
||||
PROGRAM_ID,
|
||||
provider,
|
||||
new anchor.BorshCoder(IDL)
|
||||
);
|
||||
|
||||
return {
|
||||
vrfClientProgram,
|
||||
switchboardProgram,
|
||||
payer,
|
||||
provider,
|
||||
};
|
||||
}
|
||||
|
||||
async function awaitCallback(
|
||||
connection: anchor.web3.Connection,
|
||||
vrfClientKey: anchor.web3.PublicKey,
|
||||
timeoutInterval: number,
|
||||
errorMsg = "Timed out waiting for VRF Client callback"
|
||||
) {
|
||||
let ws: number | undefined = undefined;
|
||||
const result: anchor.BN = await sbv2Utils
|
||||
.promiseWithTimeout(
|
||||
timeoutInterval,
|
||||
new Promise(
|
||||
(
|
||||
resolve: (result: anchor.BN) => void,
|
||||
reject: (reason: string) => void
|
||||
) => {
|
||||
ws = connection.onAccountChange(
|
||||
vrfClientKey,
|
||||
async (
|
||||
accountInfo: anchor.web3.AccountInfo<Buffer>,
|
||||
context: anchor.web3.Context
|
||||
) => {
|
||||
const clientState = VrfClient.decode(accountInfo.data);
|
||||
if (clientState.result.gt(new anchor.BN(0))) {
|
||||
resolve(clientState.result);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
),
|
||||
new Error(errorMsg)
|
||||
)
|
||||
.finally(async () => {
|
||||
if (ws) {
|
||||
await connection.removeAccountChangeListener(ws);
|
||||
}
|
||||
ws = undefined;
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const clearLastLine = () => {
|
||||
process.stdout.moveCursor(0, -1); // up one line
|
||||
process.stdout.clearLine(1); // from cursor to end
|
||||
};
|
||||
|
||||
function writeStatus(
|
||||
i: number,
|
||||
numLoops: number,
|
||||
successes: number,
|
||||
failures: number
|
||||
) {
|
||||
process.stdout.write(`# ${i} / ${numLoops}\n`);
|
||||
process.stdout.write(`${chalk.green("Success:", successes)} / ${i}\n`);
|
||||
process.stdout.write(`${chalk.red("Errors: ", failures)} / ${i}\n`);
|
||||
}
|
||||
|
||||
function clearStatus() {
|
||||
process.stdout.moveCursor(0, -1); // up one line
|
||||
process.stdout.clearLine(1); // from cursor to end
|
||||
process.stdout.moveCursor(0, -1); // up one line
|
||||
process.stdout.clearLine(1); // from cursor to end
|
||||
process.stdout.moveCursor(0, -1); // up one line
|
||||
process.stdout.clearLine(1); // from cursor to end
|
||||
}
|
||||
|
||||
async function fetchTransactions(
|
||||
connection: Connection,
|
||||
pubkey: PublicKey,
|
||||
numTransactions = 10
|
||||
): Promise<any[]> {
|
||||
const signatures = (
|
||||
await connection.getSignaturesForAddress(
|
||||
pubkey,
|
||||
{ limit: numTransactions },
|
||||
"confirmed"
|
||||
)
|
||||
).map((t) => t.signature);
|
||||
|
||||
console.log(`FETCHED ${signatures.length} transactions`);
|
||||
|
||||
let parsedTxns: ParsedTransactionWithMeta[] | null = null;
|
||||
while (!parsedTxns) {
|
||||
parsedTxns = await connection.getParsedTransactions(
|
||||
signatures,
|
||||
"confirmed"
|
||||
);
|
||||
|
||||
if (!parsedTxns || parsedTxns.length !== signatures.length) {
|
||||
await sbv2Utils.sleep(1000);
|
||||
}
|
||||
}
|
||||
|
||||
return parsedTxns.map((tx, i) => {
|
||||
return {
|
||||
signature: signatures[i],
|
||||
logs: tx.meta.logMessages,
|
||||
};
|
||||
});
|
||||
}
|
|
@ -1,40 +1,46 @@
|
|||
import * as borsh from "@project-serum/borsh" // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||
import { Connection, PublicKey } from "@solana/web3.js"
|
||||
import BN from "bn.js" // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||
import { PROGRAM_ID } from "../programId"
|
||||
import * as borsh from "@project-serum/borsh"; // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||
import { Connection, PublicKey } from "@solana/web3.js";
|
||||
import BN from "bn.js"; // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||
import { PROGRAM_ID } from "../programId";
|
||||
|
||||
export interface VrfClientFields {
|
||||
bump: number
|
||||
maxResult: BN
|
||||
resultBuffer: Array<number>
|
||||
result: BN
|
||||
lastTimestamp: BN
|
||||
authority: PublicKey
|
||||
vrf: PublicKey
|
||||
bump: number;
|
||||
maxResult: BN;
|
||||
resultBuffer: Array<number>;
|
||||
result: BN;
|
||||
lastTimestamp: BN;
|
||||
authority: PublicKey;
|
||||
vrf: PublicKey;
|
||||
}
|
||||
|
||||
export interface VrfClientJSON {
|
||||
bump: number
|
||||
maxResult: string
|
||||
resultBuffer: Array<number>
|
||||
result: string
|
||||
lastTimestamp: string
|
||||
authority: string
|
||||
vrf: string
|
||||
bump: number;
|
||||
maxResult: string;
|
||||
resultBuffer: Array<number>;
|
||||
result: string;
|
||||
lastTimestamp: string;
|
||||
authority: string;
|
||||
vrf: string;
|
||||
}
|
||||
|
||||
export class VrfClient {
|
||||
readonly bump: number
|
||||
readonly maxResult: BN
|
||||
readonly resultBuffer: Array<number>
|
||||
readonly result: BN
|
||||
readonly lastTimestamp: BN
|
||||
readonly authority: PublicKey
|
||||
readonly vrf: PublicKey
|
||||
readonly bump: number;
|
||||
|
||||
readonly maxResult: BN;
|
||||
|
||||
readonly resultBuffer: Array<number>;
|
||||
|
||||
readonly result: BN;
|
||||
|
||||
readonly lastTimestamp: BN;
|
||||
|
||||
readonly authority: PublicKey;
|
||||
|
||||
readonly vrf: PublicKey;
|
||||
|
||||
static readonly discriminator = Buffer.from([
|
||||
230, 174, 157, 153, 51, 18, 230, 163,
|
||||
])
|
||||
]);
|
||||
|
||||
static readonly layout = borsh.struct([
|
||||
borsh.u8("bump"),
|
||||
|
@ -44,58 +50,58 @@ export class VrfClient {
|
|||
borsh.i64("lastTimestamp"),
|
||||
borsh.publicKey("authority"),
|
||||
borsh.publicKey("vrf"),
|
||||
])
|
||||
]);
|
||||
|
||||
constructor(fields: VrfClientFields) {
|
||||
this.bump = fields.bump
|
||||
this.maxResult = fields.maxResult
|
||||
this.resultBuffer = fields.resultBuffer
|
||||
this.result = fields.result
|
||||
this.lastTimestamp = fields.lastTimestamp
|
||||
this.authority = fields.authority
|
||||
this.vrf = fields.vrf
|
||||
this.bump = fields.bump;
|
||||
this.maxResult = fields.maxResult;
|
||||
this.resultBuffer = fields.resultBuffer;
|
||||
this.result = fields.result;
|
||||
this.lastTimestamp = fields.lastTimestamp;
|
||||
this.authority = fields.authority;
|
||||
this.vrf = fields.vrf;
|
||||
}
|
||||
|
||||
static async fetch(
|
||||
c: Connection,
|
||||
address: PublicKey
|
||||
): Promise<VrfClient | null> {
|
||||
const info = await c.getAccountInfo(address)
|
||||
const info = await c.getAccountInfo(address);
|
||||
|
||||
if (info === null) {
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
if (!info.owner.equals(PROGRAM_ID)) {
|
||||
throw new Error("account doesn't belong to this program")
|
||||
throw new Error("account doesn't belong to this program");
|
||||
}
|
||||
|
||||
return this.decode(info.data)
|
||||
return this.decode(info.data);
|
||||
}
|
||||
|
||||
static async fetchMultiple(
|
||||
c: Connection,
|
||||
addresses: PublicKey[]
|
||||
): Promise<Array<VrfClient | null>> {
|
||||
const infos = await c.getMultipleAccountsInfo(addresses)
|
||||
const infos = await c.getMultipleAccountsInfo(addresses);
|
||||
|
||||
return infos.map((info) => {
|
||||
if (info === null) {
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
if (!info.owner.equals(PROGRAM_ID)) {
|
||||
throw new Error("account doesn't belong to this program")
|
||||
throw new Error("account doesn't belong to this program");
|
||||
}
|
||||
|
||||
return this.decode(info.data)
|
||||
})
|
||||
return this.decode(info.data);
|
||||
});
|
||||
}
|
||||
|
||||
static decode(data: Buffer): VrfClient {
|
||||
if (!data.slice(0, 8).equals(VrfClient.discriminator)) {
|
||||
throw new Error("invalid account discriminator")
|
||||
throw new Error("invalid account discriminator");
|
||||
}
|
||||
|
||||
const dec = VrfClient.layout.decode(data.slice(8))
|
||||
const dec = VrfClient.layout.decode(data.slice(8));
|
||||
|
||||
return new VrfClient({
|
||||
bump: dec.bump,
|
||||
|
@ -105,7 +111,7 @@ export class VrfClient {
|
|||
lastTimestamp: dec.lastTimestamp,
|
||||
authority: dec.authority,
|
||||
vrf: dec.vrf,
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
toJSON(): VrfClientJSON {
|
||||
|
@ -117,7 +123,7 @@ export class VrfClient {
|
|||
lastTimestamp: this.lastTimestamp.toString(),
|
||||
authority: this.authority.toString(),
|
||||
vrf: this.vrf.toString(),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static fromJSON(obj: VrfClientJSON): VrfClient {
|
||||
|
@ -129,6 +135,6 @@ export class VrfClient {
|
|||
lastTimestamp: new BN(obj.lastTimestamp),
|
||||
authority: new PublicKey(obj.authority),
|
||||
vrf: new PublicKey(obj.vrf),
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { PublicKey } from "@solana/web3.js"
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
|
||||
// Program ID passed with the cli --program-id flag when running the code generator. Do not edit, it will get overwritten.
|
||||
export const PROGRAM_ID_CLI = new PublicKey(
|
||||
"HjjRFjCyQH3ne6Gg8Yn3TQafrrYecRrphwLwnh2A26vM"
|
||||
)
|
||||
);
|
||||
|
||||
// This constant will not get overwritten on subsequent code generations and it's safe to modify it's value.
|
||||
export const PROGRAM_ID: PublicKey = PROGRAM_ID_CLI
|
||||
export const PROGRAM_ID: PublicKey = PROGRAM_ID_CLI;
|
||||
|
|
|
@ -7,6 +7,9 @@
|
|||
"url": "https://github.com/switchboard-xyz/switchboard-v2",
|
||||
"directory": "programs/anchor-vrf-parser"
|
||||
},
|
||||
"bin": {
|
||||
"sbv2-vrf-example": "./sbv2-vrf-example.sh"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "echo \"For workspace anchor-vrf-parser, run 'anchor build' from the project root\" && exit 0",
|
||||
"lint": "eslint --ext .js,.json,.ts 'src/**' --fix"
|
||||
|
@ -20,7 +23,8 @@
|
|||
"@switchboard-xyz/switchboard-v2": "^0.0.120",
|
||||
"chalk": "^4.1.2",
|
||||
"child_process": "^1.0.2",
|
||||
"dotenv": "^16.0.1"
|
||||
"dotenv": "^16.0.1",
|
||||
"yargs": "^17.5.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@switchboard-xyz/switchboardv2-cli": "^0.2.14",
|
||||
|
@ -43,7 +47,7 @@
|
|||
"prettier-plugin-organize-imports": "^2.3.4",
|
||||
"shx": "^0.3.4",
|
||||
"ts-mocha": "^9.0.2",
|
||||
"ts-node": "^10.4.0",
|
||||
"typescript": "^4.3.5"
|
||||
"ts-node": "^10.8.0",
|
||||
"typescript": "^4.7.3"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
script_dir=$(dirname "$(readlink -f "$BASH_SOURCE")")
|
||||
# echo "$script_dir"
|
||||
|
||||
# /usr/bin/env node --enable-source-maps "$script_dir"/cli.js "$@"
|
||||
# "$script_dir"/node_modules/.bin/ts-node-esm "$script_dir"/cli.ts "$@"
|
||||
/usr/bin/env ts-node-esm "$script_dir"/cli.ts "$@"
|
|
@ -111,6 +111,9 @@ impl RequestResult<'_> {
|
|||
state_seeds,
|
||||
)?;
|
||||
|
||||
let mut client_state = ctx.accounts.state.load_mut()?;
|
||||
client_state.result = 0;
|
||||
|
||||
emit!(RequestingRandomness{
|
||||
vrf_client: ctx.accounts.state.key(),
|
||||
max_result: max_result,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import * as anchor from "@project-serum/anchor";
|
||||
import { AnchorProvider, Program } from "@project-serum/anchor";
|
||||
import { AnchorProvider } from "@project-serum/anchor";
|
||||
import * as spl from "@solana/spl-token";
|
||||
import {
|
||||
SystemProgram,
|
||||
|
@ -29,7 +29,7 @@ describe("anchor-vrf-parser test", () => {
|
|||
// const vrfClientProgram = anchor.workspace
|
||||
// .AnchorVrfParser as Program<AnchorVrfParser>;
|
||||
|
||||
const vrfClientProgram = new Program(
|
||||
const vrfClientProgram = new anchor.Program(
|
||||
IDL,
|
||||
PROGRAM_ID,
|
||||
provider,
|
||||
|
@ -164,6 +164,28 @@ describe("anchor-vrf-parser test", () => {
|
|||
spl.ASSOCIATED_TOKEN_PROGRAM_ID
|
||||
);
|
||||
|
||||
// watch VrfClientState for a populated result
|
||||
let ws: number | undefined = undefined;
|
||||
const waitForResultPromise = new Promise(
|
||||
(
|
||||
resolve: (result: anchor.BN) => void,
|
||||
reject: (reason: string) => void
|
||||
) => {
|
||||
ws = vrfClientProgram.provider.connection.onAccountChange(
|
||||
vrfClientKey,
|
||||
async (accountInfo: AccountInfo<Buffer>, context: Context) => {
|
||||
const clientState: VrfClientState = vrfClientAccountCoder.decode(
|
||||
"VrfClient",
|
||||
accountInfo.data
|
||||
);
|
||||
if (clientState.result.gt(new anchor.BN(0))) {
|
||||
resolve(clientState.result);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
// Request randomness
|
||||
console.log(`Sending RequestRandomness instruction`);
|
||||
const requestTxn = vrfClientProgram.methods.requestResult!({
|
||||
|
|
|
@ -1,12 +1,18 @@
|
|||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"ts-node": {
|
||||
"compilerOptions": {
|
||||
"module": "commonjs"
|
||||
}
|
||||
},
|
||||
"compilerOptions": {
|
||||
"types": ["mocha", "chai"],
|
||||
"types": ["mocha", "chai", "node"],
|
||||
"typeRoots": ["./node_modules/@types"],
|
||||
"module": "CommonJS",
|
||||
"module": "commonjs",
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"strict": false,
|
||||
"strictNullChecks": false,
|
||||
"paths": {
|
||||
"@switchboard-xyz/switchboard-v2": ["../../libraries/ts"],
|
||||
"@switchboard-xyz/sbv2-utils": ["../../libraries/sbv2-utils"],
|
||||
|
@ -16,12 +22,8 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"tests/**/*",
|
||||
"client/**/*",
|
||||
"../../../target/types/anchor_vrf_parser",
|
||||
"../../../target/idl/anchor_vrf_parser"
|
||||
],
|
||||
"include": ["tests/**/*", "./cli.ts", "./client/**/*"],
|
||||
"exclude": ["target", "lib"],
|
||||
"references": [
|
||||
{ "path": "../../libraries/ts" },
|
||||
{ "path": "../../libraries/sbv2-utils" }
|
||||
|
|
55
yarn.lock
55
yarn.lock
|
@ -4149,6 +4149,48 @@
|
|||
"@svgr/plugin-jsx" "^6.2.1"
|
||||
"@svgr/plugin-svgo" "^6.2.0"
|
||||
|
||||
"@switchboard-xyz/switchboard-v2@^0.0.115":
|
||||
version "0.0.115"
|
||||
resolved "https://registry.npmjs.org/@switchboard-xyz/switchboard-v2/-/switchboard-v2-0.0.115.tgz#9450357d8341b0432975363716ce44954408d83a"
|
||||
integrity sha512-eM9CGs1HaKpExPtb56IQG0+j+Oy6nXM+5g4U7JK6DOR3SwbevCHoTjZt8EIgMuWr1M5NnGK9c0aRMygow+lFIg==
|
||||
dependencies:
|
||||
"@project-serum/anchor" "^0.24.2"
|
||||
"@solana/spl-governance" "^0.0.34"
|
||||
"@solana/spl-token" "^0.2.0"
|
||||
"@solana/web3.js" "^1.44.3"
|
||||
"@types/big.js" "^6.1.4"
|
||||
assert "^2.0.0"
|
||||
big.js "^6.2.0"
|
||||
bs58 "^4.0.1"
|
||||
chan "^0.6.1"
|
||||
crypto-js "^4.0.0"
|
||||
glob "^8.0.3"
|
||||
long "^4.0.0"
|
||||
mocha "^9.1.1"
|
||||
node-fetch "^3.2.3"
|
||||
protobufjs "^6.11.3"
|
||||
|
||||
"@switchboard-xyz/switchboard-v2@^0.0.120":
|
||||
version "0.0.120"
|
||||
resolved "https://registry.npmjs.org/@switchboard-xyz/switchboard-v2/-/switchboard-v2-0.0.120.tgz#b48d2292489771535dc4e9c6b5241115096291ec"
|
||||
integrity sha512-BCdKacL4osK32SYo/OlNcSMO8rAwdo7ArtUQ0BTSKqce6wgL2naXFg82KmGIG4+SuoxNLFvkbG0/zT8UYliwgw==
|
||||
dependencies:
|
||||
"@project-serum/anchor" "^0.24.2"
|
||||
"@solana/spl-governance" "^0.0.34"
|
||||
"@solana/spl-token" "^0.2.0"
|
||||
"@solana/web3.js" "^1.44.3"
|
||||
"@types/big.js" "^6.1.4"
|
||||
assert "^2.0.0"
|
||||
big.js "^6.2.0"
|
||||
bs58 "^4.0.1"
|
||||
chan "^0.6.1"
|
||||
crypto-js "^4.0.0"
|
||||
glob "^8.0.3"
|
||||
long "^4.0.0"
|
||||
mocha "^9.1.1"
|
||||
node-fetch "^3.2.6"
|
||||
protobufjs "^6.11.3"
|
||||
|
||||
"@szmarczak/http-timer@^1.1.2":
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421"
|
||||
|
@ -12033,6 +12075,15 @@ node-fetch@2, node-fetch@2.6.7, node-fetch@^2.2.0, node-fetch@^2.6.1, node-fetch
|
|||
dependencies:
|
||||
whatwg-url "^5.0.0"
|
||||
|
||||
node-fetch@^3.2.3:
|
||||
version "3.2.8"
|
||||
resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-3.2.8.tgz#0d9516dcf43a758d78d6dbe538adf0b1f6a4944e"
|
||||
integrity sha512-KtpD1YhGszhntMpBDyp5lyagk8KIMopC1LEb7cQUAh7zcosaX5uK8HnbNb2i3NTQK3sIawCItS0uFC3QzcLHdg==
|
||||
dependencies:
|
||||
data-uri-to-buffer "^4.0.0"
|
||||
fetch-blob "^3.1.4"
|
||||
formdata-polyfill "^4.0.10"
|
||||
|
||||
node-fetch@^3.2.6:
|
||||
version "3.2.6"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.2.6.tgz#6d4627181697a9d9674aae0d61548e0d629b31b9"
|
||||
|
@ -17104,9 +17155,9 @@ yargs@16.2.0, yargs@^16.2.0:
|
|||
y18n "^5.0.5"
|
||||
yargs-parser "^20.2.2"
|
||||
|
||||
yargs@^17.0.1, yargs@^17.3.1:
|
||||
yargs@^17.0.1, yargs@^17.3.1, yargs@^17.5.1:
|
||||
version "17.5.1"
|
||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.5.1.tgz#e109900cab6fcb7fd44b1d8249166feb0b36e58e"
|
||||
resolved "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz#e109900cab6fcb7fd44b1d8249166feb0b36e58e"
|
||||
integrity sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==
|
||||
dependencies:
|
||||
cliui "^7.0.2"
|
||||
|
|
Loading…
Reference in New Issue