@solana/transactions 2.0.0-experimental.e1ca396 → 2.0.0-experimental.e374ac6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +7 -3
  2. package/dist/index.browser.cjs +226 -165
  3. package/dist/index.browser.cjs.map +1 -1
  4. package/dist/index.browser.js +226 -169
  5. package/dist/index.browser.js.map +1 -1
  6. package/dist/index.native.js +226 -169
  7. package/dist/index.native.js.map +1 -1
  8. package/dist/index.node.cjs +226 -165
  9. package/dist/index.node.cjs.map +1 -1
  10. package/dist/index.node.js +228 -169
  11. package/dist/index.node.js.map +1 -1
  12. package/dist/types/accounts.d.ts.map +1 -1
  13. package/dist/types/blockhash.d.ts +4 -7
  14. package/dist/types/blockhash.d.ts.map +1 -1
  15. package/dist/types/compilable-transaction.d.ts +4 -4
  16. package/dist/types/compile-address-table-lookups.d.ts +1 -1
  17. package/dist/types/compile-header.d.ts +1 -1
  18. package/dist/types/compile-instructions.d.ts +1 -1
  19. package/dist/types/compile-lifetime-token.d.ts +1 -1
  20. package/dist/types/compile-static-accounts.d.ts +1 -1
  21. package/dist/types/compile-transaction.d.ts +3 -3
  22. package/dist/types/create-transaction.d.ts +1 -1
  23. package/dist/types/decompile-transaction.d.ts +12 -4
  24. package/dist/types/decompile-transaction.d.ts.map +1 -1
  25. package/dist/types/durable-nonce.d.ts +2 -2
  26. package/dist/types/durable-nonce.d.ts.map +1 -1
  27. package/dist/types/fee-payer.d.ts +2 -2
  28. package/dist/types/index.d.ts +13 -11
  29. package/dist/types/index.d.ts.map +1 -1
  30. package/dist/types/instructions.d.ts +4 -2
  31. package/dist/types/instructions.d.ts.map +1 -1
  32. package/dist/types/message.d.ts +6 -6
  33. package/dist/types/serializers/address-table-lookup.d.ts +1 -1
  34. package/dist/types/serializers/header.d.ts +1 -1
  35. package/dist/types/serializers/index.d.ts +2 -2
  36. package/dist/types/serializers/instruction.d.ts +1 -1
  37. package/dist/types/serializers/message.d.ts +1 -1
  38. package/dist/types/serializers/message.d.ts.map +1 -1
  39. package/dist/types/serializers/transaction-version.d.ts +1 -1
  40. package/dist/types/serializers/transaction-version.d.ts.map +1 -1
  41. package/dist/types/serializers/transaction.d.ts +7 -4
  42. package/dist/types/serializers/transaction.d.ts.map +1 -1
  43. package/dist/types/signatures.d.ts +2 -2
  44. package/dist/types/signatures.d.ts.map +1 -1
  45. package/dist/types/unsigned-transaction.d.ts +1 -1
  46. package/dist/types/wire-transaction.d.ts +1 -1
  47. package/package.json +18 -39
  48. package/dist/index.development.js +0 -1628
  49. package/dist/index.development.js.map +0 -1
  50. package/dist/index.production.min.js +0 -32
package/README.md CHANGED
@@ -30,7 +30,7 @@ const transferTransaction = pipe(
30
30
  createTransaction({ version: 0 }),
31
31
  tx => setTransactionFeePayer(myAddress, tx),
32
32
  tx => setTransactionLifetimeUsingBlockhash(latestBlockhash, tx),
33
- tx => appendTransactionInstruction(createTransferInstruction(myAddress, toAddress, amountInLamports), tx)
33
+ tx => appendTransactionInstruction(createTransferInstruction(myAddress, toAddress, amountInLamports), tx),
34
34
  );
35
35
  ```
36
36
 
@@ -144,7 +144,7 @@ const nonce =
144
144
 
145
145
  const durableNonceTransaction = setTransactionLifetimeUsingDurableNonce(
146
146
  { nonce, nonceAccountAddress, nonceAuthorityAddress },
147
- tx
147
+ tx,
148
148
  );
149
149
  ```
150
150
 
@@ -210,14 +210,18 @@ const memoTransaction = appendTransactionInstruction(
210
210
  data: new TextEncoder().encode('Hello world!'),
211
211
  programAddress: address('MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr'),
212
212
  },
213
- tx
213
+ tx,
214
214
  );
215
215
  ```
216
216
 
217
+ If you'd like to add multiple instructions to a transaction at once, you may use the `appendTransactionInstructions` function instead which accepts an array of instructions.
218
+
217
219
  #### `prependTransactionInstruction()`
218
220
 
219
221
  Given an instruction, this method will return a new transaction with that instruction having been added to the beginning of the list of existing instructions.
220
222
 
223
+ If you'd like to prepend multiple instructions to a transaction at once, you may use the `prependTransactionInstructions` function instead which accepts an array of instructions.
224
+
221
225
  See [`appendTransactionInstruction()`](#appendtransactioninstruction) for an example of how to use this function.
222
226
 
223
227
  ## Signing transactions
@@ -1,14 +1,36 @@
1
1
  'use strict';
2
2
 
3
+ var errors = require('@solana/errors');
3
4
  var codecsStrings = require('@solana/codecs-strings');
4
5
  var addresses = require('@solana/addresses');
6
+ var functional = require('@solana/functional');
5
7
  var codecsCore = require('@solana/codecs-core');
6
8
  var codecsDataStructures = require('@solana/codecs-data-structures');
7
9
  var codecsNumbers = require('@solana/codecs-numbers');
8
- var functional = require('@solana/functional');
9
10
  var keys = require('@solana/keys');
10
11
 
11
12
  // src/blockhash.ts
13
+ var base58Encoder;
14
+ function assertIsBlockhash(putativeBlockhash) {
15
+ if (!base58Encoder)
16
+ base58Encoder = codecsStrings.getBase58Encoder();
17
+ if (
18
+ // Lowest value (32 bytes of zeroes)
19
+ putativeBlockhash.length < 32 || // Highest value (32 bytes of 255)
20
+ putativeBlockhash.length > 44
21
+ ) {
22
+ throw new errors.SolanaError(errors.SOLANA_ERROR__BLOCKHASH_STRING_LENGTH_OUT_OF_RANGE, {
23
+ actualLength: putativeBlockhash.length
24
+ });
25
+ }
26
+ const bytes = base58Encoder.encode(putativeBlockhash);
27
+ const numBytes = bytes.byteLength;
28
+ if (numBytes !== 32) {
29
+ throw new errors.SolanaError(errors.SOLANA_ERROR__BLOCKHASH_BYTE_LENGTH_OUT_OF_RANGE, {
30
+ actualLength: numBytes
31
+ });
32
+ }
33
+ }
12
34
 
13
35
  // src/unsigned-transaction.ts
14
36
  function getUnsignedTransaction(transaction) {
@@ -25,29 +47,6 @@ function getUnsignedTransaction(transaction) {
25
47
  }
26
48
 
27
49
  // src/blockhash.ts
28
- var base58Encoder;
29
- function assertIsBlockhash(putativeBlockhash) {
30
- if (!base58Encoder)
31
- base58Encoder = codecsStrings.getBase58Encoder();
32
- try {
33
- if (
34
- // Lowest value (32 bytes of zeroes)
35
- putativeBlockhash.length < 32 || // Highest value (32 bytes of 255)
36
- putativeBlockhash.length > 44
37
- ) {
38
- throw new Error("Expected input string to decode to a byte array of length 32.");
39
- }
40
- const bytes = base58Encoder.encode(putativeBlockhash);
41
- const numBytes = bytes.byteLength;
42
- if (numBytes !== 32) {
43
- throw new Error(`Expected input string to decode to a byte array of length 32. Actual length: ${numBytes}`);
44
- }
45
- } catch (e) {
46
- throw new Error(`\`${putativeBlockhash}\` is not a blockhash`, {
47
- cause: e
48
- });
49
- }
50
- }
51
50
  function isTransactionWithBlockhashLifetime(transaction) {
52
51
  const lifetimeConstraintShapeMatches = "lifetimeConstraint" in transaction && typeof transaction.lifetimeConstraint.blockhash === "string" && typeof transaction.lifetimeConstraint.lastValidBlockHeight === "bigint";
53
52
  if (!lifetimeConstraintShapeMatches)
@@ -61,7 +60,7 @@ function isTransactionWithBlockhashLifetime(transaction) {
61
60
  }
62
61
  function assertIsTransactionWithBlockhashLifetime(transaction) {
63
62
  if (!isTransactionWithBlockhashLifetime(transaction)) {
64
- throw new Error("Transaction does not have a blockhash lifetime");
63
+ throw new errors.SolanaError(errors.SOLANA_ERROR__TRANSACTION_EXPECTED_BLOCKHASH_LIFETIME);
65
64
  }
66
65
  }
67
66
  function setTransactionLifetimeUsingBlockhash(blockhashLifetimeConstraint, transaction) {
@@ -87,8 +86,6 @@ function createTransaction({
87
86
  Object.freeze(out);
88
87
  return out;
89
88
  }
90
-
91
- // ../instructions/dist/index.browser.js
92
89
  var AccountRole = /* @__PURE__ */ ((AccountRole2) => {
93
90
  AccountRole2[AccountRole2["WRITABLE_SIGNER"] = /* 3 */
94
91
  3] = "WRITABLE_SIGNER";
@@ -110,13 +107,11 @@ function isWritableRole(role) {
110
107
  function mergeRoles(roleA, roleB) {
111
108
  return roleA | roleB;
112
109
  }
113
-
114
- // src/durable-nonce.ts
115
110
  var RECENT_BLOCKHASHES_SYSVAR_ADDRESS = "SysvarRecentB1ockHashes11111111111111111111";
116
111
  var SYSTEM_PROGRAM_ADDRESS = "11111111111111111111111111111111";
117
112
  function assertIsDurableNonceTransaction(transaction) {
118
113
  if (!isDurableNonceTransaction(transaction)) {
119
- throw new Error("Transaction is not a durable nonce transaction");
114
+ throw new errors.SolanaError(errors.SOLANA_ERROR__TRANSACTION_EXPECTED_NONCE_LIFETIME);
120
115
  }
121
116
  }
122
117
  function createAdvanceNonceAccountInstruction(nonceAccountAddress, nonceAuthorityAddress) {
@@ -202,21 +197,181 @@ function setTransactionFeePayer(feePayer, transaction) {
202
197
 
203
198
  // src/instructions.ts
204
199
  function appendTransactionInstruction(instruction, transaction) {
200
+ return appendTransactionInstructions([instruction], transaction);
201
+ }
202
+ function appendTransactionInstructions(instructions, transaction) {
205
203
  const out = {
206
204
  ...getUnsignedTransaction(transaction),
207
- instructions: [...transaction.instructions, instruction]
205
+ instructions: [...transaction.instructions, ...instructions]
208
206
  };
209
207
  Object.freeze(out);
210
208
  return out;
211
209
  }
212
210
  function prependTransactionInstruction(instruction, transaction) {
211
+ return prependTransactionInstructions([instruction], transaction);
212
+ }
213
+ function prependTransactionInstructions(instructions, transaction) {
213
214
  const out = {
214
215
  ...getUnsignedTransaction(transaction),
215
- instructions: [instruction, ...transaction.instructions]
216
+ instructions: [...instructions, ...transaction.instructions]
216
217
  };
217
218
  Object.freeze(out);
218
219
  return out;
219
220
  }
221
+
222
+ // src/decompile-transaction.ts
223
+ function getAccountMetas(message) {
224
+ const { header } = message;
225
+ const numWritableSignerAccounts = header.numSignerAccounts - header.numReadonlySignerAccounts;
226
+ const numWritableNonSignerAccounts = message.staticAccounts.length - header.numSignerAccounts - header.numReadonlyNonSignerAccounts;
227
+ const accountMetas = [];
228
+ let accountIndex = 0;
229
+ for (let i = 0; i < numWritableSignerAccounts; i++) {
230
+ accountMetas.push({
231
+ address: message.staticAccounts[accountIndex],
232
+ role: AccountRole.WRITABLE_SIGNER
233
+ });
234
+ accountIndex++;
235
+ }
236
+ for (let i = 0; i < header.numReadonlySignerAccounts; i++) {
237
+ accountMetas.push({
238
+ address: message.staticAccounts[accountIndex],
239
+ role: AccountRole.READONLY_SIGNER
240
+ });
241
+ accountIndex++;
242
+ }
243
+ for (let i = 0; i < numWritableNonSignerAccounts; i++) {
244
+ accountMetas.push({
245
+ address: message.staticAccounts[accountIndex],
246
+ role: AccountRole.WRITABLE
247
+ });
248
+ accountIndex++;
249
+ }
250
+ for (let i = 0; i < header.numReadonlyNonSignerAccounts; i++) {
251
+ accountMetas.push({
252
+ address: message.staticAccounts[accountIndex],
253
+ role: AccountRole.READONLY
254
+ });
255
+ accountIndex++;
256
+ }
257
+ return accountMetas;
258
+ }
259
+ function getAddressLookupMetas(compiledAddressTableLookups, addressesByLookupTableAddress) {
260
+ const compiledAddressTableLookupAddresses = compiledAddressTableLookups.map((l) => l.lookupTableAddress);
261
+ const missing = compiledAddressTableLookupAddresses.filter((a) => addressesByLookupTableAddress[a] === void 0);
262
+ if (missing.length > 0) {
263
+ throw new errors.SolanaError(errors.SOLANA_ERROR__TRANSACTION_FAILED_TO_DECOMPILE_ADDRESS_LOOKUP_TABLE_CONTENTS_MISSING, {
264
+ lookupTableAddresses: missing
265
+ });
266
+ }
267
+ const readOnlyMetas = [];
268
+ const writableMetas = [];
269
+ for (const lookup of compiledAddressTableLookups) {
270
+ const addresses = addressesByLookupTableAddress[lookup.lookupTableAddress];
271
+ const highestIndex = Math.max(...lookup.readableIndices, ...lookup.writableIndices);
272
+ if (highestIndex >= addresses.length) {
273
+ throw new errors.SolanaError(
274
+ errors.SOLANA_ERROR__TRANSACTION_FAILED_TO_DECOMPILE_ADDRESS_LOOKUP_TABLE_INDEX_OUT_OF_RANGE,
275
+ {
276
+ highestKnownIndex: addresses.length - 1,
277
+ highestRequestedIndex: highestIndex,
278
+ lookupTableAddress: lookup.lookupTableAddress
279
+ }
280
+ );
281
+ }
282
+ const readOnlyForLookup = lookup.readableIndices.map((r) => ({
283
+ address: addresses[r],
284
+ addressIndex: r,
285
+ lookupTableAddress: lookup.lookupTableAddress,
286
+ role: AccountRole.READONLY
287
+ }));
288
+ readOnlyMetas.push(...readOnlyForLookup);
289
+ const writableForLookup = lookup.writableIndices.map((w) => ({
290
+ address: addresses[w],
291
+ addressIndex: w,
292
+ lookupTableAddress: lookup.lookupTableAddress,
293
+ role: AccountRole.WRITABLE
294
+ }));
295
+ writableMetas.push(...writableForLookup);
296
+ }
297
+ return [...writableMetas, ...readOnlyMetas];
298
+ }
299
+ function convertInstruction(instruction, accountMetas) {
300
+ const programAddress = accountMetas[instruction.programAddressIndex]?.address;
301
+ if (!programAddress) {
302
+ throw new errors.SolanaError(errors.SOLANA_ERROR__TRANSACTION_FAILED_TO_DECOMPILE_INSTRUCTION_PROGRAM_ADDRESS_NOT_FOUND, {
303
+ index: instruction.programAddressIndex
304
+ });
305
+ }
306
+ const accounts = instruction.accountIndices?.map((accountIndex) => accountMetas[accountIndex]);
307
+ const { data } = instruction;
308
+ return {
309
+ programAddress,
310
+ ...accounts && accounts.length ? { accounts } : {},
311
+ ...data && data.length ? { data } : {}
312
+ };
313
+ }
314
+ function getLifetimeConstraint(messageLifetimeToken, firstInstruction, lastValidBlockHeight) {
315
+ if (!firstInstruction || !isAdvanceNonceAccountInstruction(firstInstruction)) {
316
+ return {
317
+ blockhash: messageLifetimeToken,
318
+ lastValidBlockHeight: lastValidBlockHeight ?? 2n ** 64n - 1n
319
+ // U64 MAX
320
+ };
321
+ } else {
322
+ const nonceAccountAddress = firstInstruction.accounts[0].address;
323
+ addresses.assertIsAddress(nonceAccountAddress);
324
+ const nonceAuthorityAddress = firstInstruction.accounts[2].address;
325
+ addresses.assertIsAddress(nonceAuthorityAddress);
326
+ return {
327
+ nonce: messageLifetimeToken,
328
+ nonceAccountAddress,
329
+ nonceAuthorityAddress
330
+ };
331
+ }
332
+ }
333
+ function convertSignatures(compiledTransaction) {
334
+ const {
335
+ compiledMessage: { staticAccounts },
336
+ signatures
337
+ } = compiledTransaction;
338
+ return signatures.reduce((acc, sig, index) => {
339
+ const allZeros = sig.every((byte) => byte === 0);
340
+ if (allZeros)
341
+ return acc;
342
+ const address = staticAccounts[index];
343
+ return { ...acc, [address]: sig };
344
+ }, {});
345
+ }
346
+ function decompileTransaction(compiledTransaction, config) {
347
+ const { compiledMessage } = compiledTransaction;
348
+ const feePayer = compiledMessage.staticAccounts[0];
349
+ if (!feePayer) {
350
+ throw new errors.SolanaError(errors.SOLANA_ERROR__TRANSACTION_FAILED_TO_DECOMPILE_FEE_PAYER_MISSING);
351
+ }
352
+ const accountMetas = getAccountMetas(compiledMessage);
353
+ const accountLookupMetas = "addressTableLookups" in compiledMessage && compiledMessage.addressTableLookups !== void 0 && compiledMessage.addressTableLookups.length > 0 ? getAddressLookupMetas(compiledMessage.addressTableLookups, config?.addressesByLookupTableAddress ?? {}) : [];
354
+ const transactionMetas = [...accountMetas, ...accountLookupMetas];
355
+ const instructions = compiledMessage.instructions.map(
356
+ (compiledInstruction) => convertInstruction(compiledInstruction, transactionMetas)
357
+ );
358
+ const firstInstruction = instructions[0];
359
+ const lifetimeConstraint = getLifetimeConstraint(
360
+ compiledMessage.lifetimeToken,
361
+ firstInstruction,
362
+ config?.lastValidBlockHeight
363
+ );
364
+ const signatures = convertSignatures(compiledTransaction);
365
+ return functional.pipe(
366
+ createTransaction({ version: compiledMessage.version }),
367
+ (tx) => setTransactionFeePayer(feePayer, tx),
368
+ (tx) => instructions.reduce((acc, instruction) => {
369
+ return appendTransactionInstruction(instruction, acc);
370
+ }, tx),
371
+ (tx) => "blockhash" in lifetimeConstraint ? setTransactionLifetimeUsingBlockhash(lifetimeConstraint, tx) : setTransactionLifetimeUsingDurableNonce(lifetimeConstraint, tx),
372
+ (tx) => Object.keys(signatures).length > 0 ? { ...tx, signatures } : tx
373
+ );
374
+ }
220
375
  function upsert(addressMap, address, update) {
221
376
  addressMap[address] = update(addressMap[address] ?? { role: AccountRole.READONLY });
222
377
  }
@@ -233,13 +388,13 @@ function getAddressMapFromInstructions(feePayer, instructions) {
233
388
  if (isWritableRole(entry.role)) {
234
389
  switch (entry[TYPE]) {
235
390
  case 0 /* FEE_PAYER */:
236
- throw new Error(
237
- `This transaction includes an address (\`${instruction.programAddress}\`) which is both invoked and set as the fee payer. Program addresses may not pay fees.`
238
- );
391
+ throw new errors.SolanaError(errors.SOLANA_ERROR__TRANSACTION_INVOKED_PROGRAMS_CANNOT_PAY_FEES, {
392
+ programAddress: instruction.programAddress
393
+ });
239
394
  default:
240
- throw new Error(
241
- `This transaction includes an address (\`${instruction.programAddress}\`) which is both invoked and marked writable. Program addresses may not be writable.`
242
- );
395
+ throw new errors.SolanaError(errors.SOLANA_ERROR__TRANSACTION_INVOKED_PROGRAMS_MUST_NOT_BE_WRITABLE, {
396
+ programAddress: instruction.programAddress
397
+ });
243
398
  }
244
399
  }
245
400
  if (entry[TYPE] === 2 /* STATIC */) {
@@ -304,8 +459,11 @@ function getAddressMapFromInstructions(feePayer, instructions) {
304
459
  addressesOfInvokedPrograms.has(account.address)
305
460
  ) {
306
461
  if (isWritableRole(accountMeta.role)) {
307
- throw new Error(
308
- `This transaction includes an address (\`${account.address}\`) which is both invoked and marked writable. Program addresses may not be writable.`
462
+ throw new errors.SolanaError(
463
+ errors.SOLANA_ERROR__TRANSACTION_INVOKED_PROGRAMS_MUST_NOT_BE_WRITABLE,
464
+ {
465
+ programAddress: account.address
466
+ }
309
467
  );
310
468
  }
311
469
  if (entry.role !== nextRole) {
@@ -595,7 +753,9 @@ function getTransactionVersionEncoder() {
595
753
  return offset;
596
754
  }
597
755
  if (value < 0 || value > 127) {
598
- throw new Error(`Transaction version must be in the range [0, 127]. \`${value}\` given.`);
756
+ throw new errors.SolanaError(errors.SOLANA_ERROR__TRANSACTION_VERSION_NUMBER_OUT_OF_RANGE, {
757
+ actualVersion: value
758
+ });
599
759
  }
600
760
  bytes.set([value | VERSION_FLAG_MASK], offset);
601
761
  return offset + 1;
@@ -682,12 +842,15 @@ function getCompiledMessageEncoder() {
682
842
  });
683
843
  }
684
844
  function getCompiledMessageDecoder() {
685
- return codecsCore.mapDecoder(codecsDataStructures.getStructDecoder(getPreludeStructDecoderTuple()), ({ addressTableLookups, ...restOfMessage }) => {
686
- if (restOfMessage.version === "legacy" || !addressTableLookups?.length) {
687
- return restOfMessage;
845
+ return codecsCore.mapDecoder(
846
+ codecsDataStructures.getStructDecoder(getPreludeStructDecoderTuple()),
847
+ ({ addressTableLookups, ...restOfMessage }) => {
848
+ if (restOfMessage.version === "legacy" || !addressTableLookups?.length) {
849
+ return restOfMessage;
850
+ }
851
+ return { ...restOfMessage, addressTableLookups };
688
852
  }
689
- return { ...restOfMessage, addressTableLookups };
690
- });
853
+ );
691
854
  }
692
855
  function getCompiledMessageCodec() {
693
856
  return codecsCore.combineCodec(getCompiledMessageEncoder(), getCompiledMessageDecoder());
@@ -710,116 +873,6 @@ function getCompiledTransaction(transaction) {
710
873
  signatures
711
874
  };
712
875
  }
713
- function getAccountMetas(message) {
714
- const { header } = message;
715
- const numWritableSignerAccounts = header.numSignerAccounts - header.numReadonlySignerAccounts;
716
- const numWritableNonSignerAccounts = message.staticAccounts.length - header.numSignerAccounts - header.numReadonlyNonSignerAccounts;
717
- const accountMetas = [];
718
- let accountIndex = 0;
719
- for (let i = 0; i < numWritableSignerAccounts; i++) {
720
- accountMetas.push({
721
- address: message.staticAccounts[accountIndex],
722
- role: AccountRole.WRITABLE_SIGNER
723
- });
724
- accountIndex++;
725
- }
726
- for (let i = 0; i < header.numReadonlySignerAccounts; i++) {
727
- accountMetas.push({
728
- address: message.staticAccounts[accountIndex],
729
- role: AccountRole.READONLY_SIGNER
730
- });
731
- accountIndex++;
732
- }
733
- for (let i = 0; i < numWritableNonSignerAccounts; i++) {
734
- accountMetas.push({
735
- address: message.staticAccounts[accountIndex],
736
- role: AccountRole.WRITABLE
737
- });
738
- accountIndex++;
739
- }
740
- for (let i = 0; i < header.numReadonlyNonSignerAccounts; i++) {
741
- accountMetas.push({
742
- address: message.staticAccounts[accountIndex],
743
- role: AccountRole.READONLY
744
- });
745
- accountIndex++;
746
- }
747
- return accountMetas;
748
- }
749
- function convertInstruction(instruction, accountMetas) {
750
- const programAddress = accountMetas[instruction.programAddressIndex]?.address;
751
- if (!programAddress) {
752
- throw new Error(`Could not find program address at index ${instruction.programAddressIndex}`);
753
- }
754
- const accounts = instruction.accountIndices?.map((accountIndex) => accountMetas[accountIndex]);
755
- const { data } = instruction;
756
- return {
757
- programAddress,
758
- ...accounts && accounts.length ? { accounts } : {},
759
- ...data && data.length ? { data } : {}
760
- };
761
- }
762
- function getLifetimeConstraint(messageLifetimeToken, firstInstruction, lastValidBlockHeight) {
763
- if (!firstInstruction || !isAdvanceNonceAccountInstruction(firstInstruction)) {
764
- return {
765
- blockhash: messageLifetimeToken,
766
- lastValidBlockHeight: lastValidBlockHeight ?? 2n ** 64n - 1n
767
- // U64 MAX
768
- };
769
- } else {
770
- const nonceAccountAddress = firstInstruction.accounts[0].address;
771
- addresses.assertIsAddress(nonceAccountAddress);
772
- const nonceAuthorityAddress = firstInstruction.accounts[2].address;
773
- addresses.assertIsAddress(nonceAuthorityAddress);
774
- return {
775
- nonce: messageLifetimeToken,
776
- nonceAccountAddress,
777
- nonceAuthorityAddress
778
- };
779
- }
780
- }
781
- function convertSignatures(compiledTransaction) {
782
- const {
783
- compiledMessage: { staticAccounts },
784
- signatures
785
- } = compiledTransaction;
786
- return signatures.reduce((acc, sig, index) => {
787
- const allZeros = sig.every((byte) => byte === 0);
788
- if (allZeros)
789
- return acc;
790
- const address = staticAccounts[index];
791
- return { ...acc, [address]: sig };
792
- }, {});
793
- }
794
- function decompileTransaction(compiledTransaction, lastValidBlockHeight) {
795
- const { compiledMessage } = compiledTransaction;
796
- if ("addressTableLookups" in compiledMessage && compiledMessage.addressTableLookups.length > 0) {
797
- throw new Error("Cannot convert transaction with addressTableLookups");
798
- }
799
- const feePayer = compiledMessage.staticAccounts[0];
800
- if (!feePayer)
801
- throw new Error("No fee payer set in CompiledTransaction");
802
- const accountMetas = getAccountMetas(compiledMessage);
803
- const instructions = compiledMessage.instructions.map(
804
- (compiledInstruction) => convertInstruction(compiledInstruction, accountMetas)
805
- );
806
- const firstInstruction = instructions[0];
807
- const lifetimeConstraint = getLifetimeConstraint(
808
- compiledMessage.lifetimeToken,
809
- firstInstruction,
810
- lastValidBlockHeight
811
- );
812
- const signatures = convertSignatures(compiledTransaction);
813
- return functional.pipe(
814
- createTransaction({ version: compiledMessage.version }),
815
- (tx) => setTransactionFeePayer(feePayer, tx),
816
- (tx) => instructions.reduce((acc, instruction) => {
817
- return appendTransactionInstruction(instruction, acc);
818
- }, tx),
819
- (tx) => "blockhash" in lifetimeConstraint ? setTransactionLifetimeUsingBlockhash(lifetimeConstraint, tx) : setTransactionLifetimeUsingDurableNonce(lifetimeConstraint, tx),
820
- (tx) => compiledTransaction.signatures.length ? { ...tx, signatures } : tx
821
- );
822
- }
823
876
 
824
877
  // src/serializers/transaction.ts
825
878
  function getCompiledTransactionEncoder() {
@@ -842,14 +895,14 @@ function getCompiledTransactionDecoder() {
842
895
  function getTransactionEncoder() {
843
896
  return codecsCore.mapEncoder(getCompiledTransactionEncoder(), getCompiledTransaction);
844
897
  }
845
- function getTransactionDecoder(lastValidBlockHeight) {
898
+ function getTransactionDecoder(config) {
846
899
  return codecsCore.mapDecoder(
847
900
  getCompiledTransactionDecoder(),
848
- (compiledTransaction) => decompileTransaction(compiledTransaction, lastValidBlockHeight)
901
+ (compiledTransaction) => decompileTransaction(compiledTransaction, config)
849
902
  );
850
903
  }
851
- function getTransactionCodec(lastValidBlockHeight) {
852
- return codecsCore.combineCodec(getTransactionEncoder(), getTransactionDecoder(lastValidBlockHeight));
904
+ function getTransactionCodec(config) {
905
+ return codecsCore.combineCodec(getTransactionEncoder(), getTransactionDecoder(config));
853
906
  }
854
907
  var base58Decoder;
855
908
  function getSignatureFromTransaction(transaction) {
@@ -857,9 +910,7 @@ function getSignatureFromTransaction(transaction) {
857
910
  base58Decoder = codecsStrings.getBase58Decoder();
858
911
  const signatureBytes = transaction.signatures[transaction.feePayer];
859
912
  if (!signatureBytes) {
860
- throw new Error(
861
- "Could not determine this transaction's signature. Make sure that the transaction has been signed by its fee payer."
862
- );
913
+ throw new errors.SolanaError(errors.SOLANA_ERROR__TRANSACTION_SIGNATURE_NOT_COMPUTABLE);
863
914
  }
864
915
  const transactionSignature = base58Decoder.decode(signatureBytes);
865
916
  return transactionSignature;
@@ -892,11 +943,17 @@ async function signTransaction(keyPairs, transaction) {
892
943
  function assertTransactionIsFullySigned(transaction) {
893
944
  const signerAddressesFromInstructions = transaction.instructions.flatMap((i) => i.accounts?.filter((a) => isSignerRole(a.role)) ?? []).map((a) => a.address);
894
945
  const requiredSigners = /* @__PURE__ */ new Set([transaction.feePayer, ...signerAddressesFromInstructions]);
946
+ const missingSigs = [];
895
947
  requiredSigners.forEach((address) => {
896
948
  if (!transaction.signatures[address]) {
897
- throw new Error(`Transaction is missing signature for address \`${address}\``);
949
+ missingSigs.push(address);
898
950
  }
899
951
  });
952
+ if (missingSigs.length > 0) {
953
+ throw new errors.SolanaError(errors.SOLANA_ERROR__TRANSACTION_MISSING_SIGNATURES, {
954
+ addresses: missingSigs
955
+ });
956
+ }
900
957
  }
901
958
  function getBase64EncodedWireTransaction(transaction) {
902
959
  const wireTransactionBytes = getTransactionEncoder().encode(transaction);
@@ -904,23 +961,27 @@ function getBase64EncodedWireTransaction(transaction) {
904
961
  }
905
962
 
906
963
  exports.appendTransactionInstruction = appendTransactionInstruction;
907
- exports.assertIsBlockhash = assertIsBlockhash;
964
+ exports.appendTransactionInstructions = appendTransactionInstructions;
908
965
  exports.assertIsDurableNonceTransaction = assertIsDurableNonceTransaction;
909
966
  exports.assertIsTransactionWithBlockhashLifetime = assertIsTransactionWithBlockhashLifetime;
910
967
  exports.assertTransactionIsFullySigned = assertTransactionIsFullySigned;
911
968
  exports.compileMessage = compileMessage;
912
969
  exports.createTransaction = createTransaction;
970
+ exports.decompileTransaction = decompileTransaction;
913
971
  exports.getBase64EncodedWireTransaction = getBase64EncodedWireTransaction;
914
972
  exports.getCompiledMessageCodec = getCompiledMessageCodec;
915
973
  exports.getCompiledMessageDecoder = getCompiledMessageDecoder;
916
974
  exports.getCompiledMessageEncoder = getCompiledMessageEncoder;
975
+ exports.getCompiledTransactionDecoder = getCompiledTransactionDecoder;
917
976
  exports.getSignatureFromTransaction = getSignatureFromTransaction;
918
977
  exports.getTransactionCodec = getTransactionCodec;
919
978
  exports.getTransactionDecoder = getTransactionDecoder;
920
979
  exports.getTransactionEncoder = getTransactionEncoder;
980
+ exports.getUnsignedTransaction = getUnsignedTransaction;
921
981
  exports.isAdvanceNonceAccountInstruction = isAdvanceNonceAccountInstruction;
922
982
  exports.partiallySignTransaction = partiallySignTransaction;
923
983
  exports.prependTransactionInstruction = prependTransactionInstruction;
984
+ exports.prependTransactionInstructions = prependTransactionInstructions;
924
985
  exports.setTransactionFeePayer = setTransactionFeePayer;
925
986
  exports.setTransactionLifetimeUsingBlockhash = setTransactionLifetimeUsingBlockhash;
926
987
  exports.setTransactionLifetimeUsingDurableNonce = setTransactionLifetimeUsingDurableNonce;