FRE-600: Fix code review blockers

- Consolidated duplicate UndoManagers to single instance
- Fixed connection promise to only resolve on 'connected' status
- Fixed WebSocketProvider import (WebsocketProvider)
- Added proper doc.destroy() cleanup
- Renamed isPresenceInitialized property to avoid conflict

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
2026-04-25 00:08:01 -04:00
parent 65b552bb08
commit 7c684a42cc
48450 changed files with 5679671 additions and 383 deletions

View File

@@ -0,0 +1,4 @@
export const Headers: typeof globalThis.Headers = globalThis.Headers;
export const Request: typeof globalThis.Request = globalThis.Request;
export const Response: typeof globalThis.Response = globalThis.Response;
export default globalThis.fetch;

View File

@@ -0,0 +1,4 @@
export const Headers: typeof globalThis.Headers = globalThis.Headers;
export const Request: typeof globalThis.Request = globalThis.Request;
export const Response: typeof globalThis.Response = globalThis.Response;
export default globalThis.fetch;

39
node_modules/@solana/web3.js/src/account-data.ts generated vendored Normal file
View File

@@ -0,0 +1,39 @@
import * as BufferLayout from '@solana/buffer-layout';
export interface IAccountStateData {
readonly typeIndex: number;
}
/**
* @internal
*/
export type AccountType<TInputData extends IAccountStateData> = {
/** The account type index (from solana upstream program) */
index: number;
/** The BufferLayout to use to build data */
layout: BufferLayout.Layout<TInputData>;
};
/**
* Decode account data buffer using an AccountType
* @internal
*/
export function decodeData<TAccountStateData extends IAccountStateData>(
type: AccountType<TAccountStateData>,
data: Uint8Array,
): TAccountStateData {
let decoded: TAccountStateData;
try {
decoded = type.layout.decode(data);
} catch (err) {
throw new Error('invalid instruction; ' + err);
}
if (decoded.typeIndex !== type.index) {
throw new Error(
`invalid account data; account type mismatch ${decoded.typeIndex} != ${type.index}`,
);
}
return decoded;
}

55
node_modules/@solana/web3.js/src/account.ts generated vendored Normal file
View File

@@ -0,0 +1,55 @@
import {Buffer} from 'buffer';
import {generatePrivateKey, getPublicKey} from './utils/ed25519';
import {toBuffer} from './utils/to-buffer';
import {PublicKey} from './publickey';
/**
* An account key pair (public and secret keys).
*
* @deprecated since v1.10.0, please use {@link Keypair} instead.
*/
export class Account {
/** @internal */
private _publicKey: Buffer;
/** @internal */
private _secretKey: Buffer;
/**
* Create a new Account object
*
* If the secretKey parameter is not provided a new key pair is randomly
* created for the account
*
* @param secretKey Secret key for the account
*/
constructor(secretKey?: Uint8Array | Array<number>) {
if (secretKey) {
const secretKeyBuffer = toBuffer(secretKey);
if (secretKey.length !== 64) {
throw new Error('bad secret key size');
}
this._publicKey = secretKeyBuffer.slice(32, 64);
this._secretKey = secretKeyBuffer.slice(0, 32);
} else {
this._secretKey = toBuffer(generatePrivateKey());
this._publicKey = toBuffer(getPublicKey(this._secretKey));
}
}
/**
* The public key for this account
*/
get publicKey(): PublicKey {
return new PublicKey(this._publicKey);
}
/**
* The **unencrypted** secret key for this account. The first 32 bytes
* is the private scalar and the last 32 bytes is the public key.
* Read more: https://blog.mozilla.org/warner/2011/11/29/ed25519-keys/
*/
get secretKey(): Buffer {
return Buffer.concat([this._secretKey, this._publicKey], 64);
}
}

4
node_modules/@solana/web3.js/src/blockhash.ts generated vendored Normal file
View File

@@ -0,0 +1,4 @@
/**
* Blockhash as Base58 string.
*/
export type Blockhash = string;

View File

@@ -0,0 +1,5 @@
import {PublicKey} from './publickey';
export const BPF_LOADER_DEPRECATED_PROGRAM_ID = new PublicKey(
'BPFLoader1111111111111111111111111111111111',
);

50
node_modules/@solana/web3.js/src/bpf-loader.ts generated vendored Normal file
View File

@@ -0,0 +1,50 @@
import type {Buffer} from 'buffer';
import {PublicKey} from './publickey';
import {Loader} from './loader';
import type {Connection} from './connection';
import type {Signer} from './keypair';
/**
* @deprecated Deprecated since Solana v1.17.20.
*/
export const BPF_LOADER_PROGRAM_ID = new PublicKey(
'BPFLoader2111111111111111111111111111111111',
);
/**
* Factory class for transactions to interact with a program loader
*
* @deprecated Deprecated since Solana v1.17.20.
*/
export class BpfLoader {
/**
* Minimum number of signatures required to load a program not including
* retries
*
* Can be used to calculate transaction fees
*/
static getMinNumSignatures(dataLength: number): number {
return Loader.getMinNumSignatures(dataLength);
}
/**
* Load a SBF program
*
* @param connection The connection to use
* @param payer Account that will pay program loading fees
* @param program Account to load the program into
* @param elf The entire ELF containing the SBF program
* @param loaderProgramId The program id of the BPF loader to use
* @return true if program was loaded successfully, false if program was already loaded
*/
static load(
connection: Connection,
payer: Signer,
program: Signer,
elf: Buffer | Uint8Array | Array<number>,
loaderProgramId: PublicKey,
): Promise<boolean> {
return Loader.load(connection, payer, program, loaderProgramId, elf);
}
}

6961
node_modules/@solana/web3.js/src/connection.ts generated vendored Normal file

File diff suppressed because it is too large Load Diff

102
node_modules/@solana/web3.js/src/epoch-schedule.ts generated vendored Normal file
View File

@@ -0,0 +1,102 @@
const MINIMUM_SLOT_PER_EPOCH = 32;
// Returns the number of trailing zeros in the binary representation of self.
function trailingZeros(n: number) {
let trailingZeros = 0;
while (n > 1) {
n /= 2;
trailingZeros++;
}
return trailingZeros;
}
// Returns the smallest power of two greater than or equal to n
function nextPowerOfTwo(n: number) {
if (n === 0) return 1;
n--;
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8;
n |= n >> 16;
n |= n >> 32;
return n + 1;
}
/**
* Epoch schedule
* (see https://docs.solana.com/terminology#epoch)
* Can be retrieved with the {@link Connection.getEpochSchedule} method
*/
export class EpochSchedule {
/** The maximum number of slots in each epoch */
public slotsPerEpoch: number;
/** The number of slots before beginning of an epoch to calculate a leader schedule for that epoch */
public leaderScheduleSlotOffset: number;
/** Indicates whether epochs start short and grow */
public warmup: boolean;
/** The first epoch with `slotsPerEpoch` slots */
public firstNormalEpoch: number;
/** The first slot of `firstNormalEpoch` */
public firstNormalSlot: number;
constructor(
slotsPerEpoch: number,
leaderScheduleSlotOffset: number,
warmup: boolean,
firstNormalEpoch: number,
firstNormalSlot: number,
) {
this.slotsPerEpoch = slotsPerEpoch;
this.leaderScheduleSlotOffset = leaderScheduleSlotOffset;
this.warmup = warmup;
this.firstNormalEpoch = firstNormalEpoch;
this.firstNormalSlot = firstNormalSlot;
}
getEpoch(slot: number): number {
return this.getEpochAndSlotIndex(slot)[0];
}
getEpochAndSlotIndex(slot: number): [number, number] {
if (slot < this.firstNormalSlot) {
const epoch =
trailingZeros(nextPowerOfTwo(slot + MINIMUM_SLOT_PER_EPOCH + 1)) -
trailingZeros(MINIMUM_SLOT_PER_EPOCH) -
1;
const epochLen = this.getSlotsInEpoch(epoch);
const slotIndex = slot - (epochLen - MINIMUM_SLOT_PER_EPOCH);
return [epoch, slotIndex];
} else {
const normalSlotIndex = slot - this.firstNormalSlot;
const normalEpochIndex = Math.floor(normalSlotIndex / this.slotsPerEpoch);
const epoch = this.firstNormalEpoch + normalEpochIndex;
const slotIndex = normalSlotIndex % this.slotsPerEpoch;
return [epoch, slotIndex];
}
}
getFirstSlotInEpoch(epoch: number): number {
if (epoch <= this.firstNormalEpoch) {
return (Math.pow(2, epoch) - 1) * MINIMUM_SLOT_PER_EPOCH;
} else {
return (
(epoch - this.firstNormalEpoch) * this.slotsPerEpoch +
this.firstNormalSlot
);
}
}
getLastSlotInEpoch(epoch: number): number {
return this.getFirstSlotInEpoch(epoch) + this.getSlotsInEpoch(epoch) - 1;
}
getSlotsInEpoch(epoch: number) {
if (epoch < this.firstNormalEpoch) {
return Math.pow(2, epoch + trailingZeros(MINIMUM_SLOT_PER_EPOCH));
} else {
return this.slotsPerEpoch;
}
}
}

133
node_modules/@solana/web3.js/src/errors.ts generated vendored Normal file
View File

@@ -0,0 +1,133 @@
import {Connection} from './connection';
import {TransactionSignature} from './transaction';
export class SendTransactionError extends Error {
private signature: TransactionSignature;
private transactionMessage: string;
private transactionLogs: string[] | Promise<string[]> | undefined;
constructor({
action,
signature,
transactionMessage,
logs,
}: {
action: 'send' | 'simulate';
signature: TransactionSignature;
transactionMessage: string;
logs?: string[];
}) {
const maybeLogsOutput = logs
? `Logs: \n${JSON.stringify(logs.slice(-10), null, 2)}. `
: '';
const guideText =
'\nCatch the `SendTransactionError` and call `getLogs()` on it for full details.';
let message: string;
switch (action) {
case 'send':
message =
`Transaction ${signature} resulted in an error. \n` +
`${transactionMessage}. ` +
maybeLogsOutput +
guideText;
break;
case 'simulate':
message =
`Simulation failed. \nMessage: ${transactionMessage}. \n` +
maybeLogsOutput +
guideText;
break;
default: {
message = `Unknown action '${((a: never) => a)(action)}'`;
}
}
super(message);
this.signature = signature;
this.transactionMessage = transactionMessage;
this.transactionLogs = logs ? logs : undefined;
}
get transactionError(): {message: string; logs?: string[]} {
return {
message: this.transactionMessage,
logs: Array.isArray(this.transactionLogs)
? this.transactionLogs
: undefined,
};
}
/* @deprecated Use `await getLogs()` instead */
get logs(): string[] | undefined {
const cachedLogs = this.transactionLogs;
if (
cachedLogs != null &&
typeof cachedLogs === 'object' &&
'then' in cachedLogs
) {
return undefined;
}
return cachedLogs;
}
async getLogs(connection: Connection): Promise<string[]> {
if (!Array.isArray(this.transactionLogs)) {
this.transactionLogs = new Promise((resolve, reject) => {
connection
.getTransaction(this.signature)
.then(tx => {
if (tx && tx.meta && tx.meta.logMessages) {
const logs = tx.meta.logMessages;
this.transactionLogs = logs;
resolve(logs);
} else {
reject(new Error('Log messages not found'));
}
})
.catch(reject);
});
}
return await this.transactionLogs;
}
}
// Keep in sync with client/src/rpc_custom_errors.rs
// Typescript `enums` thwart tree-shaking. See https://bargsten.org/jsts/enums/
export const SolanaJSONRPCErrorCode = {
JSON_RPC_SERVER_ERROR_BLOCK_CLEANED_UP: -32001,
JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE: -32002,
JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE: -32003,
JSON_RPC_SERVER_ERROR_BLOCK_NOT_AVAILABLE: -32004,
JSON_RPC_SERVER_ERROR_NODE_UNHEALTHY: -32005,
JSON_RPC_SERVER_ERROR_TRANSACTION_PRECOMPILE_VERIFICATION_FAILURE: -32006,
JSON_RPC_SERVER_ERROR_SLOT_SKIPPED: -32007,
JSON_RPC_SERVER_ERROR_NO_SNAPSHOT: -32008,
JSON_RPC_SERVER_ERROR_LONG_TERM_STORAGE_SLOT_SKIPPED: -32009,
JSON_RPC_SERVER_ERROR_KEY_EXCLUDED_FROM_SECONDARY_INDEX: -32010,
JSON_RPC_SERVER_ERROR_TRANSACTION_HISTORY_NOT_AVAILABLE: -32011,
JSON_RPC_SCAN_ERROR: -32012,
JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_LEN_MISMATCH: -32013,
JSON_RPC_SERVER_ERROR_BLOCK_STATUS_NOT_AVAILABLE_YET: -32014,
JSON_RPC_SERVER_ERROR_UNSUPPORTED_TRANSACTION_VERSION: -32015,
JSON_RPC_SERVER_ERROR_MIN_CONTEXT_SLOT_NOT_REACHED: -32016,
} as const;
export type SolanaJSONRPCErrorCodeEnum =
(typeof SolanaJSONRPCErrorCode)[keyof typeof SolanaJSONRPCErrorCode];
export class SolanaJSONRPCError extends Error {
code: SolanaJSONRPCErrorCodeEnum | unknown;
data?: any;
constructor(
{
code,
message,
data,
}: Readonly<{code: unknown; message: string; data?: any}>,
customMessage?: string,
) {
super(customMessage != null ? `${customMessage}: ${message}` : message);
this.code = code;
this.data = data;
this.name = 'SolanaJSONRPCError';
}
}

18
node_modules/@solana/web3.js/src/fee-calculator.ts generated vendored Normal file
View File

@@ -0,0 +1,18 @@
import * as BufferLayout from '@solana/buffer-layout';
/**
* https://github.com/solana-labs/solana/blob/90bedd7e067b5b8f3ddbb45da00a4e9cabb22c62/sdk/src/fee_calculator.rs#L7-L11
*
* @internal
*/
export const FeeCalculatorLayout = BufferLayout.nu64('lamportsPerSignature');
/**
* Calculator for transaction fees.
*
* @deprecated Deprecated since Solana v1.8.0.
*/
export interface FeeCalculator {
/** Cost in lamports to validate a signature. */
lamportsPerSignature: number;
}

16
node_modules/@solana/web3.js/src/fetch-impl.ts generated vendored Normal file
View File

@@ -0,0 +1,16 @@
import * as nodeFetch from 'node-fetch';
export default (typeof globalThis.fetch === 'function'
? // The Fetch API is supported experimentally in Node 17.5+ and natively in Node 18+.
globalThis.fetch
: // Otherwise use the polyfill.
async function (
input: nodeFetch.RequestInfo,
init?: nodeFetch.RequestInit,
): Promise<nodeFetch.Response> {
const processedInput =
typeof input === 'string' && input.slice(0, 2) === '//'
? 'https:' + input
: input;
return await nodeFetch.default(processedInput, init);
}) as typeof globalThis.fetch;

24
node_modules/@solana/web3.js/src/index.ts generated vendored Normal file
View File

@@ -0,0 +1,24 @@
export * from './account';
export * from './blockhash';
export * from './bpf-loader-deprecated';
export * from './bpf-loader';
export * from './connection';
export * from './epoch-schedule';
export * from './errors';
export * from './fee-calculator';
export * from './keypair';
export * from './loader';
export * from './message';
export * from './nonce-account';
export * from './programs';
export * from './publickey';
export * from './transaction';
export * from './validator-info';
export * from './vote-account';
export * from './sysvar';
export * from './utils';
/**
* There are 1-billion lamports in one SOL
*/
export const LAMPORTS_PER_SOL = 1000000000;

58
node_modules/@solana/web3.js/src/instruction.ts generated vendored Normal file
View File

@@ -0,0 +1,58 @@
import {Buffer} from 'buffer';
import * as BufferLayout from '@solana/buffer-layout';
import * as Layout from './layout';
export interface IInstructionInputData {
readonly instruction: number;
}
/**
* @internal
*/
export type InstructionType<TInputData extends IInstructionInputData> = {
/** The Instruction index (from solana upstream program) */
index: number;
/** The BufferLayout to use to build data */
layout: BufferLayout.Layout<TInputData>;
};
/**
* Populate a buffer of instruction data using an InstructionType
* @internal
*/
export function encodeData<TInputData extends IInstructionInputData>(
type: InstructionType<TInputData>,
fields?: any,
): Buffer {
const allocLength =
type.layout.span >= 0 ? type.layout.span : Layout.getAlloc(type, fields);
const data = Buffer.alloc(allocLength);
const layoutFields = Object.assign({instruction: type.index}, fields);
type.layout.encode(layoutFields, data);
return data;
}
/**
* Decode instruction data buffer using an InstructionType
* @internal
*/
export function decodeData<TInputData extends IInstructionInputData>(
type: InstructionType<TInputData>,
buffer: Buffer,
): TInputData {
let data: TInputData;
try {
data = type.layout.decode(buffer);
} catch (err) {
throw new Error('invalid instruction; ' + err);
}
if (data.instruction !== type.index) {
throw new Error(
`invalid instruction; instruction index mismatch ${data.instruction} != ${type.index}`,
);
}
return data;
}

102
node_modules/@solana/web3.js/src/keypair.ts generated vendored Normal file
View File

@@ -0,0 +1,102 @@
import {generateKeypair, getPublicKey, Ed25519Keypair} from './utils/ed25519';
import {PublicKey} from './publickey';
/**
* Keypair signer interface
*/
export interface Signer {
publicKey: PublicKey;
secretKey: Uint8Array;
}
/**
* An account keypair used for signing transactions.
*/
export class Keypair {
private _keypair: Ed25519Keypair;
/**
* Create a new keypair instance.
* Generate random keypair if no {@link Ed25519Keypair} is provided.
*
* @param {Ed25519Keypair} keypair ed25519 keypair
*/
constructor(keypair?: Ed25519Keypair) {
this._keypair = keypair ?? generateKeypair();
}
/**
* Generate a new random keypair
*
* @returns {Keypair} Keypair
*/
static generate(): Keypair {
return new Keypair(generateKeypair());
}
/**
* Create a keypair from a raw secret key byte array.
*
* This method should only be used to recreate a keypair from a previously
* generated secret key. Generating keypairs from a random seed should be done
* with the {@link Keypair.fromSeed} method.
*
* @throws error if the provided secret key is invalid and validation is not skipped.
*
* @param secretKey secret key byte array
* @param options skip secret key validation
*
* @returns {Keypair} Keypair
*/
static fromSecretKey(
secretKey: Uint8Array,
options?: {skipValidation?: boolean},
): Keypair {
if (secretKey.byteLength !== 64) {
throw new Error('bad secret key size');
}
const publicKey = secretKey.slice(32, 64);
if (!options || !options.skipValidation) {
const privateScalar = secretKey.slice(0, 32);
const computedPublicKey = getPublicKey(privateScalar);
for (let ii = 0; ii < 32; ii++) {
if (publicKey[ii] !== computedPublicKey[ii]) {
throw new Error('provided secretKey is invalid');
}
}
}
return new Keypair({publicKey, secretKey});
}
/**
* Generate a keypair from a 32 byte seed.
*
* @param seed seed byte array
*
* @returns {Keypair} Keypair
*/
static fromSeed(seed: Uint8Array): Keypair {
const publicKey = getPublicKey(seed);
const secretKey = new Uint8Array(64);
secretKey.set(seed);
secretKey.set(publicKey, 32);
return new Keypair({publicKey, secretKey});
}
/**
* The public key for this keypair
*
* @returns {PublicKey} PublicKey
*/
get publicKey(): PublicKey {
return new PublicKey(this._keypair.publicKey);
}
/**
* The raw secret key for this keypair
* @returns {Uint8Array} Secret key in an array of Uint8 bytes
*/
get secretKey(): Uint8Array {
return new Uint8Array(this._keypair.secretKey);
}
}

188
node_modules/@solana/web3.js/src/layout.ts generated vendored Normal file
View File

@@ -0,0 +1,188 @@
import {Buffer} from 'buffer';
import * as BufferLayout from '@solana/buffer-layout';
import {VoteAuthorizeWithSeedArgs} from './programs/vote';
/**
* Layout for a public key
*/
export const publicKey = (property: string = 'publicKey') => {
return BufferLayout.blob(32, property);
};
/**
* Layout for a signature
*/
export const signature = (property: string = 'signature') => {
return BufferLayout.blob(64, property);
};
/**
* Layout for a 64bit unsigned value
*/
export const uint64 = (property: string = 'uint64') => {
return BufferLayout.blob(8, property);
};
interface IRustStringShim
extends Omit<
BufferLayout.Structure<
Readonly<{
length: number;
lengthPadding: number;
chars: Uint8Array;
}>
>,
'decode' | 'encode' | 'replicate'
> {
alloc: (str: string) => number;
decode: (b: Uint8Array, offset?: number) => string;
encode: (str: string, b: Uint8Array, offset?: number) => number;
replicate: (property: string) => this;
}
/**
* Layout for a Rust String type
*/
export const rustString = (
property: string = 'string',
): BufferLayout.Layout<string> => {
const rsl = BufferLayout.struct<
Readonly<{
length?: number;
lengthPadding?: number;
chars: Uint8Array;
}>
>(
[
BufferLayout.u32('length'),
BufferLayout.u32('lengthPadding'),
BufferLayout.blob(BufferLayout.offset(BufferLayout.u32(), -8), 'chars'),
],
property,
);
const _decode = rsl.decode.bind(rsl);
const _encode = rsl.encode.bind(rsl);
const rslShim = rsl as unknown as IRustStringShim;
rslShim.decode = (b: Uint8Array, offset?: number) => {
const data = _decode(b, offset);
return data['chars'].toString();
};
rslShim.encode = (str: string, b: Uint8Array, offset?: number) => {
const data = {
chars: Buffer.from(str, 'utf8'),
};
return _encode(data, b, offset);
};
rslShim.alloc = (str: string) => {
return (
BufferLayout.u32().span +
BufferLayout.u32().span +
Buffer.from(str, 'utf8').length
);
};
return rslShim;
};
/**
* Layout for an Authorized object
*/
export const authorized = (property: string = 'authorized') => {
return BufferLayout.struct<
Readonly<{
staker: Uint8Array;
withdrawer: Uint8Array;
}>
>([publicKey('staker'), publicKey('withdrawer')], property);
};
/**
* Layout for a Lockup object
*/
export const lockup = (property: string = 'lockup') => {
return BufferLayout.struct<
Readonly<{
custodian: Uint8Array;
epoch: number;
unixTimestamp: number;
}>
>(
[
BufferLayout.ns64('unixTimestamp'),
BufferLayout.ns64('epoch'),
publicKey('custodian'),
],
property,
);
};
/**
* Layout for a VoteInit object
*/
export const voteInit = (property: string = 'voteInit') => {
return BufferLayout.struct<
Readonly<{
authorizedVoter: Uint8Array;
authorizedWithdrawer: Uint8Array;
commission: number;
nodePubkey: Uint8Array;
}>
>(
[
publicKey('nodePubkey'),
publicKey('authorizedVoter'),
publicKey('authorizedWithdrawer'),
BufferLayout.u8('commission'),
],
property,
);
};
/**
* Layout for a VoteAuthorizeWithSeedArgs object
*/
export const voteAuthorizeWithSeedArgs = (
property: string = 'voteAuthorizeWithSeedArgs',
) => {
return BufferLayout.struct<VoteAuthorizeWithSeedArgs>(
[
BufferLayout.u32('voteAuthorizationType'),
publicKey('currentAuthorityDerivedKeyOwnerPubkey'),
rustString('currentAuthorityDerivedKeySeed'),
publicKey('newAuthorized'),
],
property,
);
};
export function getAlloc(type: any, fields: any): number {
const getItemAlloc = (item: any): number => {
if (item.span >= 0) {
return item.span;
} else if (typeof item.alloc === 'function') {
return item.alloc(fields[item.property]);
} else if ('count' in item && 'elementLayout' in item) {
const field = fields[item.property];
if (Array.isArray(field)) {
return field.length * getItemAlloc(item.elementLayout);
}
} else if ('fields' in item) {
// This is a `Structure` whose size needs to be recursively measured.
return getAlloc({layout: item}, fields[item.property]);
}
// Couldn't determine allocated size of layout
return 0;
};
let alloc = 0;
type.layout.fields.forEach((item: any) => {
alloc += getItemAlloc(item);
});
return alloc;
}

267
node_modules/@solana/web3.js/src/loader.ts generated vendored Normal file
View File

@@ -0,0 +1,267 @@
import {Buffer} from 'buffer';
import * as BufferLayout from '@solana/buffer-layout';
import {PublicKey} from './publickey';
import {Transaction, PACKET_DATA_SIZE} from './transaction';
import {MS_PER_SLOT} from './timing';
import {SYSVAR_RENT_PUBKEY} from './sysvar';
import {sendAndConfirmTransaction} from './utils/send-and-confirm-transaction';
import {sleep} from './utils/sleep';
import type {Connection} from './connection';
import type {Signer} from './keypair';
import {SystemProgram} from './programs/system';
import {IInstructionInputData} from './instruction';
// Keep program chunks under PACKET_DATA_SIZE, leaving enough room for the
// rest of the Transaction fields
//
// TODO: replace 300 with a proper constant for the size of the other
// Transaction fields
const CHUNK_SIZE = PACKET_DATA_SIZE - 300;
/**
* Program loader interface
*/
export class Loader {
/**
* @internal
*/
constructor() {}
/**
* Amount of program data placed in each load Transaction
*/
static chunkSize: number = CHUNK_SIZE;
/**
* Minimum number of signatures required to load a program not including
* retries
*
* Can be used to calculate transaction fees
*/
static getMinNumSignatures(dataLength: number): number {
return (
2 * // Every transaction requires two signatures (payer + program)
(Math.ceil(dataLength / Loader.chunkSize) +
1 + // Add one for Create transaction
1) // Add one for Finalize transaction
);
}
/**
* Loads a generic program
*
* @param connection The connection to use
* @param payer System account that pays to load the program
* @param program Account to load the program into
* @param programId Public key that identifies the loader
* @param data Program octets
* @return true if program was loaded successfully, false if program was already loaded
*/
static async load(
connection: Connection,
payer: Signer,
program: Signer,
programId: PublicKey,
data: Buffer | Uint8Array | Array<number>,
): Promise<boolean> {
{
const balanceNeeded = await connection.getMinimumBalanceForRentExemption(
data.length,
);
// Fetch program account info to check if it has already been created
const programInfo = await connection.getAccountInfo(
program.publicKey,
'confirmed',
);
let transaction: Transaction | null = null;
if (programInfo !== null) {
if (programInfo.executable) {
console.error('Program load failed, account is already executable');
return false;
}
if (programInfo.data.length !== data.length) {
transaction = transaction || new Transaction();
transaction.add(
SystemProgram.allocate({
accountPubkey: program.publicKey,
space: data.length,
}),
);
}
if (!programInfo.owner.equals(programId)) {
transaction = transaction || new Transaction();
transaction.add(
SystemProgram.assign({
accountPubkey: program.publicKey,
programId,
}),
);
}
if (programInfo.lamports < balanceNeeded) {
transaction = transaction || new Transaction();
transaction.add(
SystemProgram.transfer({
fromPubkey: payer.publicKey,
toPubkey: program.publicKey,
lamports: balanceNeeded - programInfo.lamports,
}),
);
}
} else {
transaction = new Transaction().add(
SystemProgram.createAccount({
fromPubkey: payer.publicKey,
newAccountPubkey: program.publicKey,
lamports: balanceNeeded > 0 ? balanceNeeded : 1,
space: data.length,
programId,
}),
);
}
// If the account is already created correctly, skip this step
// and proceed directly to loading instructions
if (transaction !== null) {
await sendAndConfirmTransaction(
connection,
transaction,
[payer, program],
{
commitment: 'confirmed',
},
);
}
}
const dataLayout = BufferLayout.struct<
Readonly<{
bytes: number[];
bytesLength: number;
bytesLengthPadding: number;
instruction: number;
offset: number;
}>
>([
BufferLayout.u32('instruction'),
BufferLayout.u32('offset'),
BufferLayout.u32('bytesLength'),
BufferLayout.u32('bytesLengthPadding'),
BufferLayout.seq(
BufferLayout.u8('byte'),
BufferLayout.offset(BufferLayout.u32(), -8),
'bytes',
),
]);
const chunkSize = Loader.chunkSize;
let offset = 0;
let array = data;
let transactions = [];
while (array.length > 0) {
const bytes = array.slice(0, chunkSize);
const data = Buffer.alloc(chunkSize + 16);
dataLayout.encode(
{
instruction: 0, // Load instruction
offset,
bytes: bytes as number[],
bytesLength: 0,
bytesLengthPadding: 0,
},
data,
);
const transaction = new Transaction().add({
keys: [{pubkey: program.publicKey, isSigner: true, isWritable: true}],
programId,
data,
});
transactions.push(
sendAndConfirmTransaction(connection, transaction, [payer, program], {
commitment: 'confirmed',
}),
);
// Delay between sends in an attempt to reduce rate limit errors
if (connection._rpcEndpoint.includes('solana.com')) {
const REQUESTS_PER_SECOND = 4;
await sleep(1000 / REQUESTS_PER_SECOND);
}
offset += chunkSize;
array = array.slice(chunkSize);
}
await Promise.all(transactions);
// Finalize the account loaded with program data for execution
{
const dataLayout = BufferLayout.struct<IInstructionInputData>([
BufferLayout.u32('instruction'),
]);
const data = Buffer.alloc(dataLayout.span);
dataLayout.encode(
{
instruction: 1, // Finalize instruction
},
data,
);
const transaction = new Transaction().add({
keys: [
{pubkey: program.publicKey, isSigner: true, isWritable: true},
{pubkey: SYSVAR_RENT_PUBKEY, isSigner: false, isWritable: false},
],
programId,
data,
});
const deployCommitment = 'processed';
const finalizeSignature = await connection.sendTransaction(
transaction,
[payer, program],
{preflightCommitment: deployCommitment},
);
const {context, value} = await connection.confirmTransaction(
{
signature: finalizeSignature,
lastValidBlockHeight: transaction.lastValidBlockHeight!,
blockhash: transaction.recentBlockhash!,
},
deployCommitment,
);
if (value.err) {
throw new Error(
`Transaction ${finalizeSignature} failed (${JSON.stringify(value)})`,
);
}
// We prevent programs from being usable until the slot after their deployment.
// See https://github.com/solana-labs/solana/pull/29654
while (
true // eslint-disable-line no-constant-condition
) {
try {
const currentSlot = await connection.getSlot({
commitment: deployCommitment,
});
if (currentSlot > context.slot) {
break;
}
} catch {
/* empty */
}
await new Promise(resolve =>
setTimeout(resolve, Math.round(MS_PER_SLOT / 2)),
);
}
}
// success
return true;
}
}

View File

@@ -0,0 +1,79 @@
import {LoadedAddresses} from '../connection';
import {PublicKey} from '../publickey';
import {TransactionInstruction} from '../transaction';
import {MessageCompiledInstruction} from './index';
export type AccountKeysFromLookups = LoadedAddresses;
export class MessageAccountKeys {
staticAccountKeys: Array<PublicKey>;
accountKeysFromLookups?: AccountKeysFromLookups;
constructor(
staticAccountKeys: Array<PublicKey>,
accountKeysFromLookups?: AccountKeysFromLookups,
) {
this.staticAccountKeys = staticAccountKeys;
this.accountKeysFromLookups = accountKeysFromLookups;
}
keySegments(): Array<Array<PublicKey>> {
const keySegments = [this.staticAccountKeys];
if (this.accountKeysFromLookups) {
keySegments.push(this.accountKeysFromLookups.writable);
keySegments.push(this.accountKeysFromLookups.readonly);
}
return keySegments;
}
get(index: number): PublicKey | undefined {
for (const keySegment of this.keySegments()) {
if (index < keySegment.length) {
return keySegment[index];
} else {
index -= keySegment.length;
}
}
return;
}
get length(): number {
return this.keySegments().flat().length;
}
compileInstructions(
instructions: Array<TransactionInstruction>,
): Array<MessageCompiledInstruction> {
// Bail early if any account indexes would overflow a u8
const U8_MAX = 255;
if (this.length > U8_MAX + 1) {
throw new Error('Account index overflow encountered during compilation');
}
const keyIndexMap = new Map();
this.keySegments()
.flat()
.forEach((key, index) => {
keyIndexMap.set(key.toBase58(), index);
});
const findKeyIndex = (key: PublicKey) => {
const keyIndex = keyIndexMap.get(key.toBase58());
if (keyIndex === undefined)
throw new Error(
'Encountered an unknown instruction account key during compilation',
);
return keyIndex;
};
return instructions.map((instruction): MessageCompiledInstruction => {
return {
programIdIndex: findKeyIndex(instruction.programId),
accountKeyIndexes: instruction.keys.map(meta =>
findKeyIndex(meta.pubkey),
),
data: instruction.data,
};
});
}
}

View File

@@ -0,0 +1,165 @@
import {MessageHeader, MessageAddressTableLookup} from './index';
import {AccountKeysFromLookups} from './account-keys';
import {AddressLookupTableAccount} from '../programs';
import {TransactionInstruction} from '../transaction';
import assert from '../utils/assert';
import {PublicKey} from '../publickey';
export type CompiledKeyMeta = {
isSigner: boolean;
isWritable: boolean;
isInvoked: boolean;
};
type KeyMetaMap = Map<string, CompiledKeyMeta>;
export class CompiledKeys {
payer: PublicKey;
keyMetaMap: KeyMetaMap;
constructor(payer: PublicKey, keyMetaMap: KeyMetaMap) {
this.payer = payer;
this.keyMetaMap = keyMetaMap;
}
static compile(
instructions: Array<TransactionInstruction>,
payer: PublicKey,
): CompiledKeys {
const keyMetaMap: KeyMetaMap = new Map();
const getOrInsertDefault = (pubkey: PublicKey): CompiledKeyMeta => {
const address = pubkey.toBase58();
let keyMeta = keyMetaMap.get(address);
if (keyMeta === undefined) {
keyMeta = {
isSigner: false,
isWritable: false,
isInvoked: false,
};
keyMetaMap.set(address, keyMeta);
}
return keyMeta;
};
const payerKeyMeta = getOrInsertDefault(payer);
payerKeyMeta.isSigner = true;
payerKeyMeta.isWritable = true;
for (const ix of instructions) {
getOrInsertDefault(ix.programId).isInvoked = true;
for (const accountMeta of ix.keys) {
const keyMeta = getOrInsertDefault(accountMeta.pubkey);
keyMeta.isSigner ||= accountMeta.isSigner;
keyMeta.isWritable ||= accountMeta.isWritable;
}
}
return new CompiledKeys(payer, keyMetaMap);
}
getMessageComponents(): [MessageHeader, Array<PublicKey>] {
const mapEntries = [...this.keyMetaMap.entries()];
assert(mapEntries.length <= 256, 'Max static account keys length exceeded');
const writableSigners = mapEntries.filter(
([, meta]) => meta.isSigner && meta.isWritable,
);
const readonlySigners = mapEntries.filter(
([, meta]) => meta.isSigner && !meta.isWritable,
);
const writableNonSigners = mapEntries.filter(
([, meta]) => !meta.isSigner && meta.isWritable,
);
const readonlyNonSigners = mapEntries.filter(
([, meta]) => !meta.isSigner && !meta.isWritable,
);
const header: MessageHeader = {
numRequiredSignatures: writableSigners.length + readonlySigners.length,
numReadonlySignedAccounts: readonlySigners.length,
numReadonlyUnsignedAccounts: readonlyNonSigners.length,
};
// sanity checks
{
assert(
writableSigners.length > 0,
'Expected at least one writable signer key',
);
const [payerAddress] = writableSigners[0];
assert(
payerAddress === this.payer.toBase58(),
'Expected first writable signer key to be the fee payer',
);
}
const staticAccountKeys = [
...writableSigners.map(([address]) => new PublicKey(address)),
...readonlySigners.map(([address]) => new PublicKey(address)),
...writableNonSigners.map(([address]) => new PublicKey(address)),
...readonlyNonSigners.map(([address]) => new PublicKey(address)),
];
return [header, staticAccountKeys];
}
extractTableLookup(
lookupTable: AddressLookupTableAccount,
): [MessageAddressTableLookup, AccountKeysFromLookups] | undefined {
const [writableIndexes, drainedWritableKeys] =
this.drainKeysFoundInLookupTable(
lookupTable.state.addresses,
keyMeta =>
!keyMeta.isSigner && !keyMeta.isInvoked && keyMeta.isWritable,
);
const [readonlyIndexes, drainedReadonlyKeys] =
this.drainKeysFoundInLookupTable(
lookupTable.state.addresses,
keyMeta =>
!keyMeta.isSigner && !keyMeta.isInvoked && !keyMeta.isWritable,
);
// Don't extract lookup if no keys were found
if (writableIndexes.length === 0 && readonlyIndexes.length === 0) {
return;
}
return [
{
accountKey: lookupTable.key,
writableIndexes,
readonlyIndexes,
},
{
writable: drainedWritableKeys,
readonly: drainedReadonlyKeys,
},
];
}
/** @internal */
private drainKeysFoundInLookupTable(
lookupTableEntries: Array<PublicKey>,
keyMetaFilter: (keyMeta: CompiledKeyMeta) => boolean,
): [Array<number>, Array<PublicKey>] {
const lookupTableIndexes = new Array();
const drainedKeys = new Array();
for (const [address, keyMeta] of this.keyMetaMap.entries()) {
if (keyMetaFilter(keyMeta)) {
const key = new PublicKey(address);
const lookupTableIndex = lookupTableEntries.findIndex(entry =>
entry.equals(key),
);
if (lookupTableIndex >= 0) {
assert(lookupTableIndex < 256, 'Max lookup table index exceeded');
lookupTableIndexes.push(lookupTableIndex);
drainedKeys.push(key);
this.keyMetaMap.delete(address);
}
}
}
return [lookupTableIndexes, drainedKeys];
}
}

47
node_modules/@solana/web3.js/src/message/index.ts generated vendored Normal file
View File

@@ -0,0 +1,47 @@
import {PublicKey} from '../publickey';
export * from './account-keys';
// note: compiled-keys is internal and doesn't need to be exported
export * from './legacy';
export * from './versioned';
export * from './v0';
/**
* The message header, identifying signed and read-only account
*/
export type MessageHeader = {
/**
* The number of signatures required for this message to be considered valid. The
* signatures must match the first `numRequiredSignatures` of `accountKeys`.
*/
numRequiredSignatures: number;
/** The last `numReadonlySignedAccounts` of the signed keys are read-only accounts */
numReadonlySignedAccounts: number;
/** The last `numReadonlySignedAccounts` of the unsigned keys are read-only accounts */
numReadonlyUnsignedAccounts: number;
};
/**
* An address table lookup used to load additional accounts
*/
export type MessageAddressTableLookup = {
accountKey: PublicKey;
writableIndexes: Array<number>;
readonlyIndexes: Array<number>;
};
/**
* An instruction to execute by a program
*
* @property {number} programIdIndex
* @property {number[]} accountKeyIndexes
* @property {Uint8Array} data
*/
export type MessageCompiledInstruction = {
/** Index into the transaction keys array indicating the program account that executes this instruction */
programIdIndex: number;
/** Ordered indices into the transaction keys array indicating which accounts to pass to the program */
accountKeyIndexes: number[];
/** The program input data */
data: Uint8Array;
};

323
node_modules/@solana/web3.js/src/message/legacy.ts generated vendored Normal file
View File

@@ -0,0 +1,323 @@
import bs58 from 'bs58';
import {Buffer} from 'buffer';
import * as BufferLayout from '@solana/buffer-layout';
import {PublicKey, PUBLIC_KEY_LENGTH} from '../publickey';
import type {Blockhash} from '../blockhash';
import * as Layout from '../layout';
import {PACKET_DATA_SIZE, VERSION_PREFIX_MASK} from '../transaction/constants';
import * as shortvec from '../utils/shortvec-encoding';
import {toBuffer} from '../utils/to-buffer';
import {
MessageHeader,
MessageAddressTableLookup,
MessageCompiledInstruction,
} from './index';
import {TransactionInstruction} from '../transaction';
import {CompiledKeys} from './compiled-keys';
import {MessageAccountKeys} from './account-keys';
import {guardedShift, guardedSplice} from '../utils/guarded-array-utils';
/**
* An instruction to execute by a program
*
* @property {number} programIdIndex
* @property {number[]} accounts
* @property {string} data
*/
export type CompiledInstruction = {
/** Index into the transaction keys array indicating the program account that executes this instruction */
programIdIndex: number;
/** Ordered indices into the transaction keys array indicating which accounts to pass to the program */
accounts: number[];
/** The program input data encoded as base 58 */
data: string;
};
/**
* Message constructor arguments
*/
export type MessageArgs = {
/** The message header, identifying signed and read-only `accountKeys` */
header: MessageHeader;
/** All the account keys used by this transaction */
accountKeys: string[] | PublicKey[];
/** The hash of a recent ledger block */
recentBlockhash: Blockhash;
/** Instructions that will be executed in sequence and committed in one atomic transaction if all succeed. */
instructions: CompiledInstruction[];
};
export type CompileLegacyArgs = {
payerKey: PublicKey;
instructions: Array<TransactionInstruction>;
recentBlockhash: Blockhash;
};
/**
* List of instructions to be processed atomically
*/
export class Message {
header: MessageHeader;
accountKeys: PublicKey[];
recentBlockhash: Blockhash;
instructions: CompiledInstruction[];
private indexToProgramIds: Map<number, PublicKey> = new Map<
number,
PublicKey
>();
constructor(args: MessageArgs) {
this.header = args.header;
this.accountKeys = args.accountKeys.map(account => new PublicKey(account));
this.recentBlockhash = args.recentBlockhash;
this.instructions = args.instructions;
this.instructions.forEach(ix =>
this.indexToProgramIds.set(
ix.programIdIndex,
this.accountKeys[ix.programIdIndex],
),
);
}
get version(): 'legacy' {
return 'legacy';
}
get staticAccountKeys(): Array<PublicKey> {
return this.accountKeys;
}
get compiledInstructions(): Array<MessageCompiledInstruction> {
return this.instructions.map(
(ix): MessageCompiledInstruction => ({
programIdIndex: ix.programIdIndex,
accountKeyIndexes: ix.accounts,
data: bs58.decode(ix.data),
}),
);
}
get addressTableLookups(): Array<MessageAddressTableLookup> {
return [];
}
getAccountKeys(): MessageAccountKeys {
return new MessageAccountKeys(this.staticAccountKeys);
}
static compile(args: CompileLegacyArgs): Message {
const compiledKeys = CompiledKeys.compile(args.instructions, args.payerKey);
const [header, staticAccountKeys] = compiledKeys.getMessageComponents();
const accountKeys = new MessageAccountKeys(staticAccountKeys);
const instructions = accountKeys.compileInstructions(args.instructions).map(
(ix: MessageCompiledInstruction): CompiledInstruction => ({
programIdIndex: ix.programIdIndex,
accounts: ix.accountKeyIndexes,
data: bs58.encode(ix.data),
}),
);
return new Message({
header,
accountKeys: staticAccountKeys,
recentBlockhash: args.recentBlockhash,
instructions,
});
}
isAccountSigner(index: number): boolean {
return index < this.header.numRequiredSignatures;
}
isAccountWritable(index: number): boolean {
const numSignedAccounts = this.header.numRequiredSignatures;
if (index >= this.header.numRequiredSignatures) {
const unsignedAccountIndex = index - numSignedAccounts;
const numUnsignedAccounts = this.accountKeys.length - numSignedAccounts;
const numWritableUnsignedAccounts =
numUnsignedAccounts - this.header.numReadonlyUnsignedAccounts;
return unsignedAccountIndex < numWritableUnsignedAccounts;
} else {
const numWritableSignedAccounts =
numSignedAccounts - this.header.numReadonlySignedAccounts;
return index < numWritableSignedAccounts;
}
}
isProgramId(index: number): boolean {
return this.indexToProgramIds.has(index);
}
programIds(): PublicKey[] {
return [...this.indexToProgramIds.values()];
}
nonProgramIds(): PublicKey[] {
return this.accountKeys.filter((_, index) => !this.isProgramId(index));
}
serialize(): Buffer {
const numKeys = this.accountKeys.length;
let keyCount: number[] = [];
shortvec.encodeLength(keyCount, numKeys);
const instructions = this.instructions.map(instruction => {
const {accounts, programIdIndex} = instruction;
const data = Array.from(bs58.decode(instruction.data));
let keyIndicesCount: number[] = [];
shortvec.encodeLength(keyIndicesCount, accounts.length);
let dataCount: number[] = [];
shortvec.encodeLength(dataCount, data.length);
return {
programIdIndex,
keyIndicesCount: Buffer.from(keyIndicesCount),
keyIndices: accounts,
dataLength: Buffer.from(dataCount),
data,
};
});
let instructionCount: number[] = [];
shortvec.encodeLength(instructionCount, instructions.length);
let instructionBuffer = Buffer.alloc(PACKET_DATA_SIZE);
Buffer.from(instructionCount).copy(instructionBuffer);
let instructionBufferLength = instructionCount.length;
instructions.forEach(instruction => {
const instructionLayout = BufferLayout.struct<
Readonly<{
data: number[];
dataLength: Uint8Array;
keyIndices: number[];
keyIndicesCount: Uint8Array;
programIdIndex: number;
}>
>([
BufferLayout.u8('programIdIndex'),
BufferLayout.blob(
instruction.keyIndicesCount.length,
'keyIndicesCount',
),
BufferLayout.seq(
BufferLayout.u8('keyIndex'),
instruction.keyIndices.length,
'keyIndices',
),
BufferLayout.blob(instruction.dataLength.length, 'dataLength'),
BufferLayout.seq(
BufferLayout.u8('userdatum'),
instruction.data.length,
'data',
),
]);
const length = instructionLayout.encode(
instruction,
instructionBuffer,
instructionBufferLength,
);
instructionBufferLength += length;
});
instructionBuffer = instructionBuffer.slice(0, instructionBufferLength);
const signDataLayout = BufferLayout.struct<
Readonly<{
keyCount: Uint8Array;
keys: Uint8Array[];
numReadonlySignedAccounts: Uint8Array;
numReadonlyUnsignedAccounts: Uint8Array;
numRequiredSignatures: Uint8Array;
recentBlockhash: Uint8Array;
}>
>([
BufferLayout.blob(1, 'numRequiredSignatures'),
BufferLayout.blob(1, 'numReadonlySignedAccounts'),
BufferLayout.blob(1, 'numReadonlyUnsignedAccounts'),
BufferLayout.blob(keyCount.length, 'keyCount'),
BufferLayout.seq(Layout.publicKey('key'), numKeys, 'keys'),
Layout.publicKey('recentBlockhash'),
]);
const transaction = {
numRequiredSignatures: Buffer.from([this.header.numRequiredSignatures]),
numReadonlySignedAccounts: Buffer.from([
this.header.numReadonlySignedAccounts,
]),
numReadonlyUnsignedAccounts: Buffer.from([
this.header.numReadonlyUnsignedAccounts,
]),
keyCount: Buffer.from(keyCount),
keys: this.accountKeys.map(key => toBuffer(key.toBytes())),
recentBlockhash: bs58.decode(this.recentBlockhash),
};
let signData = Buffer.alloc(2048);
const length = signDataLayout.encode(transaction, signData);
instructionBuffer.copy(signData, length);
return signData.slice(0, length + instructionBuffer.length);
}
/**
* Decode a compiled message into a Message object.
*/
static from(buffer: Buffer | Uint8Array | Array<number>): Message {
// Slice up wire data
let byteArray = [...buffer];
const numRequiredSignatures = guardedShift(byteArray);
if (
numRequiredSignatures !==
(numRequiredSignatures & VERSION_PREFIX_MASK)
) {
throw new Error(
'Versioned messages must be deserialized with VersionedMessage.deserialize()',
);
}
const numReadonlySignedAccounts = guardedShift(byteArray);
const numReadonlyUnsignedAccounts = guardedShift(byteArray);
const accountCount = shortvec.decodeLength(byteArray);
let accountKeys = [];
for (let i = 0; i < accountCount; i++) {
const account = guardedSplice(byteArray, 0, PUBLIC_KEY_LENGTH);
accountKeys.push(new PublicKey(Buffer.from(account)));
}
const recentBlockhash = guardedSplice(byteArray, 0, PUBLIC_KEY_LENGTH);
const instructionCount = shortvec.decodeLength(byteArray);
let instructions: CompiledInstruction[] = [];
for (let i = 0; i < instructionCount; i++) {
const programIdIndex = guardedShift(byteArray);
const accountCount = shortvec.decodeLength(byteArray);
const accounts = guardedSplice(byteArray, 0, accountCount);
const dataLength = shortvec.decodeLength(byteArray);
const dataSlice = guardedSplice(byteArray, 0, dataLength);
const data = bs58.encode(Buffer.from(dataSlice));
instructions.push({
programIdIndex,
accounts,
data,
});
}
const messageArgs = {
header: {
numRequiredSignatures,
numReadonlySignedAccounts,
numReadonlyUnsignedAccounts,
},
recentBlockhash: bs58.encode(Buffer.from(recentBlockhash)),
accountKeys,
instructions,
};
return new Message(messageArgs);
}
}

513
node_modules/@solana/web3.js/src/message/v0.ts generated vendored Normal file
View File

@@ -0,0 +1,513 @@
import bs58 from 'bs58';
import * as BufferLayout from '@solana/buffer-layout';
import * as Layout from '../layout';
import {Blockhash} from '../blockhash';
import {
MessageHeader,
MessageAddressTableLookup,
MessageCompiledInstruction,
} from './index';
import {PublicKey, PUBLIC_KEY_LENGTH} from '../publickey';
import * as shortvec from '../utils/shortvec-encoding';
import assert from '../utils/assert';
import {PACKET_DATA_SIZE, VERSION_PREFIX_MASK} from '../transaction/constants';
import {TransactionInstruction} from '../transaction';
import {AddressLookupTableAccount} from '../programs';
import {CompiledKeys} from './compiled-keys';
import {AccountKeysFromLookups, MessageAccountKeys} from './account-keys';
import {guardedShift, guardedSplice} from '../utils/guarded-array-utils';
/**
* Message constructor arguments
*/
export type MessageV0Args = {
/** The message header, identifying signed and read-only `accountKeys` */
header: MessageHeader;
/** The static account keys used by this transaction */
staticAccountKeys: PublicKey[];
/** The hash of a recent ledger block */
recentBlockhash: Blockhash;
/** Instructions that will be executed in sequence and committed in one atomic transaction if all succeed. */
compiledInstructions: MessageCompiledInstruction[];
/** Instructions that will be executed in sequence and committed in one atomic transaction if all succeed. */
addressTableLookups: MessageAddressTableLookup[];
};
export type CompileV0Args = {
payerKey: PublicKey;
instructions: Array<TransactionInstruction>;
recentBlockhash: Blockhash;
addressLookupTableAccounts?: Array<AddressLookupTableAccount>;
};
export type GetAccountKeysArgs =
| {
accountKeysFromLookups?: AccountKeysFromLookups | null;
}
| {
addressLookupTableAccounts?: AddressLookupTableAccount[] | null;
};
export class MessageV0 {
header: MessageHeader;
staticAccountKeys: Array<PublicKey>;
recentBlockhash: Blockhash;
compiledInstructions: Array<MessageCompiledInstruction>;
addressTableLookups: Array<MessageAddressTableLookup>;
constructor(args: MessageV0Args) {
this.header = args.header;
this.staticAccountKeys = args.staticAccountKeys;
this.recentBlockhash = args.recentBlockhash;
this.compiledInstructions = args.compiledInstructions;
this.addressTableLookups = args.addressTableLookups;
}
get version(): 0 {
return 0;
}
get numAccountKeysFromLookups(): number {
let count = 0;
for (const lookup of this.addressTableLookups) {
count += lookup.readonlyIndexes.length + lookup.writableIndexes.length;
}
return count;
}
getAccountKeys(args?: GetAccountKeysArgs): MessageAccountKeys {
let accountKeysFromLookups: AccountKeysFromLookups | undefined;
if (
args &&
'accountKeysFromLookups' in args &&
args.accountKeysFromLookups
) {
if (
this.numAccountKeysFromLookups !=
args.accountKeysFromLookups.writable.length +
args.accountKeysFromLookups.readonly.length
) {
throw new Error(
'Failed to get account keys because of a mismatch in the number of account keys from lookups',
);
}
accountKeysFromLookups = args.accountKeysFromLookups;
} else if (
args &&
'addressLookupTableAccounts' in args &&
args.addressLookupTableAccounts
) {
accountKeysFromLookups = this.resolveAddressTableLookups(
args.addressLookupTableAccounts,
);
} else if (this.addressTableLookups.length > 0) {
throw new Error(
'Failed to get account keys because address table lookups were not resolved',
);
}
return new MessageAccountKeys(
this.staticAccountKeys,
accountKeysFromLookups,
);
}
isAccountSigner(index: number): boolean {
return index < this.header.numRequiredSignatures;
}
isAccountWritable(index: number): boolean {
const numSignedAccounts = this.header.numRequiredSignatures;
const numStaticAccountKeys = this.staticAccountKeys.length;
if (index >= numStaticAccountKeys) {
const lookupAccountKeysIndex = index - numStaticAccountKeys;
const numWritableLookupAccountKeys = this.addressTableLookups.reduce(
(count, lookup) => count + lookup.writableIndexes.length,
0,
);
return lookupAccountKeysIndex < numWritableLookupAccountKeys;
} else if (index >= this.header.numRequiredSignatures) {
const unsignedAccountIndex = index - numSignedAccounts;
const numUnsignedAccounts = numStaticAccountKeys - numSignedAccounts;
const numWritableUnsignedAccounts =
numUnsignedAccounts - this.header.numReadonlyUnsignedAccounts;
return unsignedAccountIndex < numWritableUnsignedAccounts;
} else {
const numWritableSignedAccounts =
numSignedAccounts - this.header.numReadonlySignedAccounts;
return index < numWritableSignedAccounts;
}
}
resolveAddressTableLookups(
addressLookupTableAccounts: AddressLookupTableAccount[],
): AccountKeysFromLookups {
const accountKeysFromLookups: AccountKeysFromLookups = {
writable: [],
readonly: [],
};
for (const tableLookup of this.addressTableLookups) {
const tableAccount = addressLookupTableAccounts.find(account =>
account.key.equals(tableLookup.accountKey),
);
if (!tableAccount) {
throw new Error(
`Failed to find address lookup table account for table key ${tableLookup.accountKey.toBase58()}`,
);
}
for (const index of tableLookup.writableIndexes) {
if (index < tableAccount.state.addresses.length) {
accountKeysFromLookups.writable.push(
tableAccount.state.addresses[index],
);
} else {
throw new Error(
`Failed to find address for index ${index} in address lookup table ${tableLookup.accountKey.toBase58()}`,
);
}
}
for (const index of tableLookup.readonlyIndexes) {
if (index < tableAccount.state.addresses.length) {
accountKeysFromLookups.readonly.push(
tableAccount.state.addresses[index],
);
} else {
throw new Error(
`Failed to find address for index ${index} in address lookup table ${tableLookup.accountKey.toBase58()}`,
);
}
}
}
return accountKeysFromLookups;
}
static compile(args: CompileV0Args): MessageV0 {
const compiledKeys = CompiledKeys.compile(args.instructions, args.payerKey);
const addressTableLookups = new Array<MessageAddressTableLookup>();
const accountKeysFromLookups: AccountKeysFromLookups = {
writable: new Array(),
readonly: new Array(),
};
const lookupTableAccounts = args.addressLookupTableAccounts || [];
for (const lookupTable of lookupTableAccounts) {
const extractResult = compiledKeys.extractTableLookup(lookupTable);
if (extractResult !== undefined) {
const [addressTableLookup, {writable, readonly}] = extractResult;
addressTableLookups.push(addressTableLookup);
accountKeysFromLookups.writable.push(...writable);
accountKeysFromLookups.readonly.push(...readonly);
}
}
const [header, staticAccountKeys] = compiledKeys.getMessageComponents();
const accountKeys = new MessageAccountKeys(
staticAccountKeys,
accountKeysFromLookups,
);
const compiledInstructions = accountKeys.compileInstructions(
args.instructions,
);
return new MessageV0({
header,
staticAccountKeys,
recentBlockhash: args.recentBlockhash,
compiledInstructions,
addressTableLookups,
});
}
serialize(): Uint8Array {
const encodedStaticAccountKeysLength = Array<number>();
shortvec.encodeLength(
encodedStaticAccountKeysLength,
this.staticAccountKeys.length,
);
const serializedInstructions = this.serializeInstructions();
const encodedInstructionsLength = Array<number>();
shortvec.encodeLength(
encodedInstructionsLength,
this.compiledInstructions.length,
);
const serializedAddressTableLookups = this.serializeAddressTableLookups();
const encodedAddressTableLookupsLength = Array<number>();
shortvec.encodeLength(
encodedAddressTableLookupsLength,
this.addressTableLookups.length,
);
const messageLayout = BufferLayout.struct<{
prefix: number;
header: MessageHeader;
staticAccountKeysLength: Uint8Array;
staticAccountKeys: Array<Uint8Array>;
recentBlockhash: Uint8Array;
instructionsLength: Uint8Array;
serializedInstructions: Uint8Array;
addressTableLookupsLength: Uint8Array;
serializedAddressTableLookups: Uint8Array;
}>([
BufferLayout.u8('prefix'),
BufferLayout.struct<MessageHeader>(
[
BufferLayout.u8('numRequiredSignatures'),
BufferLayout.u8('numReadonlySignedAccounts'),
BufferLayout.u8('numReadonlyUnsignedAccounts'),
],
'header',
),
BufferLayout.blob(
encodedStaticAccountKeysLength.length,
'staticAccountKeysLength',
),
BufferLayout.seq(
Layout.publicKey(),
this.staticAccountKeys.length,
'staticAccountKeys',
),
Layout.publicKey('recentBlockhash'),
BufferLayout.blob(encodedInstructionsLength.length, 'instructionsLength'),
BufferLayout.blob(
serializedInstructions.length,
'serializedInstructions',
),
BufferLayout.blob(
encodedAddressTableLookupsLength.length,
'addressTableLookupsLength',
),
BufferLayout.blob(
serializedAddressTableLookups.length,
'serializedAddressTableLookups',
),
]);
const serializedMessage = new Uint8Array(PACKET_DATA_SIZE);
const MESSAGE_VERSION_0_PREFIX = 1 << 7;
const serializedMessageLength = messageLayout.encode(
{
prefix: MESSAGE_VERSION_0_PREFIX,
header: this.header,
staticAccountKeysLength: new Uint8Array(encodedStaticAccountKeysLength),
staticAccountKeys: this.staticAccountKeys.map(key => key.toBytes()),
recentBlockhash: bs58.decode(this.recentBlockhash),
instructionsLength: new Uint8Array(encodedInstructionsLength),
serializedInstructions,
addressTableLookupsLength: new Uint8Array(
encodedAddressTableLookupsLength,
),
serializedAddressTableLookups,
},
serializedMessage,
);
return serializedMessage.slice(0, serializedMessageLength);
}
private serializeInstructions(): Uint8Array {
let serializedLength = 0;
const serializedInstructions = new Uint8Array(PACKET_DATA_SIZE);
for (const instruction of this.compiledInstructions) {
const encodedAccountKeyIndexesLength = Array<number>();
shortvec.encodeLength(
encodedAccountKeyIndexesLength,
instruction.accountKeyIndexes.length,
);
const encodedDataLength = Array<number>();
shortvec.encodeLength(encodedDataLength, instruction.data.length);
const instructionLayout = BufferLayout.struct<{
programIdIndex: number;
encodedAccountKeyIndexesLength: Uint8Array;
accountKeyIndexes: number[];
encodedDataLength: Uint8Array;
data: Uint8Array;
}>([
BufferLayout.u8('programIdIndex'),
BufferLayout.blob(
encodedAccountKeyIndexesLength.length,
'encodedAccountKeyIndexesLength',
),
BufferLayout.seq(
BufferLayout.u8(),
instruction.accountKeyIndexes.length,
'accountKeyIndexes',
),
BufferLayout.blob(encodedDataLength.length, 'encodedDataLength'),
BufferLayout.blob(instruction.data.length, 'data'),
]);
serializedLength += instructionLayout.encode(
{
programIdIndex: instruction.programIdIndex,
encodedAccountKeyIndexesLength: new Uint8Array(
encodedAccountKeyIndexesLength,
),
accountKeyIndexes: instruction.accountKeyIndexes,
encodedDataLength: new Uint8Array(encodedDataLength),
data: instruction.data,
},
serializedInstructions,
serializedLength,
);
}
return serializedInstructions.slice(0, serializedLength);
}
private serializeAddressTableLookups(): Uint8Array {
let serializedLength = 0;
const serializedAddressTableLookups = new Uint8Array(PACKET_DATA_SIZE);
for (const lookup of this.addressTableLookups) {
const encodedWritableIndexesLength = Array<number>();
shortvec.encodeLength(
encodedWritableIndexesLength,
lookup.writableIndexes.length,
);
const encodedReadonlyIndexesLength = Array<number>();
shortvec.encodeLength(
encodedReadonlyIndexesLength,
lookup.readonlyIndexes.length,
);
const addressTableLookupLayout = BufferLayout.struct<{
accountKey: Uint8Array;
encodedWritableIndexesLength: Uint8Array;
writableIndexes: number[];
encodedReadonlyIndexesLength: Uint8Array;
readonlyIndexes: number[];
}>([
Layout.publicKey('accountKey'),
BufferLayout.blob(
encodedWritableIndexesLength.length,
'encodedWritableIndexesLength',
),
BufferLayout.seq(
BufferLayout.u8(),
lookup.writableIndexes.length,
'writableIndexes',
),
BufferLayout.blob(
encodedReadonlyIndexesLength.length,
'encodedReadonlyIndexesLength',
),
BufferLayout.seq(
BufferLayout.u8(),
lookup.readonlyIndexes.length,
'readonlyIndexes',
),
]);
serializedLength += addressTableLookupLayout.encode(
{
accountKey: lookup.accountKey.toBytes(),
encodedWritableIndexesLength: new Uint8Array(
encodedWritableIndexesLength,
),
writableIndexes: lookup.writableIndexes,
encodedReadonlyIndexesLength: new Uint8Array(
encodedReadonlyIndexesLength,
),
readonlyIndexes: lookup.readonlyIndexes,
},
serializedAddressTableLookups,
serializedLength,
);
}
return serializedAddressTableLookups.slice(0, serializedLength);
}
static deserialize(serializedMessage: Uint8Array): MessageV0 {
let byteArray = [...serializedMessage];
const prefix = guardedShift(byteArray);
const maskedPrefix = prefix & VERSION_PREFIX_MASK;
assert(
prefix !== maskedPrefix,
`Expected versioned message but received legacy message`,
);
const version = maskedPrefix;
assert(
version === 0,
`Expected versioned message with version 0 but found version ${version}`,
);
const header: MessageHeader = {
numRequiredSignatures: guardedShift(byteArray),
numReadonlySignedAccounts: guardedShift(byteArray),
numReadonlyUnsignedAccounts: guardedShift(byteArray),
};
const staticAccountKeys = [];
const staticAccountKeysLength = shortvec.decodeLength(byteArray);
for (let i = 0; i < staticAccountKeysLength; i++) {
staticAccountKeys.push(
new PublicKey(guardedSplice(byteArray, 0, PUBLIC_KEY_LENGTH)),
);
}
const recentBlockhash = bs58.encode(
guardedSplice(byteArray, 0, PUBLIC_KEY_LENGTH),
);
const instructionCount = shortvec.decodeLength(byteArray);
const compiledInstructions: MessageCompiledInstruction[] = [];
for (let i = 0; i < instructionCount; i++) {
const programIdIndex = guardedShift(byteArray);
const accountKeyIndexesLength = shortvec.decodeLength(byteArray);
const accountKeyIndexes = guardedSplice(
byteArray,
0,
accountKeyIndexesLength,
);
const dataLength = shortvec.decodeLength(byteArray);
const data = new Uint8Array(guardedSplice(byteArray, 0, dataLength));
compiledInstructions.push({
programIdIndex,
accountKeyIndexes,
data,
});
}
const addressTableLookupsCount = shortvec.decodeLength(byteArray);
const addressTableLookups: MessageAddressTableLookup[] = [];
for (let i = 0; i < addressTableLookupsCount; i++) {
const accountKey = new PublicKey(
guardedSplice(byteArray, 0, PUBLIC_KEY_LENGTH),
);
const writableIndexesLength = shortvec.decodeLength(byteArray);
const writableIndexes = guardedSplice(
byteArray,
0,
writableIndexesLength,
);
const readonlyIndexesLength = shortvec.decodeLength(byteArray);
const readonlyIndexes = guardedSplice(
byteArray,
0,
readonlyIndexesLength,
);
addressTableLookups.push({
accountKey,
writableIndexes,
readonlyIndexes,
});
}
return new MessageV0({
header,
staticAccountKeys,
recentBlockhash,
compiledInstructions,
addressTableLookups,
});
}
}

36
node_modules/@solana/web3.js/src/message/versioned.ts generated vendored Normal file
View File

@@ -0,0 +1,36 @@
import {VERSION_PREFIX_MASK} from '../transaction/constants';
import {Message} from './legacy';
import {MessageV0} from './v0';
export type VersionedMessage = Message | MessageV0;
// eslint-disable-next-line no-redeclare
export const VersionedMessage = {
deserializeMessageVersion(serializedMessage: Uint8Array): 'legacy' | number {
const prefix = serializedMessage[0];
const maskedPrefix = prefix & VERSION_PREFIX_MASK;
// if the highest bit of the prefix is not set, the message is not versioned
if (maskedPrefix === prefix) {
return 'legacy';
}
// the lower 7 bits of the prefix indicate the message version
return maskedPrefix;
},
deserialize: (serializedMessage: Uint8Array): VersionedMessage => {
const version =
VersionedMessage.deserializeMessageVersion(serializedMessage);
if (version === 'legacy') {
return Message.from(serializedMessage);
}
if (version === 0) {
return MessageV0.deserialize(serializedMessage);
} else {
throw new Error(
`Transaction message version ${version} deserialization is not supported`,
);
}
},
};

82
node_modules/@solana/web3.js/src/nonce-account.ts generated vendored Normal file
View File

@@ -0,0 +1,82 @@
import * as BufferLayout from '@solana/buffer-layout';
import {Buffer} from 'buffer';
import * as Layout from './layout';
import {PublicKey} from './publickey';
import type {FeeCalculator} from './fee-calculator';
import {FeeCalculatorLayout} from './fee-calculator';
import {toBuffer} from './utils/to-buffer';
/**
* See https://github.com/solana-labs/solana/blob/0ea2843ec9cdc517572b8e62c959f41b55cf4453/sdk/src/nonce_state.rs#L29-L32
*
* @internal
*/
const NonceAccountLayout = BufferLayout.struct<
Readonly<{
authorizedPubkey: Uint8Array;
feeCalculator: Readonly<{
lamportsPerSignature: number;
}>;
nonce: Uint8Array;
state: number;
version: number;
}>
>([
BufferLayout.u32('version'),
BufferLayout.u32('state'),
Layout.publicKey('authorizedPubkey'),
Layout.publicKey('nonce'),
BufferLayout.struct<Readonly<{lamportsPerSignature: number}>>(
[FeeCalculatorLayout],
'feeCalculator',
),
]);
export const NONCE_ACCOUNT_LENGTH = NonceAccountLayout.span;
/**
* A durable nonce is a 32 byte value encoded as a base58 string.
*/
export type DurableNonce = string;
type NonceAccountArgs = {
authorizedPubkey: PublicKey;
nonce: DurableNonce;
feeCalculator: FeeCalculator;
};
/**
* NonceAccount class
*/
export class NonceAccount {
authorizedPubkey: PublicKey;
nonce: DurableNonce;
feeCalculator: FeeCalculator;
/**
* @internal
*/
constructor(args: NonceAccountArgs) {
this.authorizedPubkey = args.authorizedPubkey;
this.nonce = args.nonce;
this.feeCalculator = args.feeCalculator;
}
/**
* Deserialize NonceAccount from the account data.
*
* @param buffer account data
* @return NonceAccount
*/
static fromAccountData(
buffer: Buffer | Uint8Array | Array<number>,
): NonceAccount {
const nonceAccount = NonceAccountLayout.decode(toBuffer(buffer), 0);
return new NonceAccount({
authorizedPubkey: new PublicKey(nonceAccount.authorizedPubkey),
nonce: new PublicKey(nonceAccount.nonce).toString(),
feeCalculator: nonceAccount.feeCalculator,
});
}
}

View File

@@ -0,0 +1,438 @@
import * as BufferLayout from '@solana/buffer-layout';
import {getU64Encoder} from '@solana/codecs-numbers';
import * as Layout from '../../layout';
import {PublicKey} from '../../publickey';
import * as bigintLayout from '../../utils/bigint';
import {SystemProgram} from '../system';
import {TransactionInstruction} from '../../transaction';
import {decodeData, encodeData, IInstructionInputData} from '../../instruction';
export * from './state';
export type CreateLookupTableParams = {
/** Account used to derive and control the new address lookup table. */
authority: PublicKey;
/** Account that will fund the new address lookup table. */
payer: PublicKey;
/** A recent slot must be used in the derivation path for each initialized table. */
recentSlot: bigint | number;
};
export type FreezeLookupTableParams = {
/** Address lookup table account to freeze. */
lookupTable: PublicKey;
/** Account which is the current authority. */
authority: PublicKey;
};
export type ExtendLookupTableParams = {
/** Address lookup table account to extend. */
lookupTable: PublicKey;
/** Account which is the current authority. */
authority: PublicKey;
/** Account that will fund the table reallocation.
* Not required if the reallocation has already been funded. */
payer?: PublicKey;
/** List of Public Keys to be added to the lookup table. */
addresses: Array<PublicKey>;
};
export type DeactivateLookupTableParams = {
/** Address lookup table account to deactivate. */
lookupTable: PublicKey;
/** Account which is the current authority. */
authority: PublicKey;
};
export type CloseLookupTableParams = {
/** Address lookup table account to close. */
lookupTable: PublicKey;
/** Account which is the current authority. */
authority: PublicKey;
/** Recipient of closed account lamports. */
recipient: PublicKey;
};
/**
* An enumeration of valid LookupTableInstructionType's
*/
export type LookupTableInstructionType =
| 'CreateLookupTable'
| 'ExtendLookupTable'
| 'CloseLookupTable'
| 'FreezeLookupTable'
| 'DeactivateLookupTable';
type LookupTableInstructionInputData = {
CreateLookupTable: IInstructionInputData &
Readonly<{
recentSlot: bigint;
bumpSeed: number;
}>;
FreezeLookupTable: IInstructionInputData;
ExtendLookupTable: IInstructionInputData &
Readonly<{
numberOfAddresses: bigint;
addresses: Array<Uint8Array>;
}>;
DeactivateLookupTable: IInstructionInputData;
CloseLookupTable: IInstructionInputData;
};
/**
* An enumeration of valid address lookup table InstructionType's
* @internal
*/
export const LOOKUP_TABLE_INSTRUCTION_LAYOUTS = Object.freeze({
CreateLookupTable: {
index: 0,
layout: BufferLayout.struct<
LookupTableInstructionInputData['CreateLookupTable']
>([
BufferLayout.u32('instruction'),
bigintLayout.u64('recentSlot'),
BufferLayout.u8('bumpSeed'),
]),
},
FreezeLookupTable: {
index: 1,
layout: BufferLayout.struct<
LookupTableInstructionInputData['FreezeLookupTable']
>([BufferLayout.u32('instruction')]),
},
ExtendLookupTable: {
index: 2,
layout: BufferLayout.struct<
LookupTableInstructionInputData['ExtendLookupTable']
>([
BufferLayout.u32('instruction'),
bigintLayout.u64(),
BufferLayout.seq(
Layout.publicKey(),
BufferLayout.offset(BufferLayout.u32(), -8),
'addresses',
),
]),
},
DeactivateLookupTable: {
index: 3,
layout: BufferLayout.struct<
LookupTableInstructionInputData['DeactivateLookupTable']
>([BufferLayout.u32('instruction')]),
},
CloseLookupTable: {
index: 4,
layout: BufferLayout.struct<
LookupTableInstructionInputData['CloseLookupTable']
>([BufferLayout.u32('instruction')]),
},
});
export class AddressLookupTableInstruction {
/**
* @internal
*/
constructor() {}
static decodeInstructionType(
instruction: TransactionInstruction,
): LookupTableInstructionType {
this.checkProgramId(instruction.programId);
const instructionTypeLayout = BufferLayout.u32('instruction');
const index = instructionTypeLayout.decode(instruction.data);
let type: LookupTableInstructionType | undefined;
for (const [layoutType, layout] of Object.entries(
LOOKUP_TABLE_INSTRUCTION_LAYOUTS,
)) {
if ((layout as any).index == index) {
type = layoutType as LookupTableInstructionType;
break;
}
}
if (!type) {
throw new Error(
'Invalid Instruction. Should be a LookupTable Instruction',
);
}
return type;
}
static decodeCreateLookupTable(
instruction: TransactionInstruction,
): CreateLookupTableParams {
this.checkProgramId(instruction.programId);
this.checkKeysLength(instruction.keys, 4);
const {recentSlot} = decodeData(
LOOKUP_TABLE_INSTRUCTION_LAYOUTS.CreateLookupTable,
instruction.data,
);
return {
authority: instruction.keys[1].pubkey,
payer: instruction.keys[2].pubkey,
recentSlot: Number(recentSlot),
};
}
static decodeExtendLookupTable(
instruction: TransactionInstruction,
): ExtendLookupTableParams {
this.checkProgramId(instruction.programId);
if (instruction.keys.length < 2) {
throw new Error(
`invalid instruction; found ${instruction.keys.length} keys, expected at least 2`,
);
}
const {addresses} = decodeData(
LOOKUP_TABLE_INSTRUCTION_LAYOUTS.ExtendLookupTable,
instruction.data,
);
return {
lookupTable: instruction.keys[0].pubkey,
authority: instruction.keys[1].pubkey,
payer:
instruction.keys.length > 2 ? instruction.keys[2].pubkey : undefined,
addresses: addresses.map(buffer => new PublicKey(buffer)),
};
}
static decodeCloseLookupTable(
instruction: TransactionInstruction,
): CloseLookupTableParams {
this.checkProgramId(instruction.programId);
this.checkKeysLength(instruction.keys, 3);
return {
lookupTable: instruction.keys[0].pubkey,
authority: instruction.keys[1].pubkey,
recipient: instruction.keys[2].pubkey,
};
}
static decodeFreezeLookupTable(
instruction: TransactionInstruction,
): FreezeLookupTableParams {
this.checkProgramId(instruction.programId);
this.checkKeysLength(instruction.keys, 2);
return {
lookupTable: instruction.keys[0].pubkey,
authority: instruction.keys[1].pubkey,
};
}
static decodeDeactivateLookupTable(
instruction: TransactionInstruction,
): DeactivateLookupTableParams {
this.checkProgramId(instruction.programId);
this.checkKeysLength(instruction.keys, 2);
return {
lookupTable: instruction.keys[0].pubkey,
authority: instruction.keys[1].pubkey,
};
}
/**
* @internal
*/
static checkProgramId(programId: PublicKey) {
if (!programId.equals(AddressLookupTableProgram.programId)) {
throw new Error(
'invalid instruction; programId is not AddressLookupTable Program',
);
}
}
/**
* @internal
*/
static checkKeysLength(keys: Array<any>, expectedLength: number) {
if (keys.length < expectedLength) {
throw new Error(
`invalid instruction; found ${keys.length} keys, expected at least ${expectedLength}`,
);
}
}
}
export class AddressLookupTableProgram {
/**
* @internal
*/
constructor() {}
static programId: PublicKey = new PublicKey(
'AddressLookupTab1e1111111111111111111111111',
);
static createLookupTable(params: CreateLookupTableParams) {
const [lookupTableAddress, bumpSeed] = PublicKey.findProgramAddressSync(
[
params.authority.toBuffer(),
getU64Encoder().encode(params.recentSlot) as Uint8Array,
],
this.programId,
);
const type = LOOKUP_TABLE_INSTRUCTION_LAYOUTS.CreateLookupTable;
const data = encodeData(type, {
recentSlot: BigInt(params.recentSlot),
bumpSeed: bumpSeed,
});
const keys = [
{
pubkey: lookupTableAddress,
isSigner: false,
isWritable: true,
},
{
pubkey: params.authority,
isSigner: true,
isWritable: false,
},
{
pubkey: params.payer,
isSigner: true,
isWritable: true,
},
{
pubkey: SystemProgram.programId,
isSigner: false,
isWritable: false,
},
];
return [
new TransactionInstruction({
programId: this.programId,
keys: keys,
data: data,
}),
lookupTableAddress,
] as [TransactionInstruction, PublicKey];
}
static freezeLookupTable(params: FreezeLookupTableParams) {
const type = LOOKUP_TABLE_INSTRUCTION_LAYOUTS.FreezeLookupTable;
const data = encodeData(type);
const keys = [
{
pubkey: params.lookupTable,
isSigner: false,
isWritable: true,
},
{
pubkey: params.authority,
isSigner: true,
isWritable: false,
},
];
return new TransactionInstruction({
programId: this.programId,
keys: keys,
data: data,
});
}
static extendLookupTable(params: ExtendLookupTableParams) {
const type = LOOKUP_TABLE_INSTRUCTION_LAYOUTS.ExtendLookupTable;
const data = encodeData(type, {
addresses: params.addresses.map(addr => addr.toBytes()),
});
const keys = [
{
pubkey: params.lookupTable,
isSigner: false,
isWritable: true,
},
{
pubkey: params.authority,
isSigner: true,
isWritable: false,
},
];
if (params.payer) {
keys.push(
{
pubkey: params.payer,
isSigner: true,
isWritable: true,
},
{
pubkey: SystemProgram.programId,
isSigner: false,
isWritable: false,
},
);
}
return new TransactionInstruction({
programId: this.programId,
keys: keys,
data: data,
});
}
static deactivateLookupTable(params: DeactivateLookupTableParams) {
const type = LOOKUP_TABLE_INSTRUCTION_LAYOUTS.DeactivateLookupTable;
const data = encodeData(type);
const keys = [
{
pubkey: params.lookupTable,
isSigner: false,
isWritable: true,
},
{
pubkey: params.authority,
isSigner: true,
isWritable: false,
},
];
return new TransactionInstruction({
programId: this.programId,
keys: keys,
data: data,
});
}
static closeLookupTable(params: CloseLookupTableParams) {
const type = LOOKUP_TABLE_INSTRUCTION_LAYOUTS.CloseLookupTable;
const data = encodeData(type);
const keys = [
{
pubkey: params.lookupTable,
isSigner: false,
isWritable: true,
},
{
pubkey: params.authority,
isSigner: true,
isWritable: false,
},
{
pubkey: params.recipient,
isSigner: false,
isWritable: true,
},
];
return new TransactionInstruction({
programId: this.programId,
keys: keys,
data: data,
});
}
}

View File

@@ -0,0 +1,84 @@
import * as BufferLayout from '@solana/buffer-layout';
import assert from '../../utils/assert';
import * as Layout from '../../layout';
import {PublicKey} from '../../publickey';
import {u64} from '../../utils/bigint';
import {decodeData} from '../../account-data';
export type AddressLookupTableState = {
deactivationSlot: bigint;
lastExtendedSlot: number;
lastExtendedSlotStartIndex: number;
authority?: PublicKey;
addresses: Array<PublicKey>;
};
export type AddressLookupTableAccountArgs = {
key: PublicKey;
state: AddressLookupTableState;
};
/// The serialized size of lookup table metadata
const LOOKUP_TABLE_META_SIZE = 56;
export class AddressLookupTableAccount {
key: PublicKey;
state: AddressLookupTableState;
constructor(args: AddressLookupTableAccountArgs) {
this.key = args.key;
this.state = args.state;
}
isActive(): boolean {
const U64_MAX = BigInt('0xffffffffffffffff');
return this.state.deactivationSlot === U64_MAX;
}
static deserialize(accountData: Uint8Array): AddressLookupTableState {
const meta = decodeData(LookupTableMetaLayout, accountData);
const serializedAddressesLen = accountData.length - LOOKUP_TABLE_META_SIZE;
assert(serializedAddressesLen >= 0, 'lookup table is invalid');
assert(serializedAddressesLen % 32 === 0, 'lookup table is invalid');
const numSerializedAddresses = serializedAddressesLen / 32;
const {addresses} = BufferLayout.struct<{addresses: Array<Uint8Array>}>([
BufferLayout.seq(Layout.publicKey(), numSerializedAddresses, 'addresses'),
]).decode(accountData.slice(LOOKUP_TABLE_META_SIZE));
return {
deactivationSlot: meta.deactivationSlot,
lastExtendedSlot: meta.lastExtendedSlot,
lastExtendedSlotStartIndex: meta.lastExtendedStartIndex,
authority:
meta.authority.length !== 0
? new PublicKey(meta.authority[0])
: undefined,
addresses: addresses.map(address => new PublicKey(address)),
};
}
}
const LookupTableMetaLayout = {
index: 1,
layout: BufferLayout.struct<{
typeIndex: number;
deactivationSlot: bigint;
lastExtendedSlot: number;
lastExtendedStartIndex: number;
authority: Array<Uint8Array>;
}>([
BufferLayout.u32('typeIndex'),
u64('deactivationSlot'),
BufferLayout.nu64('lastExtendedSlot'),
BufferLayout.u8('lastExtendedStartIndex'),
BufferLayout.u8(), // option
BufferLayout.seq(
Layout.publicKey(),
BufferLayout.offset(BufferLayout.u8(), -1),
'authority',
),
]),
};

View File

@@ -0,0 +1,281 @@
import * as BufferLayout from '@solana/buffer-layout';
import {
encodeData,
decodeData,
InstructionType,
IInstructionInputData,
} from '../instruction';
import {PublicKey} from '../publickey';
import {TransactionInstruction} from '../transaction';
import {u64} from '../utils/bigint';
/**
* Compute Budget Instruction class
*/
export class ComputeBudgetInstruction {
/**
* @internal
*/
constructor() {}
/**
* Decode a compute budget instruction and retrieve the instruction type.
*/
static decodeInstructionType(
instruction: TransactionInstruction,
): ComputeBudgetInstructionType {
this.checkProgramId(instruction.programId);
const instructionTypeLayout = BufferLayout.u8('instruction');
const typeIndex = instructionTypeLayout.decode(instruction.data);
let type: ComputeBudgetInstructionType | undefined;
for (const [ixType, layout] of Object.entries(
COMPUTE_BUDGET_INSTRUCTION_LAYOUTS,
)) {
if (layout.index == typeIndex) {
type = ixType as ComputeBudgetInstructionType;
break;
}
}
if (!type) {
throw new Error(
'Instruction type incorrect; not a ComputeBudgetInstruction',
);
}
return type;
}
/**
* Decode request units compute budget instruction and retrieve the instruction params.
*/
static decodeRequestUnits(
instruction: TransactionInstruction,
): RequestUnitsParams {
this.checkProgramId(instruction.programId);
const {units, additionalFee} = decodeData(
COMPUTE_BUDGET_INSTRUCTION_LAYOUTS.RequestUnits,
instruction.data,
);
return {units, additionalFee};
}
/**
* Decode request heap frame compute budget instruction and retrieve the instruction params.
*/
static decodeRequestHeapFrame(
instruction: TransactionInstruction,
): RequestHeapFrameParams {
this.checkProgramId(instruction.programId);
const {bytes} = decodeData(
COMPUTE_BUDGET_INSTRUCTION_LAYOUTS.RequestHeapFrame,
instruction.data,
);
return {bytes};
}
/**
* Decode set compute unit limit compute budget instruction and retrieve the instruction params.
*/
static decodeSetComputeUnitLimit(
instruction: TransactionInstruction,
): SetComputeUnitLimitParams {
this.checkProgramId(instruction.programId);
const {units} = decodeData(
COMPUTE_BUDGET_INSTRUCTION_LAYOUTS.SetComputeUnitLimit,
instruction.data,
);
return {units};
}
/**
* Decode set compute unit price compute budget instruction and retrieve the instruction params.
*/
static decodeSetComputeUnitPrice(
instruction: TransactionInstruction,
): SetComputeUnitPriceParams {
this.checkProgramId(instruction.programId);
const {microLamports} = decodeData(
COMPUTE_BUDGET_INSTRUCTION_LAYOUTS.SetComputeUnitPrice,
instruction.data,
);
return {microLamports};
}
/**
* @internal
*/
static checkProgramId(programId: PublicKey) {
if (!programId.equals(ComputeBudgetProgram.programId)) {
throw new Error(
'invalid instruction; programId is not ComputeBudgetProgram',
);
}
}
}
/**
* An enumeration of valid ComputeBudgetInstructionType's
*/
export type ComputeBudgetInstructionType =
// FIXME
// It would be preferable for this type to be `keyof ComputeBudgetInstructionInputData`
// but Typedoc does not transpile `keyof` expressions.
// See https://github.com/TypeStrong/typedoc/issues/1894
| 'RequestUnits'
| 'RequestHeapFrame'
| 'SetComputeUnitLimit'
| 'SetComputeUnitPrice';
type ComputeBudgetInstructionInputData = {
RequestUnits: IInstructionInputData & Readonly<RequestUnitsParams>;
RequestHeapFrame: IInstructionInputData & Readonly<RequestHeapFrameParams>;
SetComputeUnitLimit: IInstructionInputData &
Readonly<SetComputeUnitLimitParams>;
SetComputeUnitPrice: IInstructionInputData &
Readonly<SetComputeUnitPriceParams>;
};
/**
* Request units instruction params
*/
export interface RequestUnitsParams {
/** Units to request for transaction-wide compute */
units: number;
/** Prioritization fee lamports */
additionalFee: number;
}
/**
* Request heap frame instruction params
*/
export type RequestHeapFrameParams = {
/** Requested transaction-wide program heap size in bytes. Must be multiple of 1024. Applies to each program, including CPIs. */
bytes: number;
};
/**
* Set compute unit limit instruction params
*/
export interface SetComputeUnitLimitParams {
/** Transaction-wide compute unit limit */
units: number;
}
/**
* Set compute unit price instruction params
*/
export interface SetComputeUnitPriceParams {
/** Transaction compute unit price used for prioritization fees */
microLamports: number | bigint;
}
/**
* An enumeration of valid ComputeBudget InstructionType's
* @internal
*/
export const COMPUTE_BUDGET_INSTRUCTION_LAYOUTS = Object.freeze<{
[Instruction in ComputeBudgetInstructionType]: InstructionType<
ComputeBudgetInstructionInputData[Instruction]
>;
}>({
RequestUnits: {
index: 0,
layout: BufferLayout.struct<
ComputeBudgetInstructionInputData['RequestUnits']
>([
BufferLayout.u8('instruction'),
BufferLayout.u32('units'),
BufferLayout.u32('additionalFee'),
]),
},
RequestHeapFrame: {
index: 1,
layout: BufferLayout.struct<
ComputeBudgetInstructionInputData['RequestHeapFrame']
>([BufferLayout.u8('instruction'), BufferLayout.u32('bytes')]),
},
SetComputeUnitLimit: {
index: 2,
layout: BufferLayout.struct<
ComputeBudgetInstructionInputData['SetComputeUnitLimit']
>([BufferLayout.u8('instruction'), BufferLayout.u32('units')]),
},
SetComputeUnitPrice: {
index: 3,
layout: BufferLayout.struct<
ComputeBudgetInstructionInputData['SetComputeUnitPrice']
>([BufferLayout.u8('instruction'), u64('microLamports')]),
},
});
/**
* Factory class for transaction instructions to interact with the Compute Budget program
*/
export class ComputeBudgetProgram {
/**
* @internal
*/
constructor() {}
/**
* Public key that identifies the Compute Budget program
*/
static programId: PublicKey = new PublicKey(
'ComputeBudget111111111111111111111111111111',
);
/**
* @deprecated Instead, call {@link setComputeUnitLimit} and/or {@link setComputeUnitPrice}
*/
static requestUnits(params: RequestUnitsParams): TransactionInstruction {
const type = COMPUTE_BUDGET_INSTRUCTION_LAYOUTS.RequestUnits;
const data = encodeData(type, params);
return new TransactionInstruction({
keys: [],
programId: this.programId,
data,
});
}
static requestHeapFrame(
params: RequestHeapFrameParams,
): TransactionInstruction {
const type = COMPUTE_BUDGET_INSTRUCTION_LAYOUTS.RequestHeapFrame;
const data = encodeData(type, params);
return new TransactionInstruction({
keys: [],
programId: this.programId,
data,
});
}
static setComputeUnitLimit(
params: SetComputeUnitLimitParams,
): TransactionInstruction {
const type = COMPUTE_BUDGET_INSTRUCTION_LAYOUTS.SetComputeUnitLimit;
const data = encodeData(type, params);
return new TransactionInstruction({
keys: [],
programId: this.programId,
data,
});
}
static setComputeUnitPrice(
params: SetComputeUnitPriceParams,
): TransactionInstruction {
const type = COMPUTE_BUDGET_INSTRUCTION_LAYOUTS.SetComputeUnitPrice;
const data = encodeData(type, {
microLamports: BigInt(params.microLamports),
});
return new TransactionInstruction({
keys: [],
programId: this.programId,
data,
});
}
}

157
node_modules/@solana/web3.js/src/programs/ed25519.ts generated vendored Normal file
View File

@@ -0,0 +1,157 @@
import {Buffer} from 'buffer';
import * as BufferLayout from '@solana/buffer-layout';
import {Keypair} from '../keypair';
import {PublicKey} from '../publickey';
import {TransactionInstruction} from '../transaction';
import assert from '../utils/assert';
import {sign} from '../utils/ed25519';
const PRIVATE_KEY_BYTES = 64;
const PUBLIC_KEY_BYTES = 32;
const SIGNATURE_BYTES = 64;
/**
* Params for creating an ed25519 instruction using a public key
*/
export type CreateEd25519InstructionWithPublicKeyParams = {
publicKey: Uint8Array;
message: Uint8Array;
signature: Uint8Array;
instructionIndex?: number;
};
/**
* Params for creating an ed25519 instruction using a private key
*/
export type CreateEd25519InstructionWithPrivateKeyParams = {
privateKey: Uint8Array;
message: Uint8Array;
instructionIndex?: number;
};
const ED25519_INSTRUCTION_LAYOUT = BufferLayout.struct<
Readonly<{
messageDataOffset: number;
messageDataSize: number;
messageInstructionIndex: number;
numSignatures: number;
padding: number;
publicKeyInstructionIndex: number;
publicKeyOffset: number;
signatureInstructionIndex: number;
signatureOffset: number;
}>
>([
BufferLayout.u8('numSignatures'),
BufferLayout.u8('padding'),
BufferLayout.u16('signatureOffset'),
BufferLayout.u16('signatureInstructionIndex'),
BufferLayout.u16('publicKeyOffset'),
BufferLayout.u16('publicKeyInstructionIndex'),
BufferLayout.u16('messageDataOffset'),
BufferLayout.u16('messageDataSize'),
BufferLayout.u16('messageInstructionIndex'),
]);
export class Ed25519Program {
/**
* @internal
*/
constructor() {}
/**
* Public key that identifies the ed25519 program
*/
static programId: PublicKey = new PublicKey(
'Ed25519SigVerify111111111111111111111111111',
);
/**
* Create an ed25519 instruction with a public key and signature. The
* public key must be a buffer that is 32 bytes long, and the signature
* must be a buffer of 64 bytes.
*/
static createInstructionWithPublicKey(
params: CreateEd25519InstructionWithPublicKeyParams,
): TransactionInstruction {
const {publicKey, message, signature, instructionIndex} = params;
assert(
publicKey.length === PUBLIC_KEY_BYTES,
`Public Key must be ${PUBLIC_KEY_BYTES} bytes but received ${publicKey.length} bytes`,
);
assert(
signature.length === SIGNATURE_BYTES,
`Signature must be ${SIGNATURE_BYTES} bytes but received ${signature.length} bytes`,
);
const publicKeyOffset = ED25519_INSTRUCTION_LAYOUT.span;
const signatureOffset = publicKeyOffset + publicKey.length;
const messageDataOffset = signatureOffset + signature.length;
const numSignatures = 1;
const instructionData = Buffer.alloc(messageDataOffset + message.length);
const index =
instructionIndex == null
? 0xffff // An index of `u16::MAX` makes it default to the current instruction.
: instructionIndex;
ED25519_INSTRUCTION_LAYOUT.encode(
{
numSignatures,
padding: 0,
signatureOffset,
signatureInstructionIndex: index,
publicKeyOffset,
publicKeyInstructionIndex: index,
messageDataOffset,
messageDataSize: message.length,
messageInstructionIndex: index,
},
instructionData,
);
instructionData.fill(publicKey, publicKeyOffset);
instructionData.fill(signature, signatureOffset);
instructionData.fill(message, messageDataOffset);
return new TransactionInstruction({
keys: [],
programId: Ed25519Program.programId,
data: instructionData,
});
}
/**
* Create an ed25519 instruction with a private key. The private key
* must be a buffer that is 64 bytes long.
*/
static createInstructionWithPrivateKey(
params: CreateEd25519InstructionWithPrivateKeyParams,
): TransactionInstruction {
const {privateKey, message, instructionIndex} = params;
assert(
privateKey.length === PRIVATE_KEY_BYTES,
`Private key must be ${PRIVATE_KEY_BYTES} bytes but received ${privateKey.length} bytes`,
);
try {
const keypair = Keypair.fromSecretKey(privateKey);
const publicKey = keypair.publicKey.toBytes();
const signature = sign(message, keypair.secretKey);
return this.createInstructionWithPublicKey({
publicKey,
message,
signature,
instructionIndex,
});
} catch (error) {
throw new Error(`Error creating instruction; ${error}`);
}
}
}

7
node_modules/@solana/web3.js/src/programs/index.ts generated vendored Normal file
View File

@@ -0,0 +1,7 @@
export * from './address-lookup-table';
export * from './compute-budget';
export * from './ed25519';
export * from './secp256k1';
export * from './stake';
export * from './system';
export * from './vote';

228
node_modules/@solana/web3.js/src/programs/secp256k1.ts generated vendored Normal file
View File

@@ -0,0 +1,228 @@
import {Buffer} from 'buffer';
import * as BufferLayout from '@solana/buffer-layout';
import {keccak_256} from '@noble/hashes/sha3';
import {PublicKey} from '../publickey';
import {TransactionInstruction} from '../transaction';
import assert from '../utils/assert';
import {publicKeyCreate, ecdsaSign} from '../utils/secp256k1';
import {toBuffer} from '../utils/to-buffer';
const PRIVATE_KEY_BYTES = 32;
const ETHEREUM_ADDRESS_BYTES = 20;
const PUBLIC_KEY_BYTES = 64;
const SIGNATURE_OFFSETS_SERIALIZED_SIZE = 11;
/**
* Params for creating an secp256k1 instruction using a public key
*/
export type CreateSecp256k1InstructionWithPublicKeyParams = {
publicKey: Buffer | Uint8Array | Array<number>;
message: Buffer | Uint8Array | Array<number>;
signature: Buffer | Uint8Array | Array<number>;
recoveryId: number;
instructionIndex?: number;
};
/**
* Params for creating an secp256k1 instruction using an Ethereum address
*/
export type CreateSecp256k1InstructionWithEthAddressParams = {
ethAddress: Buffer | Uint8Array | Array<number> | string;
message: Buffer | Uint8Array | Array<number>;
signature: Buffer | Uint8Array | Array<number>;
recoveryId: number;
instructionIndex?: number;
};
/**
* Params for creating an secp256k1 instruction using a private key
*/
export type CreateSecp256k1InstructionWithPrivateKeyParams = {
privateKey: Buffer | Uint8Array | Array<number>;
message: Buffer | Uint8Array | Array<number>;
instructionIndex?: number;
};
const SECP256K1_INSTRUCTION_LAYOUT = BufferLayout.struct<
Readonly<{
ethAddress: Uint8Array;
ethAddressInstructionIndex: number;
ethAddressOffset: number;
messageDataOffset: number;
messageDataSize: number;
messageInstructionIndex: number;
numSignatures: number;
recoveryId: number;
signature: Uint8Array;
signatureInstructionIndex: number;
signatureOffset: number;
}>
>([
BufferLayout.u8('numSignatures'),
BufferLayout.u16('signatureOffset'),
BufferLayout.u8('signatureInstructionIndex'),
BufferLayout.u16('ethAddressOffset'),
BufferLayout.u8('ethAddressInstructionIndex'),
BufferLayout.u16('messageDataOffset'),
BufferLayout.u16('messageDataSize'),
BufferLayout.u8('messageInstructionIndex'),
BufferLayout.blob(20, 'ethAddress'),
BufferLayout.blob(64, 'signature'),
BufferLayout.u8('recoveryId'),
]);
export class Secp256k1Program {
/**
* @internal
*/
constructor() {}
/**
* Public key that identifies the secp256k1 program
*/
static programId: PublicKey = new PublicKey(
'KeccakSecp256k11111111111111111111111111111',
);
/**
* Construct an Ethereum address from a secp256k1 public key buffer.
* @param {Buffer} publicKey a 64 byte secp256k1 public key buffer
*/
static publicKeyToEthAddress(
publicKey: Buffer | Uint8Array | Array<number>,
): Buffer {
assert(
publicKey.length === PUBLIC_KEY_BYTES,
`Public key must be ${PUBLIC_KEY_BYTES} bytes but received ${publicKey.length} bytes`,
);
try {
return Buffer.from(keccak_256(toBuffer(publicKey))).slice(
-ETHEREUM_ADDRESS_BYTES,
);
} catch (error) {
throw new Error(`Error constructing Ethereum address: ${error}`);
}
}
/**
* Create an secp256k1 instruction with a public key. The public key
* must be a buffer that is 64 bytes long.
*/
static createInstructionWithPublicKey(
params: CreateSecp256k1InstructionWithPublicKeyParams,
): TransactionInstruction {
const {publicKey, message, signature, recoveryId, instructionIndex} =
params;
return Secp256k1Program.createInstructionWithEthAddress({
ethAddress: Secp256k1Program.publicKeyToEthAddress(publicKey),
message,
signature,
recoveryId,
instructionIndex,
});
}
/**
* Create an secp256k1 instruction with an Ethereum address. The address
* must be a hex string or a buffer that is 20 bytes long.
*/
static createInstructionWithEthAddress(
params: CreateSecp256k1InstructionWithEthAddressParams,
): TransactionInstruction {
const {
ethAddress: rawAddress,
message,
signature,
recoveryId,
instructionIndex = 0,
} = params;
let ethAddress;
if (typeof rawAddress === 'string') {
if (rawAddress.startsWith('0x')) {
ethAddress = Buffer.from(rawAddress.substr(2), 'hex');
} else {
ethAddress = Buffer.from(rawAddress, 'hex');
}
} else {
ethAddress = rawAddress;
}
assert(
ethAddress.length === ETHEREUM_ADDRESS_BYTES,
`Address must be ${ETHEREUM_ADDRESS_BYTES} bytes but received ${ethAddress.length} bytes`,
);
const dataStart = 1 + SIGNATURE_OFFSETS_SERIALIZED_SIZE;
const ethAddressOffset = dataStart;
const signatureOffset = dataStart + ethAddress.length;
const messageDataOffset = signatureOffset + signature.length + 1;
const numSignatures = 1;
const instructionData = Buffer.alloc(
SECP256K1_INSTRUCTION_LAYOUT.span + message.length,
);
SECP256K1_INSTRUCTION_LAYOUT.encode(
{
numSignatures,
signatureOffset,
signatureInstructionIndex: instructionIndex,
ethAddressOffset,
ethAddressInstructionIndex: instructionIndex,
messageDataOffset,
messageDataSize: message.length,
messageInstructionIndex: instructionIndex,
signature: toBuffer(signature),
ethAddress: toBuffer(ethAddress),
recoveryId,
},
instructionData,
);
instructionData.fill(toBuffer(message), SECP256K1_INSTRUCTION_LAYOUT.span);
return new TransactionInstruction({
keys: [],
programId: Secp256k1Program.programId,
data: instructionData,
});
}
/**
* Create an secp256k1 instruction with a private key. The private key
* must be a buffer that is 32 bytes long.
*/
static createInstructionWithPrivateKey(
params: CreateSecp256k1InstructionWithPrivateKeyParams,
): TransactionInstruction {
const {privateKey: pkey, message, instructionIndex} = params;
assert(
pkey.length === PRIVATE_KEY_BYTES,
`Private key must be ${PRIVATE_KEY_BYTES} bytes but received ${pkey.length} bytes`,
);
try {
const privateKey = toBuffer(pkey);
const publicKey = publicKeyCreate(
privateKey,
false /* isCompressed */,
).slice(1); // throw away leading byte
const messageHash = Buffer.from(keccak_256(toBuffer(message)));
const [signature, recoveryId] = ecdsaSign(messageHash, privateKey);
return this.createInstructionWithPublicKey({
publicKey,
message,
signature,
recoveryId,
instructionIndex,
});
} catch (error) {
throw new Error(`Error creating instruction; ${error}`);
}
}
}

952
node_modules/@solana/web3.js/src/programs/stake.ts generated vendored Normal file
View File

@@ -0,0 +1,952 @@
import * as BufferLayout from '@solana/buffer-layout';
import {
encodeData,
decodeData,
InstructionType,
IInstructionInputData,
} from '../instruction';
import * as Layout from '../layout';
import {PublicKey} from '../publickey';
import {SystemProgram} from './system';
import {
SYSVAR_CLOCK_PUBKEY,
SYSVAR_RENT_PUBKEY,
SYSVAR_STAKE_HISTORY_PUBKEY,
} from '../sysvar';
import {Transaction, TransactionInstruction} from '../transaction';
import {toBuffer} from '../utils/to-buffer';
/**
* Address of the stake config account which configures the rate
* of stake warmup and cooldown as well as the slashing penalty.
*/
export const STAKE_CONFIG_ID = new PublicKey(
'StakeConfig11111111111111111111111111111111',
);
/**
* Stake account authority info
*/
export class Authorized {
/** stake authority */
staker: PublicKey;
/** withdraw authority */
withdrawer: PublicKey;
/**
* Create a new Authorized object
* @param staker the stake authority
* @param withdrawer the withdraw authority
*/
constructor(staker: PublicKey, withdrawer: PublicKey) {
this.staker = staker;
this.withdrawer = withdrawer;
}
}
type AuthorizedRaw = Readonly<{
staker: Uint8Array;
withdrawer: Uint8Array;
}>;
/**
* Stake account lockup info
*/
export class Lockup {
/** Unix timestamp of lockup expiration */
unixTimestamp: number;
/** Epoch of lockup expiration */
epoch: number;
/** Lockup custodian authority */
custodian: PublicKey;
/**
* Create a new Lockup object
*/
constructor(unixTimestamp: number, epoch: number, custodian: PublicKey) {
this.unixTimestamp = unixTimestamp;
this.epoch = epoch;
this.custodian = custodian;
}
/**
* Default, inactive Lockup value
*/
static default: Lockup = new Lockup(0, 0, PublicKey.default);
}
type LockupRaw = Readonly<{
custodian: Uint8Array;
epoch: number;
unixTimestamp: number;
}>;
/**
* Create stake account transaction params
*/
export type CreateStakeAccountParams = {
/** Address of the account which will fund creation */
fromPubkey: PublicKey;
/** Address of the new stake account */
stakePubkey: PublicKey;
/** Authorities of the new stake account */
authorized: Authorized;
/** Lockup of the new stake account */
lockup?: Lockup;
/** Funding amount */
lamports: number;
};
/**
* Create stake account with seed transaction params
*/
export type CreateStakeAccountWithSeedParams = {
fromPubkey: PublicKey;
stakePubkey: PublicKey;
basePubkey: PublicKey;
seed: string;
authorized: Authorized;
lockup?: Lockup;
lamports: number;
};
/**
* Initialize stake instruction params
*/
export type InitializeStakeParams = {
stakePubkey: PublicKey;
authorized: Authorized;
lockup?: Lockup;
};
/**
* Delegate stake instruction params
*/
export type DelegateStakeParams = {
stakePubkey: PublicKey;
authorizedPubkey: PublicKey;
votePubkey: PublicKey;
};
/**
* Authorize stake instruction params
*/
export type AuthorizeStakeParams = {
stakePubkey: PublicKey;
authorizedPubkey: PublicKey;
newAuthorizedPubkey: PublicKey;
stakeAuthorizationType: StakeAuthorizationType;
custodianPubkey?: PublicKey;
};
/**
* Authorize stake instruction params using a derived key
*/
export type AuthorizeWithSeedStakeParams = {
stakePubkey: PublicKey;
authorityBase: PublicKey;
authoritySeed: string;
authorityOwner: PublicKey;
newAuthorizedPubkey: PublicKey;
stakeAuthorizationType: StakeAuthorizationType;
custodianPubkey?: PublicKey;
};
/**
* Split stake instruction params
*/
export type SplitStakeParams = {
stakePubkey: PublicKey;
authorizedPubkey: PublicKey;
splitStakePubkey: PublicKey;
lamports: number;
};
/**
* Split with seed transaction params
*/
export type SplitStakeWithSeedParams = {
stakePubkey: PublicKey;
authorizedPubkey: PublicKey;
splitStakePubkey: PublicKey;
basePubkey: PublicKey;
seed: string;
lamports: number;
};
/**
* Withdraw stake instruction params
*/
export type WithdrawStakeParams = {
stakePubkey: PublicKey;
authorizedPubkey: PublicKey;
toPubkey: PublicKey;
lamports: number;
custodianPubkey?: PublicKey;
};
/**
* Deactivate stake instruction params
*/
export type DeactivateStakeParams = {
stakePubkey: PublicKey;
authorizedPubkey: PublicKey;
};
/**
* Merge stake instruction params
*/
export type MergeStakeParams = {
stakePubkey: PublicKey;
sourceStakePubKey: PublicKey;
authorizedPubkey: PublicKey;
};
/**
* Stake Instruction class
*/
export class StakeInstruction {
/**
* @internal
*/
constructor() {}
/**
* Decode a stake instruction and retrieve the instruction type.
*/
static decodeInstructionType(
instruction: TransactionInstruction,
): StakeInstructionType {
this.checkProgramId(instruction.programId);
const instructionTypeLayout = BufferLayout.u32('instruction');
const typeIndex = instructionTypeLayout.decode(instruction.data);
let type: StakeInstructionType | undefined;
for (const [ixType, layout] of Object.entries(STAKE_INSTRUCTION_LAYOUTS)) {
if (layout.index == typeIndex) {
type = ixType as StakeInstructionType;
break;
}
}
if (!type) {
throw new Error('Instruction type incorrect; not a StakeInstruction');
}
return type;
}
/**
* Decode a initialize stake instruction and retrieve the instruction params.
*/
static decodeInitialize(
instruction: TransactionInstruction,
): InitializeStakeParams {
this.checkProgramId(instruction.programId);
this.checkKeyLength(instruction.keys, 2);
const {authorized, lockup} = decodeData(
STAKE_INSTRUCTION_LAYOUTS.Initialize,
instruction.data,
);
return {
stakePubkey: instruction.keys[0].pubkey,
authorized: new Authorized(
new PublicKey(authorized.staker),
new PublicKey(authorized.withdrawer),
),
lockup: new Lockup(
lockup.unixTimestamp,
lockup.epoch,
new PublicKey(lockup.custodian),
),
};
}
/**
* Decode a delegate stake instruction and retrieve the instruction params.
*/
static decodeDelegate(
instruction: TransactionInstruction,
): DelegateStakeParams {
this.checkProgramId(instruction.programId);
this.checkKeyLength(instruction.keys, 6);
decodeData(STAKE_INSTRUCTION_LAYOUTS.Delegate, instruction.data);
return {
stakePubkey: instruction.keys[0].pubkey,
votePubkey: instruction.keys[1].pubkey,
authorizedPubkey: instruction.keys[5].pubkey,
};
}
/**
* Decode an authorize stake instruction and retrieve the instruction params.
*/
static decodeAuthorize(
instruction: TransactionInstruction,
): AuthorizeStakeParams {
this.checkProgramId(instruction.programId);
this.checkKeyLength(instruction.keys, 3);
const {newAuthorized, stakeAuthorizationType} = decodeData(
STAKE_INSTRUCTION_LAYOUTS.Authorize,
instruction.data,
);
const o: AuthorizeStakeParams = {
stakePubkey: instruction.keys[0].pubkey,
authorizedPubkey: instruction.keys[2].pubkey,
newAuthorizedPubkey: new PublicKey(newAuthorized),
stakeAuthorizationType: {
index: stakeAuthorizationType,
},
};
if (instruction.keys.length > 3) {
o.custodianPubkey = instruction.keys[3].pubkey;
}
return o;
}
/**
* Decode an authorize-with-seed stake instruction and retrieve the instruction params.
*/
static decodeAuthorizeWithSeed(
instruction: TransactionInstruction,
): AuthorizeWithSeedStakeParams {
this.checkProgramId(instruction.programId);
this.checkKeyLength(instruction.keys, 2);
const {
newAuthorized,
stakeAuthorizationType,
authoritySeed,
authorityOwner,
} = decodeData(
STAKE_INSTRUCTION_LAYOUTS.AuthorizeWithSeed,
instruction.data,
);
const o: AuthorizeWithSeedStakeParams = {
stakePubkey: instruction.keys[0].pubkey,
authorityBase: instruction.keys[1].pubkey,
authoritySeed: authoritySeed,
authorityOwner: new PublicKey(authorityOwner),
newAuthorizedPubkey: new PublicKey(newAuthorized),
stakeAuthorizationType: {
index: stakeAuthorizationType,
},
};
if (instruction.keys.length > 3) {
o.custodianPubkey = instruction.keys[3].pubkey;
}
return o;
}
/**
* Decode a split stake instruction and retrieve the instruction params.
*/
static decodeSplit(instruction: TransactionInstruction): SplitStakeParams {
this.checkProgramId(instruction.programId);
this.checkKeyLength(instruction.keys, 3);
const {lamports} = decodeData(
STAKE_INSTRUCTION_LAYOUTS.Split,
instruction.data,
);
return {
stakePubkey: instruction.keys[0].pubkey,
splitStakePubkey: instruction.keys[1].pubkey,
authorizedPubkey: instruction.keys[2].pubkey,
lamports,
};
}
/**
* Decode a merge stake instruction and retrieve the instruction params.
*/
static decodeMerge(instruction: TransactionInstruction): MergeStakeParams {
this.checkProgramId(instruction.programId);
this.checkKeyLength(instruction.keys, 3);
decodeData(STAKE_INSTRUCTION_LAYOUTS.Merge, instruction.data);
return {
stakePubkey: instruction.keys[0].pubkey,
sourceStakePubKey: instruction.keys[1].pubkey,
authorizedPubkey: instruction.keys[4].pubkey,
};
}
/**
* Decode a withdraw stake instruction and retrieve the instruction params.
*/
static decodeWithdraw(
instruction: TransactionInstruction,
): WithdrawStakeParams {
this.checkProgramId(instruction.programId);
this.checkKeyLength(instruction.keys, 5);
const {lamports} = decodeData(
STAKE_INSTRUCTION_LAYOUTS.Withdraw,
instruction.data,
);
const o: WithdrawStakeParams = {
stakePubkey: instruction.keys[0].pubkey,
toPubkey: instruction.keys[1].pubkey,
authorizedPubkey: instruction.keys[4].pubkey,
lamports,
};
if (instruction.keys.length > 5) {
o.custodianPubkey = instruction.keys[5].pubkey;
}
return o;
}
/**
* Decode a deactivate stake instruction and retrieve the instruction params.
*/
static decodeDeactivate(
instruction: TransactionInstruction,
): DeactivateStakeParams {
this.checkProgramId(instruction.programId);
this.checkKeyLength(instruction.keys, 3);
decodeData(STAKE_INSTRUCTION_LAYOUTS.Deactivate, instruction.data);
return {
stakePubkey: instruction.keys[0].pubkey,
authorizedPubkey: instruction.keys[2].pubkey,
};
}
/**
* @internal
*/
static checkProgramId(programId: PublicKey) {
if (!programId.equals(StakeProgram.programId)) {
throw new Error('invalid instruction; programId is not StakeProgram');
}
}
/**
* @internal
*/
static checkKeyLength(keys: Array<any>, expectedLength: number) {
if (keys.length < expectedLength) {
throw new Error(
`invalid instruction; found ${keys.length} keys, expected at least ${expectedLength}`,
);
}
}
}
/**
* An enumeration of valid StakeInstructionType's
*/
export type StakeInstructionType =
// FIXME
// It would be preferable for this type to be `keyof StakeInstructionInputData`
// but Typedoc does not transpile `keyof` expressions.
// See https://github.com/TypeStrong/typedoc/issues/1894
| 'Authorize'
| 'AuthorizeWithSeed'
| 'Deactivate'
| 'Delegate'
| 'Initialize'
| 'Merge'
| 'Split'
| 'Withdraw';
type StakeInstructionInputData = {
Authorize: IInstructionInputData &
Readonly<{
newAuthorized: Uint8Array;
stakeAuthorizationType: number;
}>;
AuthorizeWithSeed: IInstructionInputData &
Readonly<{
authorityOwner: Uint8Array;
authoritySeed: string;
instruction: number;
newAuthorized: Uint8Array;
stakeAuthorizationType: number;
}>;
Deactivate: IInstructionInputData;
Delegate: IInstructionInputData;
Initialize: IInstructionInputData &
Readonly<{
authorized: AuthorizedRaw;
lockup: LockupRaw;
}>;
Merge: IInstructionInputData;
Split: IInstructionInputData &
Readonly<{
lamports: number;
}>;
Withdraw: IInstructionInputData &
Readonly<{
lamports: number;
}>;
};
/**
* An enumeration of valid stake InstructionType's
* @internal
*/
export const STAKE_INSTRUCTION_LAYOUTS = Object.freeze<{
[Instruction in StakeInstructionType]: InstructionType<
StakeInstructionInputData[Instruction]
>;
}>({
Initialize: {
index: 0,
layout: BufferLayout.struct<StakeInstructionInputData['Initialize']>([
BufferLayout.u32('instruction'),
Layout.authorized(),
Layout.lockup(),
]),
},
Authorize: {
index: 1,
layout: BufferLayout.struct<StakeInstructionInputData['Authorize']>([
BufferLayout.u32('instruction'),
Layout.publicKey('newAuthorized'),
BufferLayout.u32('stakeAuthorizationType'),
]),
},
Delegate: {
index: 2,
layout: BufferLayout.struct<StakeInstructionInputData['Delegate']>([
BufferLayout.u32('instruction'),
]),
},
Split: {
index: 3,
layout: BufferLayout.struct<StakeInstructionInputData['Split']>([
BufferLayout.u32('instruction'),
BufferLayout.ns64('lamports'),
]),
},
Withdraw: {
index: 4,
layout: BufferLayout.struct<StakeInstructionInputData['Withdraw']>([
BufferLayout.u32('instruction'),
BufferLayout.ns64('lamports'),
]),
},
Deactivate: {
index: 5,
layout: BufferLayout.struct<StakeInstructionInputData['Deactivate']>([
BufferLayout.u32('instruction'),
]),
},
Merge: {
index: 7,
layout: BufferLayout.struct<StakeInstructionInputData['Merge']>([
BufferLayout.u32('instruction'),
]),
},
AuthorizeWithSeed: {
index: 8,
layout: BufferLayout.struct<StakeInstructionInputData['AuthorizeWithSeed']>(
[
BufferLayout.u32('instruction'),
Layout.publicKey('newAuthorized'),
BufferLayout.u32('stakeAuthorizationType'),
Layout.rustString('authoritySeed'),
Layout.publicKey('authorityOwner'),
],
),
},
});
/**
* Stake authorization type
*/
export type StakeAuthorizationType = {
/** The Stake Authorization index (from solana-stake-program) */
index: number;
};
/**
* An enumeration of valid StakeAuthorizationLayout's
*/
export const StakeAuthorizationLayout = Object.freeze({
Staker: {
index: 0,
},
Withdrawer: {
index: 1,
},
});
/**
* Factory class for transactions to interact with the Stake program
*/
export class StakeProgram {
/**
* @internal
*/
constructor() {}
/**
* Public key that identifies the Stake program
*/
static programId: PublicKey = new PublicKey(
'Stake11111111111111111111111111111111111111',
);
/**
* Max space of a Stake account
*
* This is generated from the solana-stake-program StakeState struct as
* `StakeStateV2::size_of()`:
* https://docs.rs/solana-stake-program/latest/solana_stake_program/stake_state/enum.StakeStateV2.html
*/
static space: number = 200;
/**
* Generate an Initialize instruction to add to a Stake Create transaction
*/
static initialize(params: InitializeStakeParams): TransactionInstruction {
const {stakePubkey, authorized, lockup: maybeLockup} = params;
const lockup: Lockup = maybeLockup || Lockup.default;
const type = STAKE_INSTRUCTION_LAYOUTS.Initialize;
const data = encodeData(type, {
authorized: {
staker: toBuffer(authorized.staker.toBuffer()),
withdrawer: toBuffer(authorized.withdrawer.toBuffer()),
},
lockup: {
unixTimestamp: lockup.unixTimestamp,
epoch: lockup.epoch,
custodian: toBuffer(lockup.custodian.toBuffer()),
},
});
const instructionData = {
keys: [
{pubkey: stakePubkey, isSigner: false, isWritable: true},
{pubkey: SYSVAR_RENT_PUBKEY, isSigner: false, isWritable: false},
],
programId: this.programId,
data,
};
return new TransactionInstruction(instructionData);
}
/**
* Generate a Transaction that creates a new Stake account at
* an address generated with `from`, a seed, and the Stake programId
*/
static createAccountWithSeed(
params: CreateStakeAccountWithSeedParams,
): Transaction {
const transaction = new Transaction();
transaction.add(
SystemProgram.createAccountWithSeed({
fromPubkey: params.fromPubkey,
newAccountPubkey: params.stakePubkey,
basePubkey: params.basePubkey,
seed: params.seed,
lamports: params.lamports,
space: this.space,
programId: this.programId,
}),
);
const {stakePubkey, authorized, lockup} = params;
return transaction.add(this.initialize({stakePubkey, authorized, lockup}));
}
/**
* Generate a Transaction that creates a new Stake account
*/
static createAccount(params: CreateStakeAccountParams): Transaction {
const transaction = new Transaction();
transaction.add(
SystemProgram.createAccount({
fromPubkey: params.fromPubkey,
newAccountPubkey: params.stakePubkey,
lamports: params.lamports,
space: this.space,
programId: this.programId,
}),
);
const {stakePubkey, authorized, lockup} = params;
return transaction.add(this.initialize({stakePubkey, authorized, lockup}));
}
/**
* Generate a Transaction that delegates Stake tokens to a validator
* Vote PublicKey. This transaction can also be used to redelegate Stake
* to a new validator Vote PublicKey.
*/
static delegate(params: DelegateStakeParams): Transaction {
const {stakePubkey, authorizedPubkey, votePubkey} = params;
const type = STAKE_INSTRUCTION_LAYOUTS.Delegate;
const data = encodeData(type);
return new Transaction().add({
keys: [
{pubkey: stakePubkey, isSigner: false, isWritable: true},
{pubkey: votePubkey, isSigner: false, isWritable: false},
{pubkey: SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false},
{
pubkey: SYSVAR_STAKE_HISTORY_PUBKEY,
isSigner: false,
isWritable: false,
},
{pubkey: STAKE_CONFIG_ID, isSigner: false, isWritable: false},
{pubkey: authorizedPubkey, isSigner: true, isWritable: false},
],
programId: this.programId,
data,
});
}
/**
* Generate a Transaction that authorizes a new PublicKey as Staker
* or Withdrawer on the Stake account.
*/
static authorize(params: AuthorizeStakeParams): Transaction {
const {
stakePubkey,
authorizedPubkey,
newAuthorizedPubkey,
stakeAuthorizationType,
custodianPubkey,
} = params;
const type = STAKE_INSTRUCTION_LAYOUTS.Authorize;
const data = encodeData(type, {
newAuthorized: toBuffer(newAuthorizedPubkey.toBuffer()),
stakeAuthorizationType: stakeAuthorizationType.index,
});
const keys = [
{pubkey: stakePubkey, isSigner: false, isWritable: true},
{pubkey: SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: true},
{pubkey: authorizedPubkey, isSigner: true, isWritable: false},
];
if (custodianPubkey) {
keys.push({
pubkey: custodianPubkey,
isSigner: true,
isWritable: false,
});
}
return new Transaction().add({
keys,
programId: this.programId,
data,
});
}
/**
* Generate a Transaction that authorizes a new PublicKey as Staker
* or Withdrawer on the Stake account.
*/
static authorizeWithSeed(params: AuthorizeWithSeedStakeParams): Transaction {
const {
stakePubkey,
authorityBase,
authoritySeed,
authorityOwner,
newAuthorizedPubkey,
stakeAuthorizationType,
custodianPubkey,
} = params;
const type = STAKE_INSTRUCTION_LAYOUTS.AuthorizeWithSeed;
const data = encodeData(type, {
newAuthorized: toBuffer(newAuthorizedPubkey.toBuffer()),
stakeAuthorizationType: stakeAuthorizationType.index,
authoritySeed: authoritySeed,
authorityOwner: toBuffer(authorityOwner.toBuffer()),
});
const keys = [
{pubkey: stakePubkey, isSigner: false, isWritable: true},
{pubkey: authorityBase, isSigner: true, isWritable: false},
{pubkey: SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false},
];
if (custodianPubkey) {
keys.push({
pubkey: custodianPubkey,
isSigner: true,
isWritable: false,
});
}
return new Transaction().add({
keys,
programId: this.programId,
data,
});
}
/**
* @internal
*/
static splitInstruction(params: SplitStakeParams): TransactionInstruction {
const {stakePubkey, authorizedPubkey, splitStakePubkey, lamports} = params;
const type = STAKE_INSTRUCTION_LAYOUTS.Split;
const data = encodeData(type, {lamports});
return new TransactionInstruction({
keys: [
{pubkey: stakePubkey, isSigner: false, isWritable: true},
{pubkey: splitStakePubkey, isSigner: false, isWritable: true},
{pubkey: authorizedPubkey, isSigner: true, isWritable: false},
],
programId: this.programId,
data,
});
}
/**
* Generate a Transaction that splits Stake tokens into another stake account
*/
static split(
params: SplitStakeParams,
// Compute the cost of allocating the new stake account in lamports
rentExemptReserve: number,
): Transaction {
const transaction = new Transaction();
transaction.add(
SystemProgram.createAccount({
fromPubkey: params.authorizedPubkey,
newAccountPubkey: params.splitStakePubkey,
lamports: rentExemptReserve,
space: this.space,
programId: this.programId,
}),
);
return transaction.add(this.splitInstruction(params));
}
/**
* Generate a Transaction that splits Stake tokens into another account
* derived from a base public key and seed
*/
static splitWithSeed(
params: SplitStakeWithSeedParams,
// If this stake account is new, compute the cost of allocating it in lamports
rentExemptReserve?: number,
): Transaction {
const {
stakePubkey,
authorizedPubkey,
splitStakePubkey,
basePubkey,
seed,
lamports,
} = params;
const transaction = new Transaction();
transaction.add(
SystemProgram.allocate({
accountPubkey: splitStakePubkey,
basePubkey,
seed,
space: this.space,
programId: this.programId,
}),
);
if (rentExemptReserve && rentExemptReserve > 0) {
transaction.add(
SystemProgram.transfer({
fromPubkey: params.authorizedPubkey,
toPubkey: splitStakePubkey,
lamports: rentExemptReserve,
}),
);
}
return transaction.add(
this.splitInstruction({
stakePubkey,
authorizedPubkey,
splitStakePubkey,
lamports,
}),
);
}
/**
* Generate a Transaction that merges Stake accounts.
*/
static merge(params: MergeStakeParams): Transaction {
const {stakePubkey, sourceStakePubKey, authorizedPubkey} = params;
const type = STAKE_INSTRUCTION_LAYOUTS.Merge;
const data = encodeData(type);
return new Transaction().add({
keys: [
{pubkey: stakePubkey, isSigner: false, isWritable: true},
{pubkey: sourceStakePubKey, isSigner: false, isWritable: true},
{pubkey: SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false},
{
pubkey: SYSVAR_STAKE_HISTORY_PUBKEY,
isSigner: false,
isWritable: false,
},
{pubkey: authorizedPubkey, isSigner: true, isWritable: false},
],
programId: this.programId,
data,
});
}
/**
* Generate a Transaction that withdraws deactivated Stake tokens.
*/
static withdraw(params: WithdrawStakeParams): Transaction {
const {stakePubkey, authorizedPubkey, toPubkey, lamports, custodianPubkey} =
params;
const type = STAKE_INSTRUCTION_LAYOUTS.Withdraw;
const data = encodeData(type, {lamports});
const keys = [
{pubkey: stakePubkey, isSigner: false, isWritable: true},
{pubkey: toPubkey, isSigner: false, isWritable: true},
{pubkey: SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false},
{
pubkey: SYSVAR_STAKE_HISTORY_PUBKEY,
isSigner: false,
isWritable: false,
},
{pubkey: authorizedPubkey, isSigner: true, isWritable: false},
];
if (custodianPubkey) {
keys.push({
pubkey: custodianPubkey,
isSigner: true,
isWritable: false,
});
}
return new Transaction().add({
keys,
programId: this.programId,
data,
});
}
/**
* Generate a Transaction that deactivates Stake tokens.
*/
static deactivate(params: DeactivateStakeParams): Transaction {
const {stakePubkey, authorizedPubkey} = params;
const type = STAKE_INSTRUCTION_LAYOUTS.Deactivate;
const data = encodeData(type);
return new Transaction().add({
keys: [
{pubkey: stakePubkey, isSigner: false, isWritable: true},
{pubkey: SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false},
{pubkey: authorizedPubkey, isSigner: true, isWritable: false},
],
programId: this.programId,
data,
});
}
}

1048
node_modules/@solana/web3.js/src/programs/system.ts generated vendored Normal file

File diff suppressed because it is too large Load Diff

586
node_modules/@solana/web3.js/src/programs/vote.ts generated vendored Normal file
View File

@@ -0,0 +1,586 @@
import * as BufferLayout from '@solana/buffer-layout';
import {
encodeData,
decodeData,
InstructionType,
IInstructionInputData,
} from '../instruction';
import * as Layout from '../layout';
import {PublicKey} from '../publickey';
import {SystemProgram} from './system';
import {SYSVAR_CLOCK_PUBKEY, SYSVAR_RENT_PUBKEY} from '../sysvar';
import {Transaction, TransactionInstruction} from '../transaction';
import {toBuffer} from '../utils/to-buffer';
/**
* Vote account info
*/
export class VoteInit {
nodePubkey: PublicKey;
authorizedVoter: PublicKey;
authorizedWithdrawer: PublicKey;
commission: number; /** [0, 100] */
constructor(
nodePubkey: PublicKey,
authorizedVoter: PublicKey,
authorizedWithdrawer: PublicKey,
commission: number,
) {
this.nodePubkey = nodePubkey;
this.authorizedVoter = authorizedVoter;
this.authorizedWithdrawer = authorizedWithdrawer;
this.commission = commission;
}
}
/**
* Create vote account transaction params
*/
export type CreateVoteAccountParams = {
fromPubkey: PublicKey;
votePubkey: PublicKey;
voteInit: VoteInit;
lamports: number;
};
/**
* InitializeAccount instruction params
*/
export type InitializeAccountParams = {
votePubkey: PublicKey;
nodePubkey: PublicKey;
voteInit: VoteInit;
};
/**
* Authorize instruction params
*/
export type AuthorizeVoteParams = {
votePubkey: PublicKey;
/** Current vote or withdraw authority, depending on `voteAuthorizationType` */
authorizedPubkey: PublicKey;
newAuthorizedPubkey: PublicKey;
voteAuthorizationType: VoteAuthorizationType;
};
/**
* AuthorizeWithSeed instruction params
*/
export type AuthorizeVoteWithSeedParams = {
currentAuthorityDerivedKeyBasePubkey: PublicKey;
currentAuthorityDerivedKeyOwnerPubkey: PublicKey;
currentAuthorityDerivedKeySeed: string;
newAuthorizedPubkey: PublicKey;
voteAuthorizationType: VoteAuthorizationType;
votePubkey: PublicKey;
};
/**
* Withdraw from vote account transaction params
*/
export type WithdrawFromVoteAccountParams = {
votePubkey: PublicKey;
authorizedWithdrawerPubkey: PublicKey;
lamports: number;
toPubkey: PublicKey;
};
/**
* Update validator identity (node pubkey) vote account instruction params.
*/
export type UpdateValidatorIdentityParams = {
votePubkey: PublicKey;
authorizedWithdrawerPubkey: PublicKey;
nodePubkey: PublicKey;
};
/**
* Vote Instruction class
*/
export class VoteInstruction {
/**
* @internal
*/
constructor() {}
/**
* Decode a vote instruction and retrieve the instruction type.
*/
static decodeInstructionType(
instruction: TransactionInstruction,
): VoteInstructionType {
this.checkProgramId(instruction.programId);
const instructionTypeLayout = BufferLayout.u32('instruction');
const typeIndex = instructionTypeLayout.decode(instruction.data);
let type: VoteInstructionType | undefined;
for (const [ixType, layout] of Object.entries(VOTE_INSTRUCTION_LAYOUTS)) {
if (layout.index == typeIndex) {
type = ixType as VoteInstructionType;
break;
}
}
if (!type) {
throw new Error('Instruction type incorrect; not a VoteInstruction');
}
return type;
}
/**
* Decode an initialize vote instruction and retrieve the instruction params.
*/
static decodeInitializeAccount(
instruction: TransactionInstruction,
): InitializeAccountParams {
this.checkProgramId(instruction.programId);
this.checkKeyLength(instruction.keys, 4);
const {voteInit} = decodeData(
VOTE_INSTRUCTION_LAYOUTS.InitializeAccount,
instruction.data,
);
return {
votePubkey: instruction.keys[0].pubkey,
nodePubkey: instruction.keys[3].pubkey,
voteInit: new VoteInit(
new PublicKey(voteInit.nodePubkey),
new PublicKey(voteInit.authorizedVoter),
new PublicKey(voteInit.authorizedWithdrawer),
voteInit.commission,
),
};
}
/**
* Decode an authorize instruction and retrieve the instruction params.
*/
static decodeAuthorize(
instruction: TransactionInstruction,
): AuthorizeVoteParams {
this.checkProgramId(instruction.programId);
this.checkKeyLength(instruction.keys, 3);
const {newAuthorized, voteAuthorizationType} = decodeData(
VOTE_INSTRUCTION_LAYOUTS.Authorize,
instruction.data,
);
return {
votePubkey: instruction.keys[0].pubkey,
authorizedPubkey: instruction.keys[2].pubkey,
newAuthorizedPubkey: new PublicKey(newAuthorized),
voteAuthorizationType: {
index: voteAuthorizationType,
},
};
}
/**
* Decode an authorize instruction and retrieve the instruction params.
*/
static decodeAuthorizeWithSeed(
instruction: TransactionInstruction,
): AuthorizeVoteWithSeedParams {
this.checkProgramId(instruction.programId);
this.checkKeyLength(instruction.keys, 3);
const {
voteAuthorizeWithSeedArgs: {
currentAuthorityDerivedKeyOwnerPubkey,
currentAuthorityDerivedKeySeed,
newAuthorized,
voteAuthorizationType,
},
} = decodeData(
VOTE_INSTRUCTION_LAYOUTS.AuthorizeWithSeed,
instruction.data,
);
return {
currentAuthorityDerivedKeyBasePubkey: instruction.keys[2].pubkey,
currentAuthorityDerivedKeyOwnerPubkey: new PublicKey(
currentAuthorityDerivedKeyOwnerPubkey,
),
currentAuthorityDerivedKeySeed: currentAuthorityDerivedKeySeed,
newAuthorizedPubkey: new PublicKey(newAuthorized),
voteAuthorizationType: {
index: voteAuthorizationType,
},
votePubkey: instruction.keys[0].pubkey,
};
}
/**
* Decode a withdraw instruction and retrieve the instruction params.
*/
static decodeWithdraw(
instruction: TransactionInstruction,
): WithdrawFromVoteAccountParams {
this.checkProgramId(instruction.programId);
this.checkKeyLength(instruction.keys, 3);
const {lamports} = decodeData(
VOTE_INSTRUCTION_LAYOUTS.Withdraw,
instruction.data,
);
return {
votePubkey: instruction.keys[0].pubkey,
authorizedWithdrawerPubkey: instruction.keys[2].pubkey,
lamports,
toPubkey: instruction.keys[1].pubkey,
};
}
/**
* @internal
*/
static checkProgramId(programId: PublicKey) {
if (!programId.equals(VoteProgram.programId)) {
throw new Error('invalid instruction; programId is not VoteProgram');
}
}
/**
* @internal
*/
static checkKeyLength(keys: Array<any>, expectedLength: number) {
if (keys.length < expectedLength) {
throw new Error(
`invalid instruction; found ${keys.length} keys, expected at least ${expectedLength}`,
);
}
}
}
/**
* An enumeration of valid VoteInstructionType's
*/
export type VoteInstructionType =
// FIXME
// It would be preferable for this type to be `keyof VoteInstructionInputData`
// but Typedoc does not transpile `keyof` expressions.
// See https://github.com/TypeStrong/typedoc/issues/1894
| 'Authorize'
| 'AuthorizeWithSeed'
| 'InitializeAccount'
| 'Withdraw'
| 'UpdateValidatorIdentity';
/** @internal */
export type VoteAuthorizeWithSeedArgs = Readonly<{
currentAuthorityDerivedKeyOwnerPubkey: Uint8Array;
currentAuthorityDerivedKeySeed: string;
newAuthorized: Uint8Array;
voteAuthorizationType: number;
}>;
type VoteInstructionInputData = {
Authorize: IInstructionInputData & {
newAuthorized: Uint8Array;
voteAuthorizationType: number;
};
AuthorizeWithSeed: IInstructionInputData & {
voteAuthorizeWithSeedArgs: VoteAuthorizeWithSeedArgs;
};
InitializeAccount: IInstructionInputData & {
voteInit: Readonly<{
authorizedVoter: Uint8Array;
authorizedWithdrawer: Uint8Array;
commission: number;
nodePubkey: Uint8Array;
}>;
};
Withdraw: IInstructionInputData & {
lamports: number;
};
UpdateValidatorIdentity: IInstructionInputData;
};
const VOTE_INSTRUCTION_LAYOUTS = Object.freeze<{
[Instruction in VoteInstructionType]: InstructionType<
VoteInstructionInputData[Instruction]
>;
}>({
InitializeAccount: {
index: 0,
layout: BufferLayout.struct<VoteInstructionInputData['InitializeAccount']>([
BufferLayout.u32('instruction'),
Layout.voteInit(),
]),
},
Authorize: {
index: 1,
layout: BufferLayout.struct<VoteInstructionInputData['Authorize']>([
BufferLayout.u32('instruction'),
Layout.publicKey('newAuthorized'),
BufferLayout.u32('voteAuthorizationType'),
]),
},
Withdraw: {
index: 3,
layout: BufferLayout.struct<VoteInstructionInputData['Withdraw']>([
BufferLayout.u32('instruction'),
BufferLayout.ns64('lamports'),
]),
},
UpdateValidatorIdentity: {
index: 4,
layout: BufferLayout.struct<
VoteInstructionInputData['UpdateValidatorIdentity']
>([BufferLayout.u32('instruction')]),
},
AuthorizeWithSeed: {
index: 10,
layout: BufferLayout.struct<VoteInstructionInputData['AuthorizeWithSeed']>([
BufferLayout.u32('instruction'),
Layout.voteAuthorizeWithSeedArgs(),
]),
},
});
/**
* VoteAuthorize type
*/
export type VoteAuthorizationType = {
/** The VoteAuthorize index (from solana-vote-program) */
index: number;
};
/**
* An enumeration of valid VoteAuthorization layouts.
*/
export const VoteAuthorizationLayout = Object.freeze({
Voter: {
index: 0,
},
Withdrawer: {
index: 1,
},
});
/**
* Factory class for transactions to interact with the Vote program
*/
export class VoteProgram {
/**
* @internal
*/
constructor() {}
/**
* Public key that identifies the Vote program
*/
static programId: PublicKey = new PublicKey(
'Vote111111111111111111111111111111111111111',
);
/**
* Max space of a Vote account
*
* This is generated from the solana-vote-program VoteState struct as
* `VoteState::size_of()`:
* https://docs.rs/solana-vote-program/1.9.5/solana_vote_program/vote_state/struct.VoteState.html#method.size_of
*
* KEEP IN SYNC WITH `VoteState::size_of()` in https://github.com/solana-labs/solana/blob/a474cb24b9238f5edcc982f65c0b37d4a1046f7e/sdk/program/src/vote/state/mod.rs#L340-L342
*/
static space: number = 3762;
/**
* Generate an Initialize instruction.
*/
static initializeAccount(
params: InitializeAccountParams,
): TransactionInstruction {
const {votePubkey, nodePubkey, voteInit} = params;
const type = VOTE_INSTRUCTION_LAYOUTS.InitializeAccount;
const data = encodeData(type, {
voteInit: {
nodePubkey: toBuffer(voteInit.nodePubkey.toBuffer()),
authorizedVoter: toBuffer(voteInit.authorizedVoter.toBuffer()),
authorizedWithdrawer: toBuffer(
voteInit.authorizedWithdrawer.toBuffer(),
),
commission: voteInit.commission,
},
});
const instructionData = {
keys: [
{pubkey: votePubkey, isSigner: false, isWritable: true},
{pubkey: SYSVAR_RENT_PUBKEY, isSigner: false, isWritable: false},
{pubkey: SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false},
{pubkey: nodePubkey, isSigner: true, isWritable: false},
],
programId: this.programId,
data,
};
return new TransactionInstruction(instructionData);
}
/**
* Generate a transaction that creates a new Vote account.
*/
static createAccount(params: CreateVoteAccountParams): Transaction {
const transaction = new Transaction();
transaction.add(
SystemProgram.createAccount({
fromPubkey: params.fromPubkey,
newAccountPubkey: params.votePubkey,
lamports: params.lamports,
space: this.space,
programId: this.programId,
}),
);
return transaction.add(
this.initializeAccount({
votePubkey: params.votePubkey,
nodePubkey: params.voteInit.nodePubkey,
voteInit: params.voteInit,
}),
);
}
/**
* Generate a transaction that authorizes a new Voter or Withdrawer on the Vote account.
*/
static authorize(params: AuthorizeVoteParams): Transaction {
const {
votePubkey,
authorizedPubkey,
newAuthorizedPubkey,
voteAuthorizationType,
} = params;
const type = VOTE_INSTRUCTION_LAYOUTS.Authorize;
const data = encodeData(type, {
newAuthorized: toBuffer(newAuthorizedPubkey.toBuffer()),
voteAuthorizationType: voteAuthorizationType.index,
});
const keys = [
{pubkey: votePubkey, isSigner: false, isWritable: true},
{pubkey: SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false},
{pubkey: authorizedPubkey, isSigner: true, isWritable: false},
];
return new Transaction().add({
keys,
programId: this.programId,
data,
});
}
/**
* Generate a transaction that authorizes a new Voter or Withdrawer on the Vote account
* where the current Voter or Withdrawer authority is a derived key.
*/
static authorizeWithSeed(params: AuthorizeVoteWithSeedParams): Transaction {
const {
currentAuthorityDerivedKeyBasePubkey,
currentAuthorityDerivedKeyOwnerPubkey,
currentAuthorityDerivedKeySeed,
newAuthorizedPubkey,
voteAuthorizationType,
votePubkey,
} = params;
const type = VOTE_INSTRUCTION_LAYOUTS.AuthorizeWithSeed;
const data = encodeData(type, {
voteAuthorizeWithSeedArgs: {
currentAuthorityDerivedKeyOwnerPubkey: toBuffer(
currentAuthorityDerivedKeyOwnerPubkey.toBuffer(),
),
currentAuthorityDerivedKeySeed: currentAuthorityDerivedKeySeed,
newAuthorized: toBuffer(newAuthorizedPubkey.toBuffer()),
voteAuthorizationType: voteAuthorizationType.index,
},
});
const keys = [
{pubkey: votePubkey, isSigner: false, isWritable: true},
{pubkey: SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false},
{
pubkey: currentAuthorityDerivedKeyBasePubkey,
isSigner: true,
isWritable: false,
},
];
return new Transaction().add({
keys,
programId: this.programId,
data,
});
}
/**
* Generate a transaction to withdraw from a Vote account.
*/
static withdraw(params: WithdrawFromVoteAccountParams): Transaction {
const {votePubkey, authorizedWithdrawerPubkey, lamports, toPubkey} = params;
const type = VOTE_INSTRUCTION_LAYOUTS.Withdraw;
const data = encodeData(type, {lamports});
const keys = [
{pubkey: votePubkey, isSigner: false, isWritable: true},
{pubkey: toPubkey, isSigner: false, isWritable: true},
{pubkey: authorizedWithdrawerPubkey, isSigner: true, isWritable: false},
];
return new Transaction().add({
keys,
programId: this.programId,
data,
});
}
/**
* Generate a transaction to withdraw safely from a Vote account.
*
* This function was created as a safeguard for vote accounts running validators, `safeWithdraw`
* checks that the withdraw amount will not exceed the specified balance while leaving enough left
* to cover rent. If you wish to close the vote account by withdrawing the full amount, call the
* `withdraw` method directly.
*/
static safeWithdraw(
params: WithdrawFromVoteAccountParams,
currentVoteAccountBalance: number,
rentExemptMinimum: number,
): Transaction {
if (params.lamports > currentVoteAccountBalance - rentExemptMinimum) {
throw new Error(
'Withdraw will leave vote account with insufficient funds.',
);
}
return VoteProgram.withdraw(params);
}
/**
* Generate a transaction to update the validator identity (node pubkey) of a Vote account.
*/
static updateValidatorIdentity(
params: UpdateValidatorIdentityParams,
): Transaction {
const {votePubkey, authorizedWithdrawerPubkey, nodePubkey} = params;
const type = VOTE_INSTRUCTION_LAYOUTS.UpdateValidatorIdentity;
const data = encodeData(type);
const keys = [
{pubkey: votePubkey, isSigner: false, isWritable: true},
{pubkey: nodePubkey, isSigner: true, isWritable: false},
{pubkey: authorizedWithdrawerPubkey, isSigner: true, isWritable: false},
];
return new Transaction().add({
keys,
programId: this.programId,
data,
});
}
}

259
node_modules/@solana/web3.js/src/publickey.ts generated vendored Normal file
View File

@@ -0,0 +1,259 @@
import BN from 'bn.js';
import bs58 from 'bs58';
import {Buffer} from 'buffer';
import {sha256} from '@noble/hashes/sha256';
import {isOnCurve} from './utils/ed25519';
import {Struct, SOLANA_SCHEMA} from './utils/borsh-schema';
import {toBuffer} from './utils/to-buffer';
/**
* Maximum length of derived pubkey seed
*/
export const MAX_SEED_LENGTH = 32;
/**
* Size of public key in bytes
*/
export const PUBLIC_KEY_LENGTH = 32;
/**
* Value to be converted into public key
*/
export type PublicKeyInitData =
| number
| string
| Uint8Array
| Array<number>
| PublicKeyData;
/**
* JSON object representation of PublicKey class
*/
export type PublicKeyData = {
/** @internal */
_bn: BN;
};
function isPublicKeyData(value: PublicKeyInitData): value is PublicKeyData {
return (value as PublicKeyData)._bn !== undefined;
}
// local counter used by PublicKey.unique()
let uniquePublicKeyCounter = 1;
/**
* A public key
*/
export class PublicKey extends Struct {
/** @internal */
_bn: BN;
/**
* Create a new PublicKey object
* @param value ed25519 public key as buffer or base-58 encoded string
*/
constructor(value: PublicKeyInitData) {
super({});
if (isPublicKeyData(value)) {
this._bn = value._bn;
} else {
if (typeof value === 'string') {
// assume base 58 encoding by default
const decoded = bs58.decode(value);
if (decoded.length != PUBLIC_KEY_LENGTH) {
throw new Error(`Invalid public key input`);
}
this._bn = new BN(decoded);
} else {
this._bn = new BN(value);
}
if (this._bn.byteLength() > PUBLIC_KEY_LENGTH) {
throw new Error(`Invalid public key input`);
}
}
}
/**
* Returns a unique PublicKey for tests and benchmarks using a counter
*/
static unique(): PublicKey {
const key = new PublicKey(uniquePublicKeyCounter);
uniquePublicKeyCounter += 1;
return new PublicKey(key.toBuffer());
}
/**
* Default public key value. The base58-encoded string representation is all ones (as seen below)
* The underlying BN number is 32 bytes that are all zeros
*/
static default: PublicKey = new PublicKey('11111111111111111111111111111111');
/**
* Checks if two publicKeys are equal
*/
equals(publicKey: PublicKey): boolean {
return this._bn.eq(publicKey._bn);
}
/**
* Return the base-58 representation of the public key
*/
toBase58(): string {
return bs58.encode(this.toBytes());
}
toJSON(): string {
return this.toBase58();
}
/**
* Return the byte array representation of the public key in big endian
*/
toBytes(): Uint8Array {
const buf = this.toBuffer();
return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
}
/**
* Return the Buffer representation of the public key in big endian
*/
toBuffer(): Buffer {
const b = this._bn.toArrayLike(Buffer);
if (b.length === PUBLIC_KEY_LENGTH) {
return b;
}
const zeroPad = Buffer.alloc(32);
b.copy(zeroPad, 32 - b.length);
return zeroPad;
}
get [Symbol.toStringTag](): string {
return `PublicKey(${this.toString()})`;
}
/**
* Return the base-58 representation of the public key
*/
toString(): string {
return this.toBase58();
}
/**
* Derive a public key from another key, a seed, and a program ID.
* The program ID will also serve as the owner of the public key, giving
* it permission to write data to the account.
*/
/* eslint-disable require-await */
static async createWithSeed(
fromPublicKey: PublicKey,
seed: string,
programId: PublicKey,
): Promise<PublicKey> {
const buffer = Buffer.concat([
fromPublicKey.toBuffer(),
Buffer.from(seed),
programId.toBuffer(),
]);
const publicKeyBytes = sha256(buffer);
return new PublicKey(publicKeyBytes);
}
/**
* Derive a program address from seeds and a program ID.
*/
/* eslint-disable require-await */
static createProgramAddressSync(
seeds: Array<Buffer | Uint8Array>,
programId: PublicKey,
): PublicKey {
let buffer = Buffer.alloc(0);
seeds.forEach(function (seed) {
if (seed.length > MAX_SEED_LENGTH) {
throw new TypeError(`Max seed length exceeded`);
}
buffer = Buffer.concat([buffer, toBuffer(seed)]);
});
buffer = Buffer.concat([
buffer,
programId.toBuffer(),
Buffer.from('ProgramDerivedAddress'),
]);
const publicKeyBytes = sha256(buffer);
if (isOnCurve(publicKeyBytes)) {
throw new Error(`Invalid seeds, address must fall off the curve`);
}
return new PublicKey(publicKeyBytes);
}
/**
* Async version of createProgramAddressSync
* For backwards compatibility
*
* @deprecated Use {@link createProgramAddressSync} instead
*/
/* eslint-disable require-await */
static async createProgramAddress(
seeds: Array<Buffer | Uint8Array>,
programId: PublicKey,
): Promise<PublicKey> {
return this.createProgramAddressSync(seeds, programId);
}
/**
* Find a valid program address
*
* Valid program addresses must fall off the ed25519 curve. This function
* iterates a nonce until it finds one that when combined with the seeds
* results in a valid program address.
*/
static findProgramAddressSync(
seeds: Array<Buffer | Uint8Array>,
programId: PublicKey,
): [PublicKey, number] {
let nonce = 255;
let address;
while (nonce != 0) {
try {
const seedsWithNonce = seeds.concat(Buffer.from([nonce]));
address = this.createProgramAddressSync(seedsWithNonce, programId);
} catch (err) {
if (err instanceof TypeError) {
throw err;
}
nonce--;
continue;
}
return [address, nonce];
}
throw new Error(`Unable to find a viable program address nonce`);
}
/**
* Async version of findProgramAddressSync
* For backwards compatibility
*
* @deprecated Use {@link findProgramAddressSync} instead
*/
static async findProgramAddress(
seeds: Array<Buffer | Uint8Array>,
programId: PublicKey,
): Promise<[PublicKey, number]> {
return this.findProgramAddressSync(seeds, programId);
}
/**
* Check that a pubkey is on the ed25519 curve.
*/
static isOnCurve(pubkeyData: PublicKeyInitData): boolean {
const pubkey = new PublicKey(pubkeyData);
return isOnCurve(pubkey.toBytes());
}
}
SOLANA_SCHEMA.set(PublicKey, {
kind: 'struct',
fields: [['_bn', 'u256']],
});

75
node_modules/@solana/web3.js/src/rpc-websocket.ts generated vendored Normal file
View File

@@ -0,0 +1,75 @@
import {
CommonClient,
ICommonWebSocket,
IWSClientAdditionalOptions,
NodeWebSocketType,
NodeWebSocketTypeOptions,
WebSocket as createRpc,
} from 'rpc-websockets';
interface IHasReadyState {
readyState: WebSocket['readyState'];
}
export default class RpcWebSocketClient extends CommonClient {
private underlyingSocket: IHasReadyState | undefined;
constructor(
address?: string,
options?: IWSClientAdditionalOptions & NodeWebSocketTypeOptions,
generate_request_id?: (
method: string,
params: object | Array<any>,
) => number,
) {
const webSocketFactory = (url: string) => {
const rpc = createRpc(url, {
autoconnect: true,
max_reconnects: 5,
reconnect: true,
reconnect_interval: 1000,
...options,
});
if ('socket' in rpc) {
this.underlyingSocket = rpc.socket as ReturnType<typeof createRpc>;
} else {
this.underlyingSocket = rpc as NodeWebSocketType;
}
return rpc as ICommonWebSocket;
};
super(webSocketFactory, address, options, generate_request_id);
}
call(
...args: Parameters<CommonClient['call']>
): ReturnType<CommonClient['call']> {
const readyState = this.underlyingSocket?.readyState;
if (readyState === 1 /* WebSocket.OPEN */) {
return super.call(...args);
}
return Promise.reject(
new Error(
'Tried to call a JSON-RPC method `' +
args[0] +
'` but the socket was not `CONNECTING` or `OPEN` (`readyState` was ' +
readyState +
')',
),
);
}
notify(
...args: Parameters<CommonClient['notify']>
): ReturnType<CommonClient['notify']> {
const readyState = this.underlyingSocket?.readyState;
if (readyState === 1 /* WebSocket.OPEN */) {
return super.notify(...args);
}
return Promise.reject(
new Error(
'Tried to send a JSON-RPC notification `' +
args[0] +
'` but the socket was not `CONNECTING` or `OPEN` (`readyState` was ' +
readyState +
')',
),
);
}
}

37
node_modules/@solana/web3.js/src/sysvar.ts generated vendored Normal file
View File

@@ -0,0 +1,37 @@
import {PublicKey} from './publickey';
export const SYSVAR_CLOCK_PUBKEY = new PublicKey(
'SysvarC1ock11111111111111111111111111111111',
);
export const SYSVAR_EPOCH_SCHEDULE_PUBKEY = new PublicKey(
'SysvarEpochSchedu1e111111111111111111111111',
);
export const SYSVAR_INSTRUCTIONS_PUBKEY = new PublicKey(
'Sysvar1nstructions1111111111111111111111111',
);
export const SYSVAR_RECENT_BLOCKHASHES_PUBKEY = new PublicKey(
'SysvarRecentB1ockHashes11111111111111111111',
);
export const SYSVAR_RENT_PUBKEY = new PublicKey(
'SysvarRent111111111111111111111111111111111',
);
export const SYSVAR_REWARDS_PUBKEY = new PublicKey(
'SysvarRewards111111111111111111111111111111',
);
export const SYSVAR_SLOT_HASHES_PUBKEY = new PublicKey(
'SysvarS1otHashes111111111111111111111111111',
);
export const SYSVAR_SLOT_HISTORY_PUBKEY = new PublicKey(
'SysvarS1otHistory11111111111111111111111111',
);
export const SYSVAR_STAKE_HISTORY_PUBKEY = new PublicKey(
'SysvarStakeHistory1111111111111111111111111',
);

23
node_modules/@solana/web3.js/src/timing.ts generated vendored Normal file
View File

@@ -0,0 +1,23 @@
// TODO: These constants should be removed in favor of reading them out of a
// Syscall account
/**
* @internal
*/
export const NUM_TICKS_PER_SECOND = 160;
/**
* @internal
*/
export const DEFAULT_TICKS_PER_SLOT = 64;
/**
* @internal
*/
export const NUM_SLOTS_PER_SECOND =
NUM_TICKS_PER_SECOND / DEFAULT_TICKS_PER_SLOT;
/**
* @internal
*/
export const MS_PER_SLOT = 1000 / NUM_SLOTS_PER_SECOND;

View File

@@ -0,0 +1,12 @@
/**
* Maximum over-the-wire size of a Transaction
*
* 1280 is IPv6 minimum MTU
* 40 bytes is the size of the IPv6 header
* 8 bytes is the size of the fragment header
*/
export const PACKET_DATA_SIZE = 1280 - 40 - 8;
export const VERSION_PREFIX_MASK = 0x7f;
export const SIGNATURE_LENGTH_IN_BYTES = 64;

View File

@@ -0,0 +1,48 @@
export class TransactionExpiredBlockheightExceededError extends Error {
signature: string;
constructor(signature: string) {
super(`Signature ${signature} has expired: block height exceeded.`);
this.signature = signature;
}
}
Object.defineProperty(
TransactionExpiredBlockheightExceededError.prototype,
'name',
{
value: 'TransactionExpiredBlockheightExceededError',
},
);
export class TransactionExpiredTimeoutError extends Error {
signature: string;
constructor(signature: string, timeoutSeconds: number) {
super(
`Transaction was not confirmed in ${timeoutSeconds.toFixed(
2,
)} seconds. It is ` +
'unknown if it succeeded or failed. Check signature ' +
`${signature} using the Solana Explorer or CLI tools.`,
);
this.signature = signature;
}
}
Object.defineProperty(TransactionExpiredTimeoutError.prototype, 'name', {
value: 'TransactionExpiredTimeoutError',
});
export class TransactionExpiredNonceInvalidError extends Error {
signature: string;
constructor(signature: string) {
super(`Signature ${signature} has expired: the nonce is no longer valid.`);
this.signature = signature;
}
}
Object.defineProperty(TransactionExpiredNonceInvalidError.prototype, 'name', {
value: 'TransactionExpiredNonceInvalidError',
});

View File

@@ -0,0 +1,5 @@
export * from './constants';
export * from './expiry-custom-errors';
export * from './legacy';
export * from './message';
export * from './versioned';

970
node_modules/@solana/web3.js/src/transaction/legacy.ts generated vendored Normal file
View File

@@ -0,0 +1,970 @@
import bs58 from 'bs58';
import {Buffer} from 'buffer';
import {PACKET_DATA_SIZE, SIGNATURE_LENGTH_IN_BYTES} from './constants';
import {Connection} from '../connection';
import {Message} from '../message';
import {PublicKey} from '../publickey';
import * as shortvec from '../utils/shortvec-encoding';
import {toBuffer} from '../utils/to-buffer';
import invariant from '../utils/assert';
import type {Signer} from '../keypair';
import type {Blockhash} from '../blockhash';
import type {CompiledInstruction} from '../message';
import {sign, verify} from '../utils/ed25519';
import {guardedSplice} from '../utils/guarded-array-utils';
/** @internal */
type MessageSignednessErrors = {
invalid?: PublicKey[];
missing?: PublicKey[];
};
/**
* Transaction signature as base-58 encoded string
*/
export type TransactionSignature = string;
export const enum TransactionStatus {
BLOCKHEIGHT_EXCEEDED,
PROCESSED,
TIMED_OUT,
NONCE_INVALID,
}
/**
* Default (empty) signature
*/
const DEFAULT_SIGNATURE = Buffer.alloc(SIGNATURE_LENGTH_IN_BYTES).fill(0);
/**
* Account metadata used to define instructions
*/
export type AccountMeta = {
/** An account's public key */
pubkey: PublicKey;
/** True if an instruction requires a transaction signature matching `pubkey` */
isSigner: boolean;
/** True if the `pubkey` can be loaded as a read-write account. */
isWritable: boolean;
};
/**
* List of TransactionInstruction object fields that may be initialized at construction
*/
export type TransactionInstructionCtorFields = {
keys: Array<AccountMeta>;
programId: PublicKey;
data?: Buffer;
};
/**
* Configuration object for Transaction.serialize()
*/
export type SerializeConfig = {
/** Require all transaction signatures be present (default: true) */
requireAllSignatures?: boolean;
/** Verify provided signatures (default: true) */
verifySignatures?: boolean;
};
/**
* @internal
*/
export interface TransactionInstructionJSON {
keys: {
pubkey: string;
isSigner: boolean;
isWritable: boolean;
}[];
programId: string;
data: number[];
}
/**
* Transaction Instruction class
*/
export class TransactionInstruction {
/**
* Public keys to include in this transaction
* Boolean represents whether this pubkey needs to sign the transaction
*/
keys: Array<AccountMeta>;
/**
* Program Id to execute
*/
programId: PublicKey;
/**
* Program input
*/
data: Buffer = Buffer.alloc(0);
constructor(opts: TransactionInstructionCtorFields) {
this.programId = opts.programId;
this.keys = opts.keys;
if (opts.data) {
this.data = opts.data;
}
}
/**
* @internal
*/
toJSON(): TransactionInstructionJSON {
return {
keys: this.keys.map(({pubkey, isSigner, isWritable}) => ({
pubkey: pubkey.toJSON(),
isSigner,
isWritable,
})),
programId: this.programId.toJSON(),
data: [...this.data],
};
}
}
/**
* Pair of signature and corresponding public key
*/
export type SignaturePubkeyPair = {
signature: Buffer | null;
publicKey: PublicKey;
};
/**
* List of Transaction object fields that may be initialized at construction
*/
export type TransactionCtorFields_DEPRECATED = {
/** Optional nonce information used for offline nonce'd transactions */
nonceInfo?: NonceInformation | null;
/** The transaction fee payer */
feePayer?: PublicKey | null;
/** One or more signatures */
signatures?: Array<SignaturePubkeyPair>;
/** A recent blockhash */
recentBlockhash?: Blockhash;
};
// For backward compatibility; an unfortunate consequence of being
// forced to over-export types by the documentation generator.
// See https://github.com/solana-labs/solana/pull/25820
export type TransactionCtorFields = TransactionCtorFields_DEPRECATED;
/**
* Blockhash-based transactions have a lifetime that are defined by
* the blockhash they include. Any transaction whose blockhash is
* too old will be rejected.
*/
export type TransactionBlockhashCtor = {
/** The transaction fee payer */
feePayer?: PublicKey | null;
/** One or more signatures */
signatures?: Array<SignaturePubkeyPair>;
/** A recent blockhash */
blockhash: Blockhash;
/** the last block chain can advance to before tx is declared expired */
lastValidBlockHeight: number;
};
/**
* Use these options to construct a durable nonce transaction.
*/
export type TransactionNonceCtor = {
/** The transaction fee payer */
feePayer?: PublicKey | null;
minContextSlot: number;
nonceInfo: NonceInformation;
/** One or more signatures */
signatures?: Array<SignaturePubkeyPair>;
};
/**
* Nonce information to be used to build an offline Transaction.
*/
export type NonceInformation = {
/** The current blockhash stored in the nonce */
nonce: Blockhash;
/** AdvanceNonceAccount Instruction */
nonceInstruction: TransactionInstruction;
};
/**
* @internal
*/
export interface TransactionJSON {
recentBlockhash: string | null;
feePayer: string | null;
nonceInfo: {
nonce: string;
nonceInstruction: TransactionInstructionJSON;
} | null;
instructions: TransactionInstructionJSON[];
signers: string[];
}
/**
* Transaction class
*/
export class Transaction {
/**
* Signatures for the transaction. Typically created by invoking the
* `sign()` method
*/
signatures: Array<SignaturePubkeyPair> = [];
/**
* The first (payer) Transaction signature
*
* @returns {Buffer | null} Buffer of payer's signature
*/
get signature(): Buffer | null {
if (this.signatures.length > 0) {
return this.signatures[0].signature;
}
return null;
}
/**
* The transaction fee payer
*/
feePayer?: PublicKey;
/**
* The instructions to atomically execute
*/
instructions: Array<TransactionInstruction> = [];
/**
* A recent transaction id. Must be populated by the caller
*/
recentBlockhash?: Blockhash;
/**
* the last block chain can advance to before tx is declared expired
* */
lastValidBlockHeight?: number;
/**
* Optional Nonce information. If populated, transaction will use a durable
* Nonce hash instead of a recentBlockhash. Must be populated by the caller
*/
nonceInfo?: NonceInformation;
/**
* If this is a nonce transaction this represents the minimum slot from which
* to evaluate if the nonce has advanced when attempting to confirm the
* transaction. This protects against a case where the transaction confirmation
* logic loads the nonce account from an old slot and assumes the mismatch in
* nonce value implies that the nonce has been advanced.
*/
minNonceContextSlot?: number;
/**
* @internal
*/
_message?: Message;
/**
* @internal
*/
_json?: TransactionJSON;
// Construct a transaction with a blockhash and lastValidBlockHeight
constructor(opts?: TransactionBlockhashCtor);
// Construct a transaction using a durable nonce
constructor(opts?: TransactionNonceCtor);
/**
* @deprecated `TransactionCtorFields` has been deprecated and will be removed in a future version.
* Please supply a `TransactionBlockhashCtor` instead.
*/
constructor(opts?: TransactionCtorFields_DEPRECATED);
/**
* Construct an empty Transaction
*/
constructor(
opts?:
| TransactionBlockhashCtor
| TransactionNonceCtor
| TransactionCtorFields_DEPRECATED,
) {
if (!opts) {
return;
}
if (opts.feePayer) {
this.feePayer = opts.feePayer;
}
if (opts.signatures) {
this.signatures = opts.signatures;
}
if (Object.prototype.hasOwnProperty.call(opts, 'nonceInfo')) {
const {minContextSlot, nonceInfo} = opts as TransactionNonceCtor;
this.minNonceContextSlot = minContextSlot;
this.nonceInfo = nonceInfo;
} else if (
Object.prototype.hasOwnProperty.call(opts, 'lastValidBlockHeight')
) {
const {blockhash, lastValidBlockHeight} =
opts as TransactionBlockhashCtor;
this.recentBlockhash = blockhash;
this.lastValidBlockHeight = lastValidBlockHeight;
} else {
const {recentBlockhash, nonceInfo} =
opts as TransactionCtorFields_DEPRECATED;
if (nonceInfo) {
this.nonceInfo = nonceInfo;
}
this.recentBlockhash = recentBlockhash;
}
}
/**
* @internal
*/
toJSON(): TransactionJSON {
return {
recentBlockhash: this.recentBlockhash || null,
feePayer: this.feePayer ? this.feePayer.toJSON() : null,
nonceInfo: this.nonceInfo
? {
nonce: this.nonceInfo.nonce,
nonceInstruction: this.nonceInfo.nonceInstruction.toJSON(),
}
: null,
instructions: this.instructions.map(instruction => instruction.toJSON()),
signers: this.signatures.map(({publicKey}) => {
return publicKey.toJSON();
}),
};
}
/**
* Add one or more instructions to this Transaction
*
* @param {Array< Transaction | TransactionInstruction | TransactionInstructionCtorFields >} items - Instructions to add to the Transaction
*/
add(
...items: Array<
Transaction | TransactionInstruction | TransactionInstructionCtorFields
>
): Transaction {
if (items.length === 0) {
throw new Error('No instructions');
}
items.forEach((item: any) => {
if ('instructions' in item) {
this.instructions = this.instructions.concat(item.instructions);
} else if ('data' in item && 'programId' in item && 'keys' in item) {
this.instructions.push(item);
} else {
this.instructions.push(new TransactionInstruction(item));
}
});
return this;
}
/**
* Compile transaction data
*/
compileMessage(): Message {
if (
this._message &&
JSON.stringify(this.toJSON()) === JSON.stringify(this._json)
) {
return this._message;
}
let recentBlockhash;
let instructions: TransactionInstruction[];
if (this.nonceInfo) {
recentBlockhash = this.nonceInfo.nonce;
if (this.instructions[0] != this.nonceInfo.nonceInstruction) {
instructions = [this.nonceInfo.nonceInstruction, ...this.instructions];
} else {
instructions = this.instructions;
}
} else {
recentBlockhash = this.recentBlockhash;
instructions = this.instructions;
}
if (!recentBlockhash) {
throw new Error('Transaction recentBlockhash required');
}
if (instructions.length < 1) {
console.warn('No instructions provided');
}
let feePayer: PublicKey;
if (this.feePayer) {
feePayer = this.feePayer;
} else if (this.signatures.length > 0 && this.signatures[0].publicKey) {
// Use implicit fee payer
feePayer = this.signatures[0].publicKey;
} else {
throw new Error('Transaction fee payer required');
}
for (let i = 0; i < instructions.length; i++) {
if (instructions[i].programId === undefined) {
throw new Error(
`Transaction instruction index ${i} has undefined program id`,
);
}
}
const programIds: string[] = [];
const accountMetas: AccountMeta[] = [];
instructions.forEach(instruction => {
instruction.keys.forEach(accountMeta => {
accountMetas.push({...accountMeta});
});
const programId = instruction.programId.toString();
if (!programIds.includes(programId)) {
programIds.push(programId);
}
});
// Append programID account metas
programIds.forEach(programId => {
accountMetas.push({
pubkey: new PublicKey(programId),
isSigner: false,
isWritable: false,
});
});
// Cull duplicate account metas
const uniqueMetas: AccountMeta[] = [];
accountMetas.forEach(accountMeta => {
const pubkeyString = accountMeta.pubkey.toString();
const uniqueIndex = uniqueMetas.findIndex(x => {
return x.pubkey.toString() === pubkeyString;
});
if (uniqueIndex > -1) {
uniqueMetas[uniqueIndex].isWritable =
uniqueMetas[uniqueIndex].isWritable || accountMeta.isWritable;
uniqueMetas[uniqueIndex].isSigner =
uniqueMetas[uniqueIndex].isSigner || accountMeta.isSigner;
} else {
uniqueMetas.push(accountMeta);
}
});
// Sort. Prioritizing first by signer, then by writable
uniqueMetas.sort(function (x, y) {
if (x.isSigner !== y.isSigner) {
// Signers always come before non-signers
return x.isSigner ? -1 : 1;
}
if (x.isWritable !== y.isWritable) {
// Writable accounts always come before read-only accounts
return x.isWritable ? -1 : 1;
}
// Otherwise, sort by pubkey, stringwise.
const options = {
localeMatcher: 'best fit',
usage: 'sort',
sensitivity: 'variant',
ignorePunctuation: false,
numeric: false,
caseFirst: 'lower',
} as Intl.CollatorOptions;
return x.pubkey
.toBase58()
.localeCompare(y.pubkey.toBase58(), 'en', options);
});
// Move fee payer to the front
const feePayerIndex = uniqueMetas.findIndex(x => {
return x.pubkey.equals(feePayer);
});
if (feePayerIndex > -1) {
const [payerMeta] = uniqueMetas.splice(feePayerIndex, 1);
payerMeta.isSigner = true;
payerMeta.isWritable = true;
uniqueMetas.unshift(payerMeta);
} else {
uniqueMetas.unshift({
pubkey: feePayer,
isSigner: true,
isWritable: true,
});
}
// Disallow unknown signers
for (const signature of this.signatures) {
const uniqueIndex = uniqueMetas.findIndex(x => {
return x.pubkey.equals(signature.publicKey);
});
if (uniqueIndex > -1) {
if (!uniqueMetas[uniqueIndex].isSigner) {
uniqueMetas[uniqueIndex].isSigner = true;
console.warn(
'Transaction references a signature that is unnecessary, ' +
'only the fee payer and instruction signer accounts should sign a transaction. ' +
'This behavior is deprecated and will throw an error in the next major version release.',
);
}
} else {
throw new Error(`unknown signer: ${signature.publicKey.toString()}`);
}
}
let numRequiredSignatures = 0;
let numReadonlySignedAccounts = 0;
let numReadonlyUnsignedAccounts = 0;
// Split out signing from non-signing keys and count header values
const signedKeys: string[] = [];
const unsignedKeys: string[] = [];
uniqueMetas.forEach(({pubkey, isSigner, isWritable}) => {
if (isSigner) {
signedKeys.push(pubkey.toString());
numRequiredSignatures += 1;
if (!isWritable) {
numReadonlySignedAccounts += 1;
}
} else {
unsignedKeys.push(pubkey.toString());
if (!isWritable) {
numReadonlyUnsignedAccounts += 1;
}
}
});
const accountKeys = signedKeys.concat(unsignedKeys);
const compiledInstructions: CompiledInstruction[] = instructions.map(
instruction => {
const {data, programId} = instruction;
return {
programIdIndex: accountKeys.indexOf(programId.toString()),
accounts: instruction.keys.map(meta =>
accountKeys.indexOf(meta.pubkey.toString()),
),
data: bs58.encode(data),
};
},
);
compiledInstructions.forEach(instruction => {
invariant(instruction.programIdIndex >= 0);
instruction.accounts.forEach(keyIndex => invariant(keyIndex >= 0));
});
return new Message({
header: {
numRequiredSignatures,
numReadonlySignedAccounts,
numReadonlyUnsignedAccounts,
},
accountKeys,
recentBlockhash,
instructions: compiledInstructions,
});
}
/**
* @internal
*/
_compile(): Message {
const message = this.compileMessage();
const signedKeys = message.accountKeys.slice(
0,
message.header.numRequiredSignatures,
);
if (this.signatures.length === signedKeys.length) {
const valid = this.signatures.every((pair, index) => {
return signedKeys[index].equals(pair.publicKey);
});
if (valid) return message;
}
this.signatures = signedKeys.map(publicKey => ({
signature: null,
publicKey,
}));
return message;
}
/**
* Get a buffer of the Transaction data that need to be covered by signatures
*/
serializeMessage(): Buffer {
return this._compile().serialize();
}
/**
* Get the estimated fee associated with a transaction
*
* @param {Connection} connection Connection to RPC Endpoint.
*
* @returns {Promise<number | null>} The estimated fee for the transaction
*/
async getEstimatedFee(connection: Connection): Promise<number | null> {
return (await connection.getFeeForMessage(this.compileMessage())).value;
}
/**
* Specify the public keys which will be used to sign the Transaction.
* The first signer will be used as the transaction fee payer account.
*
* Signatures can be added with either `partialSign` or `addSignature`
*
* @deprecated Deprecated since v0.84.0. Only the fee payer needs to be
* specified and it can be set in the Transaction constructor or with the
* `feePayer` property.
*/
setSigners(...signers: Array<PublicKey>) {
if (signers.length === 0) {
throw new Error('No signers');
}
const seen = new Set();
this.signatures = signers
.filter(publicKey => {
const key = publicKey.toString();
if (seen.has(key)) {
return false;
} else {
seen.add(key);
return true;
}
})
.map(publicKey => ({signature: null, publicKey}));
}
/**
* Sign the Transaction with the specified signers. Multiple signatures may
* be applied to a Transaction. The first signature is considered "primary"
* and is used identify and confirm transactions.
*
* If the Transaction `feePayer` is not set, the first signer will be used
* as the transaction fee payer account.
*
* Transaction fields should not be modified after the first call to `sign`,
* as doing so may invalidate the signature and cause the Transaction to be
* rejected.
*
* The Transaction must be assigned a valid `recentBlockhash` before invoking this method
*
* @param {Array<Signer>} signers Array of signers that will sign the transaction
*/
sign(...signers: Array<Signer>) {
if (signers.length === 0) {
throw new Error('No signers');
}
// Dedupe signers
const seen = new Set();
const uniqueSigners = [];
for (const signer of signers) {
const key = signer.publicKey.toString();
if (seen.has(key)) {
continue;
} else {
seen.add(key);
uniqueSigners.push(signer);
}
}
this.signatures = uniqueSigners.map(signer => ({
signature: null,
publicKey: signer.publicKey,
}));
const message = this._compile();
this._partialSign(message, ...uniqueSigners);
}
/**
* Partially sign a transaction with the specified accounts. All accounts must
* correspond to either the fee payer or a signer account in the transaction
* instructions.
*
* All the caveats from the `sign` method apply to `partialSign`
*
* @param {Array<Signer>} signers Array of signers that will sign the transaction
*/
partialSign(...signers: Array<Signer>) {
if (signers.length === 0) {
throw new Error('No signers');
}
// Dedupe signers
const seen = new Set();
const uniqueSigners = [];
for (const signer of signers) {
const key = signer.publicKey.toString();
if (seen.has(key)) {
continue;
} else {
seen.add(key);
uniqueSigners.push(signer);
}
}
const message = this._compile();
this._partialSign(message, ...uniqueSigners);
}
/**
* @internal
*/
_partialSign(message: Message, ...signers: Array<Signer>) {
const signData = message.serialize();
signers.forEach(signer => {
const signature = sign(signData, signer.secretKey);
this._addSignature(signer.publicKey, toBuffer(signature));
});
}
/**
* Add an externally created signature to a transaction. The public key
* must correspond to either the fee payer or a signer account in the transaction
* instructions.
*
* @param {PublicKey} pubkey Public key that will be added to the transaction.
* @param {Buffer} signature An externally created signature to add to the transaction.
*/
addSignature(pubkey: PublicKey, signature: Buffer) {
this._compile(); // Ensure signatures array is populated
this._addSignature(pubkey, signature);
}
/**
* @internal
*/
_addSignature(pubkey: PublicKey, signature: Buffer) {
invariant(signature.length === 64);
const index = this.signatures.findIndex(sigpair =>
pubkey.equals(sigpair.publicKey),
);
if (index < 0) {
throw new Error(`unknown signer: ${pubkey.toString()}`);
}
this.signatures[index].signature = Buffer.from(signature);
}
/**
* Verify signatures of a Transaction
* Optional parameter specifies if we're expecting a fully signed Transaction or a partially signed one.
* If no boolean is provided, we expect a fully signed Transaction by default.
*
* @param {boolean} [requireAllSignatures=true] Require a fully signed Transaction
*/
verifySignatures(requireAllSignatures: boolean = true): boolean {
const signatureErrors = this._getMessageSignednessErrors(
this.serializeMessage(),
requireAllSignatures,
);
return !signatureErrors;
}
/**
* @internal
*/
_getMessageSignednessErrors(
message: Uint8Array,
requireAllSignatures: boolean,
): MessageSignednessErrors | undefined {
const errors: MessageSignednessErrors = {};
for (const {signature, publicKey} of this.signatures) {
if (signature === null) {
if (requireAllSignatures) {
(errors.missing ||= []).push(publicKey);
}
} else {
if (!verify(signature, message, publicKey.toBytes())) {
(errors.invalid ||= []).push(publicKey);
}
}
}
return errors.invalid || errors.missing ? errors : undefined;
}
/**
* Serialize the Transaction in the wire format.
*
* @param {Buffer} [config] Config of transaction.
*
* @returns {Buffer} Signature of transaction in wire format.
*/
serialize(config?: SerializeConfig): Buffer {
const {requireAllSignatures, verifySignatures} = Object.assign(
{requireAllSignatures: true, verifySignatures: true},
config,
);
const signData = this.serializeMessage();
if (verifySignatures) {
const sigErrors = this._getMessageSignednessErrors(
signData,
requireAllSignatures,
);
if (sigErrors) {
let errorMessage = 'Signature verification failed.';
if (sigErrors.invalid) {
errorMessage += `\nInvalid signature for public key${
sigErrors.invalid.length === 1 ? '' : '(s)'
} [\`${sigErrors.invalid.map(p => p.toBase58()).join('`, `')}\`].`;
}
if (sigErrors.missing) {
errorMessage += `\nMissing signature for public key${
sigErrors.missing.length === 1 ? '' : '(s)'
} [\`${sigErrors.missing.map(p => p.toBase58()).join('`, `')}\`].`;
}
throw new Error(errorMessage);
}
}
return this._serialize(signData);
}
/**
* @internal
*/
_serialize(signData: Buffer): Buffer {
const {signatures} = this;
const signatureCount: number[] = [];
shortvec.encodeLength(signatureCount, signatures.length);
const transactionLength =
signatureCount.length + signatures.length * 64 + signData.length;
const wireTransaction = Buffer.alloc(transactionLength);
invariant(signatures.length < 256);
Buffer.from(signatureCount).copy(wireTransaction, 0);
signatures.forEach(({signature}, index) => {
if (signature !== null) {
invariant(signature.length === 64, `signature has invalid length`);
Buffer.from(signature).copy(
wireTransaction,
signatureCount.length + index * 64,
);
}
});
signData.copy(
wireTransaction,
signatureCount.length + signatures.length * 64,
);
invariant(
wireTransaction.length <= PACKET_DATA_SIZE,
`Transaction too large: ${wireTransaction.length} > ${PACKET_DATA_SIZE}`,
);
return wireTransaction;
}
/**
* Deprecated method
* @internal
*/
get keys(): Array<PublicKey> {
invariant(this.instructions.length === 1);
return this.instructions[0].keys.map(keyObj => keyObj.pubkey);
}
/**
* Deprecated method
* @internal
*/
get programId(): PublicKey {
invariant(this.instructions.length === 1);
return this.instructions[0].programId;
}
/**
* Deprecated method
* @internal
*/
get data(): Buffer {
invariant(this.instructions.length === 1);
return this.instructions[0].data;
}
/**
* Parse a wire transaction into a Transaction object.
*
* @param {Buffer | Uint8Array | Array<number>} buffer Signature of wire Transaction
*
* @returns {Transaction} Transaction associated with the signature
*/
static from(buffer: Buffer | Uint8Array | Array<number>): Transaction {
// Slice up wire data
let byteArray = [...buffer];
const signatureCount = shortvec.decodeLength(byteArray);
let signatures = [];
for (let i = 0; i < signatureCount; i++) {
const signature = guardedSplice(byteArray, 0, SIGNATURE_LENGTH_IN_BYTES);
signatures.push(bs58.encode(Buffer.from(signature)));
}
return Transaction.populate(Message.from(byteArray), signatures);
}
/**
* Populate Transaction object from message and signatures
*
* @param {Message} message Message of transaction
* @param {Array<string>} signatures List of signatures to assign to the transaction
*
* @returns {Transaction} The populated Transaction
*/
static populate(
message: Message,
signatures: Array<string> = [],
): Transaction {
const transaction = new Transaction();
transaction.recentBlockhash = message.recentBlockhash;
if (message.header.numRequiredSignatures > 0) {
transaction.feePayer = message.accountKeys[0];
}
signatures.forEach((signature, index) => {
const sigPubkeyPair = {
signature:
signature == bs58.encode(DEFAULT_SIGNATURE)
? null
: bs58.decode(signature),
publicKey: message.accountKeys[index],
};
transaction.signatures.push(sigPubkeyPair);
});
message.instructions.forEach(instruction => {
const keys = instruction.accounts.map(account => {
const pubkey = message.accountKeys[account];
return {
pubkey,
isSigner:
transaction.signatures.some(
keyObj => keyObj.publicKey.toString() === pubkey.toString(),
) || message.isAccountSigner(account),
isWritable: message.isAccountWritable(account),
};
});
transaction.instructions.push(
new TransactionInstruction({
keys,
programId: message.accountKeys[instruction.programIdIndex],
data: bs58.decode(instruction.data),
}),
);
});
transaction._message = message;
transaction._json = transaction.toJSON();
return transaction;
}
}

140
node_modules/@solana/web3.js/src/transaction/message.ts generated vendored Normal file
View File

@@ -0,0 +1,140 @@
import {AccountKeysFromLookups} from '../message/account-keys';
import assert from '../utils/assert';
import {toBuffer} from '../utils/to-buffer';
import {Blockhash} from '../blockhash';
import {Message, MessageV0, VersionedMessage} from '../message';
import {PublicKey} from '../publickey';
import {AddressLookupTableAccount} from '../programs';
import {AccountMeta, TransactionInstruction} from './legacy';
export type TransactionMessageArgs = {
payerKey: PublicKey;
instructions: Array<TransactionInstruction>;
recentBlockhash: Blockhash;
};
export type DecompileArgs =
| {
accountKeysFromLookups: AccountKeysFromLookups;
}
| {
addressLookupTableAccounts: AddressLookupTableAccount[];
};
export class TransactionMessage {
payerKey: PublicKey;
instructions: Array<TransactionInstruction>;
recentBlockhash: Blockhash;
constructor(args: TransactionMessageArgs) {
this.payerKey = args.payerKey;
this.instructions = args.instructions;
this.recentBlockhash = args.recentBlockhash;
}
static decompile(
message: VersionedMessage,
args?: DecompileArgs,
): TransactionMessage {
const {header, compiledInstructions, recentBlockhash} = message;
const {
numRequiredSignatures,
numReadonlySignedAccounts,
numReadonlyUnsignedAccounts,
} = header;
const numWritableSignedAccounts =
numRequiredSignatures - numReadonlySignedAccounts;
assert(numWritableSignedAccounts > 0, 'Message header is invalid');
const numWritableUnsignedAccounts =
message.staticAccountKeys.length -
numRequiredSignatures -
numReadonlyUnsignedAccounts;
assert(numWritableUnsignedAccounts >= 0, 'Message header is invalid');
const accountKeys = message.getAccountKeys(args);
const payerKey = accountKeys.get(0);
if (payerKey === undefined) {
throw new Error(
'Failed to decompile message because no account keys were found',
);
}
const instructions: TransactionInstruction[] = [];
for (const compiledIx of compiledInstructions) {
const keys: AccountMeta[] = [];
for (const keyIndex of compiledIx.accountKeyIndexes) {
const pubkey = accountKeys.get(keyIndex);
if (pubkey === undefined) {
throw new Error(
`Failed to find key for account key index ${keyIndex}`,
);
}
const isSigner = keyIndex < numRequiredSignatures;
let isWritable;
if (isSigner) {
isWritable = keyIndex < numWritableSignedAccounts;
} else if (keyIndex < accountKeys.staticAccountKeys.length) {
isWritable =
keyIndex - numRequiredSignatures < numWritableUnsignedAccounts;
} else {
isWritable =
keyIndex - accountKeys.staticAccountKeys.length <
// accountKeysFromLookups cannot be undefined because we already found a pubkey for this index above
accountKeys.accountKeysFromLookups!.writable.length;
}
keys.push({
pubkey,
isSigner: keyIndex < header.numRequiredSignatures,
isWritable,
});
}
const programId = accountKeys.get(compiledIx.programIdIndex);
if (programId === undefined) {
throw new Error(
`Failed to find program id for program id index ${compiledIx.programIdIndex}`,
);
}
instructions.push(
new TransactionInstruction({
programId,
data: toBuffer(compiledIx.data),
keys,
}),
);
}
return new TransactionMessage({
payerKey,
instructions,
recentBlockhash,
});
}
compileToLegacyMessage(): Message {
return Message.compile({
payerKey: this.payerKey,
recentBlockhash: this.recentBlockhash,
instructions: this.instructions,
});
}
compileToV0Message(
addressLookupTableAccounts?: AddressLookupTableAccount[],
): MessageV0 {
return MessageV0.compile({
payerKey: this.payerKey,
recentBlockhash: this.recentBlockhash,
instructions: this.instructions,
addressLookupTableAccounts,
});
}
}

View File

@@ -0,0 +1,127 @@
import * as BufferLayout from '@solana/buffer-layout';
import {Signer} from '../keypair';
import assert from '../utils/assert';
import {VersionedMessage} from '../message/versioned';
import {SIGNATURE_LENGTH_IN_BYTES} from './constants';
import * as shortvec from '../utils/shortvec-encoding';
import * as Layout from '../layout';
import {sign} from '../utils/ed25519';
import {PublicKey} from '../publickey';
import {guardedSplice} from '../utils/guarded-array-utils';
export type TransactionVersion = 'legacy' | 0;
/**
* Versioned transaction class
*/
export class VersionedTransaction {
signatures: Array<Uint8Array>;
message: VersionedMessage;
get version(): TransactionVersion {
return this.message.version;
}
constructor(message: VersionedMessage, signatures?: Array<Uint8Array>) {
if (signatures !== undefined) {
assert(
signatures.length === message.header.numRequiredSignatures,
'Expected signatures length to be equal to the number of required signatures',
);
this.signatures = signatures;
} else {
const defaultSignatures = [];
for (let i = 0; i < message.header.numRequiredSignatures; i++) {
defaultSignatures.push(new Uint8Array(SIGNATURE_LENGTH_IN_BYTES));
}
this.signatures = defaultSignatures;
}
this.message = message;
}
serialize(): Uint8Array {
const serializedMessage = this.message.serialize();
const encodedSignaturesLength = Array<number>();
shortvec.encodeLength(encodedSignaturesLength, this.signatures.length);
const transactionLayout = BufferLayout.struct<{
encodedSignaturesLength: Uint8Array;
signatures: Array<Uint8Array>;
serializedMessage: Uint8Array;
}>([
BufferLayout.blob(
encodedSignaturesLength.length,
'encodedSignaturesLength',
),
BufferLayout.seq(
Layout.signature(),
this.signatures.length,
'signatures',
),
BufferLayout.blob(serializedMessage.length, 'serializedMessage'),
]);
const serializedTransaction = new Uint8Array(2048);
const serializedTransactionLength = transactionLayout.encode(
{
encodedSignaturesLength: new Uint8Array(encodedSignaturesLength),
signatures: this.signatures,
serializedMessage,
},
serializedTransaction,
);
return serializedTransaction.slice(0, serializedTransactionLength);
}
static deserialize(serializedTransaction: Uint8Array): VersionedTransaction {
let byteArray = [...serializedTransaction];
const signatures = [];
const signaturesLength = shortvec.decodeLength(byteArray);
for (let i = 0; i < signaturesLength; i++) {
signatures.push(
new Uint8Array(guardedSplice(byteArray, 0, SIGNATURE_LENGTH_IN_BYTES)),
);
}
const message = VersionedMessage.deserialize(new Uint8Array(byteArray));
return new VersionedTransaction(message, signatures);
}
sign(signers: Array<Signer>) {
const messageData = this.message.serialize();
const signerPubkeys = this.message.staticAccountKeys.slice(
0,
this.message.header.numRequiredSignatures,
);
for (const signer of signers) {
const signerIndex = signerPubkeys.findIndex(pubkey =>
pubkey.equals(signer.publicKey),
);
assert(
signerIndex >= 0,
`Cannot sign with non signer key ${signer.publicKey.toBase58()}`,
);
this.signatures[signerIndex] = sign(messageData, signer.secretKey);
}
}
addSignature(publicKey: PublicKey, signature: Uint8Array) {
assert(signature.byteLength === 64, 'Signature must be 64 bytes long');
const signerPubkeys = this.message.staticAccountKeys.slice(
0,
this.message.header.numRequiredSignatures,
);
const signerIndex = signerPubkeys.findIndex(pubkey =>
pubkey.equals(publicKey),
);
assert(
signerIndex >= 0,
`Can not add signature; \`${publicKey.toBase58()}\` is not required to sign this transaction`,
);
this.signatures[signerIndex] = signature;
}
}

8
node_modules/@solana/web3.js/src/utils/assert.ts generated vendored Normal file
View File

@@ -0,0 +1,8 @@
export default function (
condition: unknown,
message?: string,
): asserts condition {
if (!condition) {
throw new Error(message || 'Assertion failed');
}
}

24
node_modules/@solana/web3.js/src/utils/bigint.ts generated vendored Normal file
View File

@@ -0,0 +1,24 @@
import {Buffer} from 'buffer';
import {blob, Layout} from '@solana/buffer-layout';
import {getU64Codec} from '@solana/codecs-numbers';
export function u64(property?: string): Layout<bigint> {
const layout = blob(8 /* bytes */, property);
const decode = layout.decode.bind(layout);
const encode = layout.encode.bind(layout);
const bigIntLayout = layout as Layout<unknown> as Layout<bigint>;
const codec = getU64Codec();
bigIntLayout.decode = (buffer: Buffer, offset: number) => {
const src = decode(buffer as Uint8Array, offset);
return codec.decode(src);
};
bigIntLayout.encode = (bigInt: bigint, buffer: Buffer, offset: number) => {
const src = codec.encode(bigInt) as Uint8Array;
return encode(src, buffer as Uint8Array, offset);
};
return bigIntLayout;
}

38
node_modules/@solana/web3.js/src/utils/borsh-schema.ts generated vendored Normal file
View File

@@ -0,0 +1,38 @@
import {Buffer} from 'buffer';
import {serialize, deserialize, deserializeUnchecked} from 'borsh';
// Class wrapping a plain object
export class Struct {
constructor(properties: any) {
Object.assign(this, properties);
}
encode(): Buffer {
return Buffer.from(serialize(SOLANA_SCHEMA, this));
}
static decode(data: Buffer): any {
return deserialize(SOLANA_SCHEMA, this, data);
}
static decodeUnchecked(data: Buffer): any {
return deserializeUnchecked(SOLANA_SCHEMA, this, data);
}
}
// Class representing a Rust-compatible enum, since enums are only strings or
// numbers in pure JS
export class Enum extends Struct {
enum: string = '';
constructor(properties: any) {
super(properties);
if (Object.keys(properties).length !== 1) {
throw new Error('Enum can only take single value');
}
Object.keys(properties).map(key => {
this.enum = key;
});
}
}
export const SOLANA_SCHEMA: Map<Function, any> = new Map();

35
node_modules/@solana/web3.js/src/utils/cluster.ts generated vendored Normal file
View File

@@ -0,0 +1,35 @@
const endpoint = {
http: {
devnet: 'http://api.devnet.solana.com',
testnet: 'http://api.testnet.solana.com',
'mainnet-beta': 'http://api.mainnet-beta.solana.com/',
},
https: {
devnet: 'https://api.devnet.solana.com',
testnet: 'https://api.testnet.solana.com',
'mainnet-beta': 'https://api.mainnet-beta.solana.com/',
},
};
export type Cluster = 'devnet' | 'testnet' | 'mainnet-beta';
/**
* Retrieves the RPC API URL for the specified cluster
* @param {Cluster} [cluster="devnet"] - The cluster name of the RPC API URL to use. Possible options: 'devnet' | 'testnet' | 'mainnet-beta'
* @param {boolean} [tls="http"] - Use TLS when connecting to cluster.
*
* @returns {string} URL string of the RPC endpoint
*/
export function clusterApiUrl(cluster?: Cluster, tls?: boolean): string {
const key = tls === false ? 'http' : 'https';
if (!cluster) {
return endpoint[key]['devnet'];
}
const url = endpoint[key][cluster];
if (!url) {
throw new Error(`Unknown ${key} cluster: ${cluster}`);
}
return url;
}

43
node_modules/@solana/web3.js/src/utils/ed25519.ts generated vendored Normal file
View File

@@ -0,0 +1,43 @@
import {ed25519} from '@noble/curves/ed25519';
/**
* A 64 byte secret key, the first 32 bytes of which is the
* private scalar and the last 32 bytes is the public key.
* Read more: https://blog.mozilla.org/warner/2011/11/29/ed25519-keys/
*/
type Ed25519SecretKey = Uint8Array;
/**
* Ed25519 Keypair
*/
export interface Ed25519Keypair {
publicKey: Uint8Array;
secretKey: Ed25519SecretKey;
}
export const generatePrivateKey = ed25519.utils.randomPrivateKey;
export const generateKeypair = (): Ed25519Keypair => {
const privateScalar = ed25519.utils.randomPrivateKey();
const publicKey = getPublicKey(privateScalar);
const secretKey = new Uint8Array(64);
secretKey.set(privateScalar);
secretKey.set(publicKey, 32);
return {
publicKey,
secretKey,
};
};
export const getPublicKey = ed25519.getPublicKey;
export function isOnCurve(publicKey: Uint8Array): boolean {
try {
ed25519.ExtendedPoint.fromHex(publicKey);
return true;
} catch {
return false;
}
}
export const sign = (
message: Parameters<typeof ed25519.sign>[0],
secretKey: Ed25519SecretKey,
) => ed25519.sign(message, secretKey.slice(0, 32));
export const verify = ed25519.verify;

View File

@@ -0,0 +1,34 @@
const END_OF_BUFFER_ERROR_MESSAGE = 'Reached end of buffer unexpectedly';
/**
* Delegates to `Array#shift`, but throws if the array is zero-length.
*/
export function guardedShift<T>(byteArray: T[]): T {
if (byteArray.length === 0) {
throw new Error(END_OF_BUFFER_ERROR_MESSAGE);
}
return byteArray.shift() as T;
}
/**
* Delegates to `Array#splice`, but throws if the section being spliced out extends past the end of
* the array.
*/
export function guardedSplice<T>(
byteArray: T[],
...args:
| [start: number, deleteCount?: number]
| [start: number, deleteCount: number, ...items: T[]]
): T[] {
const [start] = args;
if (
args.length === 2 // Implies that `deleteCount` was supplied
? start + (args[1] ?? 0) > byteArray.length
: start >= byteArray.length
) {
throw new Error(END_OF_BUFFER_ERROR_MESSAGE);
}
return byteArray.splice(
...(args as Parameters<typeof Array.prototype.splice>),
);
}

5
node_modules/@solana/web3.js/src/utils/index.ts generated vendored Normal file
View File

@@ -0,0 +1,5 @@
export * from './borsh-schema';
export * from './cluster';
export type {Ed25519Keypair} from './ed25519';
export * from './send-and-confirm-raw-transaction';
export * from './send-and-confirm-transaction';

View File

@@ -0,0 +1,26 @@
const URL_RE = /^[^:]+:\/\/([^:[]+|\[[^\]]+\])(:\d+)?(.*)/i;
export function makeWebsocketUrl(endpoint: string) {
const matches = endpoint.match(URL_RE);
if (matches == null) {
throw TypeError(`Failed to validate endpoint URL \`${endpoint}\``);
}
const [
_, // eslint-disable-line @typescript-eslint/no-unused-vars
hostish,
portWithColon,
rest,
] = matches;
const protocol = endpoint.startsWith('https:') ? 'wss:' : 'ws:';
const startPort =
portWithColon == null ? null : parseInt(portWithColon.slice(1), 10);
const websocketPort =
// Only shift the port by +1 as a convention for ws(s) only if given endpoint
// is explicitly specifying the endpoint port (HTTP-based RPC), assuming
// we're directly trying to connect to agave-validator's ws listening port.
// When the endpoint omits the port, we're connecting to the protocol
// default ports: http(80) or https(443) and it's assumed we're behind a reverse
// proxy which manages WebSocket upgrade and backend port redirection.
startPort == null ? '' : `:${startPort + 1}`;
return `${protocol}//${hostish}${websocketPort}${rest}`;
}

View File

@@ -0,0 +1,14 @@
export function promiseTimeout<T>(
promise: Promise<T>,
timeoutMs: number,
): Promise<T | null> {
let timeoutId: ReturnType<typeof setTimeout>;
const timeoutPromise: Promise<null> = new Promise(resolve => {
timeoutId = setTimeout(() => resolve(null), timeoutMs);
});
return Promise.race([promise, timeoutPromise]).then((result: T | null) => {
clearTimeout(timeoutId);
return result;
});
}

11
node_modules/@solana/web3.js/src/utils/secp256k1.ts generated vendored Normal file
View File

@@ -0,0 +1,11 @@
import {secp256k1} from '@noble/curves/secp256k1';
export const ecdsaSign = (
msgHash: Parameters<typeof secp256k1.sign>[0],
privKey: Parameters<typeof secp256k1.sign>[1],
) => {
const signature = secp256k1.sign(msgHash, privKey);
return [signature.toCompactRawBytes(), signature.recovery!] as const;
};
export const isValidPrivateKey = secp256k1.utils.isValidPrivateKey;
export const publicKeyCreate = secp256k1.getPublicKey;

View File

@@ -0,0 +1,110 @@
import type {Buffer} from 'buffer';
import {
BlockheightBasedTransactionConfirmationStrategy,
Connection,
DurableNonceTransactionConfirmationStrategy,
TransactionConfirmationStrategy,
} from '../connection';
import type {TransactionSignature} from '../transaction';
import type {ConfirmOptions} from '../connection';
import {SendTransactionError} from '../errors';
/**
* Send and confirm a raw transaction
*
* If `commitment` option is not specified, defaults to 'max' commitment.
*
* @param {Connection} connection
* @param {Buffer} rawTransaction
* @param {TransactionConfirmationStrategy} confirmationStrategy
* @param {ConfirmOptions} [options]
* @returns {Promise<TransactionSignature>}
*/
export async function sendAndConfirmRawTransaction(
connection: Connection,
rawTransaction: Buffer,
confirmationStrategy: TransactionConfirmationStrategy,
options?: ConfirmOptions,
): Promise<TransactionSignature>;
/**
* @deprecated Calling `sendAndConfirmRawTransaction()` without a `confirmationStrategy`
* is no longer supported and will be removed in a future version.
*/
// eslint-disable-next-line no-redeclare
export async function sendAndConfirmRawTransaction(
connection: Connection,
rawTransaction: Buffer,
options?: ConfirmOptions,
): Promise<TransactionSignature>;
// eslint-disable-next-line no-redeclare
export async function sendAndConfirmRawTransaction(
connection: Connection,
rawTransaction: Buffer,
confirmationStrategyOrConfirmOptions:
| TransactionConfirmationStrategy
| ConfirmOptions
| undefined,
maybeConfirmOptions?: ConfirmOptions,
): Promise<TransactionSignature> {
let confirmationStrategy: TransactionConfirmationStrategy | undefined;
let options: ConfirmOptions | undefined;
if (
confirmationStrategyOrConfirmOptions &&
Object.prototype.hasOwnProperty.call(
confirmationStrategyOrConfirmOptions,
'lastValidBlockHeight',
)
) {
confirmationStrategy =
confirmationStrategyOrConfirmOptions as BlockheightBasedTransactionConfirmationStrategy;
options = maybeConfirmOptions;
} else if (
confirmationStrategyOrConfirmOptions &&
Object.prototype.hasOwnProperty.call(
confirmationStrategyOrConfirmOptions,
'nonceValue',
)
) {
confirmationStrategy =
confirmationStrategyOrConfirmOptions as DurableNonceTransactionConfirmationStrategy;
options = maybeConfirmOptions;
} else {
options = confirmationStrategyOrConfirmOptions as
| ConfirmOptions
| undefined;
}
const sendOptions = options && {
skipPreflight: options.skipPreflight,
preflightCommitment: options.preflightCommitment || options.commitment,
minContextSlot: options.minContextSlot,
};
const signature = await connection.sendRawTransaction(
rawTransaction,
sendOptions,
);
const commitment = options && options.commitment;
const confirmationPromise = confirmationStrategy
? connection.confirmTransaction(confirmationStrategy, commitment)
: connection.confirmTransaction(signature, commitment);
const status = (await confirmationPromise).value;
if (status.err) {
if (signature != null) {
throw new SendTransactionError({
action: sendOptions?.skipPreflight ? 'send' : 'simulate',
signature: signature,
transactionMessage: `Status: (${JSON.stringify(status)})`,
});
}
throw new Error(
`Raw transaction ${signature} failed (${JSON.stringify(status)})`,
);
}
return signature;
}

View File

@@ -0,0 +1,106 @@
import {Connection, SignatureResult} from '../connection';
import {Transaction} from '../transaction';
import type {ConfirmOptions} from '../connection';
import type {Signer} from '../keypair';
import type {TransactionSignature} from '../transaction';
import {SendTransactionError} from '../errors';
/**
* Sign, send and confirm a transaction.
*
* If `commitment` option is not specified, defaults to 'max' commitment.
*
* @param {Connection} connection
* @param {Transaction} transaction
* @param {Array<Signer>} signers
* @param {ConfirmOptions} [options]
* @returns {Promise<TransactionSignature>}
*/
export async function sendAndConfirmTransaction(
connection: Connection,
transaction: Transaction,
signers: Array<Signer>,
options?: ConfirmOptions &
Readonly<{
// A signal that, when aborted, cancels any outstanding transaction confirmation operations
abortSignal?: AbortSignal;
}>,
): Promise<TransactionSignature> {
const sendOptions = options && {
skipPreflight: options.skipPreflight,
preflightCommitment: options.preflightCommitment || options.commitment,
maxRetries: options.maxRetries,
minContextSlot: options.minContextSlot,
};
const signature = await connection.sendTransaction(
transaction,
signers,
sendOptions,
);
let status: SignatureResult;
if (
transaction.recentBlockhash != null &&
transaction.lastValidBlockHeight != null
) {
status = (
await connection.confirmTransaction(
{
abortSignal: options?.abortSignal,
signature: signature,
blockhash: transaction.recentBlockhash,
lastValidBlockHeight: transaction.lastValidBlockHeight,
},
options && options.commitment,
)
).value;
} else if (
transaction.minNonceContextSlot != null &&
transaction.nonceInfo != null
) {
const {nonceInstruction} = transaction.nonceInfo;
const nonceAccountPubkey = nonceInstruction.keys[0].pubkey;
status = (
await connection.confirmTransaction(
{
abortSignal: options?.abortSignal,
minContextSlot: transaction.minNonceContextSlot,
nonceAccountPubkey,
nonceValue: transaction.nonceInfo.nonce,
signature,
},
options && options.commitment,
)
).value;
} else {
if (options?.abortSignal != null) {
console.warn(
'sendAndConfirmTransaction(): A transaction with a deprecated confirmation strategy was ' +
'supplied along with an `abortSignal`. Only transactions having `lastValidBlockHeight` ' +
'or a combination of `nonceInfo` and `minNonceContextSlot` are abortable.',
);
}
status = (
await connection.confirmTransaction(
signature,
options && options.commitment,
)
).value;
}
if (status.err) {
if (signature != null) {
throw new SendTransactionError({
action: 'send',
signature: signature,
transactionMessage: `Status: (${JSON.stringify(status)})`,
});
}
throw new Error(
`Transaction ${signature} failed (${JSON.stringify(status)})`,
);
}
return signature;
}

View File

@@ -0,0 +1,28 @@
export function decodeLength(bytes: Array<number>): number {
let len = 0;
let size = 0;
for (;;) {
let elem = bytes.shift() as number;
len |= (elem & 0x7f) << (size * 7);
size += 1;
if ((elem & 0x80) === 0) {
break;
}
}
return len;
}
export function encodeLength(bytes: Array<number>, len: number) {
let rem_len = len;
for (;;) {
let elem = rem_len & 0x7f;
rem_len >>= 7;
if (rem_len == 0) {
bytes.push(elem);
break;
} else {
elem |= 0x80;
bytes.push(elem);
}
}
}

4
node_modules/@solana/web3.js/src/utils/sleep.ts generated vendored Normal file
View File

@@ -0,0 +1,4 @@
// zzz
export function sleep(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}

11
node_modules/@solana/web3.js/src/utils/to-buffer.ts generated vendored Normal file
View File

@@ -0,0 +1,11 @@
import {Buffer} from 'buffer';
export const toBuffer = (arr: Buffer | Uint8Array | Array<number>): Buffer => {
if (Buffer.isBuffer(arr)) {
return arr;
} else if (arr instanceof Uint8Array) {
return Buffer.from(arr.buffer, arr.byteOffset, arr.byteLength);
} else {
return Buffer.from(arr);
}
};

108
node_modules/@solana/web3.js/src/validator-info.ts generated vendored Normal file
View File

@@ -0,0 +1,108 @@
import {Buffer} from 'buffer';
import {
assert as assertType,
optional,
string,
type as pick,
} from 'superstruct';
import * as Layout from './layout';
import * as shortvec from './utils/shortvec-encoding';
import {PublicKey, PUBLIC_KEY_LENGTH} from './publickey';
import {guardedShift, guardedSplice} from './utils/guarded-array-utils';
export const VALIDATOR_INFO_KEY = new PublicKey(
'Va1idator1nfo111111111111111111111111111111',
);
/**
* @internal
*/
type ConfigKey = {
publicKey: PublicKey;
isSigner: boolean;
};
/**
* Info used to identity validators.
*/
export type Info = {
/** validator name */
name: string;
/** optional, validator website */
website?: string;
/** optional, extra information the validator chose to share */
details?: string;
/** optional, validator logo URL */
iconUrl?: string;
/** optional, used to identify validators on keybase.io */
keybaseUsername?: string;
};
const InfoString = pick({
name: string(),
website: optional(string()),
details: optional(string()),
iconUrl: optional(string()),
keybaseUsername: optional(string()),
});
/**
* ValidatorInfo class
*/
export class ValidatorInfo {
/**
* validator public key
*/
key: PublicKey;
/**
* validator information
*/
info: Info;
/**
* Construct a valid ValidatorInfo
*
* @param key validator public key
* @param info validator information
*/
constructor(key: PublicKey, info: Info) {
this.key = key;
this.info = info;
}
/**
* Deserialize ValidatorInfo from the config account data. Exactly two config
* keys are required in the data.
*
* @param buffer config account data
* @return null if info was not found
*/
static fromConfigData(
buffer: Buffer | Uint8Array | Array<number>,
): ValidatorInfo | null {
let byteArray = [...buffer];
const configKeyCount = shortvec.decodeLength(byteArray);
if (configKeyCount !== 2) return null;
const configKeys: Array<ConfigKey> = [];
for (let i = 0; i < 2; i++) {
const publicKey = new PublicKey(
guardedSplice(byteArray, 0, PUBLIC_KEY_LENGTH),
);
const isSigner = guardedShift(byteArray) === 1;
configKeys.push({publicKey, isSigner});
}
if (configKeys[0].publicKey.equals(VALIDATOR_INFO_KEY)) {
if (configKeys[1].isSigner) {
const rawInfo: any = Layout.rustString().decode(Buffer.from(byteArray));
const info = JSON.parse(rawInfo as string);
assertType(info, InfoString);
return new ValidatorInfo(configKeys[1].publicKey, info);
}
}
return null;
}
}

236
node_modules/@solana/web3.js/src/vote-account.ts generated vendored Normal file
View File

@@ -0,0 +1,236 @@
import * as BufferLayout from '@solana/buffer-layout';
import type {Buffer} from 'buffer';
import * as Layout from './layout';
import {PublicKey} from './publickey';
import {toBuffer} from './utils/to-buffer';
export const VOTE_PROGRAM_ID = new PublicKey(
'Vote111111111111111111111111111111111111111',
);
export type Lockout = {
slot: number;
confirmationCount: number;
};
/**
* History of how many credits earned by the end of each epoch
*/
export type EpochCredits = Readonly<{
epoch: number;
credits: number;
prevCredits: number;
}>;
export type AuthorizedVoter = Readonly<{
epoch: number;
authorizedVoter: PublicKey;
}>;
type AuthorizedVoterRaw = Readonly<{
authorizedVoter: Uint8Array;
epoch: number;
}>;
type PriorVoters = Readonly<{
buf: PriorVoterRaw[];
idx: number;
isEmpty: number;
}>;
export type PriorVoter = Readonly<{
authorizedPubkey: PublicKey;
epochOfLastAuthorizedSwitch: number;
targetEpoch: number;
}>;
type PriorVoterRaw = Readonly<{
authorizedPubkey: Uint8Array;
epochOfLastAuthorizedSwitch: number;
targetEpoch: number;
}>;
export type BlockTimestamp = Readonly<{
slot: number;
timestamp: number;
}>;
type VoteAccountData = Readonly<{
authorizedVoters: AuthorizedVoterRaw[];
authorizedWithdrawer: Uint8Array;
commission: number;
epochCredits: EpochCredits[];
lastTimestamp: BlockTimestamp;
nodePubkey: Uint8Array;
priorVoters: PriorVoters;
rootSlot: number;
rootSlotValid: number;
votes: Lockout[];
}>;
/**
* See https://github.com/solana-labs/solana/blob/8a12ed029cfa38d4a45400916c2463fb82bbec8c/programs/vote_api/src/vote_state.rs#L68-L88
*
* @internal
*/
const VoteAccountLayout = BufferLayout.struct<VoteAccountData>([
Layout.publicKey('nodePubkey'),
Layout.publicKey('authorizedWithdrawer'),
BufferLayout.u8('commission'),
BufferLayout.nu64(), // votes.length
BufferLayout.seq<Lockout>(
BufferLayout.struct([
BufferLayout.nu64('slot'),
BufferLayout.u32('confirmationCount'),
]),
BufferLayout.offset(BufferLayout.u32(), -8),
'votes',
),
BufferLayout.u8('rootSlotValid'),
BufferLayout.nu64('rootSlot'),
BufferLayout.nu64(), // authorizedVoters.length
BufferLayout.seq<AuthorizedVoterRaw>(
BufferLayout.struct([
BufferLayout.nu64('epoch'),
Layout.publicKey('authorizedVoter'),
]),
BufferLayout.offset(BufferLayout.u32(), -8),
'authorizedVoters',
),
BufferLayout.struct<PriorVoters>(
[
BufferLayout.seq(
BufferLayout.struct([
Layout.publicKey('authorizedPubkey'),
BufferLayout.nu64('epochOfLastAuthorizedSwitch'),
BufferLayout.nu64('targetEpoch'),
]),
32,
'buf',
),
BufferLayout.nu64('idx'),
BufferLayout.u8('isEmpty'),
],
'priorVoters',
),
BufferLayout.nu64(), // epochCredits.length
BufferLayout.seq<EpochCredits>(
BufferLayout.struct([
BufferLayout.nu64('epoch'),
BufferLayout.nu64('credits'),
BufferLayout.nu64('prevCredits'),
]),
BufferLayout.offset(BufferLayout.u32(), -8),
'epochCredits',
),
BufferLayout.struct<BlockTimestamp>(
[BufferLayout.nu64('slot'), BufferLayout.nu64('timestamp')],
'lastTimestamp',
),
]);
type VoteAccountArgs = {
nodePubkey: PublicKey;
authorizedWithdrawer: PublicKey;
commission: number;
rootSlot: number | null;
votes: Lockout[];
authorizedVoters: AuthorizedVoter[];
priorVoters: PriorVoter[];
epochCredits: EpochCredits[];
lastTimestamp: BlockTimestamp;
};
/**
* VoteAccount class
*/
export class VoteAccount {
nodePubkey: PublicKey;
authorizedWithdrawer: PublicKey;
commission: number;
rootSlot: number | null;
votes: Lockout[];
authorizedVoters: AuthorizedVoter[];
priorVoters: PriorVoter[];
epochCredits: EpochCredits[];
lastTimestamp: BlockTimestamp;
/**
* @internal
*/
constructor(args: VoteAccountArgs) {
this.nodePubkey = args.nodePubkey;
this.authorizedWithdrawer = args.authorizedWithdrawer;
this.commission = args.commission;
this.rootSlot = args.rootSlot;
this.votes = args.votes;
this.authorizedVoters = args.authorizedVoters;
this.priorVoters = args.priorVoters;
this.epochCredits = args.epochCredits;
this.lastTimestamp = args.lastTimestamp;
}
/**
* Deserialize VoteAccount from the account data.
*
* @param buffer account data
* @return VoteAccount
*/
static fromAccountData(
buffer: Buffer | Uint8Array | Array<number>,
): VoteAccount {
const versionOffset = 4;
const va = VoteAccountLayout.decode(toBuffer(buffer), versionOffset);
let rootSlot: number | null = va.rootSlot;
if (!va.rootSlotValid) {
rootSlot = null;
}
return new VoteAccount({
nodePubkey: new PublicKey(va.nodePubkey),
authorizedWithdrawer: new PublicKey(va.authorizedWithdrawer),
commission: va.commission,
votes: va.votes,
rootSlot,
authorizedVoters: va.authorizedVoters.map(parseAuthorizedVoter),
priorVoters: getPriorVoters(va.priorVoters),
epochCredits: va.epochCredits,
lastTimestamp: va.lastTimestamp,
});
}
}
function parseAuthorizedVoter({
authorizedVoter,
epoch,
}: AuthorizedVoterRaw): AuthorizedVoter {
return {
epoch,
authorizedVoter: new PublicKey(authorizedVoter),
};
}
function parsePriorVoters({
authorizedPubkey,
epochOfLastAuthorizedSwitch,
targetEpoch,
}: PriorVoterRaw): PriorVoter {
return {
authorizedPubkey: new PublicKey(authorizedPubkey),
epochOfLastAuthorizedSwitch,
targetEpoch,
};
}
function getPriorVoters({buf, idx, isEmpty}: PriorVoters): PriorVoter[] {
if (isEmpty) {
return [];
}
return [
...buf.slice(idx + 1).map(parsePriorVoters),
...buf.slice(0, idx).map(parsePriorVoters),
];
}