fix: removed oracle import from solana.js for browser compatibility

This commit is contained in:
Conner Gallagher 2023-03-23 09:28:41 -06:00
parent dd7ab4bce7
commit b9f796a51d
18 changed files with 240 additions and 882 deletions

View File

@ -7,7 +7,7 @@
"editor.wordWrap": "on", "editor.wordWrap": "on",
"[typescript]": { "[typescript]": {
"editor.codeActionsOnSave": { "editor.codeActionsOnSave": {
"source.organizeImports": true "source.organizeImports": false
} }
} }
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "@switchboard-xyz/solana.js", "name": "@switchboard-xyz/solana.js",
"version": "2.1.1", "version": "2.1.2",
"author": "", "author": "",
"license": "MIT", "license": "MIT",
"description": "API wrapper for integrating with the Switchboard V2 program on Solana", "description": "API wrapper for integrating with the Switchboard V2 program on Solana",
@ -46,7 +46,6 @@
"@solana/spl-token": "^0.3.6", "@solana/spl-token": "^0.3.6",
"@solana/web3.js": "^1.73.0", "@solana/web3.js": "^1.73.0",
"@switchboard-xyz/common": "^2.1.33", "@switchboard-xyz/common": "^2.1.33",
"@switchboard-xyz/oracle": "^2.1.13",
"dotenv": "^16.0.3", "dotenv": "^16.0.3",
"lodash": "^4.17.21" "lodash": "^4.17.21"
}, },

View File

@ -7,22 +7,22 @@ import {
QueueAccount, QueueAccount,
SwitchboardNetwork, SwitchboardNetwork,
SwitchboardProgram, SwitchboardProgram,
} from '..'; } from '.';
import { AnchorProvider } from '@coral-xyz/anchor'; import { AnchorProvider } from '@coral-xyz/anchor';
import { Connection, Keypair, PublicKey } from '@solana/web3.js'; import { Connection, Keypair, PublicKey } from '@solana/web3.js';
import { // import {
IOracleConfig, // IOracleConfig,
NodeOracle, // NodeOracle,
ReleaseChannel, // ReleaseChannel,
ReleaseChannelVersion, // ReleaseChannelVersion,
} from '@switchboard-xyz/oracle'; // } from '@switchboard-xyz/oracle';
import fs from 'fs'; import fs from 'fs';
import _ from 'lodash'; import _ from 'lodash';
import os from 'os'; import os from 'os';
import path from 'path'; import path from 'path';
export type NodeConfig = IOracleConfig & Partial<ReleaseChannelVersion>; // export type NodeConfig = IOracleConfig & Partial<ReleaseChannelVersion>;
export function findAnchorTomlWallet(workingDir = process.cwd()): string { export function findAnchorTomlWallet(workingDir = process.cwd()): string {
let numDirs = 3; let numDirs = 3;
@ -58,7 +58,7 @@ export function findAnchorTomlWallet(workingDir = process.cwd()): string {
throw new Error(`Failed to find wallet path in Anchor.toml`); throw new Error(`Failed to find wallet path in Anchor.toml`);
} }
export type SwitchboardTestContextV2Init = Omit< export type SwitchboardTestContextInit = Omit<
Omit<NetworkInitParams, 'authority'>, Omit<NetworkInitParams, 'authority'>,
'oracles' 'oracles'
> & { > & {
@ -69,7 +69,7 @@ export type SwitchboardTestContextV2Init = Omit<
// oraclePubkey: Ei4HcqRQtf6TfwbuRXKRwCtt8PDXhmq9NhYLWpoh23xp // oraclePubkey: Ei4HcqRQtf6TfwbuRXKRwCtt8PDXhmq9NhYLWpoh23xp
// TIP: Do NOT define an authority and defaul to Anchor.toml wallet // TIP: Do NOT define an authority and defaul to Anchor.toml wallet
export const DEFAULT_LOCALNET_NETWORK: SwitchboardTestContextV2Init = { export const DEFAULT_LOCALNET_NETWORK: SwitchboardTestContextInit = {
name: 'Localnet Queue', name: 'Localnet Queue',
metadata: 'Localnet Metadata', metadata: 'Localnet Metadata',
keypair: Keypair.fromSecretKey( keypair: Keypair.fromSecretKey(
@ -112,8 +112,8 @@ export const DEFAULT_LOCALNET_NETWORK: SwitchboardTestContextV2Init = {
}, },
}; };
export class SwitchboardTestContextV2 { export class SwitchboardTestContext {
_oracle?: NodeOracle; // _oracle?: NodeOracle;
constructor( constructor(
readonly network: LoadedSwitchboardNetwork, readonly network: LoadedSwitchboardNetwork,
@ -134,10 +134,10 @@ export class SwitchboardTestContextV2 {
static async load( static async load(
connection: Connection, connection: Connection,
networkInitParams?: Partial<SwitchboardTestContextV2Init>, networkInitParams?: Partial<SwitchboardTestContextInit>,
walletPath?: string, walletPath?: string,
programId?: PublicKey programId?: PublicKey
): Promise<SwitchboardTestContextV2> { ): Promise<SwitchboardTestContext> {
const walletFsPath = walletPath ?? findAnchorTomlWallet(); const walletFsPath = walletPath ?? findAnchorTomlWallet();
const wallet = Keypair.fromSecretKey( const wallet = Keypair.fromSecretKey(
new Uint8Array(JSON.parse(fs.readFileSync(walletFsPath, 'utf-8'))) new Uint8Array(JSON.parse(fs.readFileSync(walletFsPath, 'utf-8')))
@ -187,7 +187,7 @@ export class SwitchboardTestContextV2 {
} }
const network = await SwitchboardNetwork.fromQueue(queueAccount); const network = await SwitchboardNetwork.fromQueue(queueAccount);
return new SwitchboardTestContextV2(network, walletFsPath); return new SwitchboardTestContext(network, walletFsPath);
} }
// eslint-disable-next-line no-empty // eslint-disable-next-line no-empty
} catch {} } catch {}
@ -204,15 +204,15 @@ export class SwitchboardTestContextV2 {
throw new Error(`Anchor wallet pubkey mismatch`); throw new Error(`Anchor wallet pubkey mismatch`);
} }
} }
return new SwitchboardTestContextV2(loadedNetwork, walletFsPath); return new SwitchboardTestContext(loadedNetwork, walletFsPath);
} }
static async loadFromProvider( static async loadFromProvider(
provider: AnchorProvider, provider: AnchorProvider,
networkInitParams?: Partial<SwitchboardTestContextV2Init>, networkInitParams?: Partial<SwitchboardTestContextInit>,
programId?: PublicKey programId?: PublicKey
): Promise<SwitchboardTestContextV2> { ): Promise<SwitchboardTestContext> {
const switchboard = await SwitchboardTestContextV2.load( const switchboard = await SwitchboardTestContext.load(
provider.connection, provider.connection,
networkInitParams, networkInitParams,
undefined, undefined,
@ -223,92 +223,92 @@ export class SwitchboardTestContextV2 {
static async initFromProvider( static async initFromProvider(
provider: AnchorProvider, provider: AnchorProvider,
networkInitParams?: Partial<SwitchboardTestContextV2Init>, networkInitParams?: Partial<SwitchboardTestContextInit>,
oracleParams?: Partial<IOracleConfig>, // oracleParams?: Partial<IOracleConfig>,
programId?: PublicKey programId?: PublicKey
): Promise<SwitchboardTestContextV2> { ): Promise<SwitchboardTestContext> {
const switchboard = await SwitchboardTestContextV2.loadFromProvider( const switchboard = await SwitchboardTestContext.loadFromProvider(
provider, provider,
networkInitParams, networkInitParams,
programId programId
); );
await switchboard.start(oracleParams); // await switchboard.start(oracleParams);
return switchboard; return switchboard;
} }
static async init( static async init(
connection: Connection, connection: Connection,
networkInitParams?: Partial<SwitchboardTestContextV2Init>, networkInitParams?: Partial<SwitchboardTestContextInit>,
oracleParams?: Partial<IOracleConfig>, // oracleParams?: Partial<IOracleConfig>,
walletPath?: string, walletPath?: string,
programId?: PublicKey programId?: PublicKey
): Promise<SwitchboardTestContextV2> { ): Promise<SwitchboardTestContext> {
const switchboard = await SwitchboardTestContextV2.load( const switchboard = await SwitchboardTestContext.load(
connection, connection,
networkInitParams, networkInitParams,
walletPath, walletPath,
programId programId
); );
await switchboard.start(oracleParams); // await switchboard.start(oracleParams);
return switchboard; return switchboard;
} }
async start( // async start(
oracleParams?: Partial<NodeConfig> | ReleaseChannel, // oracleParams?: Partial<NodeConfig> | ReleaseChannel,
timeout = 60 // timeout = 60
) { // ) {
const releaseChannel: ReleaseChannel = // const releaseChannel: ReleaseChannel =
typeof oracleParams === 'string' && // typeof oracleParams === 'string' &&
(oracleParams === 'testnet' || // (oracleParams === 'testnet' ||
oracleParams === 'mainnet' || // oracleParams === 'mainnet' ||
oracleParams === 'latest') // oracleParams === 'latest')
? oracleParams // ? oracleParams
: 'testnet'; // : 'testnet';
const baseConfig: NodeConfig = { // const baseConfig: NodeConfig = {
chain: 'solana', // chain: 'solana',
releaseChannel: releaseChannel, // releaseChannel: releaseChannel,
network: // network:
this.program.cluster === 'mainnet-beta' // this.program.cluster === 'mainnet-beta'
? 'mainnet' // ? 'mainnet'
: this.program.cluster, // : this.program.cluster,
rpcUrl: this.program.connection.rpcEndpoint, // rpcUrl: this.program.connection.rpcEndpoint,
oracleKey: this.oracle.publicKey.toBase58(), // oracleKey: this.oracle.publicKey.toBase58(),
secretPath: this.walletPath, // secretPath: this.walletPath,
envVariables: { // envVariables: {
VERBOSE: '1', // VERBOSE: '1',
DEBUG: '1', // DEBUG: '1',
DISABLE_NONE_QUEUE: '1', // DISABLE_NONE_QUEUE: '1',
DISABLE_METRICS: '1', // DISABLE_METRICS: '1',
}, // },
}; // };
const config: NodeConfig = // const config: NodeConfig =
typeof oracleParams === 'string' // typeof oracleParams === 'string'
? baseConfig // ? baseConfig
: _.merge(baseConfig, oracleParams); // : _.merge(baseConfig, oracleParams);
this._oracle = await NodeOracle.fromReleaseChannel({ // this._oracle = await NodeOracle.fromReleaseChannel({
...config, // ...config,
releaseChannel: releaseChannel, // releaseChannel: releaseChannel,
chain: 'solana', // chain: 'solana',
}); // });
console.log(`Starting Switchboard oracle ...`); // console.log(`Starting Switchboard oracle ...`);
await this._oracle.startAndAwait(timeout); // await this._oracle.startAndAwait(timeout);
} // }
stop() { // stop() {
if (this._oracle) { // if (this._oracle) {
const stopped = this._oracle.stop(); // const stopped = this._oracle.stop();
if (!stopped) { // if (!stopped) {
console.error(`Failed to stop docker oracle`); // console.error(`Failed to stop docker oracle`);
this._oracle.kill(); // this._oracle.kill();
} // }
} // }
} // }
public static findAnchorTomlWallet(workingDir = process.cwd()): string { public static findAnchorTomlWallet(workingDir = process.cwd()): string {
try { try {
@ -326,3 +326,5 @@ export class SwitchboardTestContextV2 {
return loadKeypair(keypairPath); return loadKeypair(keypairPath);
} }
} }
export { SwitchboardTestContext as SwitchboardTestContextV2 };

View File

@ -9,6 +9,6 @@ export * from './SwitchboardError';
export * from './SwitchboardEvents'; export * from './SwitchboardEvents';
export * from './SwitchboardNetwork'; export * from './SwitchboardNetwork';
export * from './SwitchboardProgram'; export * from './SwitchboardProgram';
export * from './test'; export * from './SwitchboardTestContext';
export * from './TransactionObject'; export * from './TransactionObject';
export * from './utils'; export * from './utils';

View File

@ -1,740 +0,0 @@
import { AggregatorAccount, JobAccount, QueueAccount } from '../accounts';
import {
DEVNET_GENESIS_HASH,
SWITCHBOARD_LABS_DEVNET_PERMISSIONLESS_QUEUE,
} from '../const';
import { AggregatorAccountData } from '../generated';
import { Mint } from '../mint';
import { SwitchboardNetwork } from '../SwitchboardNetwork';
import { AnchorWallet, SwitchboardProgram } from '../SwitchboardProgram';
import { TransactionObject } from '../TransactionObject';
import * as anchor from '@coral-xyz/anchor';
import { clusterApiUrl, Connection, Keypair, PublicKey } from '@solana/web3.js';
import { BNtoDateTimeString, OracleJob } from '@switchboard-xyz/common';
import * as dotenv from 'dotenv';
import fs from 'fs';
import path from 'path';
export const LATEST_DOCKER_VERSION = 'dev-v2-RC_01_05_23_03_24';
/** Get the program data address for a given programId
* @param programId the programId for a given on-chain program
* @return the publicKey of the address holding the upgradeable program buffer
*/
export const getProgramDataAddress = (programId: PublicKey): PublicKey => {
return PublicKey.findProgramAddressSync(
[programId.toBytes()],
new PublicKey('BPFLoaderUpgradeab1e11111111111111111111111')
)[0];
};
/** Get the IDL address for a given programId
* @param programId the programId for a given on-chain program
* @return the publicKey of the IDL address
*/
export const getIdlAddress = async (
programId: PublicKey
): Promise<PublicKey> => {
const base = (await PublicKey.findProgramAddress([], programId))[0];
return PublicKey.createWithSeed(base, 'anchor:idl', programId);
};
export class SwitchboardTestContext {
constructor(
readonly program: SwitchboardProgram,
readonly queue: QueueAccount,
readonly payerTokenWallet: PublicKey
) {}
public static async load(
provider: anchor.AnchorProvider,
params?: {
tokenAmount?: number;
queueKey?: PublicKey | string;
filePath?: string;
}
): Promise<SwitchboardTestContext> {
// fetch genesis hash
const genesisHash = await provider.connection.getGenesisHash();
if (genesisHash === DEVNET_GENESIS_HASH) {
// if queueKey is defined should we bother loading the local env?
// first try to load the local env
try {
const testContext = await SwitchboardTestContext.loadFromEnv(
provider,
params?.filePath ?? undefined,
params?.tokenAmount ?? undefined
);
// verify the oracle is heartbeating
const oracles = await testContext.queue.loadActiveOracleAccounts();
if (oracles.length > 0) {
return testContext;
}
// eslint-disable-next-line no-empty
} catch (error) {}
// fallback to the devnet permissionless queue
try {
const testContext = await SwitchboardTestContext.loadDevnetQueue(
provider,
params?.queueKey ?? undefined,
params?.tokenAmount ?? undefined
);
// verify the oracle is heartbeating
const oracles = await testContext.queue.loadActiveOracleAccounts();
if (oracles.length > 0) {
return testContext;
}
// eslint-disable-next-line no-empty
} catch (error) {}
throw new Error(
`Failed to load a Switchboard environment from a local file or the devnet permissionless queue`
);
} else {
const testContext = await SwitchboardTestContext.loadFromEnv(
provider,
params?.filePath ?? undefined,
params?.tokenAmount ?? undefined
);
return testContext;
}
}
/** Load SwitchboardTestContext using a specified queue
* @param provider anchor Provider containing connection and payer Keypair
* @param queueKey the oracle queue to load
* @param tokenAmount number of tokens to populate in switchboard mint's associated token account
*/
static async loadDevnetQueue(
provider: anchor.AnchorProvider,
queueKey: PublicKey | string = SWITCHBOARD_LABS_DEVNET_PERMISSIONLESS_QUEUE,
tokenAmount = 0
) {
const payerKeypair = (provider.wallet as AnchorWallet).payer;
const balance = await provider.connection.getBalance(
payerKeypair.publicKey
);
if (!balance) {
try {
await provider.connection.requestAirdrop(
payerKeypair.publicKey,
1_000_000_000
);
// eslint-disable-next-line no-empty
} catch {}
}
const program = await SwitchboardProgram.load(
'devnet',
provider.connection,
payerKeypair
).catch(error => {
throw new Error(
`Failed to load the SBV2 program for the given cluster, ${error.message}`
);
});
const queueAccount = new QueueAccount(
program,
typeof queueKey === 'string' ? new PublicKey(queueKey) : queueKey
);
try {
await queueAccount.loadData();
const oracles = await queueAccount.loadOracles();
if (oracles.length < 1) {
throw new Error(`OracleQueue has no active oracles heartbeating`);
}
} catch (error) {
throw new Error(
`Failed to load the SBV2 queue for the given cluster, ${
(error as any).message
}`
);
}
const [userTokenAmount] = await program.mint.getOrCreateWrappedUser(
program.walletPubkey,
{ fundUpTo: tokenAmount }
);
return new SwitchboardTestContext(program, queueAccount, userTokenAmount);
}
/** Recursively loop through directories and return the filepath of switchboard.env
* @param envFileName alternative filename to search for. defaults to switchboard.env
* @returns the filepath for a switchboard env file to load
*/
public static findSwitchboardEnv(envFileName = 'switchboard.env'): string {
const NotFoundError = new Error(
'failed to find switchboard.env file in current directory recursively'
);
let retryCount = 5;
let currentDirectory = process.cwd();
while (retryCount > 0) {
// look for switchboard.env
try {
const currentPath = path.join(currentDirectory, envFileName);
if (fs.existsSync(currentPath)) {
return currentPath;
}
// eslint-disable-next-line no-empty
} catch {}
// look for .switchboard directory
try {
const localSbvPath = path.join(currentDirectory, '.switchboard');
if (fs.existsSync(localSbvPath)) {
const localSbvEnvPath = path.join(localSbvPath, envFileName);
if (fs.existsSync(localSbvEnvPath)) {
return localSbvEnvPath;
}
}
// eslint-disable-next-line no-empty
} catch {}
currentDirectory = path.join(currentDirectory, '../');
--retryCount;
}
throw NotFoundError;
}
/** Load SwitchboardTestContext from an env file containing $SWITCHBOARD_PROGRAM_ID, $ORACLE_QUEUE, $AGGREGATOR
* @param provider anchor Provider containing connection and payer Keypair
* @param filePath filesystem path to env file
* @param tokenAmount number of tokens to populate in switchboard mint's associated token account
*/
public static async loadFromEnv(
provider: anchor.AnchorProvider,
filePath = SwitchboardTestContext.findSwitchboardEnv(),
tokenAmount = 0
): Promise<SwitchboardTestContext> {
// eslint-disable-next-line node/no-unpublished-require
dotenv.config({ path: filePath });
if (!process.env.SWITCHBOARD_PROGRAM_ID) {
throw new Error(`your env file must have $SWITCHBOARD_PROGRAM_ID set`);
}
if (!process.env.ORACLE_QUEUE) {
throw new Error(`your env file must have $ORACLE_QUEUE set`);
}
const program = await SwitchboardProgram.load(
'devnet',
provider.connection,
(provider.wallet as AnchorWallet).payer,
new PublicKey(process.env.SWITCHBOARD_PROGRAM_ID)
);
const balance = await provider.connection.getBalance(program.walletPubkey);
if (!balance) {
try {
const airdropSignature = await provider.connection.requestAirdrop(
program.walletPubkey,
1_000_000_000
);
await provider.connection.confirmTransaction(airdropSignature);
// eslint-disable-next-line no-empty
} catch {}
}
const queueAccount = new QueueAccount(
program,
new PublicKey(process.env.ORACLE_QUEUE)
);
const [userTokenAmount] = await program.mint.getOrCreateWrappedUser(
program.walletPubkey,
{ fundUpTo: tokenAmount }
);
return new SwitchboardTestContext(program, queueAccount, userTokenAmount);
}
/**
* Create a static data feed that resolves to an expected value
* @param value - the static value the feed will resolve to
* @param timeout - the number of milliseconds to wait before timing out
*/
public async createStaticFeed(
value: number,
timeout = 30000
): Promise<[AggregatorAccount, AggregatorAccountData]> {
const [aggregatorAccount] = await this.queue.createFeed({
name: `Value ${value}`,
batchSize: 1,
minRequiredOracleResults: 1,
minRequiredJobResults: 1,
minUpdateDelaySeconds: 10,
enable: true,
queueAuthorityPubkey: this.program.walletPubkey,
jobs: [
{
data: OracleJob.encodeDelimited(
OracleJob.create({
tasks: [
OracleJob.Task.create({
valueTask: OracleJob.ValueTask.create({
value,
}),
}),
],
})
).finish(),
},
],
});
const [state] = await aggregatorAccount.openRoundAndAwaitResult(
undefined,
timeout
);
return [aggregatorAccount, state];
}
/**
* Update an existing aggregator that resolves to a new static value then await the new result
* @params aggregatorAccount - the aggregator account to modify
* @param value - the static value the feed will resolve to
* @param timeout - the number of milliseconds to wait before timing out
*/
public async updateStaticFeed(
aggregatorAccount: AggregatorAccount,
value: number,
timeout = 30000
): Promise<[AggregatorAccount, AggregatorAccountData]> {
const aggregator = await aggregatorAccount.loadData();
const [jobAccount, jobInit] = JobAccount.createInstructions(
this.program,
this.program.walletPubkey,
{
data: OracleJob.encodeDelimited(
OracleJob.create({
tasks: [
OracleJob.Task.create({
valueTask: OracleJob.ValueTask.create({
value,
}),
}),
],
})
).finish(),
}
);
const oldJobKeys = aggregator.jobPubkeysData.filter(
pubkey => !pubkey.equals(PublicKey.default)
);
const oldJobs: Array<[JobAccount, number]> = oldJobKeys.map((pubkey, i) => [
new JobAccount(this.program, pubkey),
i,
]);
const removeJobTxns = oldJobs.map(job =>
aggregatorAccount.removeJobInstruction(this.program.walletPubkey, {
job: job[0],
jobIdx: job[1],
})
);
const addJobTxn = aggregatorAccount.addJobInstruction(
this.program.walletPubkey,
{ job: jobAccount }
);
const txns = TransactionObject.pack([
...jobInit,
...removeJobTxns,
addJobTxn,
]);
await this.program.signAndSendAll(txns);
const [state] = await aggregatorAccount.openRoundAndAwaitResult(
undefined,
timeout
);
return [aggregatorAccount, state];
}
static async createEnvironment(
payerKeypairPath: string,
alternateProgramId?: PublicKey
): Promise<SwitchboardTestEnvironment> {
const fullKeypairPath =
payerKeypairPath.startsWith('/') || payerKeypairPath.startsWith('C:')
? payerKeypairPath
: path.join(process.cwd(), payerKeypairPath);
if (!fs.existsSync(fullKeypairPath)) {
throw new Error('Failed to find payer keypair path');
}
const payerKeypair = Keypair.fromSecretKey(
new Uint8Array(
JSON.parse(
fs.readFileSync(fullKeypairPath, {
encoding: 'utf-8',
})
)
)
);
const connection = new Connection(clusterApiUrl('devnet'), {
commitment: 'confirmed',
});
const program = await SwitchboardProgram.load(
'devnet',
connection,
payerKeypair,
alternateProgramId
);
const [userTokenWallet] = await program.mint.getOrCreateWrappedUser(
program.walletPubkey,
{ amount: 0 }
);
const programDataAddress = getProgramDataAddress(program.programId);
const idlAddress = await getIdlAddress(program.programId);
// use pre-generated keypairs so we dont need to rely on account loading
const dataBufferKeypair = Keypair.generate();
const crankBufferKeypair = Keypair.generate();
const oracleStakingWalletKeypair = Keypair.generate();
const [accounts] = await SwitchboardNetwork.create(program, {
name: 'Test Queue',
metadata: `created ${BNtoDateTimeString(
new anchor.BN(Math.floor(Date.now() / 1000))
)}`,
authority: payerKeypair,
reward: 0,
minStake: 0,
queueSize: 10,
dataBufferKeypair: dataBufferKeypair,
unpermissionedFeeds: true,
unpermissionedVrf: true,
enableBufferRelayers: true,
slashingEnabled: false,
cranks: [
{
name: 'Test Crank',
maxRows: 100,
dataBufferKeypair: crankBufferKeypair,
},
],
oracles: [
{
name: 'Test Oracle',
enable: true,
stakingWalletKeypair: oracleStakingWalletKeypair,
queueAuthorityPubkey: program.walletPubkey,
},
],
});
const crank = accounts.cranks.shift();
if (!crank) {
throw new Error(`Failed to create the crank`);
}
const oracle = accounts.oracles.shift();
if (!oracle) {
throw new Error(`Failed to create the oracle`);
}
// async load the accounts
const programState = await accounts.programState.account.loadData();
return new SwitchboardTestEnvironment({
switchboardProgramId: program.programId,
switchboardProgramDataAddress: programDataAddress,
switchboardIdlAddress: idlAddress,
switchboardProgramState: accounts.programState.account.publicKey,
switchboardVault: programState.tokenVault,
switchboardMint: programState.tokenMint.equals(PublicKey.default)
? Mint.native
: programState.tokenMint,
tokenWallet: userTokenWallet,
oracleQueue: accounts.queue.account.publicKey,
oracleQueueAuthority: program.walletPubkey,
oracleQueueBuffer: dataBufferKeypair.publicKey,
crank: crank.account.publicKey,
crankBuffer: crankBufferKeypair.publicKey,
oracle: oracle.account.publicKey,
oracleAuthority: program.walletPubkey,
oracleEscrow: oracleStakingWalletKeypair.publicKey,
oraclePermissions: oracle.permission.account.publicKey,
payerKeypairPath: fullKeypairPath,
});
}
}
export interface ISwitchboardTestEnvironment {
switchboardProgramId: PublicKey;
switchboardProgramDataAddress: PublicKey;
switchboardIdlAddress: PublicKey;
switchboardProgramState: PublicKey;
switchboardVault: PublicKey;
switchboardMint: PublicKey;
tokenWallet: PublicKey;
oracleQueue: PublicKey;
oracleQueueAuthority: PublicKey;
oracleQueueBuffer: PublicKey;
crank: PublicKey;
crankBuffer: PublicKey;
oracle: PublicKey;
oracleAuthority: PublicKey;
oracleEscrow: PublicKey;
oraclePermissions: PublicKey;
payerKeypairPath: string;
}
export class SwitchboardTestEnvironment implements ISwitchboardTestEnvironment {
switchboardProgramId: PublicKey;
switchboardProgramDataAddress: PublicKey;
switchboardIdlAddress: PublicKey;
switchboardProgramState: PublicKey;
switchboardVault: PublicKey;
switchboardMint: PublicKey;
tokenWallet: PublicKey;
oracleQueue: PublicKey;
oracleQueueAuthority: PublicKey;
oracleQueueBuffer: PublicKey;
crank: PublicKey;
crankBuffer: PublicKey;
oracle: PublicKey;
oracleAuthority: PublicKey;
oracleEscrow: PublicKey;
oraclePermissions: PublicKey;
payerKeypairPath: string;
constructor(ctx: ISwitchboardTestEnvironment) {
this.switchboardProgramId = ctx.switchboardProgramId;
this.switchboardProgramDataAddress = ctx.switchboardProgramDataAddress;
this.switchboardIdlAddress = ctx.switchboardIdlAddress;
this.switchboardProgramState = ctx.switchboardProgramState;
this.switchboardVault = ctx.switchboardVault;
this.switchboardMint = ctx.switchboardMint;
this.tokenWallet = ctx.tokenWallet;
this.oracleQueue = ctx.oracleQueue;
this.oracleQueueAuthority = ctx.oracleQueueAuthority;
this.oracleQueueBuffer = ctx.oracleQueueBuffer;
this.crank = ctx.crank;
this.crankBuffer = ctx.crankBuffer;
this.oracle = ctx.oracle;
this.oracleAuthority = ctx.oracleAuthority;
this.oracleEscrow = ctx.oracleEscrow;
this.oraclePermissions = ctx.oraclePermissions;
this.payerKeypairPath = ctx.payerKeypairPath;
}
public get envFileString(): string {
// const fileString = Object.keys(this)
// .map(key => {
// if (this[key] instanceof PublicKey) {
// return `${camelToUpperCaseWithUnderscores(key)}="${this[
// key
// ].toBase58()}"`;
// }
// return;
// })
// .filter(Boolean)
// .join('\n');
return `SWITCHBOARD_PROGRAM_ID="${this.switchboardProgramId.toBase58()}"
SWITCHBOARD_PROGRAM_DATA_ADDRESS="${this.switchboardProgramDataAddress.toBase58()}"
SWITCHBOARD_IDL_ADDRESS="${this.switchboardIdlAddress.toBase58()}"
SWITCHBOARD_PROGRAM_STATE="${this.switchboardProgramState.toBase58()}"
SWITCHBOARD_VAULT="${this.switchboardVault.toBase58()}"
SWITCHBOARD_MINT="${this.switchboardMint.toBase58()}"
TOKEN_WALLET="${this.tokenWallet.toBase58()}"
ORACLE_QUEUE="${this.oracleQueue.toBase58()}"
ORACLE_QUEUE_AUTHORITY="${this.oracleQueueAuthority.toBase58()}"
ORACLE_QUEUE_BUFFER="${this.oracleQueueBuffer.toBase58()}"
CRANK="${this.crank.toBase58()}"
CRANK_BUFFER="${this.crankBuffer.toBase58()}"
ORACLE="${this.oracle.toBase58()}"
ORACLE_AUTHORITY="${this.oracleAuthority.toBase58()}"
ORACLE_ESCROW="${this.oracleEscrow.toBase58()}"
ORACLE_PERMISSIONS="${this.oraclePermissions.toBase58()}"`;
}
public get anchorToml(): string {
return [
`
[test]
startup_wait = 10000
[test.validator]
url = "https://api.devnet.solana.com"
`,
Object.keys(this)
.map(key => {
if (this[key] instanceof PublicKey) {
return `[[test.validator.clone]] # ${key}\naddress = "${this[key]}"`;
}
})
.filter(Boolean)
.join('\n\n'),
].join('\n\n');
}
public get accountCloneString(): string {
const accounts = Object.keys(this).map(key => {
if (typeof this[key] === 'string') {
return;
}
return `--clone ${(this[key] as PublicKey).toBase58()} \`# ${key}\` `;
});
return accounts.filter(Boolean).join(`\\\n`);
}
public get dockerCompose(): string {
return `version: "3.3"
services:
oracle:
image: "switchboardlabs/node:\${SBV2_ORACLE_VERSION:-${LATEST_DOCKER_VERSION}}" # https://hub.docker.com/r/switchboardlabs/node/tags
network_mode: host
restart: always
secrets:
- PAYER_SECRETS
environment:
- VERBOSE=1
- CLUSTER=\${CLUSTER:-localnet}
- HEARTBEAT_INTERVAL=30 # Seconds
- ORACLE_KEY=${this.oracle.toBase58()}
- TASK_RUNNER_SOLANA_RPC=${clusterApiUrl('mainnet-beta')}
# - RPC_URL=\${RPC_URL}
secrets:
PAYER_SECRETS:
file: ${this.payerKeypairPath}`;
}
public get localValidatorScript(): string {
return `#!/bin/bash
mkdir -p .anchor/test-ledger
solana-test-validator -r --ledger .anchor/test-ledger --mint ${this.oracleAuthority.toBase58()} --bind-address 0.0.0.0 --url ${clusterApiUrl(
'devnet'
)} --rpc-port 8899 ${this.accountCloneString}`;
}
public get startOracleScript(): string {
return `#!/usr/bin/env bash
script_dir=$( cd -- "$( dirname -- "\${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
docker-compose -f "$script_dir"/docker-compose.switchboard.yml up`;
}
public toJSON(): ISwitchboardTestEnvironment {
return {
switchboardProgramId: this.switchboardProgramId,
switchboardProgramDataAddress: this.switchboardProgramDataAddress,
switchboardIdlAddress: this.switchboardIdlAddress,
switchboardProgramState: this.switchboardProgramState,
switchboardVault: this.switchboardVault,
switchboardMint: this.switchboardMint,
tokenWallet: this.tokenWallet,
oracleQueue: this.oracleQueue,
oracleQueueAuthority: this.oracleQueueAuthority,
oracleQueueBuffer: this.oracleQueueBuffer,
crank: this.crank,
crankBuffer: this.crankBuffer,
oracle: this.oracle,
oracleAuthority: this.oracleAuthority,
oracleEscrow: this.oracleEscrow,
oraclePermissions: this.oraclePermissions,
payerKeypairPath: this.payerKeypairPath,
};
}
/** Write switchboard test environment to filesystem */
public writeAll(outputDir: string): void {
fs.mkdirSync(outputDir, { recursive: true });
this.writeEnv(outputDir);
this.writeJSON(outputDir);
this.writeScripts(outputDir);
this.writeDockerCompose(outputDir);
this.writeAnchorToml(outputDir);
}
/** Write the env file to filesystem */
public writeEnv(filePath: string): void {
const ENV_FILE_PATH = path.join(filePath, 'switchboard.env');
fs.writeFileSync(ENV_FILE_PATH, this.envFileString);
}
public writeJSON(outputDir: string): void {
const JSON_FILE_PATH = path.join(outputDir, 'switchboard.json');
fs.writeFileSync(
JSON_FILE_PATH,
JSON.stringify(
this.toJSON(),
(key, value) => {
if (value instanceof PublicKey) {
return value.toBase58();
}
return value;
},
2
)
);
}
public writeScripts(outputDir: string): void {
// create script to start local validator with accounts cloned
const LOCAL_VALIDATOR_SCRIPT = path.join(
outputDir,
'start-local-validator.sh'
);
fs.writeFileSync(LOCAL_VALIDATOR_SCRIPT, this.localValidatorScript);
fs.chmodSync(LOCAL_VALIDATOR_SCRIPT, '755');
// create bash script to start local oracle
const ORACLE_SCRIPT = path.join(outputDir, 'start-oracle.sh');
fs.writeFileSync(ORACLE_SCRIPT, this.startOracleScript);
fs.chmodSync(ORACLE_SCRIPT, '755');
}
public writeDockerCompose(outputDir: string): void {
const DOCKER_COMPOSE_FILEPATH = path.join(
outputDir,
'docker-compose.switchboard.yml'
);
fs.writeFileSync(DOCKER_COMPOSE_FILEPATH, this.dockerCompose);
}
public writeAnchorToml(outputDir: string) {
const ANCHOR_TOML_FILEPATH = path.join(
outputDir,
'Anchor.switchboard.toml'
);
fs.writeFileSync(ANCHOR_TOML_FILEPATH, this.anchorToml);
}
}
export function camelToUpperCaseWithUnderscores(str: string): string {
// Use a regular expression to match any uppercase or lowercase letters followed by uppercase letters
// and replace them with a matched group (the uppercase or lowercase letters) followed by an underscore
// and the uppercase letter
return (
str
.replace(/([a-z]+)([A-Z])/g, (_, p1, p2) => p1 + '_' + p2)
// Make the entire string uppercase
.toUpperCase()
);
}

View File

@ -1,2 +0,0 @@
export * from './SwitchboardTestContext';
export * from './SwitchboardTestContextV2';

View File

@ -59,7 +59,6 @@ importers:
'@solana/web3.js': ^1.73.0 '@solana/web3.js': ^1.73.0
'@switchboard-xyz/common': ^2.1.33 '@switchboard-xyz/common': ^2.1.33
'@switchboard-xyz/eslint-config': latest '@switchboard-xyz/eslint-config': latest
'@switchboard-xyz/oracle': ^2.1.13
'@types/chai': ^4.3.4 '@types/chai': ^4.3.4
'@types/lodash': ^4.14.191 '@types/lodash': ^4.14.191
'@types/mocha': ^10.0.0 '@types/mocha': ^10.0.0
@ -85,7 +84,6 @@ importers:
'@solana/spl-token': 0.3.6_@solana+web3.js@1.73.0 '@solana/spl-token': 0.3.6_@solana+web3.js@1.73.0
'@solana/web3.js': 1.73.0 '@solana/web3.js': 1.73.0
'@switchboard-xyz/common': 2.1.33 '@switchboard-xyz/common': 2.1.33
'@switchboard-xyz/oracle': 2.1.13
dotenv: 16.0.3 dotenv: 16.0.3
lodash: 4.17.21 lodash: 4.17.21
devDependencies: devDependencies:
@ -113,6 +111,7 @@ importers:
'@coral-xyz/anchor': ^0.27.0 '@coral-xyz/anchor': ^0.27.0
'@solana/web3.js': ^1.73.3 '@solana/web3.js': ^1.73.3
'@switchboard-xyz/common': ^2.1.33 '@switchboard-xyz/common': ^2.1.33
'@switchboard-xyz/oracle': ^2.1.13
'@switchboard-xyz/solana.js': workspace:* '@switchboard-xyz/solana.js': workspace:*
'@types/chai': ^4.3.0 '@types/chai': ^4.3.0
'@types/mocha': ^9.0.0 '@types/mocha': ^9.0.0
@ -128,6 +127,7 @@ importers:
'@coral-xyz/anchor': 0.27.0 '@coral-xyz/anchor': 0.27.0
'@solana/web3.js': 1.73.3 '@solana/web3.js': 1.73.3
'@switchboard-xyz/common': 2.1.33 '@switchboard-xyz/common': 2.1.33
'@switchboard-xyz/oracle': 2.1.13
'@switchboard-xyz/solana.js': link:../../javascript/solana.js '@switchboard-xyz/solana.js': link:../../javascript/solana.js
node-fetch: 2.6.7 node-fetch: 2.6.7
devDependencies: devDependencies:
@ -146,6 +146,7 @@ importers:
'@coral-xyz/anchor': ^0.27.0 '@coral-xyz/anchor': ^0.27.0
'@solana/web3.js': ^1.73.3 '@solana/web3.js': ^1.73.3
'@switchboard-xyz/common': ^2.1.33 '@switchboard-xyz/common': ^2.1.33
'@switchboard-xyz/oracle': ^2.1.13
'@switchboard-xyz/solana.js': workspace:* '@switchboard-xyz/solana.js': workspace:*
'@types/chai': ^4.3.0 '@types/chai': ^4.3.0
'@types/mocha': ^9.0.0 '@types/mocha': ^9.0.0
@ -159,6 +160,7 @@ importers:
'@coral-xyz/anchor': 0.27.0 '@coral-xyz/anchor': 0.27.0
'@solana/web3.js': 1.73.3 '@solana/web3.js': 1.73.3
'@switchboard-xyz/common': 2.1.33 '@switchboard-xyz/common': 2.1.33
'@switchboard-xyz/oracle': 2.1.13
'@switchboard-xyz/solana.js': link:../../javascript/solana.js '@switchboard-xyz/solana.js': link:../../javascript/solana.js
devDependencies: devDependencies:
'@types/chai': 4.3.4 '@types/chai': 4.3.4
@ -174,6 +176,7 @@ importers:
specifiers: specifiers:
'@coral-xyz/anchor': ^0.27.0 '@coral-xyz/anchor': ^0.27.0
'@switchboard-xyz/common': ^2.1.33 '@switchboard-xyz/common': ^2.1.33
'@switchboard-xyz/oracle': ^2.1.13
'@switchboard-xyz/solana.js': workspace:* '@switchboard-xyz/solana.js': workspace:*
'@types/bn.js': ^5.1.0 '@types/bn.js': ^5.1.0
'@types/chai': ^4.3.0 '@types/chai': ^4.3.0
@ -186,6 +189,7 @@ importers:
dependencies: dependencies:
'@coral-xyz/anchor': 0.27.0 '@coral-xyz/anchor': 0.27.0
'@switchboard-xyz/common': 2.1.33 '@switchboard-xyz/common': 2.1.33
'@switchboard-xyz/oracle': 2.1.13
'@switchboard-xyz/solana.js': link:../../javascript/solana.js '@switchboard-xyz/solana.js': link:../../javascript/solana.js
devDependencies: devDependencies:
'@types/bn.js': 5.1.1 '@types/bn.js': 5.1.1
@ -205,6 +209,7 @@ importers:
'@solana/spl-token': ^0.3.6 '@solana/spl-token': ^0.3.6
'@solana/web3.js': ^1.73.3 '@solana/web3.js': ^1.73.3
'@switchboard-xyz/common': ^2.1.33 '@switchboard-xyz/common': ^2.1.33
'@switchboard-xyz/oracle': ^2.1.13
'@switchboard-xyz/solana.js': workspace:* '@switchboard-xyz/solana.js': workspace:*
'@types/chai': ^4.3.0 '@types/chai': ^4.3.0
'@types/mocha': ^9.0.0 '@types/mocha': ^9.0.0
@ -231,6 +236,7 @@ importers:
'@solana/spl-token': 0.3.6_@solana+web3.js@1.73.3 '@solana/spl-token': 0.3.6_@solana+web3.js@1.73.3
'@solana/web3.js': 1.73.3 '@solana/web3.js': 1.73.3
'@switchboard-xyz/common': 2.1.33 '@switchboard-xyz/common': 2.1.33
'@switchboard-xyz/oracle': 2.1.13
'@switchboard-xyz/solana.js': link:../../javascript/solana.js '@switchboard-xyz/solana.js': link:../../javascript/solana.js
chalk: 4.1.2 chalk: 4.1.2
dotenv: 16.0.3 dotenv: 16.0.3
@ -257,6 +263,7 @@ importers:
'@coral-xyz/anchor': ^0.27.0 '@coral-xyz/anchor': ^0.27.0
'@solana/web3.js': ^1.73.3 '@solana/web3.js': ^1.73.3
'@switchboard-xyz/common': ^2.1.33 '@switchboard-xyz/common': ^2.1.33
'@switchboard-xyz/oracle': ^2.1.13
'@switchboard-xyz/solana.js': workspace:* '@switchboard-xyz/solana.js': workspace:*
'@types/chai': ^4.3.0 '@types/chai': ^4.3.0
'@types/mocha': ^9.0.0 '@types/mocha': ^9.0.0
@ -270,6 +277,7 @@ importers:
'@coral-xyz/anchor': 0.27.0 '@coral-xyz/anchor': 0.27.0
'@solana/web3.js': 1.73.3 '@solana/web3.js': 1.73.3
'@switchboard-xyz/common': 2.1.33 '@switchboard-xyz/common': 2.1.33
'@switchboard-xyz/oracle': 2.1.13
'@switchboard-xyz/solana.js': link:../../javascript/solana.js '@switchboard-xyz/solana.js': link:../../javascript/solana.js
devDependencies: devDependencies:
'@types/chai': 4.3.4 '@types/chai': 4.3.4
@ -1022,7 +1030,7 @@ packages:
dependencies: dependencies:
'@types/http-cache-semantics': 4.0.1 '@types/http-cache-semantics': 4.0.1
'@types/keyv': 3.1.4 '@types/keyv': 3.1.4
'@types/node': 18.11.18 '@types/node': 17.0.45
'@types/responselike': 1.0.0 '@types/responselike': 1.0.0
dev: false dev: false
@ -1050,7 +1058,7 @@ packages:
/@types/keyv/3.1.4: /@types/keyv/3.1.4:
resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==}
dependencies: dependencies:
'@types/node': 18.11.18 '@types/node': 17.0.45
dev: false dev: false
/@types/lodash/4.14.191: /@types/lodash/4.14.191:
@ -1081,7 +1089,6 @@ packages:
/@types/node/17.0.45: /@types/node/17.0.45:
resolution: {integrity: sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==} resolution: {integrity: sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==}
dev: true
/@types/node/18.11.18: /@types/node/18.11.18:
resolution: {integrity: sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==} resolution: {integrity: sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==}
@ -1097,7 +1104,7 @@ packages:
/@types/responselike/1.0.0: /@types/responselike/1.0.0:
resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==} resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==}
dependencies: dependencies:
'@types/node': 18.11.18 '@types/node': 17.0.45
dev: false dev: false
/@types/semver/7.3.13: /@types/semver/7.3.13:
@ -1113,7 +1120,7 @@ packages:
resolution: {integrity: sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==} resolution: {integrity: sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==}
requiresBuild: true requiresBuild: true
dependencies: dependencies:
'@types/node': 18.11.18 '@types/node': 17.0.45
dev: false dev: false
optional: true optional: true

View File

@ -15,6 +15,7 @@
"@coral-xyz/anchor": "^0.27.0", "@coral-xyz/anchor": "^0.27.0",
"@solana/web3.js": "^1.73.3", "@solana/web3.js": "^1.73.3",
"@switchboard-xyz/common": "^2.1.33", "@switchboard-xyz/common": "^2.1.33",
"@switchboard-xyz/oracle": "^2.1.13",
"@switchboard-xyz/solana.js": "workspace:*", "@switchboard-xyz/solana.js": "workspace:*",
"node-fetch": "^2.6" "node-fetch": "^2.6"
}, },

View File

@ -1,6 +1,7 @@
import * as anchor from "@coral-xyz/anchor"; import * as anchor from "@coral-xyz/anchor";
import { OracleJob, sleep } from "@switchboard-xyz/common"; import { OracleJob, sleep } from "@switchboard-xyz/common";
import { SwitchboardTestContextV2 } from "@switchboard-xyz/solana.js"; import { NodeOracle } from "@switchboard-xyz/oracle";
import { SwitchboardTestContext } from "@switchboard-xyz/solana.js";
import fetch from "node-fetch"; import fetch from "node-fetch";
import { AnchorBufferParser } from "../target/types/anchor_buffer_parser"; import { AnchorBufferParser } from "../target/types/anchor_buffer_parser";
@ -11,13 +12,14 @@ describe("anchor-buffer-parser test", () => {
const bufferParserProgram: anchor.Program<AnchorBufferParser> = const bufferParserProgram: anchor.Program<AnchorBufferParser> =
anchor.workspace.AnchorBufferParser; anchor.workspace.AnchorBufferParser;
let switchboard: SwitchboardTestContextV2; let switchboard: SwitchboardTestContext;
let oracle: NodeOracle;
before(async () => { before(async () => {
switchboard = await SwitchboardTestContextV2.loadFromProvider(provider, { switchboard = await SwitchboardTestContext.loadFromProvider(provider, {
// You can provide a keypair to so the PDA schemes dont change between test runs // You can provide a keypair to so the PDA schemes dont change between test runs
name: "Test Queue", name: "Test Queue",
keypair: SwitchboardTestContextV2.loadKeypair("~/.keypairs/queue.json"), keypair: SwitchboardTestContext.loadKeypair("~/.keypairs/queue.json"),
queueSize: 10, queueSize: 10,
reward: 0, reward: 0,
minStake: 0, minStake: 0,
@ -28,18 +30,33 @@ describe("anchor-buffer-parser test", () => {
oracle: { oracle: {
name: "Test Oracle", name: "Test Oracle",
enable: true, enable: true,
stakingWalletKeypair: SwitchboardTestContextV2.loadKeypair( stakingWalletKeypair: SwitchboardTestContext.loadKeypair(
"~/.keypairs/oracleWallet.json" "~/.keypairs/oracleWallet.json"
), ),
}, },
}); });
await switchboard.start();
oracle = await NodeOracle.fromReleaseChannel({
chain: "solana",
releaseChannel: "testnet",
network: "localnet", // disables production capabilities like monitoring and alerts
rpcUrl: switchboard.program.connection.rpcEndpoint,
oracleKey: switchboard.oracle.publicKey.toBase58(),
secretPath: switchboard.walletPath,
silent: false, // set to true to suppress oracle logs in the console
envVariables: {
VERBOSE: "1",
DEBUG: "1",
DISABLE_NONCE_QUEUE: "1",
DISABLE_METRICS: "1",
},
});
await oracle.startAndAwait();
}); });
after(async () => { after(() => {
if (switchboard) { oracle?.stop();
switchboard.stop();
}
}); });
it("Create and read buffer account", async () => { it("Create and read buffer account", async () => {

View File

@ -15,6 +15,7 @@
"@coral-xyz/anchor": "^0.27.0", "@coral-xyz/anchor": "^0.27.0",
"@solana/web3.js": "^1.73.3", "@solana/web3.js": "^1.73.3",
"@switchboard-xyz/common": "^2.1.33", "@switchboard-xyz/common": "^2.1.33",
"@switchboard-xyz/oracle": "^2.1.13",
"@switchboard-xyz/solana.js": "workspace:*" "@switchboard-xyz/solana.js": "workspace:*"
}, },
"devDependencies": { "devDependencies": {

View File

@ -2,12 +2,11 @@ import * as anchor from "@coral-xyz/anchor";
import { OracleJob, sleep } from "@switchboard-xyz/common"; import { OracleJob, sleep } from "@switchboard-xyz/common";
import { import {
AggregatorAccount, AggregatorAccount,
SwitchboardTestContextV2, SwitchboardTestContext,
} from "@switchboard-xyz/solana.js"; } from "@switchboard-xyz/solana.js";
import assert from "assert"; import assert from "assert";
import chai from "chai";
import { AnchorFeedParser } from "../target/types/anchor_feed_parser"; import { AnchorFeedParser } from "../target/types/anchor_feed_parser";
const expect = chai.expect; import { NodeOracle } from "@switchboard-xyz/oracle";
describe("anchor-feed-parser test", () => { describe("anchor-feed-parser test", () => {
const provider = anchor.AnchorProvider.env(); const provider = anchor.AnchorProvider.env();
@ -16,14 +15,16 @@ describe("anchor-feed-parser test", () => {
const feedParserProgram: anchor.Program<AnchorFeedParser> = const feedParserProgram: anchor.Program<AnchorFeedParser> =
anchor.workspace.AnchorFeedParser; anchor.workspace.AnchorFeedParser;
let switchboard: SwitchboardTestContextV2; let switchboard: SwitchboardTestContext;
let oracle: NodeOracle;
let aggregatorAccount: AggregatorAccount; let aggregatorAccount: AggregatorAccount;
before(async () => { before(async () => {
switchboard = await SwitchboardTestContextV2.loadFromProvider(provider, { switchboard = await SwitchboardTestContext.loadFromProvider(provider, {
// You can provide a keypair to so the PDA schemes dont change between test runs // You can provide a keypair to so the PDA schemes dont change between test runs
name: "Test Queue", name: "Test Queue",
keypair: SwitchboardTestContextV2.loadKeypair("~/.keypairs/queue.json"), keypair: SwitchboardTestContext.loadKeypair("~/.keypairs/queue.json"),
queueSize: 10, queueSize: 10,
reward: 0, reward: 0,
minStake: 0, minStake: 0,
@ -34,18 +35,33 @@ describe("anchor-feed-parser test", () => {
oracle: { oracle: {
name: "Test Oracle", name: "Test Oracle",
enable: true, enable: true,
stakingWalletKeypair: SwitchboardTestContextV2.loadKeypair( stakingWalletKeypair: SwitchboardTestContext.loadKeypair(
"~/.keypairs/oracleWallet.json" "~/.keypairs/oracleWallet.json"
), ),
}, },
}); });
await switchboard.start();
oracle = await NodeOracle.fromReleaseChannel({
chain: "solana",
releaseChannel: "testnet",
network: "localnet", // disables production capabilities like monitoring and alerts
rpcUrl: switchboard.program.connection.rpcEndpoint,
oracleKey: switchboard.oracle.publicKey.toBase58(),
secretPath: switchboard.walletPath,
silent: false, // set to true to suppress oracle logs in the console
envVariables: {
VERBOSE: "1",
DEBUG: "1",
DISABLE_NONCE_QUEUE: "1",
DISABLE_METRICS: "1",
},
});
await oracle.startAndAwait();
}); });
after(async () => { after(() => {
if (switchboard) { oracle?.stop();
switchboard.stop();
}
}); });
it("Creates a static feed that resolves to 100", async () => { it("Creates a static feed that resolves to 100", async () => {

View File

@ -44,4 +44,4 @@ address = "7hkp1xfPBcD2t1vZMoWWQPzipHVcXeLAAaiGXdPSfDie"
address="GvDMxPzN1sCj7L26YDK2HnMRXEQmQ2aemov8YBtPS7vR" address="GvDMxPzN1sCj7L26YDK2HnMRXEQmQ2aemov8YBtPS7vR"
[[test.validator.clone]] # sbv2 SOL/USD History Buffer [[test.validator.clone]] # sbv2 SOL/USD History Buffer
address="7LLvRhMs73FqcLkA8jvEE1AM2mYZXTmqfUv8GAEurymx" address="9GPTMZmtNU61ULAZoGxDZmnZoWeF8zvBmKp4WZY6Ln6j"

View File

@ -15,6 +15,7 @@
"dependencies": { "dependencies": {
"@coral-xyz/anchor": "^0.27.0", "@coral-xyz/anchor": "^0.27.0",
"@switchboard-xyz/common": "^2.1.33", "@switchboard-xyz/common": "^2.1.33",
"@switchboard-xyz/oracle": "^2.1.13",
"@switchboard-xyz/solana.js": "workspace:*" "@switchboard-xyz/solana.js": "workspace:*"
}, },
"devDependencies": { "devDependencies": {

View File

@ -4,15 +4,16 @@ import { Program } from "@coral-xyz/anchor";
import { OracleJob } from "@switchboard-xyz/common"; import { OracleJob } from "@switchboard-xyz/common";
import { import {
AggregatorAccount, AggregatorAccount,
SwitchboardTestContextV2, SwitchboardTestContext,
} from "@switchboard-xyz/solana.js"; } from "@switchboard-xyz/solana.js";
import { AnchorHistoryParser } from "../target/types/anchor_history_parser"; import { AnchorHistoryParser } from "../target/types/anchor_history_parser";
import { NodeOracle } from "@switchboard-xyz/oracle";
export const AGGREGATOR_PUBKEY: anchor.web3.PublicKey = export const AGGREGATOR_PUBKEY: anchor.web3.PublicKey =
new anchor.web3.PublicKey("GvDMxPzN1sCj7L26YDK2HnMRXEQmQ2aemov8YBtPS7vR"); new anchor.web3.PublicKey("GvDMxPzN1sCj7L26YDK2HnMRXEQmQ2aemov8YBtPS7vR");
export const HISTORY_BUFFER_PUBKEY: anchor.web3.PublicKey = export const HISTORY_BUFFER_PUBKEY: anchor.web3.PublicKey =
new anchor.web3.PublicKey("7LLvRhMs73FqcLkA8jvEE1AM2mYZXTmqfUv8GAEurymx"); new anchor.web3.PublicKey("9GPTMZmtNU61ULAZoGxDZmnZoWeF8zvBmKp4WZY6Ln6j");
export const sleep = (ms: number): Promise<any> => export const sleep = (ms: number): Promise<any> =>
new Promise((s) => setTimeout(s, ms)); new Promise((s) => setTimeout(s, ms));
@ -28,13 +29,14 @@ describe("anchor-history-parser", () => {
let aggregatorAccount: AggregatorAccount; let aggregatorAccount: AggregatorAccount;
let historyBuffer: anchor.web3.PublicKey; let historyBuffer: anchor.web3.PublicKey;
let switchboard: SwitchboardTestContextV2; let switchboard: SwitchboardTestContext;
let oracle: NodeOracle;
before(async () => { before(async () => {
switchboard = await SwitchboardTestContextV2.loadFromProvider(provider, { switchboard = await SwitchboardTestContext.loadFromProvider(provider, {
// You can provide a keypair to so the PDA schemes dont change between test runs // You can provide a keypair to so the PDA schemes dont change between test runs
name: "Test Queue", name: "Test Queue",
keypair: SwitchboardTestContextV2.loadKeypair("~/.keypairs/queue.json"), keypair: SwitchboardTestContext.loadKeypair("~/.keypairs/queue.json"),
queueSize: 10, queueSize: 10,
reward: 0, reward: 0,
minStake: 0, minStake: 0,
@ -45,18 +47,33 @@ describe("anchor-history-parser", () => {
oracle: { oracle: {
name: "Test Oracle", name: "Test Oracle",
enable: true, enable: true,
stakingWalletKeypair: SwitchboardTestContextV2.loadKeypair( stakingWalletKeypair: SwitchboardTestContext.loadKeypair(
"~/.keypairs/oracleWallet.json" "~/.keypairs/oracleWallet.json"
), ),
}, },
}); });
await switchboard.start();
oracle = await NodeOracle.fromReleaseChannel({
chain: "solana",
releaseChannel: "testnet",
network: "localnet", // disables production capabilities like monitoring and alerts
rpcUrl: switchboard.program.connection.rpcEndpoint,
oracleKey: switchboard.oracle.publicKey.toBase58(),
secretPath: switchboard.walletPath,
silent: false, // set to true to suppress oracle logs in the console
envVariables: {
VERBOSE: "1",
DEBUG: "1",
DISABLE_NONCE_QUEUE: "1",
DISABLE_METRICS: "1",
},
});
await oracle.startAndAwait();
}); });
after(async () => { after(() => {
if (switchboard) { oracle?.stop();
switchboard.stop();
}
}); });
/** Example showing how to create a new data feed with a history buffer storing 200k samples. /** Example showing how to create a new data feed with a history buffer storing 200k samples.

View File

@ -20,6 +20,7 @@
"@solana/spl-token": "^0.3.6", "@solana/spl-token": "^0.3.6",
"@solana/web3.js": "^1.73.3", "@solana/web3.js": "^1.73.3",
"@switchboard-xyz/common": "^2.1.33", "@switchboard-xyz/common": "^2.1.33",
"@switchboard-xyz/oracle": "^2.1.13",
"@switchboard-xyz/solana.js": "workspace:*", "@switchboard-xyz/solana.js": "workspace:*",
"chalk": "^4.1.2", "chalk": "^4.1.2",
"dotenv": "^16.0.1", "dotenv": "^16.0.1",

View File

@ -17,12 +17,13 @@ import {
PermissionAccount, PermissionAccount,
QueueAccount, QueueAccount,
SwitchboardProgram, SwitchboardProgram,
SwitchboardTestContextV2, SwitchboardTestContext,
SWITCHBOARD_LABS_DEVNET_PERMISSIONLESS_QUEUE, SWITCHBOARD_LABS_DEVNET_PERMISSIONLESS_QUEUE,
types, types,
} from "@switchboard-xyz/solana.js"; } from "@switchboard-xyz/solana.js";
import { AnchorVrfParser } from "../target/types/anchor_vrf_parser"; import { AnchorVrfParser } from "../target/types/anchor_vrf_parser";
import { NodeOracle } from "@switchboard-xyz/oracle";
describe("anchor-vrf-parser test", () => { describe("anchor-vrf-parser test", () => {
const provider = AnchorProvider.env(); const provider = AnchorProvider.env();
@ -56,7 +57,9 @@ describe("anchor-vrf-parser test", () => {
ixData: vrfIxCoder.encode("updateResult", ""), // pass any params for instruction here ixData: vrfIxCoder.encode("updateResult", ""), // pass any params for instruction here
}; };
let switchboard: SwitchboardTestContextV2; let switchboard: SwitchboardTestContext;
let oracle: NodeOracle;
let queueAccount: QueueAccount; let queueAccount: QueueAccount;
let queue: types.OracleQueueAccountData; let queue: types.OracleQueueAccountData;
@ -70,10 +73,10 @@ describe("anchor-vrf-parser test", () => {
SWITCHBOARD_LABS_DEVNET_PERMISSIONLESS_QUEUE SWITCHBOARD_LABS_DEVNET_PERMISSIONLESS_QUEUE
); );
} else { } else {
switchboard = await SwitchboardTestContextV2.loadFromProvider(provider, { switchboard = await SwitchboardTestContext.loadFromProvider(provider, {
// You can provide a keypair to so the PDA schemes dont change between test runs // You can provide a keypair to so the PDA schemes dont change between test runs
name: "Test Queue", name: "Test Queue",
keypair: SwitchboardTestContextV2.loadKeypair("~/.keypairs/queue.json"), keypair: SwitchboardTestContext.loadKeypair("~/.keypairs/queue.json"),
queueSize: 10, queueSize: 10,
reward: 0, reward: 0,
minStake: 0, minStake: 0,
@ -84,19 +87,36 @@ describe("anchor-vrf-parser test", () => {
oracle: { oracle: {
name: "Test Oracle", name: "Test Oracle",
enable: true, enable: true,
stakingWalletKeypair: SwitchboardTestContextV2.loadKeypair( stakingWalletKeypair: SwitchboardTestContext.loadKeypair(
"~/.keypairs/oracleWallet.json" "~/.keypairs/oracleWallet.json"
), ),
}, },
}); });
queueAccount = switchboard.queue; queueAccount = switchboard.queue;
queue = await queueAccount.loadData(); queue = await queueAccount.loadData();
await switchboard.start();
oracle = await NodeOracle.fromReleaseChannel({
chain: "solana",
releaseChannel: "testnet",
network: "localnet", // disables production capabilities like monitoring and alerts
rpcUrl: switchboard.program.connection.rpcEndpoint,
oracleKey: switchboard.oracle.publicKey.toBase58(),
secretPath: switchboard.walletPath,
silent: false, // set to true to suppress oracle logs in the console
envVariables: {
VERBOSE: "1",
DEBUG: "1",
DISABLE_NONCE_QUEUE: "1",
DISABLE_METRICS: "1",
},
});
await oracle.startAndAwait();
} }
}); });
after(() => { after(() => {
switchboard?.stop(); oracle?.stop();
}); });
it("Creates a vrfClient account", async () => { it("Creates a vrfClient account", async () => {

View File

@ -16,6 +16,7 @@
"@coral-xyz/anchor": "^0.27.0", "@coral-xyz/anchor": "^0.27.0",
"@solana/web3.js": "^1.73.3", "@solana/web3.js": "^1.73.3",
"@switchboard-xyz/common": "^2.1.33", "@switchboard-xyz/common": "^2.1.33",
"@switchboard-xyz/oracle": "^2.1.13",
"@switchboard-xyz/solana.js": "workspace:*" "@switchboard-xyz/solana.js": "workspace:*"
}, },
"devDependencies": { "devDependencies": {

View File

@ -6,9 +6,10 @@ import {
TransactionInstruction, TransactionInstruction,
} from "@solana/web3.js"; } from "@solana/web3.js";
import { OracleJob, sleep } from "@switchboard-xyz/common"; import { OracleJob, sleep } from "@switchboard-xyz/common";
import { NodeOracle } from "@switchboard-xyz/oracle";
import { import {
AggregatorAccount, AggregatorAccount,
SwitchboardTestContextV2, SwitchboardTestContext,
} from "@switchboard-xyz/solana.js"; } from "@switchboard-xyz/solana.js";
import assert from "assert"; import assert from "assert";
import fs from "fs"; import fs from "fs";
@ -37,13 +38,14 @@ describe("native-feed-parser test", () => {
anchor.setProvider(provider); anchor.setProvider(provider);
let aggregatorAccount: AggregatorAccount; let aggregatorAccount: AggregatorAccount;
let switchboard: SwitchboardTestContextV2; let switchboard: SwitchboardTestContext;
let oracle: NodeOracle;
before(async () => { before(async () => {
switchboard = await SwitchboardTestContextV2.loadFromProvider(provider, { switchboard = await SwitchboardTestContext.loadFromProvider(provider, {
// You can provide a keypair to so the PDA schemes dont change between test runs // You can provide a keypair to so the PDA schemes dont change between test runs
name: "Test Queue", name: "Test Queue",
keypair: SwitchboardTestContextV2.loadKeypair("~/.keypairs/queue.json"), keypair: SwitchboardTestContext.loadKeypair("~/.keypairs/queue.json"),
queueSize: 10, queueSize: 10,
reward: 0, reward: 0,
minStake: 0, minStake: 0,
@ -54,18 +56,33 @@ describe("native-feed-parser test", () => {
oracle: { oracle: {
name: "Test Oracle", name: "Test Oracle",
enable: true, enable: true,
stakingWalletKeypair: SwitchboardTestContextV2.loadKeypair( stakingWalletKeypair: SwitchboardTestContext.loadKeypair(
"~/.keypairs/oracleWallet.json" "~/.keypairs/oracleWallet.json"
), ),
}, },
}); });
await switchboard.start();
oracle = await NodeOracle.fromReleaseChannel({
chain: "solana",
releaseChannel: "testnet",
network: "localnet", // disables production capabilities like monitoring and alerts
rpcUrl: switchboard.program.connection.rpcEndpoint,
oracleKey: switchboard.oracle.publicKey.toBase58(),
secretPath: switchboard.walletPath,
silent: false, // set to true to suppress oracle logs in the console
envVariables: {
VERBOSE: "1",
DEBUG: "1",
DISABLE_NONCE_QUEUE: "1",
DISABLE_METRICS: "1",
},
});
await oracle.startAndAwait();
}); });
after(async () => { after(() => {
if (switchboard) { oracle?.stop();
switchboard.stop();
}
}); });
it("Read SOL/USD Feed", async () => { it("Read SOL/USD Feed", async () => {