diff --git a/web3.js/module.d.ts b/web3.js/module.d.ts index fe3a0a35ac..40fbe06397 100644 --- a/web3.js/module.d.ts +++ b/web3.js/module.d.ts @@ -475,6 +475,65 @@ declare module '@solana/web3.js' { } // === src/system-program.js === + export type CreateAccountParams = { + fromPubkey: PublicKey; + newAccountPubkey: PublicKey; + lamports: number; + space: number; + programId: PublicKey; + }; + + export type TransferParams = { + fromPubkey: PublicKey; + toPubkey: PublicKey; + lamports: number; + }; + + export type AssignParams = { + fromPubkey: PublicKey; + programId: PublicKey; + }; + + export type CreateAccountWithSeedParams = { + fromPubkey: PublicKey; + newAccountPubkey: PublicKey; + basePubkey: PublicKey; + seed: string; + lamports: number; + space: number; + programId: PublicKey; + }; + + export type CreateNonceAccountParams = { + fromPubkey: PublicKey; + noncePubkey: PublicKey; + authorizedPubkey: PublicKey; + lamports: number; + }; + + export type InitializeNonceParams = { + noncePubkey: PublicKey; + authorizedPubkey: PublicKey; + }; + + export type AdvanceNonceParams = { + noncePubkey: PublicKey; + authorizedPubkey: PublicKey; + }; + + export type WithdrawNonceParams = { + noncePubkey: PublicKey; + authorizedPubkey: PublicKey; + toPubkey: PublicKey; + lamports: number; + }; + + export type AuthorizeNonceParams = { + noncePubkey: PublicKey; + authorizedPubkey: PublicKey; + newAuthorizedPubkey: PublicKey; + }; + export class SystemProgram { static programId: PublicKey; static nonceSpace: number; @@ -538,17 +597,30 @@ declare module '@solana/web3.js' { [type in SystemInstructionType]: InstructionType; }; - export class SystemInstruction extends TransactionInstruction { - type: SystemInstructionType; - fromPublicKey: PublicKey | null; - toPublicKey: PublicKey | null; - amount: number | null; - - constructor( - opts: TransactionInstructionCtorFields, - type: SystemInstructionType, - ); - static from(instruction: TransactionInstruction): SystemInstruction; + export class SystemInstruction { + static decodeInstructionType( + instruction: TransactionInstruction, + ): SystemInstructionType; + static decodeCreateAccount( + instruction: TransactionInstruction, + ): CreateAccountParams; + static decodeTransfer(instruction: TransactionInstruction): TransferParams; + static decodeAssign(instruction: TransactionInstruction): AssignParams; + static decodeCreateWithSeed( + instruction: TransactionInstruction, + ): CreateAccountWithSeedParams; + static decodeNonceInitialize( + instruction: TransactionInstruction, + ): InitializeNonceParams; + static decodeNonceAdvance( + instruction: TransactionInstruction, + ): AdvanceNonceParams; + static decodeNonceWithdraw( + instruction: TransactionInstruction, + ): WithdrawNonceParams; + static decodeNonceAuthorize( + instruction: TransactionInstruction, + ): AuthorizeNonceParams; } // === src/loader.js === diff --git a/web3.js/module.flow.js b/web3.js/module.flow.js index 0c346fae24..871279d7a4 100644 --- a/web3.js/module.flow.js +++ b/web3.js/module.flow.js @@ -371,53 +371,79 @@ declare module '@solana/web3.js' { } // === src/system-program.js === + declare export type CreateAccountParams = {| + fromPubkey: PublicKey, + newAccountPubkey: PublicKey, + lamports: number, + space: number, + programId: PublicKey, + |}; + + declare export type TransferParams = {| + fromPubkey: PublicKey, + toPubkey: PublicKey, + lamports: number, + |}; + + declare export type AssignParams = {| + fromPubkey: PublicKey, + programId: PublicKey, + |}; + + declare export type CreateAccountWithSeedParams = {| + fromPubkey: PublicKey, + newAccountPubkey: PublicKey, + basePubkey: PublicKey, + seed: string, + lamports: number, + space: number, + programId: PublicKey, + |}; + + declare export type CreateNonceAccountParams = {| + fromPubkey: PublicKey, + noncePubkey: PublicKey, + authorizedPubkey: PublicKey, + lamports: number, + |}; + + declare export type InitializeNonceParams = {| + noncePubkey: PublicKey, + authorizedPubkey: PublicKey, + |}; + + declare export type AdvanceNonceParams = {| + noncePubkey: PublicKey, + authorizedPubkey: PublicKey, + |}; + + declare export type WithdrawNonceParams = {| + noncePubkey: PublicKey, + authorizedPubkey: PublicKey, + toPubkey: PublicKey, + lamports: number, + |}; + + declare export type AuthorizeNonceParams = {| + noncePubkey: PublicKey, + authorizedPubkey: PublicKey, + newAuthorizedPubkey: PublicKey, + |}; + declare export class SystemProgram { static programId: PublicKey; static nonceSpace: number; - static createAccount( - from: PublicKey, - newAccount: PublicKey, - lamports: number, - space: number, - programId: PublicKey, - ): Transaction; - static transfer( - from: PublicKey, - to: PublicKey, - amount: number, - ): Transaction; - static assign(from: PublicKey, programId: PublicKey): Transaction; + static createAccount(params: CreateAccountParams): Transaction; + static transfer(params: TransferParams): Transaction; + static assign(params: AssignParams): Transaction; static createAccountWithSeed( - from: PublicKey, - newAccount: PublicKey, - base: PublicKey, - seed: string, - lamports: number, - space: number, - programId: PublicKey, - ): Transaction; - static createNonceAccount( - from: PublicKey, - nonceAccount: PublicKey, - authorizedPubkey: PublicKey, - lamports: number, - ): Transaction; - static nonceAdvance( - nonceAccount: PublicKey, - authorizedPubkey: PublicKey, - ): TransactionInstruction; - static nonceWithdraw( - nonceAccount: PublicKey, - authorizedPubkey: PublicKey, - to: PublicKey, - lamports: number, - ): Transaction; - static nonceAuthorize( - nonceAccount: PublicKey, - authorizedPubkey: PublicKey, - newAuthorized: PublicKey, + params: CreateAccountWithSeedParams, ): Transaction; + static createNonceAccount(params: CreateNonceAccountParams): Transaction; + static nonceAdvance(params: AdvanceNonceParams): TransactionInstruction; + static nonceWithdraw(params: WithdrawNonceParams): Transaction; + static nonceAuthorize(params: AuthorizeNonceParams): Transaction; } declare export type SystemInstructionType = @@ -434,17 +460,27 @@ declare module '@solana/web3.js' { [SystemInstructionType]: InstructionType, }; - declare export class SystemInstruction extends TransactionInstruction { - type: SystemInstructionType; - fromPublicKey: PublicKey | null; - toPublicKey: PublicKey | null; - amount: number | null; - - constructor( - opts?: TransactionInstructionCtorFields, - type: SystemInstructionType, - ): SystemInstruction; - static from(instruction: TransactionInstruction): SystemInstruction; + declare export class SystemInstruction { + static decodeCreateAccount( + instruction: TransactionInstruction, + ): CreateAccountParams; + static decodeTransfer(instruction: TransactionInstruction): TransferParams; + static decodeAssign(instruction: TransactionInstruction): AssignParams; + static decodeCreateWithSeed( + instruction: TransactionInstruction, + ): CreateAccountWithSeedParams; + static decodeNonceInitialize( + instruction: TransactionInstruction, + ): InitializeNonceParams; + static decodeNonceAdvance( + instruction: TransactionInstruction, + ): AdvanceNonceParams; + static decodeNonceWithdraw( + instruction: TransactionInstruction, + ): WithdrawNonceParams; + static decodeNonceAuthorize( + instruction: TransactionInstruction, + ): AuthorizeNonceParams; } // === src/validator-info.js === diff --git a/web3.js/src/budget-program.js b/web3.js/src/budget-program.js index 7905cbaf3f..9275a487f3 100644 --- a/web3.js/src/budget-program.js +++ b/web3.js/src/budget-program.js @@ -191,13 +191,13 @@ export class BudgetProgram { } const trimmedData = data.slice(0, pos); - const transaction = SystemProgram.createAccount( - from, - program, - amount, - trimmedData.length, - this.programId, - ); + const transaction = SystemProgram.createAccount({ + fromPubkey: from, + newAccountPubkey: program, + lamports: amount, + space: trimmedData.length, + programId: this.programId, + }); return transaction.add({ keys: [ @@ -227,13 +227,13 @@ export class BudgetProgram { } const trimmedData = data.slice(0, pos); - const transaction = SystemProgram.createAccount( - from, - program, - amount, - trimmedData.length, - this.programId, - ); + const transaction = SystemProgram.createAccount({ + fromPubkey: from, + newAccountPubkey: program, + lamports: amount, + space: trimmedData.length, + programId: this.programId, + }); return transaction.add({ keys: [{pubkey: program, isSigner: false, isWritable: true}], @@ -260,13 +260,13 @@ export class BudgetProgram { } const trimmedData = data.slice(0, pos); - const transaction = SystemProgram.createAccount( - from, - program, - amount, - trimmedData.length, - this.programId, - ); + const transaction = SystemProgram.createAccount({ + fromPubkey: from, + newAccountPubkey: program, + lamports: amount, + space: trimmedData.length, + programId: this.programId, + }); return transaction.add({ keys: [{pubkey: program, isSigner: false, isWritable: true}], @@ -316,13 +316,13 @@ export class BudgetProgram { const trimmedData = data.slice(0, pos); - const transaction = SystemProgram.createAccount( - from, - program, - amount, - trimmedData.length, - this.programId, - ); + const transaction = SystemProgram.createAccount({ + fromPubkey: from, + newAccountPubkey: program, + lamports: amount, + space: trimmedData.length, + programId: this.programId, + }); return transaction.add({ keys: [{pubkey: program, isSigner: false, isWritable: true}], diff --git a/web3.js/src/loader.js b/web3.js/src/loader.js index 1217ab7d96..5da3c69051 100644 --- a/web3.js/src/loader.js +++ b/web3.js/src/loader.js @@ -58,13 +58,13 @@ export class Loader { const balanceNeeded = await connection.getMinimumBalanceForRentExemption( data.length, ); - const transaction = SystemProgram.createAccount( - payer.publicKey, - program.publicKey, - balanceNeeded > 0 ? balanceNeeded : 1, - data.length, + const transaction = SystemProgram.createAccount({ + fromPubkey: payer.publicKey, + newAccountPubkey: program.publicKey, + lamports: balanceNeeded > 0 ? balanceNeeded : 1, + space: data.length, programId, - ); + }); await sendAndConfirmTransaction(connection, transaction, payer, program); } diff --git a/web3.js/src/stake-program.js b/web3.js/src/stake-program.js index 7956ed84e5..68560bb7bf 100644 --- a/web3.js/src/stake-program.js +++ b/web3.js/src/stake-program.js @@ -463,15 +463,15 @@ export class StakeProgram { static createAccountWithSeed( params: CreateStakeAccountWithSeedParams, ): Transaction { - let transaction = SystemProgram.createAccountWithSeed( - params.fromPubkey, - params.stakePubkey, - params.basePubkey, - params.seed, - params.lamports, - this.space, - this.programId, - ); + let transaction = 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})); @@ -481,13 +481,13 @@ export class StakeProgram { * Generate a Transaction that creates a new Stake account */ static createAccount(params: CreateStakeAccountParams): Transaction { - let transaction = SystemProgram.createAccount( - params.fromPubkey, - params.stakePubkey, - params.lamports, - this.space, - this.programId, - ); + let transaction = 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})); @@ -557,13 +557,13 @@ export class StakeProgram { static split(params: SplitStakeParams): Transaction { const {stakePubkey, authorizedPubkey, splitStakePubkey, lamports} = params; - let transaction = SystemProgram.createAccount( - stakePubkey, - splitStakePubkey, - 0, - this.space, - this.programId, - ); + let transaction = SystemProgram.createAccount({ + fromPubkey: stakePubkey, + newAccountPubkey: splitStakePubkey, + lamports: 0, + space: this.space, + programId: this.programId, + }); transaction.instructions[0].keys[0].isSigner = false; const type = STAKE_INSTRUCTION_LAYOUTS.Split; const data = encodeData(type, {lamports}); diff --git a/web3.js/src/system-program.js b/web3.js/src/system-program.js index 11ca4f03f5..582f128036 100644 --- a/web3.js/src/system-program.js +++ b/web3.js/src/system-program.js @@ -2,116 +2,351 @@ import * as BufferLayout from 'buffer-layout'; -import {encodeData} from './instruction'; +import {encodeData, decodeData} from './instruction'; import * as Layout from './layout'; import {PublicKey} from './publickey'; import {SYSVAR_RECENT_BLOCKHASHES_PUBKEY, SYSVAR_RENT_PUBKEY} from './sysvar'; import {Transaction, TransactionInstruction} from './transaction'; -import type {TransactionInstructionCtorFields} 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} fromPubkey + * @property {PublicKey} programId + */ +export type AssignParams = {| + fromPubkey: 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} AssignParams + * @property {PublicKey} fromPubkey + * @property {PublicKey} programId + */ +export type CreateNonceAccountParams = {| + fromPubkey: PublicKey, + noncePubkey: PublicKey, + authorizedPubkey: PublicKey, + lamports: number, +|}; + +/** + * 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, +|}; /** * System Instruction class */ -export class SystemInstruction extends TransactionInstruction { +export class SystemInstruction { /** - * Type of SystemInstruction + * Decode a system instruction and retrieve the instruction type. */ - _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); - this._type = type; - } - - static from(instruction: TransactionInstruction): SystemInstruction { - if (!instruction.programId.equals(SystemProgram.programId)) { - throw new Error('programId incorrect; not SystemProgram'); - } + 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 new SystemInstruction( - { - keys: instruction.keys, - programId: instruction.programId, - data: instruction.data, - }, - type, + + 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), + }; } /** - * Type of SystemInstruction + * Decode a transfer system instruction and retrieve the instruction params. */ - get type(): SystemInstructionType { - return this._type; + 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, + }; } /** - * The `from` public key of the instruction; - * returns null if SystemInstructionType does not support this field + * Decode an assign system instruction and retrieve the instruction params. */ - get fromPublicKey(): PublicKey | null { - switch (this.type) { - case 'Create': - case 'CreateWithSeed': - case 'WithdrawNonceAccount': - case 'Transfer': - return this.keys[0].pubkey; - default: - return null; + static decodeAssign(instruction: TransactionInstruction): AssignParams { + this.checkProgramId(instruction.programId); + this.checkKeyLength(instruction.keys, 1); + + const {programId} = decodeData( + SYSTEM_INSTRUCTION_LAYOUTS.Assign, + instruction.data, + ); + + return { + fromPubkey: instruction.keys[0].pubkey, + 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'); } } /** - * The `to` public key of the instruction; - * returns null if SystemInstructionType does not support this field + * @private */ - get toPublicKey(): PublicKey | null { - switch (this.type) { - case 'Create': - case 'CreateWithSeed': - case 'WithdrawNonceAccount': - case 'Transfer': - return this.keys[1].pubkey; - default: - return null; - } - } - - /** - * The `amount` or `lamports` of the instruction; - * returns null if SystemInstructionType does not support this field - */ - get amount(): number | null { - const data = SYSTEM_INSTRUCTION_LAYOUTS[this.type].layout.decode(this.data); - switch (this.type) { - case 'Create': - case 'CreateWithSeed': - case 'WithdrawNonceAccount': - case 'Transfer': - return data.lamports; - default: - return null; + static checkKeyLength(keys: Array, expectedLength: number) { + if (keys.length !== expectedLength) { + throw new Error( + `invalid instruction; key length mismatch ${keys.length} != ${expectedLength}`, + ); } } } @@ -212,24 +447,18 @@ export class SystemProgram { /** * Generate a Transaction that creates a new account */ - static createAccount( - from: PublicKey, - newAccount: PublicKey, - lamports: number, - space: number, - programId: PublicKey, - ): Transaction { + static createAccount(params: CreateAccountParams): Transaction { const type = SYSTEM_INSTRUCTION_LAYOUTS.Create; const data = encodeData(type, { - lamports, - space, - programId: programId.toBuffer(), + lamports: params.lamports, + space: params.space, + programId: params.programId.toBuffer(), }); return new Transaction().add({ keys: [ - {pubkey: from, isSigner: true, isWritable: true}, - {pubkey: newAccount, isSigner: true, isWritable: true}, + {pubkey: params.fromPubkey, isSigner: true, isWritable: true}, + {pubkey: params.newAccountPubkey, isSigner: true, isWritable: true}, ], programId: this.programId, data, @@ -239,18 +468,14 @@ export class SystemProgram { /** * Generate a Transaction that transfers lamports from one account to another */ - static transfer( - from: PublicKey, - to: PublicKey, - lamports: number, - ): Transaction { + static transfer(params: TransferParams): Transaction { const type = SYSTEM_INSTRUCTION_LAYOUTS.Transfer; - const data = encodeData(type, {lamports}); + const data = encodeData(type, {lamports: params.lamports}); return new Transaction().add({ keys: [ - {pubkey: from, isSigner: true, isWritable: true}, - {pubkey: to, isSigner: false, isWritable: true}, + {pubkey: params.fromPubkey, isSigner: true, isWritable: true}, + {pubkey: params.toPubkey, isSigner: false, isWritable: true}, ], programId: this.programId, data, @@ -260,12 +485,12 @@ export class SystemProgram { /** * Generate a Transaction that assigns an account to a program */ - static assign(from: PublicKey, programId: PublicKey): Transaction { + static assign(params: AssignParams): Transaction { const type = SYSTEM_INSTRUCTION_LAYOUTS.Assign; - const data = encodeData(type, {programId: programId.toBuffer()}); + const data = encodeData(type, {programId: params.programId.toBuffer()}); return new Transaction().add({ - keys: [{pubkey: from, isSigner: true, isWritable: true}], + keys: [{pubkey: params.fromPubkey, isSigner: true, isWritable: true}], programId: this.programId, data, }); @@ -276,27 +501,21 @@ export class SystemProgram { * an address generated with `from`, a seed, and programId */ static createAccountWithSeed( - from: PublicKey, - newAccount: PublicKey, - base: PublicKey, - seed: string, - lamports: number, - space: number, - programId: PublicKey, + params: CreateAccountWithSeedParams, ): Transaction { const type = SYSTEM_INSTRUCTION_LAYOUTS.CreateWithSeed; const data = encodeData(type, { - base: base.toBuffer(), - seed, - lamports, - space, - programId: programId.toBuffer(), + base: params.basePubkey.toBuffer(), + seed: params.seed, + lamports: params.lamports, + space: params.space, + programId: params.programId.toBuffer(), }); return new Transaction().add({ keys: [ - {pubkey: from, isSigner: true, isWritable: true}, - {pubkey: newAccount, isSigner: false, isWritable: true}, + {pubkey: params.fromPubkey, isSigner: true, isWritable: true}, + {pubkey: params.newAccountPubkey, isSigner: false, isWritable: true}, ], programId: this.programId, data, @@ -306,28 +525,37 @@ export class SystemProgram { /** * Generate a Transaction that creates a new Nonce account */ - static createNonceAccount( - from: PublicKey, - nonceAccount: PublicKey, - authorizedPubkey: PublicKey, - lamports: number, - ): Transaction { - let transaction = SystemProgram.createAccount( - from, - nonceAccount, - lamports, - this.nonceSpace, - this.programId, - ); - - const type = SYSTEM_INSTRUCTION_LAYOUTS.InitializeNonceAccount; - const data = encodeData(type, { - authorized: authorizedPubkey.toBuffer(), + static createNonceAccount(params: CreateNonceAccountParams): Transaction { + let transaction = SystemProgram.createAccount({ + fromPubkey: params.fromPubkey, + newAccountPubkey: params.noncePubkey, + lamports: params.lamports, + space: this.nonceSpace, + programId: this.programId, }); - return transaction.add({ + 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: nonceAccount, isSigner: false, isWritable: true}, + {pubkey: params.noncePubkey, isSigner: false, isWritable: true}, { pubkey: SYSVAR_RECENT_BLOCKHASHES_PUBKEY, isSigner: false, @@ -337,27 +565,25 @@ export class SystemProgram { ], programId: this.programId, data, - }); + }; + return new TransactionInstruction(instructionData); } /** * Generate an instruction to advance the nonce in a Nonce account */ - static nonceAdvance( - nonceAccount: PublicKey, - authorizedPubkey: PublicKey, - ): TransactionInstruction { + static nonceAdvance(params: AdvanceNonceParams): TransactionInstruction { const type = SYSTEM_INSTRUCTION_LAYOUTS.AdvanceNonceAccount; const data = encodeData(type); const instructionData = { keys: [ - {pubkey: nonceAccount, isSigner: false, isWritable: true}, + {pubkey: params.noncePubkey, isSigner: false, isWritable: true}, { pubkey: SYSVAR_RECENT_BLOCKHASHES_PUBKEY, isSigner: false, isWritable: false, }, - {pubkey: authorizedPubkey, isSigner: true, isWritable: false}, + {pubkey: params.authorizedPubkey, isSigner: true, isWritable: false}, ], programId: this.programId, data, @@ -368,19 +594,14 @@ export class SystemProgram { /** * Generate a Transaction that withdraws lamports from a Nonce account */ - static nonceWithdraw( - nonceAccount: PublicKey, - authorizedPubkey: PublicKey, - to: PublicKey, - lamports: number, - ): Transaction { + static nonceWithdraw(params: WithdrawNonceParams): Transaction { const type = SYSTEM_INSTRUCTION_LAYOUTS.WithdrawNonceAccount; - const data = encodeData(type, {lamports}); + const data = encodeData(type, {lamports: params.lamports}); return new Transaction().add({ keys: [ - {pubkey: nonceAccount, isSigner: false, isWritable: true}, - {pubkey: to, isSigner: false, isWritable: true}, + {pubkey: params.noncePubkey, isSigner: false, isWritable: true}, + {pubkey: params.toPubkey, isSigner: false, isWritable: true}, { pubkey: SYSVAR_RECENT_BLOCKHASHES_PUBKEY, isSigner: false, @@ -391,7 +612,7 @@ export class SystemProgram { isSigner: false, isWritable: false, }, - {pubkey: authorizedPubkey, isSigner: true, isWritable: false}, + {pubkey: params.authorizedPubkey, isSigner: true, isWritable: false}, ], programId: this.programId, data, @@ -402,20 +623,16 @@ export class SystemProgram { * Generate a Transaction that authorizes a new PublicKey as the authority * on a Nonce account. */ - static nonceAuthorize( - nonceAccount: PublicKey, - authorizedPubkey: PublicKey, - newAuthorized: PublicKey, - ): Transaction { + static nonceAuthorize(params: AuthorizeNonceParams): Transaction { const type = SYSTEM_INSTRUCTION_LAYOUTS.AuthorizeNonceAccount; const data = encodeData(type, { - newAuthorized: newAuthorized.toBuffer(), + authorized: params.newAuthorizedPubkey.toBuffer(), }); return new Transaction().add({ keys: [ - {pubkey: nonceAccount, isSigner: false, isWritable: true}, - {pubkey: authorizedPubkey, isSigner: true, isWritable: false}, + {pubkey: params.noncePubkey, isSigner: false, isWritable: true}, + {pubkey: params.authorizedPubkey, isSigner: true, isWritable: false}, ], programId: this.programId, data, diff --git a/web3.js/test/connection.test.js b/web3.js/test/connection.test.js index e15f58d454..1d46c4c55b 100644 --- a/web3.js/test/connection.test.js +++ b/web3.js/test/connection.test.js @@ -109,10 +109,10 @@ test('get program accounts', async () => { result: {Ok: null}, }, ]); - let transaction = SystemProgram.assign( - account0.publicKey, - programId.publicKey, - ); + let transaction = SystemProgram.assign({ + fromPubkey: account0.publicKey, + programId: programId.publicKey, + }); await sendAndConfirmTransaction(connection, transaction, account0); mockRpc.push([ @@ -140,7 +140,11 @@ test('get program accounts', async () => { result: {Ok: null}, }, ]); - transaction = SystemProgram.assign(account1.publicKey, programId.publicKey); + transaction = SystemProgram.assign({ + fromPubkey: account1.publicKey, + programId: programId.publicKey, + }); + await sendAndConfirmTransaction(connection, transaction, account1); mockGetRecentBlockhash('recent'); @@ -651,14 +655,16 @@ test('get confirmed block', async () => { url, { method: 'getConfirmedBlock', - params: [10000], + params: [Number.MAX_SAFE_INTEGER], }, { error: null, result: null, }, ]); - await expect(connection.getConfirmedBlock(10000)).rejects.toThrow(); + await expect( + connection.getConfirmedBlock(Number.MAX_SAFE_INTEGER), + ).rejects.toThrow(); }); test('get recent blockhash', async () => { @@ -962,11 +968,11 @@ test('transaction', async () => { }, ]); - const transaction = SystemProgram.transfer( - accountFrom.publicKey, - accountTo.publicKey, - 10, - ); + const transaction = SystemProgram.transfer({ + fromPubkey: accountFrom.publicKey, + toPubkey: accountTo.publicKey, + lamports: 10, + }); const signature = await connection.sendTransaction(transaction, accountFrom); mockRpc.push([ @@ -1088,12 +1094,16 @@ test('multi-instruction transaction', async () => { // 1. Move(accountFrom, accountTo) // 2. Move(accountTo, accountFrom) - const transaction = SystemProgram.transfer( - accountFrom.publicKey, - accountTo.publicKey, - 100, - ).add( - SystemProgram.transfer(accountTo.publicKey, accountFrom.publicKey, 100), + const transaction = SystemProgram.transfer({ + fromPubkey: accountFrom.publicKey, + toPubkey: accountTo.publicKey, + lamports: 100, + }).add( + SystemProgram.transfer({ + fromPubkey: accountTo.publicKey, + toPubkey: accountFrom.publicKey, + lamports: 100, + }), ); const signature = await connection.sendTransaction( transaction, @@ -1149,11 +1159,11 @@ test('account change notification', async () => { await connection.requestAirdrop(owner.publicKey, LAMPORTS_PER_SOL); try { - let transaction = SystemProgram.transfer( - owner.publicKey, - programAccount.publicKey, - balanceNeeded, - ); + let transaction = SystemProgram.transfer({ + fromPubkey: owner.publicKey, + toPubkey: programAccount.publicKey, + lamports: balanceNeeded, + }); await sendAndConfirmTransaction(connection, transaction, owner); } catch (err) { await connection.removeAccountChangeListener(subscriptionId); @@ -1213,11 +1223,11 @@ test('program account change notification', async () => { await connection.requestAirdrop(owner.publicKey, LAMPORTS_PER_SOL); try { - let transaction = SystemProgram.transfer( - owner.publicKey, - programAccount.publicKey, - balanceNeeded, - ); + let transaction = SystemProgram.transfer({ + fromPubkey: owner.publicKey, + toPubkey: programAccount.publicKey, + lamports: balanceNeeded, + }); await sendAndConfirmTransaction(connection, transaction, owner); } catch (err) { await connection.removeProgramAccountChangeListener(subscriptionId); diff --git a/web3.js/test/nonce.test.js b/web3.js/test/nonce.test.js index 6165b8d76c..8de2a7d0db 100644 --- a/web3.js/test/nonce.test.js +++ b/web3.js/test/nonce.test.js @@ -86,12 +86,12 @@ test('create and query nonce account', async () => { }, ]); - const transaction = SystemProgram.createNonceAccount( - from.publicKey, - nonceAccount.publicKey, - from.publicKey, - minimumAmount, - ); + const transaction = SystemProgram.createNonceAccount({ + fromPubkey: from.publicKey, + noncePubkey: nonceAccount.publicKey, + authorizedPubkey: from.publicKey, + lamports: minimumAmount, + }); await connection.sendTransaction(transaction, from, nonceAccount); const expectedData = Buffer.alloc(68); diff --git a/web3.js/test/stake-program.test.js b/web3.js/test/stake-program.test.js index 1b74037ad6..88adeed5c2 100644 --- a/web3.js/test/stake-program.test.js +++ b/web3.js/test/stake-program.test.js @@ -12,7 +12,6 @@ import { StakeInstruction, StakeProgram, SystemInstruction, - SystemProgram, Transaction, } from '../src'; import {mockRpcEnabled} from './__mocks__/node-fetch'; @@ -34,6 +33,7 @@ test('createAccountWithSeed', () => { const authorizedPubkey = new Account().publicKey; const authorized = new Authorized(authorizedPubkey, authorizedPubkey); const lockup = new Lockup(0, 0, fromPubkey); + const lamports = 123; const transaction = StakeProgram.createAccountWithSeed({ fromPubkey, stakePubkey: newAccountPubkey, @@ -41,19 +41,26 @@ test('createAccountWithSeed', () => { seed, authorized, lockup, - lamports: 123, + lamports, }); - expect(transaction.instructions).toHaveLength(2); const [systemInstruction, stakeInstruction] = transaction.instructions; - expect(systemInstruction.programId).toEqual(SystemProgram.programId); - - // TODO decode system instruction - - const params = StakeInstruction.decodeInitialize(stakeInstruction); - expect(params.stakePubkey).toEqual(newAccountPubkey); - expect(params.authorized).toEqual(authorized); - expect(params.lockup).toEqual(lockup); + const systemParams = { + fromPubkey, + newAccountPubkey, + basePubkey: fromPubkey, + seed, + lamports, + space: StakeProgram.space, + programId: StakeProgram.programId, + }; + expect(systemParams).toEqual( + SystemInstruction.decodeCreateWithSeed(systemInstruction), + ); + const initParams = {stakePubkey: newAccountPubkey, authorized, lockup}; + expect(initParams).toEqual( + StakeInstruction.decodeInitialize(stakeInstruction), + ); }); test('createAccount', () => { @@ -62,24 +69,31 @@ test('createAccount', () => { const authorizedPubkey = new Account().publicKey; const authorized = new Authorized(authorizedPubkey, authorizedPubkey); const lockup = new Lockup(0, 0, fromPubkey); + const lamports = 123; const transaction = StakeProgram.createAccount({ fromPubkey, stakePubkey: newAccountPubkey, authorized, lockup, - lamports: 123, + lamports, }); - expect(transaction.instructions).toHaveLength(2); const [systemInstruction, stakeInstruction] = transaction.instructions; - expect(systemInstruction.programId).toEqual(SystemProgram.programId); + const systemParams = { + fromPubkey, + newAccountPubkey, + lamports, + space: StakeProgram.space, + programId: StakeProgram.programId, + }; + expect(systemParams).toEqual( + SystemInstruction.decodeCreateAccount(systemInstruction), + ); - // TODO decode system instruction - - const params = StakeInstruction.decodeInitialize(stakeInstruction); - expect(params.stakePubkey).toEqual(newAccountPubkey); - expect(params.authorized).toEqual(authorized); - expect(params.lockup).toEqual(lockup); + const initParams = {stakePubkey: newAccountPubkey, authorized, lockup}; + expect(initParams).toEqual( + StakeInstruction.decodeInitialize(stakeInstruction), + ); }); test('delegate', () => { @@ -127,7 +141,16 @@ test('split', () => { const transaction = StakeProgram.split(params); expect(transaction.instructions).toHaveLength(2); const [systemInstruction, stakeInstruction] = transaction.instructions; - expect(systemInstruction.programId).toEqual(SystemProgram.programId); + const systemParams = { + fromPubkey: stakePubkey, + newAccountPubkey: splitStakePubkey, + lamports: 0, + space: StakeProgram.space, + programId: StakeProgram.programId, + }; + expect(systemParams).toEqual( + SystemInstruction.decodeCreateAccount(systemInstruction), + ); expect(params).toEqual(StakeInstruction.decodeSplit(stakeInstruction)); }); @@ -182,13 +205,10 @@ test('StakeInstructions', () => { ); expect(createWithSeedTransaction.instructions).toHaveLength(2); - const systemInstruction = SystemInstruction.from( + const systemInstructionType = SystemInstruction.decodeInstructionType( createWithSeedTransaction.instructions[0], ); - expect(systemInstruction.fromPublicKey).toEqual(from.publicKey); - expect(systemInstruction.toPublicKey).toEqual(newAccountPubkey); - expect(systemInstruction.amount).toEqual(amount); - expect(systemInstruction.programId).toEqual(SystemProgram.programId); + expect(systemInstructionType).toEqual('CreateWithSeed'); const stakeInstructionType = StakeInstruction.decodeInstructionType( createWithSeedTransaction.instructions[1], @@ -237,6 +257,35 @@ test('live staking actions', async () => { 'recent', ); + { + // Create Stake account without seed + const newStakeAccount = new Account(); + let createAndInitialize = StakeProgram.createAccount({ + fromPubkey: from.publicKey, + stakePubkey: newStakeAccount.publicKey, + authorized: new Authorized(authorized.publicKey, authorized.publicKey), + lockup: new Lockup(0, 0, new PublicKey('0x00')), + lamports: minimumAmount + 42, + }); + + await sendAndConfirmRecentTransaction( + connection, + createAndInitialize, + from, + newStakeAccount, + ); + expect(await connection.getBalance(newStakeAccount.publicKey)).toEqual( + minimumAmount + 42, + ); + + let delegation = StakeProgram.delegate({ + stakePubkey: newStakeAccount.publicKey, + authorizedPubkey: authorized.publicKey, + votePubkey, + }); + await sendAndConfirmRecentTransaction(connection, delegation, authorized); + } + // Create Stake account with seed const seed = 'test string'; const newAccountPubkey = PublicKey.createWithSeed( diff --git a/web3.js/test/system-program.test.js b/web3.js/test/system-program.test.js index b5006e74cb..a7021ab5e1 100644 --- a/web3.js/test/system-program.test.js +++ b/web3.js/test/system-program.test.js @@ -7,6 +7,7 @@ import { SystemInstruction, SystemProgram, Transaction, + TransactionInstruction, sendAndConfirmRecentTransaction, LAMPORTS_PER_SOL, } from '../src'; @@ -20,219 +21,129 @@ if (!mockRpcEnabled) { } test('createAccount', () => { - const from = new Account(); - const newAccount = new Account(); - let transaction; - - transaction = SystemProgram.createAccount( - from.publicKey, - newAccount.publicKey, - 123, - BudgetProgram.space, - BudgetProgram.programId, + const params = { + fromPubkey: new Account().publicKey, + newAccountPubkey: new Account().publicKey, + lamports: 123, + space: BudgetProgram.space, + programId: BudgetProgram.programId, + }; + const transaction = SystemProgram.createAccount(params); + expect(transaction.instructions).toHaveLength(1); + const [systemInstruction] = transaction.instructions; + expect(params).toEqual( + SystemInstruction.decodeCreateAccount(systemInstruction), ); - - expect(transaction.keys).toHaveLength(2); - expect(transaction.programId).toEqual(SystemProgram.programId); - // TODO: Validate transaction contents more }); test('transfer', () => { - const from = new Account(); - const to = new Account(); - let transaction; - - transaction = SystemProgram.transfer(from.publicKey, to.publicKey, 123); - - expect(transaction.keys).toHaveLength(2); - expect(transaction.programId).toEqual(SystemProgram.programId); - // TODO: Validate transaction contents more + const params = { + fromPubkey: new Account().publicKey, + toPubkey: new Account().publicKey, + lamports: 123, + }; + const transaction = SystemProgram.transfer(params); + expect(transaction.instructions).toHaveLength(1); + const [systemInstruction] = transaction.instructions; + expect(params).toEqual(SystemInstruction.decodeTransfer(systemInstruction)); }); test('assign', () => { - const from = new Account(); - const to = new Account(); - let transaction; - - transaction = SystemProgram.assign(from.publicKey, to.publicKey); - - expect(transaction.keys).toHaveLength(1); - expect(transaction.programId).toEqual(SystemProgram.programId); - // TODO: Validate transaction contents more + const params = { + fromPubkey: new Account().publicKey, + programId: new Account().publicKey, + }; + const transaction = SystemProgram.assign(params); + expect(transaction.instructions).toHaveLength(1); + const [systemInstruction] = transaction.instructions; + expect(params).toEqual(SystemInstruction.decodeAssign(systemInstruction)); }); test('createAccountWithSeed', () => { - const from = new Account(); - const newAccount = new Account(); - let transaction; - - transaction = SystemProgram.createAccountWithSeed( - from.publicKey, - newAccount.publicKey, - from.publicKey, - 'hi there', - 123, - BudgetProgram.space, - BudgetProgram.programId, + const fromPubkey = new Account().publicKey; + const params = { + fromPubkey, + newAccountPubkey: new Account().publicKey, + basePubkey: fromPubkey, + seed: 'hi there', + lamports: 123, + space: BudgetProgram.space, + programId: BudgetProgram.programId, + }; + const transaction = SystemProgram.createAccountWithSeed(params); + expect(transaction.instructions).toHaveLength(1); + const [systemInstruction] = transaction.instructions; + expect(params).toEqual( + SystemInstruction.decodeCreateWithSeed(systemInstruction), ); - - expect(transaction.keys).toHaveLength(2); - expect(transaction.programId).toEqual(SystemProgram.programId); - // TODO: Validate transaction contents more }); test('createNonceAccount', () => { - const from = new Account(); - const nonceAccount = new Account(); - - const transaction = SystemProgram.createNonceAccount( - from.publicKey, - nonceAccount.publicKey, - from.publicKey, - 123, - ); + const fromPubkey = new Account().publicKey; + const params = { + fromPubkey, + noncePubkey: new Account().publicKey, + authorizedPubkey: fromPubkey, + lamports: 123, + }; + const transaction = SystemProgram.createNonceAccount(params); expect(transaction.instructions).toHaveLength(2); - expect(transaction.instructions[0].programId).toEqual( - SystemProgram.programId, + const [createInstruction, initInstruction] = transaction.instructions; + + const createParams = { + fromPubkey: params.fromPubkey, + newAccountPubkey: params.noncePubkey, + lamports: params.lamports, + space: SystemProgram.nonceSpace, + programId: SystemProgram.programId, + }; + expect(createParams).toEqual( + SystemInstruction.decodeCreateAccount(createInstruction), ); - expect(transaction.instructions[1].programId).toEqual( - SystemProgram.programId, + + const initParams = { + noncePubkey: params.noncePubkey, + authorizedPubkey: fromPubkey, + }; + expect(initParams).toEqual( + SystemInstruction.decodeNonceInitialize(initInstruction), ); - // TODO: Validate transaction contents more +}); + +test('nonceAdvance', () => { + const params = { + noncePubkey: new Account().publicKey, + authorizedPubkey: new Account().publicKey, + }; + const instruction = SystemProgram.nonceAdvance(params); + expect(params).toEqual(SystemInstruction.decodeNonceAdvance(instruction)); }); test('nonceWithdraw', () => { - const from = new Account(); - const nonceAccount = new Account(); - const to = new Account(); - - const transaction = SystemProgram.nonceWithdraw( - nonceAccount.publicKey, - from.publicKey, - to.publicKey, - 123, - ); - - expect(transaction.keys).toHaveLength(5); - expect(transaction.programId).toEqual(SystemProgram.programId); - // TODO: Validate transaction contents more + const params = { + noncePubkey: new Account().publicKey, + authorizedPubkey: new Account().publicKey, + toPubkey: new Account().publicKey, + lamports: 123, + }; + const transaction = SystemProgram.nonceWithdraw(params); + expect(transaction.instructions).toHaveLength(1); + const [instruction] = transaction.instructions; + expect(params).toEqual(SystemInstruction.decodeNonceWithdraw(instruction)); }); test('nonceAuthorize', () => { - const nonceAccount = new Account(); - const authorized = new Account(); - const newAuthorized = new Account(); + const params = { + noncePubkey: new Account().publicKey, + authorizedPubkey: new Account().publicKey, + newAuthorizedPubkey: new Account().publicKey, + }; - const transaction = SystemProgram.nonceAuthorize( - nonceAccount.publicKey, - authorized.publicKey, - newAuthorized.publicKey, - ); - - expect(transaction.keys).toHaveLength(2); - expect(transaction.programId).toEqual(SystemProgram.programId); - // TODO: Validate transaction contents more -}); - -test('SystemInstruction create', () => { - const from = new Account(); - const to = new Account(); - const program = new Account(); - const amount = 42; - const space = 100; - const recentBlockhash = 'EETubP5AKHgjPAhzPAFcb8BAY1hMH639CWCFTqi3hq1k'; // Arbitrary known recentBlockhash - const create = SystemProgram.createAccount( - from.publicKey, - to.publicKey, - amount, - space, - program.publicKey, - ); - const transaction = new Transaction({recentBlockhash}).add(create); - - const systemInstruction = SystemInstruction.from(transaction.instructions[0]); - expect(systemInstruction.fromPublicKey).toEqual(from.publicKey); - expect(systemInstruction.toPublicKey).toEqual(to.publicKey); - expect(systemInstruction.amount).toEqual(amount); - expect(systemInstruction.programId).toEqual(SystemProgram.programId); -}); - -test('SystemInstruction transfer', () => { - const from = new Account(); - const to = new Account(); - const amount = 42; - const recentBlockhash = 'EETubP5AKHgjPAhzPAFcb8BAY1hMH639CWCFTqi3hq1k'; // Arbitrary known recentBlockhash - const transfer = SystemProgram.transfer(from.publicKey, to.publicKey, amount); - const transaction = new Transaction({recentBlockhash}).add(transfer); - transaction.sign(from); - - const systemInstruction = SystemInstruction.from(transaction.instructions[0]); - expect(systemInstruction.fromPublicKey).toEqual(from.publicKey); - expect(systemInstruction.toPublicKey).toEqual(to.publicKey); - expect(systemInstruction.amount).toEqual(amount); - expect(systemInstruction.programId).toEqual(SystemProgram.programId); -}); - -test('SystemInstruction assign', () => { - const from = new Account(); - const program = new Account(); - const recentBlockhash = 'EETubP5AKHgjPAhzPAFcb8BAY1hMH639CWCFTqi3hq1k'; // Arbitrary known recentBlockhash - const assign = SystemProgram.assign(from.publicKey, program.publicKey); - const transaction = new Transaction({recentBlockhash}).add(assign); - transaction.sign(from); - - const systemInstruction = SystemInstruction.from(transaction.instructions[0]); - expect(systemInstruction.fromPublicKey).toBeNull(); - expect(systemInstruction.toPublicKey).toBeNull(); - expect(systemInstruction.amount).toBeNull(); - expect(systemInstruction.programId).toEqual(SystemProgram.programId); -}); - -test('SystemInstruction createWithSeed', () => { - const from = new Account(); - const to = new Account(); - const program = new Account(); - const amount = 42; - const space = 100; - const recentBlockhash = 'EETubP5AKHgjPAhzPAFcb8BAY1hMH639CWCFTqi3hq1k'; // Arbitrary known recentBlockhash - const create = SystemProgram.createAccountWithSeed( - from.publicKey, - to.publicKey, - from.publicKey, - 'hi there', - amount, - space, - program.publicKey, - ); - const transaction = new Transaction({recentBlockhash}).add(create); - - const systemInstruction = SystemInstruction.from(transaction.instructions[0]); - expect(systemInstruction.fromPublicKey).toEqual(from.publicKey); - expect(systemInstruction.toPublicKey).toEqual(to.publicKey); - expect(systemInstruction.amount).toEqual(amount); - expect(systemInstruction.programId).toEqual(SystemProgram.programId); -}); - -test('SystemInstruction nonceWithdraw', () => { - const nonceAccount = new Account(); - const authorized = new Account(); - const to = new Account(); - const amount = 42; - const recentBlockhash = 'EETubP5AKHgjPAhzPAFcb8BAY1hMH639CWCFTqi3hq1k'; // Arbitrary known recentBlockhash - const nonceWithdraw = SystemProgram.nonceWithdraw( - nonceAccount.publicKey, - authorized.publicKey, - to.publicKey, - amount, - ); - const transaction = new Transaction({recentBlockhash}).add(nonceWithdraw); - - const systemInstruction = SystemInstruction.from(transaction.instructions[0]); - expect(systemInstruction.fromPublicKey).toEqual(nonceAccount.publicKey); - expect(systemInstruction.toPublicKey).toEqual(to.publicKey); - expect(systemInstruction.amount).toEqual(amount); - expect(systemInstruction.programId).toEqual(SystemProgram.programId); + const transaction = SystemProgram.nonceAuthorize(params); + expect(transaction.instructions).toHaveLength(1); + const [instruction] = transaction.instructions; + expect(params).toEqual(SystemInstruction.decodeNonceAuthorize(instruction)); }); test('non-SystemInstruction error', () => { @@ -249,7 +160,9 @@ test('non-SystemInstruction error', () => { data: Buffer.from([2, 0, 0, 0]), }; expect(() => { - new SystemInstruction(badProgramId, 'Create'); + SystemInstruction.decodeInstructionType( + new TransactionInstruction(badProgramId), + ); }).toThrow(); const amount = 123; @@ -264,12 +177,12 @@ test('non-SystemInstruction error', () => { transaction.sign(from); expect(() => { - SystemInstruction.from(transaction.instructions[1]); + SystemInstruction.decodeInstructionType(transaction.instructions[1]); }).toThrow(); transaction.instructions[0].data[0] = 11; expect(() => { - SystemInstruction.from(transaction.instructions[0]); + SystemInstruction.decodeInstructionType(transaction.instructions[0]); }).toThrow(); }); @@ -284,20 +197,22 @@ test('live Nonce actions', async () => { const from = new Account(); const to = new Account(); const authority = new Account(); + const newAuthority = new Account(); await connection.requestAirdrop(from.publicKey, 2 * LAMPORTS_PER_SOL); await connection.requestAirdrop(authority.publicKey, LAMPORTS_PER_SOL); + await connection.requestAirdrop(newAuthority.publicKey, LAMPORTS_PER_SOL); const minimumAmount = await connection.getMinimumBalanceForRentExemption( SystemProgram.nonceSpace, 'recent', ); - let createNonceAccount = SystemProgram.createNonceAccount( - from.publicKey, - nonceAccount.publicKey, - from.publicKey, - minimumAmount, - ); + let createNonceAccount = SystemProgram.createNonceAccount({ + fromPubkey: from.publicKey, + noncePubkey: nonceAccount.publicKey, + authorizedPubkey: from.publicKey, + lamports: minimumAmount, + }); await sendAndConfirmRecentTransaction( connection, createNonceAccount, @@ -311,33 +226,74 @@ test('live Nonce actions', async () => { const nonceQuery2 = await connection.getNonce(nonceAccount.publicKey); expect(nonceQuery1.nonce).toEqual(nonceQuery2.nonce); + // Wait for blockhash to advance await sleep(500); const advanceNonce = new Transaction().add( - SystemProgram.nonceAdvance(nonceAccount.publicKey, from.publicKey), + SystemProgram.nonceAdvance({ + noncePubkey: nonceAccount.publicKey, + authorizedPubkey: from.publicKey, + }), ); await sendAndConfirmRecentTransaction(connection, advanceNonce, from); - const nonceQuery3 = await connection.getNonce(nonceAccount.publicKey); expect(nonceQuery1.nonce).not.toEqual(nonceQuery3.nonce); const nonce = nonceQuery3.nonce; + // Wait for blockhash to advance await sleep(500); - let transfer = SystemProgram.transfer( - from.publicKey, - to.publicKey, - minimumAmount, + const authorizeNonce = new Transaction().add( + SystemProgram.nonceAuthorize({ + noncePubkey: nonceAccount.publicKey, + authorizedPubkey: from.publicKey, + newAuthorizedPubkey: newAuthority.publicKey, + }), ); + await sendAndConfirmRecentTransaction(connection, authorizeNonce, from); + + let transfer = SystemProgram.transfer({ + fromPubkey: from.publicKey, + toPubkey: to.publicKey, + lamports: minimumAmount, + }); transfer.nonceInfo = { nonce, - nonceInstruction: SystemProgram.nonceAdvance( - nonceAccount.publicKey, - from.publicKey, - ), + nonceInstruction: SystemProgram.nonceAdvance({ + noncePubkey: nonceAccount.publicKey, + authorizedPubkey: newAuthority.publicKey, + }), }; - await sendAndConfirmRecentTransaction(connection, transfer, from); + await sendAndConfirmRecentTransaction( + connection, + transfer, + from, + newAuthority, + ); const toBalance = await connection.getBalance(to.publicKey); expect(toBalance).toEqual(minimumAmount); + + // Wait for blockhash to advance + await sleep(500); + + const withdrawAccount = new Account(); + const withdrawNonce = new Transaction().add( + SystemProgram.nonceWithdraw({ + noncePubkey: nonceAccount.publicKey, + authorizedPubkey: newAuthority.publicKey, + lamports: minimumAmount, + toPubkey: withdrawAccount.publicKey, + }), + ); + await sendAndConfirmRecentTransaction( + connection, + withdrawNonce, + newAuthority, + ); + expect(await connection.getBalance(nonceAccount.publicKey)).toEqual(0); + const withdrawBalance = await connection.getBalance( + withdrawAccount.publicKey, + ); + expect(withdrawBalance).toEqual(minimumAmount); }); diff --git a/web3.js/test/transaction-payer.test.js b/web3.js/test/transaction-payer.test.js index 801b515cc9..5a383e6518 100644 --- a/web3.js/test/transaction-payer.test.js +++ b/web3.js/test/transaction-payer.test.js @@ -100,11 +100,11 @@ test('transaction-payer', async () => { }, ]); - const transaction = SystemProgram.transfer( - accountFrom.publicKey, - accountTo.publicKey, - 10, - ); + const transaction = SystemProgram.transfer({ + fromPubkey: accountFrom.publicKey, + toPubkey: accountTo.publicKey, + lamports: 10, + }); const signature = await connection.sendTransaction( transaction, diff --git a/web3.js/test/transaction.test.js b/web3.js/test/transaction.test.js index 30b63eea25..1d71a8b233 100644 --- a/web3.js/test/transaction.test.js +++ b/web3.js/test/transaction.test.js @@ -12,11 +12,11 @@ test('signPartial', () => { const account1 = new Account(); const account2 = new Account(); const recentBlockhash = account1.publicKey.toBase58(); // Fake recentBlockhash - const transfer = SystemProgram.transfer( - account1.publicKey, - account2.publicKey, - 123, - ); + const transfer = SystemProgram.transfer({ + fromPubkey: account1.publicKey, + toPubkey: account2.publicKey, + lamports: 123, + }); const transaction = new Transaction({recentBlockhash}).add(transfer); transaction.sign(account1, account2); @@ -33,16 +33,16 @@ test('transfer signatures', () => { const account1 = new Account(); const account2 = new Account(); const recentBlockhash = account1.publicKey.toBase58(); // Fake recentBlockhash - const transfer1 = SystemProgram.transfer( - account1.publicKey, - account2.publicKey, - 123, - ); - const transfer2 = SystemProgram.transfer( - account2.publicKey, - account1.publicKey, - 123, - ); + const transfer1 = SystemProgram.transfer({ + fromPubkey: account1.publicKey, + toPubkey: account2.publicKey, + lamports: 123, + }); + const transfer2 = SystemProgram.transfer({ + fromPubkey: account2.publicKey, + toPubkey: account1.publicKey, + lamports: 123, + }); const orgTransaction = new Transaction({recentBlockhash}).add( transfer1, @@ -62,16 +62,16 @@ test('dedup signatures', () => { const account1 = new Account(); const account2 = new Account(); const recentBlockhash = account1.publicKey.toBase58(); // Fake recentBlockhash - const transfer1 = SystemProgram.transfer( - account1.publicKey, - account2.publicKey, - 123, - ); - const transfer2 = SystemProgram.transfer( - account1.publicKey, - account2.publicKey, - 123, - ); + const transfer1 = SystemProgram.transfer({ + fromPubkey: account1.publicKey, + toPubkey: account2.publicKey, + lamports: 123, + }); + const transfer2 = SystemProgram.transfer({ + fromPubkey: account1.publicKey, + toPubkey: account2.publicKey, + lamports: 123, + }); const orgTransaction = new Transaction({recentBlockhash}).add( transfer1, @@ -88,14 +88,18 @@ test('use nonce', () => { const nonceInfo = { nonce, - nonceInstruction: SystemProgram.nonceAdvance( - nonceAccount.publicKey, - account1.publicKey, - ), + nonceInstruction: SystemProgram.nonceAdvance({ + noncePubkey: nonceAccount.publicKey, + authorizedPubkey: account1.publicKey, + }), }; const transferTransaction = new Transaction({nonceInfo}).add( - SystemProgram.transfer(account1.publicKey, account2.publicKey, 123), + SystemProgram.transfer({ + fromPubkey: account1.publicKey, + toPubkey: account2.publicKey, + lamports: 123, + }), ); transferTransaction.sign(account1); @@ -137,7 +141,11 @@ test('parse wire format and serialize', () => { const recipient = new PublicKey( 'J3dxNj7nDRRqRRXuEMynDG57DkZK4jYRuv3Garmb1i99', ); // Arbitrary known public key - const transfer = SystemProgram.transfer(sender.publicKey, recipient, 49); + const transfer = SystemProgram.transfer({ + fromPubkey: sender.publicKey, + toPubkey: recipient, + lamports: 49, + }); const expectedTransaction = new Transaction({recentBlockhash}).add(transfer); expectedTransaction.sign(sender); @@ -198,7 +206,11 @@ test('serialize unsigned transaction', () => { const recipient = new PublicKey( 'J3dxNj7nDRRqRRXuEMynDG57DkZK4jYRuv3Garmb1i99', ); // Arbitrary known public key - const transfer = SystemProgram.transfer(sender.publicKey, recipient, 49); + const transfer = SystemProgram.transfer({ + fromPubkey: sender.publicKey, + toPubkey: recipient, + lamports: 49, + }); const expectedTransaction = new Transaction({recentBlockhash}).add(transfer); const wireTransactionArray = [