solana/web3.js/src/system-program.js

847 lines
23 KiB
JavaScript
Raw Normal View History

2018-09-18 12:46:59 -07:00
// @flow
import * as BufferLayout from 'buffer-layout';
2018-09-18 12:46:59 -07:00
import {encodeData, decodeData} from './instruction';
import * as Layout from './layout';
import {NONCE_ACCOUNT_LENGTH} from './nonce-account';
import {PublicKey} from './publickey';
import {SYSVAR_RECENT_BLOCKHASHES_PUBKEY, SYSVAR_RENT_PUBKEY} from './sysvar';
import {Transaction, TransactionInstruction} from './transaction';
/**
* Create account system transaction params
* @typedef {Object} CreateAccountParams
* @property {PublicKey} fromPubkey
* @property {PublicKey} newAccountPubkey
* @property {number} lamports
* @property {number} space
* @property {PublicKey} programId
*/
export type CreateAccountParams = {|
fromPubkey: PublicKey,
newAccountPubkey: PublicKey,
lamports: number,
space: number,
programId: PublicKey,
|};
/**
* Transfer system transaction params
* @typedef {Object} TransferParams
* @property {PublicKey} fromPubkey
* @property {PublicKey} toPubkey
* @property {number} lamports
*/
export type TransferParams = {|
fromPubkey: PublicKey,
toPubkey: PublicKey,
lamports: number,
|};
/**
* Assign system transaction params
* @typedef {Object} AssignParams
* @property {PublicKey} accountPubkey
* @property {PublicKey} programId
*/
export type AssignParams = {|
accountPubkey: PublicKey,
programId: PublicKey,
|};
/**
* Create account with seed system transaction params
* @typedef {Object} CreateAccountWithSeedParams
* @property {PublicKey} fromPubkey
* @property {PublicKey} newAccountPubkey
* @property {PublicKey} basePubkey
* @property {string} seed
* @property {number} lamports
* @property {number} space
* @property {PublicKey} programId
*/
export type CreateAccountWithSeedParams = {|
fromPubkey: PublicKey,
newAccountPubkey: PublicKey,
basePubkey: PublicKey,
seed: string,
lamports: number,
space: number,
programId: PublicKey,
|};
/**
* Create nonce account system transaction params
* @typedef {Object} CreateNonceAccountParams
* @property {PublicKey} fromPubkey
* @property {PublicKey} noncePubkey
* @property {PublicKey} authorizedPubkey
* @property {number} lamports
*/
export type CreateNonceAccountParams = {|
fromPubkey: PublicKey,
noncePubkey: PublicKey,
authorizedPubkey: PublicKey,
lamports: number,
|};
/**
* Create nonce account with seed system transaction params
* @typedef {Object} CreateNonceAccountWithSeedParams
* @property {PublicKey} fromPubkey
* @property {PublicKey} noncePubkey
* @property {PublicKey} authorizedPubkey
* @property {PublicKey} basePubkey
* @property {string} seed
* @property {number} lamports
*/
export type CreateNonceAccountWithSeedParams = {|
fromPubkey: PublicKey,
noncePubkey: PublicKey,
authorizedPubkey: PublicKey,
lamports: number,
basePubkey: PublicKey,
seed: string,
|};
/**
* Initialize nonce account system instruction params
* @typedef {Object} InitializeNonceParams
* @property {PublicKey} fromPubkey
* @property {PublicKey} programId
*/
export type InitializeNonceParams = {|
noncePubkey: PublicKey,
authorizedPubkey: PublicKey,
|};
/**
* Advance nonce account system instruction params
* @typedef {Object} AdvanceNonceParams
* @property {PublicKey} fromPubkey
* @property {PublicKey} programId
*/
export type AdvanceNonceParams = {|
noncePubkey: PublicKey,
authorizedPubkey: PublicKey,
|};
/**
* Withdraw nonce account system transaction params
* @typedef {Object} WithdrawNonceParams
* @property {PublicKey} noncePubkey
* @property {PublicKey} authorizedPubkey
* @property {PublicKey} toPubkey
* @property {number} lamports
*/
export type WithdrawNonceParams = {|
noncePubkey: PublicKey,
authorizedPubkey: PublicKey,
toPubkey: PublicKey,
lamports: number,
|};
/**
* Authorize nonce account system transaction params
* @typedef {Object} AuthorizeNonceParams
* @property {PublicKey} noncePubkey
* @property {PublicKey} authorizedPubkey
* @property {PublicKey} newAuthorizedPubkey
*/
export type AuthorizeNonceParams = {|
noncePubkey: PublicKey,
authorizedPubkey: PublicKey,
newAuthorizedPubkey: PublicKey,
|};
/**
* Allocate account system transaction params
* @typedef {Object} AllocateParams
* @property {PublicKey} accountPubkey
* @property {number} space
*/
export type AllocateParams = {|
accountPubkey: PublicKey,
space: number,
|};
/**
* Allocate account with seed system transaction params
* @typedef {Object} AllocateWithSeedParams
* @property {PublicKey} accountPubkey
* @property {PublicKey} basePubkey
* @property {string} seed
* @property {number} space
* @property {PublicKey} programId
*/
export type AllocateWithSeedParams = {|
accountPubkey: PublicKey,
basePubkey: PublicKey,
seed: string,
space: number,
programId: PublicKey,
|};
/**
* Assign account with seed system transaction params
* @typedef {Object} AssignWithSeedParams
* @property {PublicKey} accountPubkey
* @property {PublicKey} basePubkey
* @property {string} seed
* @property {PublicKey} programId
*/
export type AssignWithSeedParams = {|
accountPubkey: PublicKey,
basePubkey: PublicKey,
seed: string,
programId: PublicKey,
|};
/**
* System Instruction class
*/
export class SystemInstruction {
/**
* Decode a system instruction and retrieve the instruction type.
*/
static decodeInstructionType(
instruction: TransactionInstruction,
): SystemInstructionType {
this.checkProgramId(instruction.programId);
const instructionTypeLayout = BufferLayout.u32('instruction');
const typeIndex = instructionTypeLayout.decode(instruction.data);
let type;
for (const t of Object.keys(SYSTEM_INSTRUCTION_LAYOUTS)) {
if (SYSTEM_INSTRUCTION_LAYOUTS[t].index == typeIndex) {
type = t;
}
}
if (!type) {
throw new Error('Instruction type incorrect; not a SystemInstruction');
}
return type;
}
/**
* Decode a create account system instruction and retrieve the instruction params.
*/
static decodeCreateAccount(
instruction: TransactionInstruction,
): CreateAccountParams {
this.checkProgramId(instruction.programId);
this.checkKeyLength(instruction.keys, 2);
const {lamports, space, programId} = decodeData(
SYSTEM_INSTRUCTION_LAYOUTS.Create,
instruction.data,
);
return {
fromPubkey: instruction.keys[0].pubkey,
newAccountPubkey: instruction.keys[1].pubkey,
lamports,
space,
programId: new PublicKey(programId),
};
}
/**
* Decode a transfer system instruction and retrieve the instruction params.
*/
static decodeTransfer(instruction: TransactionInstruction): TransferParams {
this.checkProgramId(instruction.programId);
this.checkKeyLength(instruction.keys, 2);
const {lamports} = decodeData(
SYSTEM_INSTRUCTION_LAYOUTS.Transfer,
instruction.data,
);
return {
fromPubkey: instruction.keys[0].pubkey,
toPubkey: instruction.keys[1].pubkey,
lamports,
};
}
/**
* Decode an allocate system instruction and retrieve the instruction params.
*/
static decodeAllocate(instruction: TransactionInstruction): AllocateParams {
this.checkProgramId(instruction.programId);
this.checkKeyLength(instruction.keys, 1);
const {space} = decodeData(
SYSTEM_INSTRUCTION_LAYOUTS.Allocate,
instruction.data,
);
return {
accountPubkey: instruction.keys[0].pubkey,
space,
};
}
/**
* Decode an allocate with seed system instruction and retrieve the instruction params.
*/
static decodeAllocateWithSeed(
instruction: TransactionInstruction,
): AllocateWithSeedParams {
this.checkProgramId(instruction.programId);
this.checkKeyLength(instruction.keys, 1);
const {base, seed, space, programId} = decodeData(
SYSTEM_INSTRUCTION_LAYOUTS.AllocateWithSeed,
instruction.data,
);
return {
accountPubkey: instruction.keys[0].pubkey,
basePubkey: new PublicKey(base),
seed,
space,
programId: new PublicKey(programId),
};
}
/**
* Decode an assign system instruction and retrieve the instruction params.
*/
static decodeAssign(instruction: TransactionInstruction): AssignParams {
this.checkProgramId(instruction.programId);
this.checkKeyLength(instruction.keys, 1);
const {programId} = decodeData(
SYSTEM_INSTRUCTION_LAYOUTS.Assign,
instruction.data,
);
return {
accountPubkey: instruction.keys[0].pubkey,
programId: new PublicKey(programId),
};
}
/**
* Decode an assign with seed system instruction and retrieve the instruction params.
*/
static decodeAssignWithSeed(
instruction: TransactionInstruction,
): AssignWithSeedParams {
this.checkProgramId(instruction.programId);
this.checkKeyLength(instruction.keys, 1);
const {base, seed, programId} = decodeData(
SYSTEM_INSTRUCTION_LAYOUTS.AssignWithSeed,
instruction.data,
);
return {
accountPubkey: instruction.keys[0].pubkey,
basePubkey: new PublicKey(base),
seed,
programId: new PublicKey(programId),
};
}
/**
* Decode a create account with seed system instruction and retrieve the instruction params.
*/
static decodeCreateWithSeed(
instruction: TransactionInstruction,
): CreateAccountWithSeedParams {
this.checkProgramId(instruction.programId);
this.checkKeyLength(instruction.keys, 2);
const {base, seed, lamports, space, programId} = decodeData(
SYSTEM_INSTRUCTION_LAYOUTS.CreateWithSeed,
instruction.data,
);
return {
fromPubkey: instruction.keys[0].pubkey,
newAccountPubkey: instruction.keys[1].pubkey,
basePubkey: new PublicKey(base),
seed,
lamports,
space,
programId: new PublicKey(programId),
};
}
/**
* Decode a nonce initialize system instruction and retrieve the instruction params.
*/
static decodeNonceInitialize(
instruction: TransactionInstruction,
): InitializeNonceParams {
this.checkProgramId(instruction.programId);
this.checkKeyLength(instruction.keys, 3);
const {authorized} = decodeData(
SYSTEM_INSTRUCTION_LAYOUTS.InitializeNonceAccount,
instruction.data,
);
return {
noncePubkey: instruction.keys[0].pubkey,
authorizedPubkey: new PublicKey(authorized),
};
}
/**
* Decode a nonce advance system instruction and retrieve the instruction params.
*/
static decodeNonceAdvance(
instruction: TransactionInstruction,
): AdvanceNonceParams {
this.checkProgramId(instruction.programId);
this.checkKeyLength(instruction.keys, 3);
decodeData(
SYSTEM_INSTRUCTION_LAYOUTS.AdvanceNonceAccount,
instruction.data,
);
return {
noncePubkey: instruction.keys[0].pubkey,
authorizedPubkey: instruction.keys[2].pubkey,
};
}
/**
* Decode a nonce withdraw system instruction and retrieve the instruction params.
*/
static decodeNonceWithdraw(
instruction: TransactionInstruction,
): WithdrawNonceParams {
this.checkProgramId(instruction.programId);
this.checkKeyLength(instruction.keys, 5);
const {lamports} = decodeData(
SYSTEM_INSTRUCTION_LAYOUTS.WithdrawNonceAccount,
instruction.data,
);
return {
noncePubkey: instruction.keys[0].pubkey,
toPubkey: instruction.keys[1].pubkey,
authorizedPubkey: instruction.keys[4].pubkey,
lamports,
};
}
/**
* Decode a nonce authorize system instruction and retrieve the instruction params.
*/
static decodeNonceAuthorize(
instruction: TransactionInstruction,
): AuthorizeNonceParams {
this.checkProgramId(instruction.programId);
this.checkKeyLength(instruction.keys, 2);
const {authorized} = decodeData(
SYSTEM_INSTRUCTION_LAYOUTS.AuthorizeNonceAccount,
instruction.data,
);
return {
noncePubkey: instruction.keys[0].pubkey,
authorizedPubkey: instruction.keys[1].pubkey,
newAuthorizedPubkey: new PublicKey(authorized),
};
}
/**
* @private
*/
static checkProgramId(programId: PublicKey) {
if (!programId.equals(SystemProgram.programId)) {
throw new Error('invalid instruction; programId is not SystemProgram');
}
}
/**
* @private
*/
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 SystemInstructionType's
* @typedef {'Create' | 'Assign' | 'Transfer' | 'CreateWithSeed'
| 'AdvanceNonceAccount' | 'WithdrawNonceAccount' | 'InitializeNonceAccount'
| 'AuthorizeNonceAccount'} SystemInstructionType
*/
export type SystemInstructionType = $Keys<typeof SYSTEM_INSTRUCTION_LAYOUTS>;
/**
* An enumeration of valid system InstructionType's
*/
export const SYSTEM_INSTRUCTION_LAYOUTS = Object.freeze({
2019-09-16 08:46:25 -07:00
Create: {
index: 0,
layout: BufferLayout.struct([
BufferLayout.u32('instruction'),
BufferLayout.ns64('lamports'),
BufferLayout.ns64('space'),
Layout.publicKey('programId'),
]),
},
2019-09-16 08:46:25 -07:00
Assign: {
index: 1,
layout: BufferLayout.struct([
BufferLayout.u32('instruction'),
Layout.publicKey('programId'),
]),
},
2019-09-16 08:46:25 -07:00
Transfer: {
index: 2,
layout: BufferLayout.struct([
BufferLayout.u32('instruction'),
BufferLayout.ns64('lamports'),
]),
},
CreateWithSeed: {
index: 3,
layout: BufferLayout.struct([
BufferLayout.u32('instruction'),
Layout.publicKey('base'),
Layout.rustString('seed'),
BufferLayout.ns64('lamports'),
BufferLayout.ns64('space'),
Layout.publicKey('programId'),
]),
},
AdvanceNonceAccount: {
index: 4,
layout: BufferLayout.struct([BufferLayout.u32('instruction')]),
},
WithdrawNonceAccount: {
index: 5,
layout: BufferLayout.struct([
BufferLayout.u32('instruction'),
BufferLayout.ns64('lamports'),
]),
},
InitializeNonceAccount: {
index: 6,
layout: BufferLayout.struct([
BufferLayout.u32('instruction'),
Layout.publicKey('authorized'),
]),
},
AuthorizeNonceAccount: {
index: 7,
layout: BufferLayout.struct([
BufferLayout.u32('instruction'),
Layout.publicKey('authorized'),
]),
},
Allocate: {
index: 8,
layout: BufferLayout.struct([
BufferLayout.u32('instruction'),
BufferLayout.ns64('space'),
]),
},
AllocateWithSeed: {
index: 9,
layout: BufferLayout.struct([
BufferLayout.u32('instruction'),
Layout.publicKey('base'),
Layout.rustString('seed'),
BufferLayout.ns64('space'),
Layout.publicKey('programId'),
]),
},
AssignWithSeed: {
index: 10,
layout: BufferLayout.struct([
BufferLayout.u32('instruction'),
Layout.publicKey('base'),
Layout.rustString('seed'),
Layout.publicKey('programId'),
]),
},
});
2018-09-18 12:46:59 -07:00
/**
2018-09-20 10:10:46 -07:00
* Factory class for transactions to interact with the System program
2018-09-18 12:46:59 -07:00
*/
2018-09-20 10:10:46 -07:00
export class SystemProgram {
2018-09-18 12:46:59 -07:00
/**
2018-09-20 10:10:46 -07:00
* Public key that identifies the System program
2018-09-18 12:46:59 -07:00
*/
2018-09-20 10:10:46 -07:00
static get programId(): PublicKey {
return new PublicKey('11111111111111111111111111111111');
2018-09-18 12:46:59 -07:00
}
/**
* Generate a transaction instruction that creates a new account
2018-09-18 12:46:59 -07:00
*/
static createAccount(params: CreateAccountParams): TransactionInstruction {
const type = SYSTEM_INSTRUCTION_LAYOUTS.Create;
const data = encodeData(type, {
lamports: params.lamports,
space: params.space,
programId: params.programId.toBuffer(),
});
2018-09-18 12:46:59 -07:00
return new TransactionInstruction({
keys: [
{pubkey: params.fromPubkey, isSigner: true, isWritable: true},
{pubkey: params.newAccountPubkey, isSigner: true, isWritable: true},
],
programId: this.programId,
2019-03-14 13:27:47 -07:00
data,
2018-09-18 12:46:59 -07:00
});
}
/**
* Generate a transaction instruction that transfers lamports from one account to another
2018-09-18 12:46:59 -07:00
*/
static transfer(params: TransferParams): TransactionInstruction {
const type = SYSTEM_INSTRUCTION_LAYOUTS.Transfer;
const data = encodeData(type, {lamports: params.lamports});
2018-09-18 12:46:59 -07:00
return new TransactionInstruction({
keys: [
{pubkey: params.fromPubkey, isSigner: true, isWritable: true},
{pubkey: params.toPubkey, isSigner: false, isWritable: true},
],
programId: this.programId,
2019-03-14 13:27:47 -07:00
data,
2018-09-18 12:46:59 -07:00
});
}
/**
* Generate a transaction instruction that assigns an account to a program
2018-09-18 12:46:59 -07:00
*/
static assign(
params: AssignParams | AssignWithSeedParams,
): TransactionInstruction {
let data;
if (params.basePubkey) {
const type = SYSTEM_INSTRUCTION_LAYOUTS.AssignWithSeed;
data = encodeData(type, {
base: params.basePubkey.toBuffer(),
seed: params.seed,
programId: params.programId.toBuffer(),
});
} else {
const type = SYSTEM_INSTRUCTION_LAYOUTS.Assign;
data = encodeData(type, {programId: params.programId.toBuffer()});
}
2018-09-18 12:46:59 -07:00
return new TransactionInstruction({
keys: [{pubkey: params.accountPubkey, isSigner: true, isWritable: true}],
programId: this.programId,
2019-03-14 13:27:47 -07:00
data,
2018-09-18 12:46:59 -07:00
});
}
/**
* Generate a transaction instruction that creates a new account at
* an address generated with `from`, a seed, and programId
*/
static createAccountWithSeed(
params: CreateAccountWithSeedParams,
): TransactionInstruction {
const type = SYSTEM_INSTRUCTION_LAYOUTS.CreateWithSeed;
const data = encodeData(type, {
base: params.basePubkey.toBuffer(),
seed: params.seed,
lamports: params.lamports,
space: params.space,
programId: params.programId.toBuffer(),
});
return new TransactionInstruction({
keys: [
{pubkey: params.fromPubkey, isSigner: true, isWritable: true},
{pubkey: params.newAccountPubkey, isSigner: false, isWritable: true},
],
programId: this.programId,
data,
});
}
/**
* Generate a transaction that creates a new Nonce account
*/
static createNonceAccount(
params: CreateNonceAccountParams | CreateNonceAccountWithSeedParams,
): Transaction {
const transaction = new Transaction();
if (params.basePubkey && params.seed) {
transaction.add(
SystemProgram.createAccountWithSeed({
fromPubkey: params.fromPubkey,
newAccountPubkey: params.noncePubkey,
basePubkey: params.basePubkey,
seed: params.seed,
lamports: params.lamports,
space: NONCE_ACCOUNT_LENGTH,
programId: this.programId,
}),
);
} else {
transaction.add(
SystemProgram.createAccount({
fromPubkey: params.fromPubkey,
newAccountPubkey: params.noncePubkey,
lamports: params.lamports,
space: NONCE_ACCOUNT_LENGTH,
programId: this.programId,
}),
);
}
const initParams = {
noncePubkey: params.noncePubkey,
authorizedPubkey: params.authorizedPubkey,
};
transaction.add(this.nonceInitialize(initParams));
return transaction;
}
/**
* Generate an instruction to initialize a Nonce account
*/
static nonceInitialize(
params: InitializeNonceParams,
): TransactionInstruction {
const type = SYSTEM_INSTRUCTION_LAYOUTS.InitializeNonceAccount;
const data = encodeData(type, {
authorized: params.authorizedPubkey.toBuffer(),
});
const instructionData = {
keys: [
{pubkey: params.noncePubkey, isSigner: false, isWritable: true},
{
pubkey: SYSVAR_RECENT_BLOCKHASHES_PUBKEY,
isSigner: false,
isWritable: false,
},
{pubkey: SYSVAR_RENT_PUBKEY, isSigner: false, isWritable: false},
],
programId: this.programId,
data,
};
return new TransactionInstruction(instructionData);
}
/**
* Generate an instruction to advance the nonce in a Nonce account
*/
static nonceAdvance(params: AdvanceNonceParams): TransactionInstruction {
const type = SYSTEM_INSTRUCTION_LAYOUTS.AdvanceNonceAccount;
const data = encodeData(type);
const instructionData = {
keys: [
{pubkey: params.noncePubkey, isSigner: false, isWritable: true},
{
pubkey: SYSVAR_RECENT_BLOCKHASHES_PUBKEY,
isSigner: false,
isWritable: false,
},
{pubkey: params.authorizedPubkey, isSigner: true, isWritable: false},
],
programId: this.programId,
data,
};
return new TransactionInstruction(instructionData);
}
/**
* Generate a transaction instruction that withdraws lamports from a Nonce account
*/
static nonceWithdraw(params: WithdrawNonceParams): TransactionInstruction {
const type = SYSTEM_INSTRUCTION_LAYOUTS.WithdrawNonceAccount;
const data = encodeData(type, {lamports: params.lamports});
return new TransactionInstruction({
keys: [
{pubkey: params.noncePubkey, isSigner: false, isWritable: true},
{pubkey: params.toPubkey, isSigner: false, isWritable: true},
{
pubkey: SYSVAR_RECENT_BLOCKHASHES_PUBKEY,
isSigner: false,
isWritable: false,
},
{
pubkey: SYSVAR_RENT_PUBKEY,
isSigner: false,
isWritable: false,
},
{pubkey: params.authorizedPubkey, isSigner: true, isWritable: false},
],
programId: this.programId,
data,
});
}
/**
* Generate a transaction instruction that authorizes a new PublicKey as the authority
* on a Nonce account.
*/
static nonceAuthorize(params: AuthorizeNonceParams): TransactionInstruction {
const type = SYSTEM_INSTRUCTION_LAYOUTS.AuthorizeNonceAccount;
const data = encodeData(type, {
authorized: params.newAuthorizedPubkey.toBuffer(),
});
return new TransactionInstruction({
keys: [
{pubkey: params.noncePubkey, isSigner: false, isWritable: true},
{pubkey: params.authorizedPubkey, isSigner: true, isWritable: false},
],
programId: this.programId,
data,
});
}
/**
* Generate a transaction instruction that allocates space in an account without funding
*/
static allocate(
params: AllocateParams | AllocateWithSeedParams,
): TransactionInstruction {
let data;
if (params.basePubkey) {
const type = SYSTEM_INSTRUCTION_LAYOUTS.AllocateWithSeed;
data = encodeData(type, {
base: params.basePubkey.toBuffer(),
seed: params.seed,
space: params.space,
programId: params.programId.toBuffer(),
});
} else {
const type = SYSTEM_INSTRUCTION_LAYOUTS.Allocate;
data = encodeData(type, {
space: params.space,
});
}
return new TransactionInstruction({
keys: [{pubkey: params.accountPubkey, isSigner: true, isWritable: true}],
programId: this.programId,
data,
});
}
2018-09-18 12:46:59 -07:00
}