diff --git a/web3.js/module.flow.js b/web3.js/module.flow.js index 631337fbbd..3a2fe59110 100644 --- a/web3.js/module.flow.js +++ b/web3.js/module.flow.js @@ -80,22 +80,25 @@ declare module '@solana/web3.js' { declare export type TransactionSignature = string; declare export type TransactionId = string; - declare type TransactionCtorFields = {| - signature?: Buffer; + declare type TransactionInstructionCtorFields = {| keys?: Array; programId?: PublicKey; - fee?: number; userdata?: Buffer; |}; + declare export class TransactionInstruction { + fee: number; + + constructor(opts?: TransactionInstructionCtorFields): TransactionInstruction; + } + + declare type TransactionCtorFields = {| + fee?: number; + |}; declare export class Transaction { signature: ?Buffer; - keys: Array; - programId: ?PublicKey; - lastId: ?TransactionId; fee: number; - userdata: Buffer; constructor(opts?: TransactionCtorFields): Transaction; sign(from: Account): void; diff --git a/web3.js/src/budget-program.js b/web3.js/src/budget-program.js index 09cd116587..984d7df823 100644 --- a/web3.js/src/budget-program.js +++ b/web3.js/src/budget-program.js @@ -203,8 +203,7 @@ export class BudgetProgram { pos += payment.length; } - return new Transaction({ - fee: 0, + return new Transaction().add({ keys: [from, to], programId: this.programId, userdata: userdata.slice(0, pos), @@ -224,8 +223,7 @@ export class BudgetProgram { pos += paymentData.length; } - return new Transaction({ - fee: 0, + return new Transaction().add({ keys: [from, program, to], programId: this.programId, userdata: userdata.slice(0, pos), @@ -245,8 +243,7 @@ export class BudgetProgram { pos += paymentData.length; } - return new Transaction({ - fee: 0, + return new Transaction().add({ keys: [from, program, to], programId: this.programId, userdata: userdata.slice(0, pos), @@ -289,8 +286,7 @@ export class BudgetProgram { paymentData.copy(userdata, pos); pos += paymentData.length; - return new Transaction({ - fee: 0, + return new Transaction().add({ keys: [from, program, to], programId: this.programId, userdata: userdata.slice(0, pos), @@ -309,8 +305,7 @@ export class BudgetProgram { userdata.writeUInt32LE(1, 0); // ApplyTimestamp instruction whenData.copy(userdata, 4); - return new Transaction({ - fee: 0, + return new Transaction().add({ keys: [from, program, to], programId: this.programId, userdata, @@ -334,8 +329,7 @@ export class BudgetProgram { userdata, ); - return new Transaction({ - fee: 0, + return new Transaction().add({ keys: [from, program, to], programId: this.programId, userdata, diff --git a/web3.js/src/loader.js b/web3.js/src/loader.js index 3dc9e68987..485ea44c80 100644 --- a/web3.js/src/loader.js +++ b/web3.js/src/loader.js @@ -58,8 +58,7 @@ export class Loader { userdata, ); - const transaction = new Transaction({ - fee: 0, + const transaction = new Transaction().add({ keys: [program.publicKey], programId: this.programId, userdata, @@ -85,8 +84,7 @@ export class Loader { userdata, ); - let transaction = new Transaction({ - fee: 0, + let transaction = new Transaction().add({ keys: [program.publicKey], programId: this.programId, userdata, diff --git a/web3.js/src/system-program.js b/web3.js/src/system-program.js index 743bca9cf7..10fbb5c5b3 100644 --- a/web3.js/src/system-program.js +++ b/web3.js/src/system-program.js @@ -46,8 +46,7 @@ export class SystemProgram { userdata, ); - return new Transaction({ - fee: 0, + return new Transaction().add({ keys: [from, newAccount], programId: SystemProgram.programId, userdata, @@ -72,8 +71,7 @@ export class SystemProgram { userdata, ); - return new Transaction({ - fee: 0, + return new Transaction().add({ keys: [from, to], programId: SystemProgram.programId, userdata, @@ -98,8 +96,7 @@ export class SystemProgram { userdata, ); - return new Transaction({ - fee: 0, + return new Transaction().add({ keys: [from], programId: SystemProgram.programId, userdata, @@ -122,8 +119,7 @@ export class SystemProgram { userdata, ); - return new Transaction({ - fee: 0, + return new Transaction().add({ keys: [programId], programId: SystemProgram.programId, userdata, diff --git a/web3.js/src/token-program.js b/web3.js/src/token-program.js index de7c7d8fc3..3515e1750c 100644 --- a/web3.js/src/token-program.js +++ b/web3.js/src/token-program.js @@ -231,8 +231,7 @@ export class Token { ); await sendAndConfirmTransaction(connection, owner, transaction); - transaction = new Transaction({ - fee: 0, + transaction = new Transaction().add({ keys: [tokenAccount.publicKey, initialAccountPublicKey], programId, userdata, @@ -283,8 +282,7 @@ export class Token { if (source) { keys.push(source); } - transaction = new Transaction({ - fee: 0, + transaction = new Transaction().add({ keys, programId: this.programId, userdata, @@ -389,8 +387,7 @@ export class Token { if (accountInfo.source) { keys.push(accountInfo.source); } - const transaction = new Transaction({ - fee: 0, + const transaction = new Transaction().add({ keys, programId: this.programId, userdata, @@ -427,8 +424,7 @@ export class Token { userdata, ); - const transaction = new Transaction({ - fee: 0, + const transaction = new Transaction().add({ keys: [owner.publicKey, account, delegate], programId: this.programId, userdata, @@ -477,8 +473,7 @@ export class Token { ); const keys = [owner.publicKey, account,newOwner]; - const transaction = new Transaction({ - fee: 0, + const transaction = new Transaction().add({ keys, programId: this.programId, userdata, diff --git a/web3.js/src/transaction.js b/web3.js/src/transaction.js index b03b84dc56..1aa422a105 100644 --- a/web3.js/src/transaction.js +++ b/web3.js/src/transaction.js @@ -20,34 +20,24 @@ export type TransactionSignature = string; export type TransactionId = string; /** - * List of Transaction object fields that may be initialized at construction + * List of TransactionInstruction object fields that may be initialized at construction * - * @typedef {Object} TransactionCtorFields - * @property {?Buffer} signature + * @typedef {Object} TransactionInstructionCtorFields * @property {?Array} keys * @property {?PublicKey} programId - * @property {?number} fee * @property {?Buffer} userdata */ -type TransactionCtorFields = {| - signature?: Buffer; +type TransactionInstructionCtorFields = {| keys?: Array; programId?: PublicKey; - fee?: number; userdata?: Buffer; |}; + /** - * Mirrors the Transaction struct in src/transaction.rs + * Transaction Instruction class */ -export class Transaction { - - /** - * Current signature of the transaction. Typically created by invoking the - * `sign()` method - */ - signature: ?Buffer; - +export class TransactionInstruction { /** * Public keys to include in this transaction */ @@ -58,6 +48,48 @@ export class Transaction { */ programId: PublicKey; + /** + * Program input + */ + userdata: Buffer = Buffer.alloc(0); + + constructor(opts?: TransactionInstructionCtorFields) { + opts && Object.assign(this, opts); + } +} + + +/** + * List of Transaction object fields that may be initialized at construction + * + * @typedef {Object} TransactionCtorFields + * @property {?Buffer} signature + * @property {?Array} keys + * @property {?PublicKey} programId + * @property {?number} fee + * @property {?Buffer} userdata + */ +type TransactionCtorFields = {| + fee?: number; +|}; + +/** + * Transaction class + */ +export class Transaction { + + /** + * Current signature of the transaction. Typically created by invoking the + * `sign()` method + */ + signature: ?Buffer; + + + /** + * The instructions to atomically execute + */ + instructions: Array = []; + /** * A recent transaction id. Must be populated by the caller */ @@ -68,23 +100,33 @@ export class Transaction { */ fee: number = 0; - /** - * Program input - */ - userdata: Buffer = Buffer.alloc(0); - constructor(opts?: TransactionCtorFields) { opts && Object.assign(this, opts); } + add(instruction: TransactionInstructionCtorFields): Transaction { + if (this.instructions.length !== 0) { + throw new Error('Multiple instructions not supported yet'); + } + + this.instructions.push(new TransactionInstruction(instruction)); + return this; + } + /** * @private */ _getSignData(): Buffer { - const {lastId, keys, programId, userdata} = this; + const {lastId} = this; if (!lastId) { throw new Error('Transaction lastId required'); } + + if (this.instructions.length !== 1) { + throw new Error('No instruction provided'); + } + + const {keys, programId, userdata} = this.instructions[0]; const programIds = [programId]; const instructions = [ { @@ -184,5 +226,33 @@ export class Transaction { signData.copy(wireTransaction, signature.length); return wireTransaction; } + + /** + * Deprecated method + * @private + */ + get keys(): Array { + assert(this.instructions.length === 1); + return this.instructions[0].keys; + } + + /** + * Deprecated method + * @private + */ + get programId(): PublicKey { + assert(this.instructions.length === 1); + return this.instructions[0].programId; + } + + /** + * Deprecated method + * @private + */ + get userdata(): Buffer { + assert(this.instructions.length === 1); + return this.instructions[0].userdata; + } + } diff --git a/web3.js/test/native-loader.test.js b/web3.js/test/native-loader.test.js index fc2162daf6..465c313ff1 100644 --- a/web3.js/test/native-loader.test.js +++ b/web3.js/test/native-loader.test.js @@ -26,8 +26,7 @@ test('load noop program', async () => { const from = await newAccountWithTokens(connection); const noopProgramId = await NativeLoader.load(connection, from, 'noop'); - const noopTransaction = new Transaction({ - fee: 0, + const noopTransaction = new Transaction().add({ keys: [from.publicKey], programId: noopProgramId, });