2018-09-18 12:46:59 -07:00
|
|
|
// @flow
|
|
|
|
|
2018-10-06 11:13:58 -07:00
|
|
|
import * as BufferLayout from 'buffer-layout';
|
2018-09-18 12:46:59 -07:00
|
|
|
|
2019-09-13 17:07:13 -07:00
|
|
|
import {Transaction, TransactionInstruction} from './transaction';
|
2018-09-30 18:42:45 -07:00
|
|
|
import {PublicKey} from './publickey';
|
2018-10-06 11:13:58 -07:00
|
|
|
import * as Layout from './layout';
|
2019-09-13 17:07:13 -07:00
|
|
|
import type {TransactionInstructionCtorFields} from './transaction';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* System Instruction class
|
|
|
|
*/
|
|
|
|
export class SystemInstruction extends TransactionInstruction {
|
|
|
|
/**
|
|
|
|
* Type of SystemInstruction
|
|
|
|
*/
|
|
|
|
type: SystemInstructionType;
|
|
|
|
|
|
|
|
constructor(
|
|
|
|
opts?: TransactionInstructionCtorFields,
|
|
|
|
type?: SystemInstructionType,
|
|
|
|
) {
|
|
|
|
if (
|
|
|
|
opts &&
|
|
|
|
opts.programId &&
|
|
|
|
!opts.programId.equals(SystemProgram.programId)
|
|
|
|
) {
|
|
|
|
throw new Error('programId incorrect; not a SystemInstruction');
|
|
|
|
}
|
|
|
|
super(opts);
|
|
|
|
if (type) {
|
|
|
|
this.type = type;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static from(instruction: TransactionInstruction): SystemInstruction {
|
|
|
|
if (!instruction.programId.equals(SystemProgram.programId)) {
|
|
|
|
throw new Error('programId incorrect; not SystemProgram');
|
|
|
|
}
|
|
|
|
|
|
|
|
const instructionTypeLayout = BufferLayout.u32('instruction');
|
|
|
|
const typeIndex = instructionTypeLayout.decode(instruction.data);
|
|
|
|
let type;
|
|
|
|
for (const t in SystemInstructionEnum) {
|
|
|
|
if (SystemInstructionEnum[t].index == typeIndex) {
|
|
|
|
type = SystemInstructionEnum[t];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!type) {
|
|
|
|
throw new Error('Instruction type incorrect; not a SystemInstruction');
|
|
|
|
}
|
|
|
|
return new SystemInstruction(
|
|
|
|
{
|
|
|
|
keys: instruction.keys,
|
|
|
|
programId: instruction.programId,
|
|
|
|
data: instruction.data,
|
|
|
|
},
|
|
|
|
type,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The `from` public key of the instruction;
|
|
|
|
* returns null if SystemInstructionType does not support this field
|
|
|
|
*/
|
|
|
|
get From(): PublicKey | null {
|
|
|
|
if (
|
|
|
|
this.type == SystemInstructionEnum.CREATE ||
|
|
|
|
this.type == SystemInstructionEnum.TRANSFER
|
|
|
|
) {
|
|
|
|
return this.keys[0].pubkey;
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The `to` public key of the instruction;
|
|
|
|
* returns null if SystemInstructionType does not support this field
|
|
|
|
*/
|
|
|
|
get To(): PublicKey | null {
|
|
|
|
if (
|
|
|
|
this.type == SystemInstructionEnum.CREATE ||
|
|
|
|
this.type == SystemInstructionEnum.TRANSFER
|
|
|
|
) {
|
|
|
|
return this.keys[1].pubkey;
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The `amount` or `lamports` of the instruction;
|
|
|
|
* returns null if SystemInstructionType does not support this field
|
|
|
|
*/
|
|
|
|
get Amount(): number | null {
|
|
|
|
const data = this.type.layout.decode(this.data);
|
|
|
|
if (this.type == SystemInstructionEnum.TRANSFER) {
|
|
|
|
return data.amount;
|
|
|
|
} else if (this.type == SystemInstructionEnum.CREATE) {
|
|
|
|
return data.lamports;
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @typedef {Object} SystemInstructionType
|
|
|
|
* @property (index} The System Instruction index (from solana-sdk)
|
|
|
|
* @property (BufferLayout} The BufferLayout to use to build data
|
|
|
|
*/
|
|
|
|
type SystemInstructionType = {|
|
|
|
|
index: number,
|
|
|
|
layout: typeof BufferLayout,
|
|
|
|
|};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* An enumeration of valid SystemInstructionTypes
|
|
|
|
*/
|
|
|
|
const SystemInstructionEnum = Object.freeze({
|
|
|
|
CREATE: {
|
|
|
|
index: 0,
|
|
|
|
layout: BufferLayout.struct([
|
|
|
|
BufferLayout.u32('instruction'),
|
|
|
|
BufferLayout.ns64('lamports'),
|
|
|
|
BufferLayout.ns64('space'),
|
|
|
|
Layout.publicKey('programId'),
|
|
|
|
]),
|
|
|
|
},
|
|
|
|
ASSIGN: {
|
|
|
|
index: 1,
|
|
|
|
layout: BufferLayout.struct([
|
|
|
|
BufferLayout.u32('instruction'),
|
|
|
|
Layout.publicKey('programId'),
|
|
|
|
]),
|
|
|
|
},
|
|
|
|
TRANSFER: {
|
|
|
|
index: 2,
|
|
|
|
layout: BufferLayout.struct([
|
|
|
|
BufferLayout.u32('instruction'),
|
|
|
|
BufferLayout.ns64('amount'),
|
|
|
|
]),
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Populate a buffer of instruction data using the SystemInstructionType
|
|
|
|
*/
|
|
|
|
function encodeData(type: SystemInstructionType, fields: Object): Buffer {
|
|
|
|
const data = Buffer.alloc(type.layout.span);
|
|
|
|
const layoutFields = Object.assign({instruction: type.index}, fields);
|
|
|
|
type.layout.encode(layoutFields, data);
|
|
|
|
return data;
|
|
|
|
}
|
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 {
|
2018-11-04 11:41:21 -08:00
|
|
|
return new PublicKey(
|
|
|
|
'0x000000000000000000000000000000000000000000000000000000000000000',
|
|
|
|
);
|
2018-09-18 12:46:59 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generate a Transaction that creates a new account
|
|
|
|
*/
|
|
|
|
static createAccount(
|
|
|
|
from: PublicKey,
|
|
|
|
newAccount: PublicKey,
|
2019-03-05 17:52:13 -08:00
|
|
|
lamports: number,
|
2018-09-18 12:46:59 -07:00
|
|
|
space: number,
|
2018-11-04 11:41:21 -08:00
|
|
|
programId: PublicKey,
|
2018-09-18 12:46:59 -07:00
|
|
|
): Transaction {
|
2019-09-13 17:07:13 -07:00
|
|
|
const type = SystemInstructionEnum.CREATE;
|
|
|
|
const data = encodeData(type, {
|
|
|
|
lamports,
|
|
|
|
space,
|
|
|
|
programId: programId.toBuffer(),
|
|
|
|
});
|
2018-09-18 12:46:59 -07:00
|
|
|
|
2018-10-23 15:17:43 -07:00
|
|
|
return new Transaction().add({
|
2019-04-10 12:31:50 -07:00
|
|
|
keys: [
|
2019-05-23 17:24:38 -07:00
|
|
|
{pubkey: from, isSigner: true, isDebitable: true},
|
|
|
|
{pubkey: newAccount, isSigner: false, isDebitable: true},
|
2019-04-10 12:31:50 -07:00
|
|
|
],
|
2018-09-20 10:10:46 -07:00
|
|
|
programId: SystemProgram.programId,
|
2019-03-14 13:27:47 -07:00
|
|
|
data,
|
2018-09-18 12:46:59 -07:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-04-11 18:20:22 -07:00
|
|
|
* Generate a Transaction that transfers lamports from one account to another
|
2018-09-18 12:46:59 -07:00
|
|
|
*/
|
2019-04-11 18:20:22 -07:00
|
|
|
static transfer(from: PublicKey, to: PublicKey, amount: number): Transaction {
|
2019-09-13 17:07:13 -07:00
|
|
|
const type = SystemInstructionEnum.TRANSFER;
|
|
|
|
const data = encodeData(type, {amount});
|
2018-09-18 12:46:59 -07:00
|
|
|
|
2018-10-23 15:17:43 -07:00
|
|
|
return new Transaction().add({
|
2019-05-23 17:24:38 -07:00
|
|
|
keys: [
|
|
|
|
{pubkey: from, isSigner: true, isDebitable: true},
|
|
|
|
{pubkey: to, isSigner: false, isDebitable: false},
|
|
|
|
],
|
2018-09-20 10:10:46 -07:00
|
|
|
programId: SystemProgram.programId,
|
2019-03-14 13:27:47 -07:00
|
|
|
data,
|
2018-09-18 12:46:59 -07:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-09-20 10:10:46 -07:00
|
|
|
* Generate a Transaction that assigns an account to a program
|
2018-09-18 12:46:59 -07:00
|
|
|
*/
|
2018-09-20 10:10:46 -07:00
|
|
|
static assign(from: PublicKey, programId: PublicKey): Transaction {
|
2019-09-13 17:07:13 -07:00
|
|
|
const type = SystemInstructionEnum.ASSIGN;
|
|
|
|
const data = encodeData(type, {programId: programId.toBuffer()});
|
2018-09-18 12:46:59 -07:00
|
|
|
|
2018-10-23 15:17:43 -07:00
|
|
|
return new Transaction().add({
|
2019-05-23 17:24:38 -07:00
|
|
|
keys: [{pubkey: from, isSigner: true, isDebitable: true}],
|
2018-09-20 10:10:46 -07:00
|
|
|
programId: SystemProgram.programId,
|
2019-03-14 13:27:47 -07:00
|
|
|
data,
|
2018-09-18 12:46:59 -07:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|