solana.js: added createMock methods to some accounts

This commit is contained in:
Conner Gallagher 2022-12-09 12:02:57 -07:00
parent dbe0f6f2a4
commit 62a4878682
8 changed files with 334 additions and 52 deletions

View File

@ -5,9 +5,11 @@ import * as errors from '../errors';
import Big from 'big.js'; import Big from 'big.js';
import { SwitchboardProgram } from '../program'; import { SwitchboardProgram } from '../program';
import { import {
AccountInfo,
AccountMeta, AccountMeta,
Commitment, Commitment,
Keypair, Keypair,
LAMPORTS_PER_SOL,
PublicKey, PublicKey,
SystemProgram, SystemProgram,
TransactionInstruction, TransactionInstruction,
@ -57,6 +59,9 @@ export class AggregatorAccount extends Account<types.AggregatorAccountData> {
*/ */
public static getMetadata = (aggregator: types.AggregatorAccountData) => public static getMetadata = (aggregator: types.AggregatorAccountData) =>
toUtf8(aggregator.metadata); toUtf8(aggregator.metadata);
public static size = 3851;
/** /**
* Get the size of an {@linkcode AggregatorAccount} on-chain. * Get the size of an {@linkcode AggregatorAccount} on-chain.
*/ */
@ -74,11 +79,39 @@ export class AggregatorAccount extends Account<types.AggregatorAccountData> {
} }
public static default(): types.AggregatorAccountData { public static default(): types.AggregatorAccountData {
const buffer = Buffer.alloc(3851, 0); const buffer = Buffer.alloc(AggregatorAccount.size, 0);
types.AggregatorAccountData.discriminator.copy(buffer, 0); types.AggregatorAccountData.discriminator.copy(buffer, 0);
return types.AggregatorAccountData.decode(buffer); return types.AggregatorAccountData.decode(buffer);
} }
public static createMock(
programId: PublicKey,
data: Partial<types.AggregatorAccountData>,
options?: {
lamports?: number;
rentEpoch?: number;
}
): AccountInfo<Buffer> {
const fields: types.AggregatorAccountDataFields = {
...AggregatorAccount.default(),
...data,
// any cleanup actions here
};
const state = new types.AggregatorAccountData(fields);
const buffer = Buffer.alloc(AggregatorAccount.size, 0);
types.AggregatorAccountData.discriminator.copy(buffer, 0);
types.AggregatorAccountData.layout.encode(state, buffer, 8);
return {
executable: false,
owner: programId,
lamports: options?.lamports ?? 1 * LAMPORTS_PER_SOL,
data: buffer,
rentEpoch: options?.rentEpoch ?? 0,
};
}
/** /**
* Invoke a callback each time an AggregatorAccount's data has changed on-chain. * Invoke a callback each time an AggregatorAccount's data has changed on-chain.
* @param callback - the callback invoked when the aggregator state changes * @param callback - the callback invoked when the aggregator state changes

View File

@ -2,6 +2,7 @@ import {
AccountInfo, AccountInfo,
Commitment, Commitment,
Keypair, Keypair,
LAMPORTS_PER_SOL,
PublicKey, PublicKey,
SystemProgram, SystemProgram,
TransactionSignature, TransactionSignature,
@ -15,7 +16,7 @@ import { Account } from './account';
import { TransactionObject } from '../transaction'; import { TransactionObject } from '../transaction';
/** /**
* Account type storing a list of SwitchboardTasks {@linkcode OracleJob.ITask} dictating how to source data off-chain. * Account type storing a list of SwitchboardTasks {@linkcode OracleJob.Task} dictating how to source data off-chain.
* *
* Data: {@linkcode types.JobAccountData} * Data: {@linkcode types.JobAccountData}
*/ */
@ -36,6 +37,64 @@ export class JobAccount extends Account<types.JobAccountData> {
*/ */
public size = this.program.account.jobAccountData.size; public size = this.program.account.jobAccountData.size;
public static getAccountSize(byteLength: number): number {
return 181 + byteLength;
}
public static default(byteLength: number): types.LeaseAccountData {
const buffer = Buffer.alloc(JobAccount.getAccountSize(byteLength), 0);
types.LeaseAccountData.discriminator.copy(buffer, 0);
return types.LeaseAccountData.decode(buffer);
}
public static createMock(
programId: PublicKey,
data: Partial<types.JobAccountData> &
({ job: OracleJob } | { tasks: Array<OracleJob.Task> }),
options?: {
lamports?: number;
rentEpoch?: number;
}
): AccountInfo<Buffer> {
let jobData: Buffer | undefined = undefined;
if ('data' in data && data.data && data.data.byteLength > 0) {
jobData = Buffer.from(data.data);
}
if ('job' in data) {
jobData = Buffer.from(OracleJob.encodeDelimited(data.job).finish());
} else if ('tasks' in data) {
jobData = Buffer.from(
OracleJob.encodeDelimited(OracleJob.fromObject(data.tasks)).finish()
);
}
if (!jobData) {
throw new Error(`No job data found to create mock`);
}
const fields: types.LeaseAccountDataFields = {
...JobAccount.default(jobData.byteLength),
...data,
// any cleanup actions here
};
const state = new types.LeaseAccountData(fields);
const buffer = Buffer.alloc(
JobAccount.getAccountSize(jobData.byteLength),
0
);
types.LeaseAccountData.discriminator.copy(buffer, 0);
types.LeaseAccountData.layout.encode(state, buffer, 8);
jobData.copy(buffer, 181);
return {
executable: false,
owner: programId,
lamports: options?.lamports ?? 1 * LAMPORTS_PER_SOL,
data: buffer,
rentEpoch: options?.rentEpoch ?? 0,
};
}
/** Load an existing JobAccount with its current on-chain state */ /** Load an existing JobAccount with its current on-chain state */
public static async load( public static async load(
program: SwitchboardProgram, program: SwitchboardProgram,

View File

@ -5,8 +5,10 @@ import { SwitchboardProgram } from '../program';
import { Account } from './account'; import { Account } from './account';
import * as spl from '@solana/spl-token'; import * as spl from '@solana/spl-token';
import { import {
AccountInfo,
AccountMeta, AccountMeta,
Keypair, Keypair,
LAMPORTS_PER_SOL,
PublicKey, PublicKey,
SystemProgram, SystemProgram,
TransactionInstruction, TransactionInstruction,
@ -26,17 +28,47 @@ import Big from 'big.js';
export class LeaseAccount extends Account<types.LeaseAccountData> { export class LeaseAccount extends Account<types.LeaseAccountData> {
static accountName = 'LeaseAccountData'; static accountName = 'LeaseAccountData';
public static size = 453;
/** /**
* Get the size of an {@linkcode LeaseAccount} on-chain. * Get the size of an {@linkcode LeaseAccount} on-chain.
*/ */
public size = this.program.account.leaseAccountData.size; public size = this.program.account.leaseAccountData.size;
public static default(): types.LeaseAccountData { public static default(): types.LeaseAccountData {
const buffer = Buffer.alloc(453, 0); const buffer = Buffer.alloc(LeaseAccount.size, 0);
types.LeaseAccountData.discriminator.copy(buffer, 0); types.LeaseAccountData.discriminator.copy(buffer, 0);
return types.LeaseAccountData.decode(buffer); return types.LeaseAccountData.decode(buffer);
} }
public static createMock(
programId: PublicKey,
data: Partial<types.LeaseAccountData>,
options?: {
lamports?: number;
rentEpoch?: number;
}
): AccountInfo<Buffer> {
const fields: types.LeaseAccountDataFields = {
...LeaseAccount.default(),
...data,
// any cleanup actions here
};
const state = new types.LeaseAccountData(fields);
const buffer = Buffer.alloc(LeaseAccount.size, 0);
types.LeaseAccountData.discriminator.copy(buffer, 0);
types.LeaseAccountData.layout.encode(state, buffer, 8);
return {
executable: false,
owner: programId,
lamports: options?.lamports ?? 1 * LAMPORTS_PER_SOL,
data: buffer,
rentEpoch: options?.rentEpoch ?? 0,
};
}
/** Load an existing LeaseAccount with its current on-chain state */ /** Load an existing LeaseAccount with its current on-chain state */
public static async load( public static async load(
program: SwitchboardProgram, program: SwitchboardProgram,

View File

@ -4,8 +4,10 @@ import { Account, OnAccountChangeCallback } from './account';
import * as anchor from '@project-serum/anchor'; import * as anchor from '@project-serum/anchor';
import { SwitchboardProgram } from '../program'; import { SwitchboardProgram } from '../program';
import { import {
AccountInfo,
Commitment, Commitment,
Keypair, Keypair,
LAMPORTS_PER_SOL,
PublicKey, PublicKey,
SystemProgram, SystemProgram,
TransactionSignature, TransactionSignature,
@ -25,17 +27,47 @@ import { TransactionObject } from '../transaction';
export class OracleAccount extends Account<types.OracleAccountData> { export class OracleAccount extends Account<types.OracleAccountData> {
static accountName = 'OracleAccountData'; static accountName = 'OracleAccountData';
public static size = 636;
/** /**
* Get the size of an {@linkcode OracleAccount} on-chain. * Get the size of an {@linkcode OracleAccount} on-chain.
*/ */
public size = this.program.account.oracleAccountData.size; public size = this.program.account.oracleAccountData.size;
public static default(): types.OracleAccountData { public static default(): types.OracleAccountData {
const buffer = Buffer.alloc(636, 0); const buffer = Buffer.alloc(OracleAccount.size, 0);
types.OracleAccountData.discriminator.copy(buffer, 0); types.OracleAccountData.discriminator.copy(buffer, 0);
return types.OracleAccountData.decode(buffer); return types.OracleAccountData.decode(buffer);
} }
public static createMock(
programId: PublicKey,
data: Partial<types.OracleAccountData>,
options?: {
lamports?: number;
rentEpoch?: number;
}
): AccountInfo<Buffer> {
const fields: types.OracleAccountDataFields = {
...OracleAccount.default(),
...data,
// any cleanup actions here
};
const state = new types.OracleAccountData(fields);
const buffer = Buffer.alloc(OracleAccount.size, 0);
types.OracleAccountData.discriminator.copy(buffer, 0);
types.OracleAccountData.layout.encode(state, buffer, 8);
return {
executable: false,
owner: programId,
lamports: options?.lamports ?? 1 * LAMPORTS_PER_SOL,
data: buffer,
rentEpoch: options?.rentEpoch ?? 0,
};
}
/** Load an existing OracleAccount with its current on-chain state */ /** Load an existing OracleAccount with its current on-chain state */
public static async load( public static async load(
program: SwitchboardProgram, program: SwitchboardProgram,

View File

@ -3,6 +3,7 @@ import { ACCOUNT_DISCRIMINATOR_SIZE } from '@project-serum/anchor';
import { import {
AccountInfo, AccountInfo,
Keypair, Keypair,
LAMPORTS_PER_SOL,
PublicKey, PublicKey,
SystemProgram, SystemProgram,
TransactionSignature, TransactionSignature,
@ -67,6 +68,13 @@ export interface PermissionSetParams {
export class PermissionAccount extends Account<types.PermissionAccountData> { export class PermissionAccount extends Account<types.PermissionAccountData> {
static accountName = 'PermissionAccountData'; static accountName = 'PermissionAccountData';
public static size = 372;
/**
* Returns the size of an on-chain {@linkcode PermissionAccount}.
*/
public readonly size = this.program.account.permissionAccountData.size;
static getPermissions( static getPermissions(
permission: types.PermissionAccountData permission: types.PermissionAccountData
): types.SwitchboardPermissionKind | PermitNone { ): types.SwitchboardPermissionKind | PermitNone {
@ -87,11 +95,39 @@ export class PermissionAccount extends Account<types.PermissionAccountData> {
} }
public static default(): types.PermissionAccountData { public static default(): types.PermissionAccountData {
const buffer = Buffer.alloc(372, 0); const buffer = Buffer.alloc(PermissionAccount.size, 0);
types.PermissionAccountData.discriminator.copy(buffer, 0); types.PermissionAccountData.discriminator.copy(buffer, 0);
return types.PermissionAccountData.decode(buffer); return types.PermissionAccountData.decode(buffer);
} }
public static createMock(
programId: PublicKey,
data: Partial<types.PermissionAccountData>,
options?: {
lamports?: number;
rentEpoch?: number;
}
): AccountInfo<Buffer> {
const fields: types.PermissionAccountDataFields = {
...PermissionAccount.default(),
...data,
// any cleanup actions here
};
const state = new types.PermissionAccountData(fields);
const buffer = Buffer.alloc(PermissionAccount.size, 0);
types.PermissionAccountData.discriminator.copy(buffer, 0);
types.PermissionAccountData.layout.encode(state, buffer, 8);
return {
executable: false,
owner: programId,
lamports: options?.lamports ?? 1 * LAMPORTS_PER_SOL,
data: buffer,
rentEpoch: options?.rentEpoch ?? 0,
};
}
/** Load an existing PermissionAccount with its current on-chain state */ /** Load an existing PermissionAccount with its current on-chain state */
public static async load( public static async load(
program: SwitchboardProgram, program: SwitchboardProgram,
@ -174,11 +210,6 @@ export class PermissionAccount extends Account<types.PermissionAccountData> {
return [account, txSignature]; return [account, txSignature];
} }
/**
* Returns the size of an on-chain {@linkcode PermissionAccount}.
*/
public readonly size = this.program.account.permissionAccountData.size;
/** /**
* Retrieve and decode the {@linkcode types.PermissionAccountData} stored in this account. * Retrieve and decode the {@linkcode types.PermissionAccountData} stored in this account.
*/ */

View File

@ -6,7 +6,9 @@ import * as spl from '@solana/spl-token';
import * as errors from '../errors'; import * as errors from '../errors';
import { Mint } from '../mint'; import { Mint } from '../mint';
import { import {
AccountInfo,
Keypair, Keypair,
LAMPORTS_PER_SOL,
PublicKey, PublicKey,
SystemProgram, SystemProgram,
TransactionInstruction, TransactionInstruction,
@ -22,12 +24,47 @@ import { TransactionObject } from '../transaction';
export class ProgramStateAccount extends Account<types.SbState> { export class ProgramStateAccount extends Account<types.SbState> {
static accountName = 'SbState'; static accountName = 'SbState';
public static size = 1128;
/**
* @return account size of the global {@linkcode ProgramStateAccount}.
*/
public readonly size = this.program.account.sbState.size;
public static default(): types.SbState { public static default(): types.SbState {
const buffer = Buffer.alloc(1128, 0); const buffer = Buffer.alloc(ProgramStateAccount.size, 0);
types.SbState.discriminator.copy(buffer, 0); types.SbState.discriminator.copy(buffer, 0);
return types.SbState.decode(buffer); return types.SbState.decode(buffer);
} }
public static createMock(
programId: PublicKey,
data: Partial<types.SbState>,
options?: {
lamports?: number;
rentEpoch?: number;
}
): AccountInfo<Buffer> {
const fields: types.SbStateFields = {
...ProgramStateAccount.default(),
...data,
// any cleanup actions here
};
const state = new types.SbState(fields);
const buffer = Buffer.alloc(ProgramStateAccount.size, 0);
types.SbState.discriminator.copy(buffer, 0);
types.SbState.layout.encode(state, buffer, 8);
return {
executable: false,
owner: programId,
lamports: options?.lamports ?? 1 * LAMPORTS_PER_SOL,
data: buffer,
rentEpoch: options?.rentEpoch ?? 0,
};
}
/** Load the ProgramStateAccount with its current on-chain state */ /** Load the ProgramStateAccount with its current on-chain state */
public static async load( public static async load(
program: SwitchboardProgram, program: SwitchboardProgram,
@ -41,6 +78,16 @@ export class ProgramStateAccount extends Account<types.SbState> {
return [account, state]; return [account, state];
} }
/**
* Retrieve and decode the {@linkcode types.SbState} stored in this account.
*/
public async loadData(): Promise<types.SbState> {
const data = await types.SbState.fetch(this.program, this.publicKey);
if (data === null)
throw new errors.AccountNotFoundError('Program State', this.publicKey);
return data;
}
/** /**
* Retrieves the {@linkcode ProgramStateAccount}, creates it if it doesn't exist; * Retrieves the {@linkcode ProgramStateAccount}, creates it if it doesn't exist;
*/ */
@ -157,6 +204,7 @@ export class ProgramStateAccount extends Account<types.SbState> {
); );
return [new ProgramStateAccount(program, publicKey), bump]; return [new ProgramStateAccount(program, publicKey), bump];
} }
/** /**
* Transfer N tokens from the program vault to a specified account. * Transfer N tokens from the program vault to a specified account.
* @param to The recipient of the vault tokens. * @param to The recipient of the vault tokens.
@ -193,28 +241,4 @@ export class ProgramStateAccount extends Account<types.SbState> {
const txnSignature = await program.signAndSend(vaultTransfer); const txnSignature = await program.signAndSend(vaultTransfer);
return txnSignature; return txnSignature;
} }
/**
* @return account size of the global {@linkcode ProgramStateAccount}.
*/
public readonly size = this.program.account.sbState.size;
/**
* Retrieve and decode the {@linkcode types.SbState} stored in this account.
*/
public async loadData(): Promise<types.SbState> {
const data = await types.SbState.fetch(this.program, this.publicKey);
if (data === null)
throw new errors.AccountNotFoundError('Program State', this.publicKey);
return data;
}
/**
* Fetch the Switchboard token mint specified in the program state account.
*/
public async getTokenMint(): Promise<spl.Mint> {
const state = await this.loadData();
const switchTokenMint = spl.getMint(
this.program.connection,
state.tokenMint
);
return switchTokenMint;
}
} }

View File

@ -1,8 +1,10 @@
import * as anchor from '@project-serum/anchor'; import * as anchor from '@project-serum/anchor';
import * as spl from '@solana/spl-token'; import * as spl from '@solana/spl-token';
import { import {
AccountInfo,
Commitment, Commitment,
Keypair, Keypair,
LAMPORTS_PER_SOL,
PublicKey, PublicKey,
SystemProgram, SystemProgram,
TransactionSignature, TransactionSignature,
@ -47,15 +49,11 @@ import { VrfAccount, VrfInitParams } from './vrfAccount';
export class QueueAccount extends Account<types.OracleQueueAccountData> { export class QueueAccount extends Account<types.OracleQueueAccountData> {
static accountName = 'OracleQueueAccountData'; static accountName = 'OracleQueueAccountData';
public static default(): types.OracleQueueAccountData {
const buffer = Buffer.alloc(1269, 0);
types.OracleQueueAccountData.discriminator.copy(buffer, 0);
return types.OracleQueueAccountData.decode(buffer);
}
/** The {@linkcode QueueDataBuffer} storing a list of oracle's that are actively heartbeating */ /** The {@linkcode QueueDataBuffer} storing a list of oracle's that are actively heartbeating */
dataBuffer?: QueueDataBuffer; dataBuffer?: QueueDataBuffer;
public static size = 1269;
/** /**
* Get the size of an {@linkcode QueueAccount} on-chain. * Get the size of an {@linkcode QueueAccount} on-chain.
*/ */
@ -66,6 +64,7 @@ export class QueueAccount extends Account<types.OracleQueueAccountData> {
*/ */
public static getName = (queue: types.OracleQueueAccountData) => public static getName = (queue: types.OracleQueueAccountData) =>
toUtf8(queue.name); toUtf8(queue.name);
/** /**
* Returns the queue's metadata buffer in a stringified format. * Returns the queue's metadata buffer in a stringified format.
*/ */
@ -84,6 +83,40 @@ export class QueueAccount extends Account<types.OracleQueueAccountData> {
return [account, state]; return [account, state];
} }
public static default(): types.OracleQueueAccountData {
const buffer = Buffer.alloc(1269, 0);
types.OracleQueueAccountData.discriminator.copy(buffer, 0);
return types.OracleQueueAccountData.decode(buffer);
}
public static createMock(
programId: PublicKey,
data: Partial<types.OracleQueueAccountData>,
options?: {
lamports?: number;
rentEpoch?: number;
}
): AccountInfo<Buffer> {
const fields: types.OracleQueueAccountDataFields = {
...QueueAccount.default(),
...data,
// any cleanup actions here
};
const state = new types.OracleQueueAccountData(fields);
const buffer = Buffer.alloc(QueueAccount.size, 0);
types.OracleQueueAccountData.discriminator.copy(buffer, 0);
types.OracleQueueAccountData.layout.encode(state, buffer, 8);
return {
executable: false,
owner: programId,
lamports: options?.lamports ?? 1 * LAMPORTS_PER_SOL,
data: buffer,
rentEpoch: options?.rentEpoch ?? 0,
};
}
/** /**
* Invoke a callback each time a QueueAccount's data has changed on-chain. * Invoke a callback each time a QueueAccount's data has changed on-chain.
* @param callback - the callback invoked when the queues state changes * @param callback - the callback invoked when the queues state changes

View File

@ -1,4 +1,10 @@
import { AccountInfo, Commitment, PublicKey } from '@solana/web3.js'; import {
AccountInfo,
Commitment,
LAMPORTS_PER_SOL,
PublicKey,
} from '@solana/web3.js';
import assert from 'assert';
import * as errors from '../errors'; import * as errors from '../errors';
import * as types from '../generated'; import * as types from '../generated';
import { SwitchboardProgram } from '../program'; import { SwitchboardProgram } from '../program';
@ -18,6 +24,48 @@ export class QueueDataBuffer extends Account<Array<PublicKey>> {
public size = 32; public size = 32;
public static getAccountSize(size: number): number {
return 8 + size * 32;
}
public static default(size = 100): Buffer {
const buffer = Buffer.alloc(QueueDataBuffer.getAccountSize(size), 0);
BUFFER_DISCRIMINATOR.copy(buffer, 0);
return buffer;
}
public static createMock(
programId: PublicKey,
data: { size?: number; oracles?: Array<PublicKey> },
options?: {
lamports?: number;
rentEpoch?: number;
}
): AccountInfo<Buffer> {
const size = data.size ?? 100;
const oracles: Array<PublicKey> = Array(size).fill(PublicKey.default);
for (const [n, oracle] of (data.oracles ?? []).entries()) {
oracles[n] = oracle;
}
const buffer = Buffer.alloc(QueueDataBuffer.getAccountSize(size), 0);
BUFFER_DISCRIMINATOR.copy(buffer, 0);
for (const [n, oracle] of oracles.entries()) {
const oracleBuffer = oracle.toBuffer();
assert(oracleBuffer.byteLength === 32);
oracleBuffer.copy(buffer, 8 + n * 32);
}
return {
executable: false,
owner: programId,
lamports: options?.lamports ?? 1 * LAMPORTS_PER_SOL,
data: buffer,
rentEpoch: options?.rentEpoch ?? 0,
};
}
/** /**
* Invoke a callback each time a QueueAccount's oracle queue buffer has changed on-chain. The buffer stores a list of oracle's and their last heartbeat timestamp. * Invoke a callback each time a QueueAccount's oracle queue buffer has changed on-chain. The buffer stores a list of oracle's and their last heartbeat timestamp.
* @param callback - the callback invoked when the queues buffer changes * @param callback - the callback invoked when the queues buffer changes
@ -82,16 +130,6 @@ export class QueueDataBuffer extends Account<Array<PublicKey>> {
return oracles; return oracles;
} }
public static getAccountSize(size: number): number {
return 8 + size * 32;
}
public static default(size = 100): Buffer {
const buffer = Buffer.alloc(QueueDataBuffer.getAccountSize(size), 0);
BUFFER_DISCRIMINATOR.copy(buffer, 0);
return buffer;
}
/** /**
* Return a queues dataBuffer * Return a queues dataBuffer
* *