diff --git a/web3.js/module.flow.js b/web3.js/module.flow.js index 9128b6ce36..9a300d5dee 100644 --- a/web3.js/module.flow.js +++ b/web3.js/module.flow.js @@ -124,13 +124,13 @@ declare module '@solana/web3.js' { declare export type TransactionSignature = string; declare type TransactionInstructionCtorFields = {| - keys: ?Array<{pubkey: PublicKey, isSigner: boolean}>, + keys: ?Array<{pubkey: PublicKey, isSigner: boolean, isDebitable: boolean}>, programId?: PublicKey, data?: Buffer, |}; declare export class TransactionInstruction { - keys: Array<{pubkey: PublicKey, isSigner: boolean}>; + keys: Array<{pubkey: PublicKey, isSigner: boolean, isDebitable: boolean}>; programId: PublicKey; data: Buffer; } diff --git a/web3.js/src/budget-program.js b/web3.js/src/budget-program.js index e869f71c16..7e24b5f288 100644 --- a/web3.js/src/budget-program.js +++ b/web3.js/src/budget-program.js @@ -201,7 +201,10 @@ export class BudgetProgram { } return new Transaction().add({ - keys: [{pubkey: from, isSigner: true}, {pubkey: to, isSigner: false}], + keys: [ + {pubkey: from, isSigner: true, isDebitable: true}, + {pubkey: to, isSigner: false, isDebitable: false}, + ], programId: this.programId, data: data.slice(0, pos), }); @@ -222,9 +225,9 @@ export class BudgetProgram { return new Transaction().add({ keys: [ - {pubkey: from, isSigner: true}, - {pubkey: program, isSigner: false}, - {pubkey: to, isSigner: false}, + {pubkey: from, isSigner: true, isDebitable: true}, + {pubkey: program, isSigner: false, isDebitable: true}, + {pubkey: to, isSigner: false, isDebitable: false}, ], programId: this.programId, data: data.slice(0, pos), @@ -246,9 +249,9 @@ export class BudgetProgram { return new Transaction().add({ keys: [ - {pubkey: from, isSigner: true}, - {pubkey: program, isSigner: false}, - {pubkey: to, isSigner: false}, + {pubkey: from, isSigner: true, isDebitable: true}, + {pubkey: program, isSigner: false, isDebitable: true}, + {pubkey: to, isSigner: false, isDebitable: false}, ], programId: this.programId, data: data.slice(0, pos), @@ -294,9 +297,9 @@ export class BudgetProgram { return new Transaction().add({ keys: [ - {pubkey: from, isSigner: true}, - {pubkey: program, isSigner: false}, - {pubkey: to, isSigner: false}, + {pubkey: from, isSigner: true, isDebitable: true}, + {pubkey: program, isSigner: false, isDebitable: true}, + {pubkey: to, isSigner: false, isDebitable: false}, ], programId: this.programId, data: data.slice(0, pos), @@ -321,9 +324,9 @@ export class BudgetProgram { return new Transaction().add({ keys: [ - {pubkey: from, isSigner: true}, - {pubkey: program, isSigner: false}, - {pubkey: to, isSigner: false}, + {pubkey: from, isSigner: true, isDebitable: true}, + {pubkey: program, isSigner: false, isDebitable: true}, + {pubkey: to, isSigner: false, isDebitable: false}, ], programId: this.programId, data, @@ -351,9 +354,9 @@ export class BudgetProgram { return new Transaction().add({ keys: [ - {pubkey: from, isSigner: true}, - {pubkey: program, isSigner: false}, - {pubkey: to, isSigner: false}, + {pubkey: from, isSigner: true, isDebitable: true}, + {pubkey: program, isSigner: false, isDebitable: true}, + {pubkey: to, isSigner: false, isDebitable: false}, ], programId: this.programId, data, diff --git a/web3.js/src/loader.js b/web3.js/src/loader.js index c482d26ef3..4eb61b7bab 100644 --- a/web3.js/src/loader.js +++ b/web3.js/src/loader.js @@ -78,7 +78,7 @@ export class Loader { ); const transaction = new Transaction().add({ - keys: [{pubkey: program.publicKey, isSigner: true}], + keys: [{pubkey: program.publicKey, isSigner: true, isDebitable: true}], programId, data, }); @@ -117,7 +117,7 @@ export class Loader { ); const transaction = new Transaction().add({ - keys: [{pubkey: program.publicKey, isSigner: true}], + keys: [{pubkey: program.publicKey, isSigner: true, isDebitable: true}], programId, data, }); diff --git a/web3.js/src/system-program.js b/web3.js/src/system-program.js index 91c27107a4..5ac67ff63c 100644 --- a/web3.js/src/system-program.js +++ b/web3.js/src/system-program.js @@ -49,8 +49,8 @@ export class SystemProgram { return new Transaction().add({ keys: [ - {pubkey: from, isSigner: true}, - {pubkey: newAccount, isSigner: false}, + {pubkey: from, isSigner: true, isDebitable: true}, + {pubkey: newAccount, isSigner: false, isDebitable: true}, ], programId: SystemProgram.programId, data, @@ -76,7 +76,10 @@ export class SystemProgram { ); return new Transaction().add({ - keys: [{pubkey: from, isSigner: true}, {pubkey: to, isSigner: false}], + keys: [ + {pubkey: from, isSigner: true, isDebitable: true}, + {pubkey: to, isSigner: false, isDebitable: false}, + ], programId: SystemProgram.programId, data, }); @@ -101,7 +104,7 @@ export class SystemProgram { ); return new Transaction().add({ - keys: [{pubkey: from, isSigner: true}], + keys: [{pubkey: from, isSigner: true, isDebitable: true}], programId: SystemProgram.programId, data, }); diff --git a/web3.js/src/token-program.js b/web3.js/src/token-program.js index d0f7a7fddb..1244db7364 100644 --- a/web3.js/src/token-program.js +++ b/web3.js/src/token-program.js @@ -237,8 +237,8 @@ export class Token { transaction = new Transaction().add({ keys: [ - {pubkey: tokenAccount.publicKey, isSigner: true}, - {pubkey: initialAccountPublicKey, isSigner: false}, + {pubkey: tokenAccount.publicKey, isSigner: true, isDebitable: false}, + {pubkey: initialAccountPublicKey, isSigner: false, isDebitable: true}, ], programId, data, @@ -292,12 +292,12 @@ export class Token { // Initialize the token account const keys = [ - {pubkey: tokenAccount.publicKey, isSigner: true}, - {pubkey: owner.publicKey, isSigner: false}, - {pubkey: this.token, isSigner: false}, + {pubkey: tokenAccount.publicKey, isSigner: true, isDebitable: true}, + {pubkey: owner.publicKey, isSigner: false, isDebitable: false}, + {pubkey: this.token, isSigner: false, isDebitable: false}, ]; if (source) { - keys.push({pubkey: source, isSigner: false}); + keys.push({pubkey: source, isSigner: false, isDebitable: false}); } transaction = new Transaction().add({ keys, @@ -496,12 +496,16 @@ export class Token { ); const keys = [ - {pubkey: owner, isSigner: true}, - {pubkey: source, isSigner: false}, - {pubkey: destination, isSigner: false}, + {pubkey: owner, isSigner: true, isDebitable: false}, + {pubkey: source, isSigner: false, isDebitable: true}, + {pubkey: destination, isSigner: false, isDebitable: true}, ]; if (accountInfo.source) { - keys.push({pubkey: accountInfo.source, isSigner: false}); + keys.push({ + pubkey: accountInfo.source, + isSigner: false, + isDebitable: true, + }); } return new TransactionInstruction({ keys, @@ -540,9 +544,9 @@ export class Token { return new TransactionInstruction({ keys: [ - {pubkey: owner, isSigner: true}, - {pubkey: account, isSigner: false}, - {pubkey: delegate, isSigner: false}, + {pubkey: owner, isSigner: true, isDebitable: false}, + {pubkey: account, isSigner: false, isDebitable: true}, + {pubkey: delegate, isSigner: false, isDebitable: true}, ], programId: this.programId, data, @@ -588,9 +592,9 @@ export class Token { return new TransactionInstruction({ keys: [ - {pubkey: owner, isSigner: true}, - {pubkey: account, isSigner: false}, - {pubkey: newOwner, isSigner: false}, + {pubkey: owner, isSigner: true, isDebitable: false}, + {pubkey: account, isSigner: false, isDebitable: true}, + {pubkey: newOwner, isSigner: false, isDebitable: true}, ], programId: this.programId, data, diff --git a/web3.js/src/transaction.js b/web3.js/src/transaction.js index 081ca78ba8..444dafaf6e 100644 --- a/web3.js/src/transaction.js +++ b/web3.js/src/transaction.js @@ -30,7 +30,7 @@ export const PACKET_DATA_SIZE = 512; * @property {?Buffer} data */ type TransactionInstructionCtorFields = {| - keys?: Array<{pubkey: PublicKey, isSigner: boolean}>, + keys?: Array<{pubkey: PublicKey, isSigner: boolean, isDebitable: boolean}>, programId?: PublicKey, data?: Buffer, |}; @@ -43,7 +43,11 @@ export class TransactionInstruction { * Public keys to include in this transaction * Boolean represents whether this pubkey needs to sign the transaction */ - keys: Array<{pubkey: PublicKey, isSigner: boolean}> = []; + keys: Array<{ + pubkey: PublicKey, + isSigner: boolean, + isDebitable: boolean, + }> = []; /** * Program Id to execute @@ -166,6 +170,13 @@ export class Transaction { if (!keys.includes(keyStr)) { if (keySignerPair.isSigner) { numRequiredSignatures += 1; + if (!keySignerPair.isDebitable) { + numCreditOnlySignedAccounts += 1; + } + } else { + if (!keySignerPair.isDebitable) { + numCreditOnlyUnsignedAccounts += 1; + } } keys.push(keyStr); } @@ -422,6 +433,20 @@ export class Transaction { const PUBKEY_LENGTH = 32; const SIGNATURE_LENGTH = 64; + function isCreditDebit( + i: number, + numRequiredSignatures: number, + numCreditOnlySignedAccounts: number, + numCreditOnlyUnsignedAccounts: number, + numKeys: number, + ): boolean { + return ( + i < numRequiredSignatures - numCreditOnlySignedAccounts || + (i >= numRequiredSignatures && + i < numKeys - numCreditOnlyUnsignedAccounts) + ); + } + let transaction = new Transaction(); // Slice up wire data @@ -435,9 +460,12 @@ export class Transaction { signatures.push(signature); } - byteArray = byteArray.slice(1); // Skip numRequiredSignatures byte - byteArray = byteArray.slice(1); // Skip numCreditOnlySignedAccounts byte - byteArray = byteArray.slice(1); // Skip numCreditOnlyUnsignedAccounts byte + const numRequiredSignatures = byteArray.shift(); + // byteArray = byteArray.slice(1); // Skip numRequiredSignatures byte + const numCreditOnlySignedAccounts = byteArray.shift(); + // byteArray = byteArray.slice(1); // Skip numCreditOnlySignedAccounts byte + const numCreditOnlyUnsignedAccounts = byteArray.shift(); + // byteArray = byteArray.slice(1); // Skip numCreditOnlyUnsignedAccounts byte const accountCount = shortvec.decodeLength(byteArray); let accounts = []; @@ -481,11 +509,19 @@ export class Transaction { }; for (let j = 0; j < instructions[i].accountIndex.length; j++) { const pubkey = new PublicKey(accounts[instructions[i].accountIndex[j]]); + instructionData.keys.push({ pubkey, isSigner: transaction.signatures.some( keyObj => keyObj.publicKey.toString() === pubkey.toString(), ), + isDebitable: isCreditDebit( + j, + numRequiredSignatures, + numCreditOnlySignedAccounts, + numCreditOnlyUnsignedAccounts, + accounts.length, + ), }); } let instruction = new TransactionInstruction(instructionData); diff --git a/web3.js/test/bpf-loader.test.js b/web3.js/test/bpf-loader.test.js index a6d120bffb..257bfdbe14 100644 --- a/web3.js/test/bpf-loader.test.js +++ b/web3.js/test/bpf-loader.test.js @@ -28,7 +28,7 @@ test('load BPF C program', async () => { const data = await fs.readFile('test/fixtures/noop-c/noop.so'); const programId = await BpfLoader.load(connection, from, data); const transaction = new Transaction().add({ - keys: [{pubkey: from.publicKey, isSigner: true}], + keys: [{pubkey: from.publicKey, isSigner: true, isDebitable: true}], programId, }); await sendAndConfirmTransaction(connection, transaction, from); @@ -47,7 +47,7 @@ test('load BPF Rust program', async () => { ); const programId = await BpfLoader.load(connection, from, data); const transaction = new Transaction().add({ - keys: [{pubkey: from.publicKey, isSigner: true}], + keys: [{pubkey: from.publicKey, isSigner: true, isDebitable: true}], programId, }); await sendAndConfirmTransaction(connection, transaction, from); diff --git a/web3.js/test/native-loader.test.js b/web3.js/test/native-loader.test.js index 9f092bca12..b8fb5362e6 100644 --- a/web3.js/test/native-loader.test.js +++ b/web3.js/test/native-loader.test.js @@ -29,7 +29,7 @@ test('load native program', async () => { 'solana_noop_program', ); const transaction = new Transaction().add({ - keys: [{pubkey: from.publicKey, isSigner: true}], + keys: [{pubkey: from.publicKey, isSigner: true, isDebitable: true}], programId, }); diff --git a/web3.js/test/transaction.test.js b/web3.js/test/transaction.test.js index e9ac56bd2f..f63210960d 100644 --- a/web3.js/test/transaction.test.js +++ b/web3.js/test/transaction.test.js @@ -93,73 +93,73 @@ test('parse wire format and serialize', () => { const wireTransaction = Buffer.from([ 1, - 91, - 132, - 173, - 1, - 218, - 94, - 253, - 18, - 27, - 79, - 207, - 114, - 27, - 167, - 127, + 47, + 50, + 66, 17, - 202, - 183, - 204, - 12, - 69, - 243, - 25, - 206, - 165, - 116, - 182, - 64, - 185, - 168, - 238, - 168, - 193, - 140, - 86, - 83, - 107, - 252, - 239, - 80, + 219, + 90, + 187, + 49, 40, - 91, - 44, - 6, - 160, - 84, - 43, - 227, - 63, - 170, - 255, - 185, - 132, - 242, - 82, - 46, - 124, - 217, - 127, - 147, - 24, - 254, - 157, + 77, + 8, + 58, + 129, + 51, + 76, 13, + 206, + 126, + 157, + 189, + 188, + 53, + 174, + 42, + 80, + 4, + 4, + 212, + 55, + 67, + 171, + 34, + 224, + 81, + 68, + 230, + 120, + 117, + 204, + 241, + 167, + 152, + 74, + 141, + 132, + 73, + 166, + 217, + 173, + 27, + 75, + 62, + 171, + 160, + 100, + 159, + 116, + 164, + 45, + 185, + 64, + 0, + 72, + 4, 1, 0, - 1, + 2, 3, 19, 152,