solana.js: updated tests
This commit is contained in:
parent
9ad1c80de5
commit
ba24f9b38d
File diff suppressed because it is too large
Load Diff
|
@ -19,8 +19,8 @@
|
|||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@project-serum/anchor": "^0.25.0",
|
||||
"@solana/web3.js": "^1.33.0",
|
||||
"@switchboard-xyz/switchboard-v2": "^0.0.165",
|
||||
"@solana/web3.js": "^1.67.2",
|
||||
"@switchboard-xyz/solana.js": "file:../solana.js",
|
||||
"big.js": "^6.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
import { clusterApiUrl, Connection, Keypair, PublicKey } from "@solana/web3.js";
|
||||
import {
|
||||
AggregatorAccount,
|
||||
loadSwitchboardProgram,
|
||||
} from "@switchboard-xyz/switchboard-v2";
|
||||
SwitchboardProgram,
|
||||
} from "@switchboard-xyz/solana.js";
|
||||
|
||||
// SOL/USD Feed https://switchboard.xyz/explorer/2/GvDMxPzN1sCj7L26YDK2HnMRXEQmQ2aemov8YBtPS7vR
|
||||
// Create your own feed here https://publish.switchboard.xyz/
|
||||
|
@ -13,20 +13,17 @@ const switchboardFeed = new PublicKey(
|
|||
|
||||
async function main() {
|
||||
// load the switchboard program
|
||||
const program = await loadSwitchboardProgram(
|
||||
const program = await SwitchboardProgram.load(
|
||||
"devnet",
|
||||
new Connection(clusterApiUrl("devnet")),
|
||||
Keypair.fromSeed(new Uint8Array(32).fill(1)) // using dummy keypair since we wont be submitting any transactions
|
||||
);
|
||||
|
||||
// load the switchboard aggregator
|
||||
const aggregator = new AggregatorAccount({
|
||||
program,
|
||||
publicKey: switchboardFeed,
|
||||
});
|
||||
const aggregator = new AggregatorAccount(program, switchboardFeed);
|
||||
|
||||
// get the result
|
||||
const result = await aggregator.getLatestValue();
|
||||
const result = await aggregator.fetchLatestValue();
|
||||
console.log(`Switchboard Result: ${result}`);
|
||||
}
|
||||
|
||||
|
|
|
@ -22,11 +22,11 @@
|
|||
"outDir": "dist",
|
||||
"rootDir": "src",
|
||||
"paths": {
|
||||
"@switchboard-xyz/switchboard-v2": ["../switchboard-v2"]
|
||||
"@switchboard-xyz/solana.js": ["../solana.js"]
|
||||
}
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["esbuild.js", "dist"],
|
||||
"references": [{ "path": "../switchboard-v2" }],
|
||||
"references": [{ "path": "../solana.js" }],
|
||||
"files": ["src/main.ts"]
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -19,9 +19,9 @@
|
|||
"dependencies": {
|
||||
"@project-serum/anchor": "^0.25.0",
|
||||
"@solana/spl-token-v2": "npm:@solana/spl-token@^0.2.0",
|
||||
"@solana/web3.js": "^1.50.1",
|
||||
"@solana/web3.js": "^1.67.2",
|
||||
"@switchboard-xyz/common": "^2.1.7",
|
||||
"@switchboard-xyz/switchboard-v2": "^0.0.165",
|
||||
"@switchboard-xyz/solana.js": "file:../solana.js",
|
||||
"chalk": "^4.1.2",
|
||||
"dotenv": "^16.0.1",
|
||||
"readline-sync": "^1.4.10"
|
||||
|
|
|
@ -1,21 +1,14 @@
|
|||
import * as anchor from "@project-serum/anchor";
|
||||
import * as spl from "@solana/spl-token-v2";
|
||||
import type { PublicKey } from "@solana/web3.js";
|
||||
import { clusterApiUrl, Connection, Keypair } from "@solana/web3.js";
|
||||
import { IOracleJob, OracleJob } from "@switchboard-xyz/common";
|
||||
import { OracleJob } from "@switchboard-xyz/common";
|
||||
import {
|
||||
AggregatorAccount,
|
||||
CrankAccount,
|
||||
JobAccount,
|
||||
LeaseAccount,
|
||||
loadSwitchboardProgram,
|
||||
SwitchboardProgram,
|
||||
OracleAccount,
|
||||
OracleQueueAccount,
|
||||
QueueAccount,
|
||||
PermissionAccount,
|
||||
ProgramStateAccount,
|
||||
programWallet,
|
||||
SwitchboardPermission,
|
||||
} from "@switchboard-xyz/switchboard-v2";
|
||||
types,
|
||||
} from "@switchboard-xyz/solana.js";
|
||||
import chalk from "chalk";
|
||||
import dotenv from "dotenv";
|
||||
import fs from "fs";
|
||||
|
@ -89,111 +82,60 @@ async function main() {
|
|||
cluster === "localnet" ? "http://localhost:8899" : clusterApiUrl(cluster);
|
||||
}
|
||||
|
||||
const program = await loadSwitchboardProgram(
|
||||
const program = await SwitchboardProgram.load(
|
||||
cluster === "localnet" ? "devnet" : cluster,
|
||||
new Connection(rpcUrl),
|
||||
authority,
|
||||
{
|
||||
commitment: "finalized",
|
||||
}
|
||||
authority
|
||||
);
|
||||
|
||||
console.log(chalk.yellow("######## Switchboard Setup ########"));
|
||||
|
||||
// Program State Account and token mint for payout rewards
|
||||
const [programStateAccount] = ProgramStateAccount.fromSeed(program);
|
||||
console.log(toAccountString("Program State", programStateAccount.publicKey));
|
||||
const mint = await programStateAccount.getTokenMint();
|
||||
const tokenAccount = await spl.createAccount(
|
||||
program.provider.connection,
|
||||
programWallet(program),
|
||||
mint.address,
|
||||
authority.publicKey,
|
||||
Keypair.generate()
|
||||
// create our token wallet for the wrapped SOL mint
|
||||
const tokenAccount = await program.mint.getOrCreateAssociatedUser(
|
||||
program.walletPubkey
|
||||
);
|
||||
|
||||
// Oracle Queue
|
||||
const queueAccount = await OracleQueueAccount.create(program, {
|
||||
name: Buffer.from("Queue-1"),
|
||||
mint: spl.NATIVE_MINT,
|
||||
const [queueAccount] = await QueueAccount.create(program, {
|
||||
name: "Queue-1",
|
||||
slashingEnabled: false,
|
||||
reward: new anchor.BN(0), // no token account needed
|
||||
minStake: new anchor.BN(0),
|
||||
authority: authority.publicKey,
|
||||
reward: 0,
|
||||
minStake: 0,
|
||||
});
|
||||
console.log(toAccountString("Oracle Queue", queueAccount.publicKey));
|
||||
|
||||
// Crank
|
||||
const crankAccount = await CrankAccount.create(program, {
|
||||
name: Buffer.from("Crank"),
|
||||
const [crankAccount] = await queueAccount.createCrank({
|
||||
name: "Crank",
|
||||
maxRows: 10,
|
||||
queueAccount,
|
||||
});
|
||||
console.log(toAccountString("Crank", crankAccount.publicKey));
|
||||
|
||||
// Oracle
|
||||
const oracleAccount = await OracleAccount.create(program, {
|
||||
name: Buffer.from("Oracle"),
|
||||
queueAccount,
|
||||
});
|
||||
console.log(toAccountString("Oracle", oracleAccount.publicKey));
|
||||
|
||||
// Oracle permissions
|
||||
const oraclePermission = await PermissionAccount.create(program, {
|
||||
authority: authority.publicKey,
|
||||
granter: queueAccount.publicKey,
|
||||
grantee: oracleAccount.publicKey,
|
||||
});
|
||||
await oraclePermission.set({
|
||||
authority,
|
||||
permission: SwitchboardPermission.PERMIT_ORACLE_HEARTBEAT,
|
||||
const [oracleAccount] = await queueAccount.createOracle({
|
||||
name: "Oracle",
|
||||
enable: true,
|
||||
});
|
||||
console.log(toAccountString(` Permission`, oraclePermission.publicKey));
|
||||
await oracleAccount.heartbeat(authority);
|
||||
console.log(toAccountString("Oracle", oracleAccount.publicKey));
|
||||
await oracleAccount.heartbeat();
|
||||
|
||||
// Aggregator
|
||||
const aggregatorAccount = await AggregatorAccount.create(program, {
|
||||
name: Buffer.from("SOL_USD"),
|
||||
|
||||
const [aggregatorAccount] = await queueAccount.createFeed({
|
||||
name: "SOL_USD",
|
||||
queueAuthority: authority,
|
||||
batchSize: 1,
|
||||
minRequiredOracleResults: 1,
|
||||
minRequiredJobResults: 1,
|
||||
minUpdateDelaySeconds: 10,
|
||||
queueAccount,
|
||||
authority: authority.publicKey,
|
||||
});
|
||||
console.log(
|
||||
toAccountString(`Aggregator (SOL/USD)`, aggregatorAccount.publicKey)
|
||||
);
|
||||
if (!aggregatorAccount.publicKey) {
|
||||
throw new Error(`failed to read Aggregator publicKey`);
|
||||
}
|
||||
|
||||
// Aggregator permissions
|
||||
const aggregatorPermission = await PermissionAccount.create(program, {
|
||||
authority: authority.publicKey,
|
||||
granter: queueAccount.publicKey,
|
||||
grantee: aggregatorAccount.publicKey,
|
||||
});
|
||||
await aggregatorPermission.set({
|
||||
authority,
|
||||
permission: SwitchboardPermission.PERMIT_ORACLE_QUEUE_USAGE,
|
||||
fundAmount: 0.5,
|
||||
enable: true,
|
||||
});
|
||||
console.log(toAccountString(` Permission`, aggregatorPermission.publicKey));
|
||||
|
||||
// Lease
|
||||
const leaseContract = await LeaseAccount.create(program, {
|
||||
loadAmount: new anchor.BN(0),
|
||||
funder: tokenAccount,
|
||||
funderAuthority: authority,
|
||||
oracleQueueAccount: queueAccount,
|
||||
aggregatorAccount,
|
||||
});
|
||||
console.log(toAccountString(` Lease`, leaseContract.publicKey));
|
||||
|
||||
// Job
|
||||
const jobDefinition: IOracleJob = {
|
||||
crankPubkey: crankAccount.publicKey,
|
||||
jobs: [
|
||||
{
|
||||
weight: 2,
|
||||
data: OracleJob.encodeDelimited(
|
||||
OracleJob.fromObject({
|
||||
tasks: [
|
||||
{
|
||||
httpTask: {
|
||||
|
@ -204,22 +146,13 @@ async function main() {
|
|||
jsonParseTask: { path: "$.result.price" },
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// using OracleJob.fromObject will convert string enums to the correct format
|
||||
// using OracleJob.create will not
|
||||
const oracleJob = OracleJob.fromObject(jobDefinition);
|
||||
|
||||
const jobKeypair = anchor.web3.Keypair.generate();
|
||||
const jobAccount = await JobAccount.create(program, {
|
||||
data: Buffer.from(OracleJob.encodeDelimited(oracleJob).finish()),
|
||||
keypair: jobKeypair,
|
||||
authority: authority.publicKey,
|
||||
})
|
||||
).finish(),
|
||||
},
|
||||
],
|
||||
});
|
||||
console.log(toAccountString(` Job (FTX)`, jobAccount.publicKey));
|
||||
|
||||
await aggregatorAccount.addJob(jobAccount, authority); // Add Job to Aggregator
|
||||
await crankAccount.push({ aggregatorAccount }); // Add Aggregator to Crank
|
||||
console.log(toAccountString("Aggregator", aggregatorAccount.publicKey));
|
||||
const aggregator = await aggregatorAccount.loadData();
|
||||
|
||||
console.log(chalk.green("\u2714 Switchboard setup complete"));
|
||||
|
||||
|
@ -241,23 +174,17 @@ async function main() {
|
|||
}
|
||||
console.log("");
|
||||
|
||||
const confirmedRoundPromise = aggregatorAccount.nextRound();
|
||||
|
||||
// Turn the Crank
|
||||
async function turnCrank(retryCount: number): Promise<number> {
|
||||
try {
|
||||
const readyPubkeys = await crankAccount.peakNextReady(5);
|
||||
if (readyPubkeys) {
|
||||
const crank = await crankAccount.loadData();
|
||||
const queue = await queueAccount.loadData();
|
||||
|
||||
const crankTurnSignature = await crankAccount.pop({
|
||||
await crankAccount.pop({
|
||||
payoutWallet: tokenAccount,
|
||||
queuePubkey: queueAccount.publicKey,
|
||||
queueAuthority: queue.authority,
|
||||
readyPubkeys,
|
||||
nonce: 0,
|
||||
crank,
|
||||
queue,
|
||||
tokenMint: mint.address,
|
||||
});
|
||||
console.log(chalk.green("\u2714 Crank turned"));
|
||||
return 0;
|
||||
|
@ -278,28 +205,11 @@ async function main() {
|
|||
|
||||
// Read Aggregators latest result
|
||||
console.log(chalk.yellow("######## Aggregator Result ########"));
|
||||
await sleep(5000);
|
||||
try {
|
||||
let result = await aggregatorAccount.getLatestValue();
|
||||
if (result === null) {
|
||||
// wait a bit longer
|
||||
await sleep(2500);
|
||||
result = await aggregatorAccount.getLatestValue();
|
||||
if (result === null) {
|
||||
throw new Error(`Aggregator currently holds no value.`);
|
||||
}
|
||||
}
|
||||
console.log(`${chalk.blue("Result:")} ${chalk.green(result)}\r\n`);
|
||||
console.log(chalk.green("\u2714 Aggregator succesfully updated!"));
|
||||
} catch (error: any) {
|
||||
if (error.message === "Aggregator currently holds no value.") {
|
||||
const confirmedRound = await confirmedRoundPromise;
|
||||
console.log(
|
||||
chalk.red("\u2716 Aggregator holds no value, was the oracle running?")
|
||||
`${chalk.blue("Result:")} ${chalk.green(confirmedRound.result.toBig())}\r\n`
|
||||
);
|
||||
return;
|
||||
}
|
||||
console.error(error);
|
||||
}
|
||||
console.log(chalk.green("\u2714 Aggregator succesfully updated!"));
|
||||
}
|
||||
|
||||
main().then(
|
||||
|
|
|
@ -1134,6 +1134,23 @@ export class AggregatorAccount extends Account<types.AggregatorAccountData> {
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load an aggregators {@linkcode AggregatorHistoryBuffer}.
|
||||
* @return the list of historical samples attached to the aggregator.
|
||||
*/
|
||||
async loadHistory(): Promise<Array<types.AggregatorHistoryRow>> {
|
||||
if (!this.history) {
|
||||
this.history = new AggregatorHistoryBuffer(
|
||||
this.program,
|
||||
(await this.loadData()).historyBuffer
|
||||
);
|
||||
}
|
||||
|
||||
const history = await this.history.loadData();
|
||||
|
||||
return history;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -392,12 +392,8 @@ export class NativeMint extends Mint {
|
|||
const signers: Keypair[] = user ? [user] : [];
|
||||
|
||||
const userAddress = this.getAssociatedAddress(owner);
|
||||
const userBalance = (await this.getBalance(owner)) ?? 0;
|
||||
|
||||
if (amount) {
|
||||
if (amount >= userBalance) {
|
||||
ixns.push(spl.createCloseAccountInstruction(userAddress, owner, owner));
|
||||
} else {
|
||||
if (amount !== undefined && amount > 0) {
|
||||
const ephemeralAccount = Keypair.generate();
|
||||
const ephemeralWallet = this.getAssociatedAddress(
|
||||
ephemeralAccount.publicKey
|
||||
|
@ -426,7 +422,6 @@ export class NativeMint extends Mint {
|
|||
ephemeralAccount.publicKey
|
||||
)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
ixns.push(spl.createCloseAccountInstruction(userAddress, owner, owner));
|
||||
}
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
import 'mocha';
|
||||
import assert from 'assert';
|
||||
|
||||
import * as sbv2 from '../src';
|
||||
import { setupTest, TestContext } from './utilts';
|
||||
import { Keypair } from '@solana/web3.js';
|
||||
import { CrankAccount, QueueAccount, types } from '../src';
|
||||
|
||||
describe('Crank Tests', () => {
|
||||
let ctx: TestContext;
|
||||
|
||||
const queueAuthority = Keypair.generate();
|
||||
|
||||
let queueAccount: QueueAccount;
|
||||
let queue: types.OracleQueueAccountData;
|
||||
|
||||
let crankAccount: CrankAccount;
|
||||
|
||||
before(async () => {
|
||||
ctx = await setupTest();
|
||||
|
||||
[queueAccount] = await sbv2.QueueAccount.create(ctx.program, {
|
||||
name: 'q1',
|
||||
metadata: '',
|
||||
queueSize: 2,
|
||||
reward: 0.0025,
|
||||
minStake: 0,
|
||||
oracleTimeout: 60,
|
||||
slashingEnabled: false,
|
||||
unpermissionedFeeds: false,
|
||||
unpermissionedVrf: true,
|
||||
enableBufferRelayers: false,
|
||||
authority: queueAuthority.publicKey,
|
||||
});
|
||||
queue = await queueAccount.loadData();
|
||||
assert(
|
||||
queue.authority.equals(queueAuthority.publicKey),
|
||||
'Incorrect queue authority'
|
||||
);
|
||||
});
|
||||
|
||||
it('Creates a Crank', async () => {
|
||||
[crankAccount] = await queueAccount.createCrank({
|
||||
name: 'Crank #1',
|
||||
maxRows: 10,
|
||||
});
|
||||
const crank = await crankAccount.loadData();
|
||||
const crankRows = await crankAccount.loadCrank();
|
||||
assert(
|
||||
crankRows.length === 0,
|
||||
`Crank should be empty but found ${crankRows.length} rows`
|
||||
);
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue