@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
@@ -1,12 +1,34 @@
1
- import { getBase58Encoder, getBase58Decoder, getBase64Decoder, getStringEncoder, getStringDecoder } from '@solana/codecs-strings';
2
- import { getAddressFromPublicKey, getAddressComparator, getAddressEncoder, getAddressDecoder, assertIsAddress } from '@solana/addresses';
3
- import { createEncoder, mapDecoder, combineCodec, mapEncoder, createDecoder } from '@solana/codecs-core';
4
- import { getStructDecoder, getStructEncoder, getArrayEncoder, getArrayDecoder, getBytesEncoder, getBytesDecoder } from '@solana/codecs-data-structures';
5
- import { getShortU16Encoder, getShortU16Decoder, getU8Encoder, getU8Decoder } from '@solana/codecs-numbers';
1
+ import { SolanaError, SOLANA_ERROR__TRANSACTION_EXPECTED_BLOCKHASH_LIFETIME, SOLANA_ERROR__TRANSACTION_EXPECTED_NONCE_LIFETIME, SOLANA_ERROR__TRANSACTION_FAILED_TO_DECOMPILE_FEE_PAYER_MISSING, SOLANA_ERROR__TRANSACTION_SIGNATURE_NOT_COMPUTABLE, SOLANA_ERROR__TRANSACTION_MISSING_SIGNATURES, SOLANA_ERROR__TRANSACTION_FAILED_TO_DECOMPILE_ADDRESS_LOOKUP_TABLE_CONTENTS_MISSING, SOLANA_ERROR__TRANSACTION_FAILED_TO_DECOMPILE_ADDRESS_LOOKUP_TABLE_INDEX_OUT_OF_RANGE, SOLANA_ERROR__TRANSACTION_FAILED_TO_DECOMPILE_INSTRUCTION_PROGRAM_ADDRESS_NOT_FOUND, SOLANA_ERROR__BLOCKHASH_STRING_LENGTH_OUT_OF_RANGE, SOLANA_ERROR__BLOCKHASH_BYTE_LENGTH_OUT_OF_RANGE, SOLANA_ERROR__TRANSACTION_INVOKED_PROGRAMS_MUST_NOT_BE_WRITABLE, SOLANA_ERROR__TRANSACTION_INVOKED_PROGRAMS_CANNOT_PAY_FEES, SOLANA_ERROR__TRANSACTION_VERSION_NUMBER_OUT_OF_RANGE } from '@solana/errors';
2
+ import { getBase58Decoder, getBase64Decoder, getStringEncoder, getBase58Encoder, getStringDecoder } from '@solana/codecs-strings';
3
+ import { getAddressFromPublicKey, assertIsAddress, getAddressComparator, getAddressEncoder, getAddressDecoder } from '@solana/addresses';
6
4
  import { pipe } from '@solana/functional';
5
+ import { createEncoder, mapDecoder, combineCodec, mapEncoder, createDecoder } from '@solana/codecs-core';
6
+ import { getStructDecoder, getArrayDecoder, getBytesDecoder, getStructEncoder, getArrayEncoder, getBytesEncoder } from '@solana/codecs-data-structures';
7
+ import { getShortU16Decoder, getShortU16Encoder, getU8Encoder, getU8Decoder } from '@solana/codecs-numbers';
7
8
  import { signBytes } from '@solana/keys';
8
9
 
9
10
  // src/blockhash.ts
11
+ var base58Encoder;
12
+ function assertIsBlockhash(putativeBlockhash) {
13
+ if (!base58Encoder)
14
+ base58Encoder = getBase58Encoder();
15
+ if (
16
+ // Lowest value (32 bytes of zeroes)
17
+ putativeBlockhash.length < 32 || // Highest value (32 bytes of 255)
18
+ putativeBlockhash.length > 44
19
+ ) {
20
+ throw new SolanaError(SOLANA_ERROR__BLOCKHASH_STRING_LENGTH_OUT_OF_RANGE, {
21
+ actualLength: putativeBlockhash.length
22
+ });
23
+ }
24
+ const bytes = base58Encoder.encode(putativeBlockhash);
25
+ const numBytes = bytes.byteLength;
26
+ if (numBytes !== 32) {
27
+ throw new SolanaError(SOLANA_ERROR__BLOCKHASH_BYTE_LENGTH_OUT_OF_RANGE, {
28
+ actualLength: numBytes
29
+ });
30
+ }
31
+ }
10
32
 
11
33
  // src/unsigned-transaction.ts
12
34
  function getUnsignedTransaction(transaction) {
@@ -23,29 +45,6 @@ function getUnsignedTransaction(transaction) {
23
45
  }
24
46
 
25
47
  // src/blockhash.ts
26
- var base58Encoder;
27
- function assertIsBlockhash(putativeBlockhash) {
28
- if (!base58Encoder)
29
- base58Encoder = getBase58Encoder();
30
- try {
31
- if (
32
- // Lowest value (32 bytes of zeroes)
33
- putativeBlockhash.length < 32 || // Highest value (32 bytes of 255)
34
- putativeBlockhash.length > 44
35
- ) {
36
- throw new Error("Expected input string to decode to a byte array of length 32.");
37
- }
38
- const bytes = base58Encoder.encode(putativeBlockhash);
39
- const numBytes = bytes.byteLength;
40
- if (numBytes !== 32) {
41
- throw new Error(`Expected input string to decode to a byte array of length 32. Actual length: ${numBytes}`);
42
- }
43
- } catch (e) {
44
- throw new Error(`\`${putativeBlockhash}\` is not a blockhash`, {
45
- cause: e
46
- });
47
- }
48
- }
49
48
  function isTransactionWithBlockhashLifetime(transaction) {
50
49
  const lifetimeConstraintShapeMatches = "lifetimeConstraint" in transaction && typeof transaction.lifetimeConstraint.blockhash === "string" && typeof transaction.lifetimeConstraint.lastValidBlockHeight === "bigint";
51
50
  if (!lifetimeConstraintShapeMatches)
@@ -59,7 +58,7 @@ function isTransactionWithBlockhashLifetime(transaction) {
59
58
  }
60
59
  function assertIsTransactionWithBlockhashLifetime(transaction) {
61
60
  if (!isTransactionWithBlockhashLifetime(transaction)) {
62
- throw new Error("Transaction does not have a blockhash lifetime");
61
+ throw new SolanaError(SOLANA_ERROR__TRANSACTION_EXPECTED_BLOCKHASH_LIFETIME);
63
62
  }
64
63
  }
65
64
  function setTransactionLifetimeUsingBlockhash(blockhashLifetimeConstraint, transaction) {
@@ -85,8 +84,6 @@ function createTransaction({
85
84
  Object.freeze(out);
86
85
  return out;
87
86
  }
88
-
89
- // ../instructions/dist/index.browser.js
90
87
  var AccountRole = /* @__PURE__ */ ((AccountRole2) => {
91
88
  AccountRole2[AccountRole2["WRITABLE_SIGNER"] = /* 3 */
92
89
  3] = "WRITABLE_SIGNER";
@@ -108,13 +105,11 @@ function isWritableRole(role) {
108
105
  function mergeRoles(roleA, roleB) {
109
106
  return roleA | roleB;
110
107
  }
111
-
112
- // src/durable-nonce.ts
113
108
  var RECENT_BLOCKHASHES_SYSVAR_ADDRESS = "SysvarRecentB1ockHashes11111111111111111111";
114
109
  var SYSTEM_PROGRAM_ADDRESS = "11111111111111111111111111111111";
115
110
  function assertIsDurableNonceTransaction(transaction) {
116
111
  if (!isDurableNonceTransaction(transaction)) {
117
- throw new Error("Transaction is not a durable nonce transaction");
112
+ throw new SolanaError(SOLANA_ERROR__TRANSACTION_EXPECTED_NONCE_LIFETIME);
118
113
  }
119
114
  }
120
115
  function createAdvanceNonceAccountInstruction(nonceAccountAddress, nonceAuthorityAddress) {
@@ -200,21 +195,181 @@ function setTransactionFeePayer(feePayer, transaction) {
200
195
 
201
196
  // src/instructions.ts
202
197
  function appendTransactionInstruction(instruction, transaction) {
198
+ return appendTransactionInstructions([instruction], transaction);
199
+ }
200
+ function appendTransactionInstructions(instructions, transaction) {
203
201
  const out = {
204
202
  ...getUnsignedTransaction(transaction),
205
- instructions: [...transaction.instructions, instruction]
203
+ instructions: [...transaction.instructions, ...instructions]
206
204
  };
207
205
  Object.freeze(out);
208
206
  return out;
209
207
  }
210
208
  function prependTransactionInstruction(instruction, transaction) {
209
+ return prependTransactionInstructions([instruction], transaction);
210
+ }
211
+ function prependTransactionInstructions(instructions, transaction) {
211
212
  const out = {
212
213
  ...getUnsignedTransaction(transaction),
213
- instructions: [instruction, ...transaction.instructions]
214
+ instructions: [...instructions, ...transaction.instructions]
214
215
  };
215
216
  Object.freeze(out);
216
217
  return out;
217
218
  }
219
+
220
+ // src/decompile-transaction.ts
221
+ function getAccountMetas(message) {
222
+ const { header } = message;
223
+ const numWritableSignerAccounts = header.numSignerAccounts - header.numReadonlySignerAccounts;
224
+ const numWritableNonSignerAccounts = message.staticAccounts.length - header.numSignerAccounts - header.numReadonlyNonSignerAccounts;
225
+ const accountMetas = [];
226
+ let accountIndex = 0;
227
+ for (let i = 0; i < numWritableSignerAccounts; i++) {
228
+ accountMetas.push({
229
+ address: message.staticAccounts[accountIndex],
230
+ role: AccountRole.WRITABLE_SIGNER
231
+ });
232
+ accountIndex++;
233
+ }
234
+ for (let i = 0; i < header.numReadonlySignerAccounts; i++) {
235
+ accountMetas.push({
236
+ address: message.staticAccounts[accountIndex],
237
+ role: AccountRole.READONLY_SIGNER
238
+ });
239
+ accountIndex++;
240
+ }
241
+ for (let i = 0; i < numWritableNonSignerAccounts; i++) {
242
+ accountMetas.push({
243
+ address: message.staticAccounts[accountIndex],
244
+ role: AccountRole.WRITABLE
245
+ });
246
+ accountIndex++;
247
+ }
248
+ for (let i = 0; i < header.numReadonlyNonSignerAccounts; i++) {
249
+ accountMetas.push({
250
+ address: message.staticAccounts[accountIndex],
251
+ role: AccountRole.READONLY
252
+ });
253
+ accountIndex++;
254
+ }
255
+ return accountMetas;
256
+ }
257
+ function getAddressLookupMetas(compiledAddressTableLookups, addressesByLookupTableAddress) {
258
+ const compiledAddressTableLookupAddresses = compiledAddressTableLookups.map((l) => l.lookupTableAddress);
259
+ const missing = compiledAddressTableLookupAddresses.filter((a) => addressesByLookupTableAddress[a] === void 0);
260
+ if (missing.length > 0) {
261
+ throw new SolanaError(SOLANA_ERROR__TRANSACTION_FAILED_TO_DECOMPILE_ADDRESS_LOOKUP_TABLE_CONTENTS_MISSING, {
262
+ lookupTableAddresses: missing
263
+ });
264
+ }
265
+ const readOnlyMetas = [];
266
+ const writableMetas = [];
267
+ for (const lookup of compiledAddressTableLookups) {
268
+ const addresses = addressesByLookupTableAddress[lookup.lookupTableAddress];
269
+ const highestIndex = Math.max(...lookup.readableIndices, ...lookup.writableIndices);
270
+ if (highestIndex >= addresses.length) {
271
+ throw new SolanaError(
272
+ SOLANA_ERROR__TRANSACTION_FAILED_TO_DECOMPILE_ADDRESS_LOOKUP_TABLE_INDEX_OUT_OF_RANGE,
273
+ {
274
+ highestKnownIndex: addresses.length - 1,
275
+ highestRequestedIndex: highestIndex,
276
+ lookupTableAddress: lookup.lookupTableAddress
277
+ }
278
+ );
279
+ }
280
+ const readOnlyForLookup = lookup.readableIndices.map((r) => ({
281
+ address: addresses[r],
282
+ addressIndex: r,
283
+ lookupTableAddress: lookup.lookupTableAddress,
284
+ role: AccountRole.READONLY
285
+ }));
286
+ readOnlyMetas.push(...readOnlyForLookup);
287
+ const writableForLookup = lookup.writableIndices.map((w) => ({
288
+ address: addresses[w],
289
+ addressIndex: w,
290
+ lookupTableAddress: lookup.lookupTableAddress,
291
+ role: AccountRole.WRITABLE
292
+ }));
293
+ writableMetas.push(...writableForLookup);
294
+ }
295
+ return [...writableMetas, ...readOnlyMetas];
296
+ }
297
+ function convertInstruction(instruction, accountMetas) {
298
+ const programAddress = accountMetas[instruction.programAddressIndex]?.address;
299
+ if (!programAddress) {
300
+ throw new SolanaError(SOLANA_ERROR__TRANSACTION_FAILED_TO_DECOMPILE_INSTRUCTION_PROGRAM_ADDRESS_NOT_FOUND, {
301
+ index: instruction.programAddressIndex
302
+ });
303
+ }
304
+ const accounts = instruction.accountIndices?.map((accountIndex) => accountMetas[accountIndex]);
305
+ const { data } = instruction;
306
+ return {
307
+ programAddress,
308
+ ...accounts && accounts.length ? { accounts } : {},
309
+ ...data && data.length ? { data } : {}
310
+ };
311
+ }
312
+ function getLifetimeConstraint(messageLifetimeToken, firstInstruction, lastValidBlockHeight) {
313
+ if (!firstInstruction || !isAdvanceNonceAccountInstruction(firstInstruction)) {
314
+ return {
315
+ blockhash: messageLifetimeToken,
316
+ lastValidBlockHeight: lastValidBlockHeight ?? 2n ** 64n - 1n
317
+ // U64 MAX
318
+ };
319
+ } else {
320
+ const nonceAccountAddress = firstInstruction.accounts[0].address;
321
+ assertIsAddress(nonceAccountAddress);
322
+ const nonceAuthorityAddress = firstInstruction.accounts[2].address;
323
+ assertIsAddress(nonceAuthorityAddress);
324
+ return {
325
+ nonce: messageLifetimeToken,
326
+ nonceAccountAddress,
327
+ nonceAuthorityAddress
328
+ };
329
+ }
330
+ }
331
+ function convertSignatures(compiledTransaction) {
332
+ const {
333
+ compiledMessage: { staticAccounts },
334
+ signatures
335
+ } = compiledTransaction;
336
+ return signatures.reduce((acc, sig, index) => {
337
+ const allZeros = sig.every((byte) => byte === 0);
338
+ if (allZeros)
339
+ return acc;
340
+ const address = staticAccounts[index];
341
+ return { ...acc, [address]: sig };
342
+ }, {});
343
+ }
344
+ function decompileTransaction(compiledTransaction, config) {
345
+ const { compiledMessage } = compiledTransaction;
346
+ const feePayer = compiledMessage.staticAccounts[0];
347
+ if (!feePayer) {
348
+ throw new SolanaError(SOLANA_ERROR__TRANSACTION_FAILED_TO_DECOMPILE_FEE_PAYER_MISSING);
349
+ }
350
+ const accountMetas = getAccountMetas(compiledMessage);
351
+ const accountLookupMetas = "addressTableLookups" in compiledMessage && compiledMessage.addressTableLookups !== void 0 && compiledMessage.addressTableLookups.length > 0 ? getAddressLookupMetas(compiledMessage.addressTableLookups, config?.addressesByLookupTableAddress ?? {}) : [];
352
+ const transactionMetas = [...accountMetas, ...accountLookupMetas];
353
+ const instructions = compiledMessage.instructions.map(
354
+ (compiledInstruction) => convertInstruction(compiledInstruction, transactionMetas)
355
+ );
356
+ const firstInstruction = instructions[0];
357
+ const lifetimeConstraint = getLifetimeConstraint(
358
+ compiledMessage.lifetimeToken,
359
+ firstInstruction,
360
+ config?.lastValidBlockHeight
361
+ );
362
+ const signatures = convertSignatures(compiledTransaction);
363
+ return pipe(
364
+ createTransaction({ version: compiledMessage.version }),
365
+ (tx) => setTransactionFeePayer(feePayer, tx),
366
+ (tx) => instructions.reduce((acc, instruction) => {
367
+ return appendTransactionInstruction(instruction, acc);
368
+ }, tx),
369
+ (tx) => "blockhash" in lifetimeConstraint ? setTransactionLifetimeUsingBlockhash(lifetimeConstraint, tx) : setTransactionLifetimeUsingDurableNonce(lifetimeConstraint, tx),
370
+ (tx) => Object.keys(signatures).length > 0 ? { ...tx, signatures } : tx
371
+ );
372
+ }
218
373
  function upsert(addressMap, address, update) {
219
374
  addressMap[address] = update(addressMap[address] ?? { role: AccountRole.READONLY });
220
375
  }
@@ -231,13 +386,13 @@ function getAddressMapFromInstructions(feePayer, instructions) {
231
386
  if (isWritableRole(entry.role)) {
232
387
  switch (entry[TYPE]) {
233
388
  case 0 /* FEE_PAYER */:
234
- throw new Error(
235
- `This transaction includes an address (\`${instruction.programAddress}\`) which is both invoked and set as the fee payer. Program addresses may not pay fees.`
236
- );
389
+ throw new SolanaError(SOLANA_ERROR__TRANSACTION_INVOKED_PROGRAMS_CANNOT_PAY_FEES, {
390
+ programAddress: instruction.programAddress
391
+ });
237
392
  default:
238
- throw new Error(
239
- `This transaction includes an address (\`${instruction.programAddress}\`) which is both invoked and marked writable. Program addresses may not be writable.`
240
- );
393
+ throw new SolanaError(SOLANA_ERROR__TRANSACTION_INVOKED_PROGRAMS_MUST_NOT_BE_WRITABLE, {
394
+ programAddress: instruction.programAddress
395
+ });
241
396
  }
242
397
  }
243
398
  if (entry[TYPE] === 2 /* STATIC */) {
@@ -302,8 +457,11 @@ function getAddressMapFromInstructions(feePayer, instructions) {
302
457
  addressesOfInvokedPrograms.has(account.address)
303
458
  ) {
304
459
  if (isWritableRole(accountMeta.role)) {
305
- throw new Error(
306
- `This transaction includes an address (\`${account.address}\`) which is both invoked and marked writable. Program addresses may not be writable.`
460
+ throw new SolanaError(
461
+ SOLANA_ERROR__TRANSACTION_INVOKED_PROGRAMS_MUST_NOT_BE_WRITABLE,
462
+ {
463
+ programAddress: account.address
464
+ }
307
465
  );
308
466
  }
309
467
  if (entry.role !== nextRole) {
@@ -593,7 +751,9 @@ function getTransactionVersionEncoder() {
593
751
  return offset;
594
752
  }
595
753
  if (value < 0 || value > 127) {
596
- throw new Error(`Transaction version must be in the range [0, 127]. \`${value}\` given.`);
754
+ throw new SolanaError(SOLANA_ERROR__TRANSACTION_VERSION_NUMBER_OUT_OF_RANGE, {
755
+ actualVersion: value
756
+ });
597
757
  }
598
758
  bytes.set([value | VERSION_FLAG_MASK], offset);
599
759
  return offset + 1;
@@ -680,12 +840,15 @@ function getCompiledMessageEncoder() {
680
840
  });
681
841
  }
682
842
  function getCompiledMessageDecoder() {
683
- return mapDecoder(getStructDecoder(getPreludeStructDecoderTuple()), ({ addressTableLookups, ...restOfMessage }) => {
684
- if (restOfMessage.version === "legacy" || !addressTableLookups?.length) {
685
- return restOfMessage;
843
+ return mapDecoder(
844
+ getStructDecoder(getPreludeStructDecoderTuple()),
845
+ ({ addressTableLookups, ...restOfMessage }) => {
846
+ if (restOfMessage.version === "legacy" || !addressTableLookups?.length) {
847
+ return restOfMessage;
848
+ }
849
+ return { ...restOfMessage, addressTableLookups };
686
850
  }
687
- return { ...restOfMessage, addressTableLookups };
688
- });
851
+ );
689
852
  }
690
853
  function getCompiledMessageCodec() {
691
854
  return combineCodec(getCompiledMessageEncoder(), getCompiledMessageDecoder());
@@ -708,116 +871,6 @@ function getCompiledTransaction(transaction) {
708
871
  signatures
709
872
  };
710
873
  }
711
- function getAccountMetas(message) {
712
- const { header } = message;
713
- const numWritableSignerAccounts = header.numSignerAccounts - header.numReadonlySignerAccounts;
714
- const numWritableNonSignerAccounts = message.staticAccounts.length - header.numSignerAccounts - header.numReadonlyNonSignerAccounts;
715
- const accountMetas = [];
716
- let accountIndex = 0;
717
- for (let i = 0; i < numWritableSignerAccounts; i++) {
718
- accountMetas.push({
719
- address: message.staticAccounts[accountIndex],
720
- role: AccountRole.WRITABLE_SIGNER
721
- });
722
- accountIndex++;
723
- }
724
- for (let i = 0; i < header.numReadonlySignerAccounts; i++) {
725
- accountMetas.push({
726
- address: message.staticAccounts[accountIndex],
727
- role: AccountRole.READONLY_SIGNER
728
- });
729
- accountIndex++;
730
- }
731
- for (let i = 0; i < numWritableNonSignerAccounts; i++) {
732
- accountMetas.push({
733
- address: message.staticAccounts[accountIndex],
734
- role: AccountRole.WRITABLE
735
- });
736
- accountIndex++;
737
- }
738
- for (let i = 0; i < header.numReadonlyNonSignerAccounts; i++) {
739
- accountMetas.push({
740
- address: message.staticAccounts[accountIndex],
741
- role: AccountRole.READONLY
742
- });
743
- accountIndex++;
744
- }
745
- return accountMetas;
746
- }
747
- function convertInstruction(instruction, accountMetas) {
748
- const programAddress = accountMetas[instruction.programAddressIndex]?.address;
749
- if (!programAddress) {
750
- throw new Error(`Could not find program address at index ${instruction.programAddressIndex}`);
751
- }
752
- const accounts = instruction.accountIndices?.map((accountIndex) => accountMetas[accountIndex]);
753
- const { data } = instruction;
754
- return {
755
- programAddress,
756
- ...accounts && accounts.length ? { accounts } : {},
757
- ...data && data.length ? { data } : {}
758
- };
759
- }
760
- function getLifetimeConstraint(messageLifetimeToken, firstInstruction, lastValidBlockHeight) {
761
- if (!firstInstruction || !isAdvanceNonceAccountInstruction(firstInstruction)) {
762
- return {
763
- blockhash: messageLifetimeToken,
764
- lastValidBlockHeight: lastValidBlockHeight ?? 2n ** 64n - 1n
765
- // U64 MAX
766
- };
767
- } else {
768
- const nonceAccountAddress = firstInstruction.accounts[0].address;
769
- assertIsAddress(nonceAccountAddress);
770
- const nonceAuthorityAddress = firstInstruction.accounts[2].address;
771
- assertIsAddress(nonceAuthorityAddress);
772
- return {
773
- nonce: messageLifetimeToken,
774
- nonceAccountAddress,
775
- nonceAuthorityAddress
776
- };
777
- }
778
- }
779
- function convertSignatures(compiledTransaction) {
780
- const {
781
- compiledMessage: { staticAccounts },
782
- signatures
783
- } = compiledTransaction;
784
- return signatures.reduce((acc, sig, index) => {
785
- const allZeros = sig.every((byte) => byte === 0);
786
- if (allZeros)
787
- return acc;
788
- const address = staticAccounts[index];
789
- return { ...acc, [address]: sig };
790
- }, {});
791
- }
792
- function decompileTransaction(compiledTransaction, lastValidBlockHeight) {
793
- const { compiledMessage } = compiledTransaction;
794
- if ("addressTableLookups" in compiledMessage && compiledMessage.addressTableLookups.length > 0) {
795
- throw new Error("Cannot convert transaction with addressTableLookups");
796
- }
797
- const feePayer = compiledMessage.staticAccounts[0];
798
- if (!feePayer)
799
- throw new Error("No fee payer set in CompiledTransaction");
800
- const accountMetas = getAccountMetas(compiledMessage);
801
- const instructions = compiledMessage.instructions.map(
802
- (compiledInstruction) => convertInstruction(compiledInstruction, accountMetas)
803
- );
804
- const firstInstruction = instructions[0];
805
- const lifetimeConstraint = getLifetimeConstraint(
806
- compiledMessage.lifetimeToken,
807
- firstInstruction,
808
- lastValidBlockHeight
809
- );
810
- const signatures = convertSignatures(compiledTransaction);
811
- return pipe(
812
- createTransaction({ version: compiledMessage.version }),
813
- (tx) => setTransactionFeePayer(feePayer, tx),
814
- (tx) => instructions.reduce((acc, instruction) => {
815
- return appendTransactionInstruction(instruction, acc);
816
- }, tx),
817
- (tx) => "blockhash" in lifetimeConstraint ? setTransactionLifetimeUsingBlockhash(lifetimeConstraint, tx) : setTransactionLifetimeUsingDurableNonce(lifetimeConstraint, tx),
818
- (tx) => compiledTransaction.signatures.length ? { ...tx, signatures } : tx
819
- );
820
- }
821
874
 
822
875
  // src/serializers/transaction.ts
823
876
  function getCompiledTransactionEncoder() {
@@ -840,14 +893,14 @@ function getCompiledTransactionDecoder() {
840
893
  function getTransactionEncoder() {
841
894
  return mapEncoder(getCompiledTransactionEncoder(), getCompiledTransaction);
842
895
  }
843
- function getTransactionDecoder(lastValidBlockHeight) {
896
+ function getTransactionDecoder(config) {
844
897
  return mapDecoder(
845
898
  getCompiledTransactionDecoder(),
846
- (compiledTransaction) => decompileTransaction(compiledTransaction, lastValidBlockHeight)
899
+ (compiledTransaction) => decompileTransaction(compiledTransaction, config)
847
900
  );
848
901
  }
849
- function getTransactionCodec(lastValidBlockHeight) {
850
- return combineCodec(getTransactionEncoder(), getTransactionDecoder(lastValidBlockHeight));
902
+ function getTransactionCodec(config) {
903
+ return combineCodec(getTransactionEncoder(), getTransactionDecoder(config));
851
904
  }
852
905
  var base58Decoder;
853
906
  function getSignatureFromTransaction(transaction) {
@@ -855,9 +908,7 @@ function getSignatureFromTransaction(transaction) {
855
908
  base58Decoder = getBase58Decoder();
856
909
  const signatureBytes = transaction.signatures[transaction.feePayer];
857
910
  if (!signatureBytes) {
858
- throw new Error(
859
- "Could not determine this transaction's signature. Make sure that the transaction has been signed by its fee payer."
860
- );
911
+ throw new SolanaError(SOLANA_ERROR__TRANSACTION_SIGNATURE_NOT_COMPUTABLE);
861
912
  }
862
913
  const transactionSignature = base58Decoder.decode(signatureBytes);
863
914
  return transactionSignature;
@@ -890,17 +941,23 @@ async function signTransaction(keyPairs, transaction) {
890
941
  function assertTransactionIsFullySigned(transaction) {
891
942
  const signerAddressesFromInstructions = transaction.instructions.flatMap((i) => i.accounts?.filter((a) => isSignerRole(a.role)) ?? []).map((a) => a.address);
892
943
  const requiredSigners = /* @__PURE__ */ new Set([transaction.feePayer, ...signerAddressesFromInstructions]);
944
+ const missingSigs = [];
893
945
  requiredSigners.forEach((address) => {
894
946
  if (!transaction.signatures[address]) {
895
- throw new Error(`Transaction is missing signature for address \`${address}\``);
947
+ missingSigs.push(address);
896
948
  }
897
949
  });
950
+ if (missingSigs.length > 0) {
951
+ throw new SolanaError(SOLANA_ERROR__TRANSACTION_MISSING_SIGNATURES, {
952
+ addresses: missingSigs
953
+ });
954
+ }
898
955
  }
899
956
  function getBase64EncodedWireTransaction(transaction) {
900
957
  const wireTransactionBytes = getTransactionEncoder().encode(transaction);
901
958
  return getBase64Decoder().decode(wireTransactionBytes);
902
959
  }
903
960
 
904
- export { appendTransactionInstruction, assertIsBlockhash, assertIsDurableNonceTransaction, assertIsTransactionWithBlockhashLifetime, assertTransactionIsFullySigned, compileMessage, createTransaction, getBase64EncodedWireTransaction, getCompiledMessageCodec, getCompiledMessageDecoder, getCompiledMessageEncoder, getSignatureFromTransaction, getTransactionCodec, getTransactionDecoder, getTransactionEncoder, isAdvanceNonceAccountInstruction, partiallySignTransaction, prependTransactionInstruction, setTransactionFeePayer, setTransactionLifetimeUsingBlockhash, setTransactionLifetimeUsingDurableNonce, signTransaction };
961
+ export { appendTransactionInstruction, appendTransactionInstructions, assertIsDurableNonceTransaction, assertIsTransactionWithBlockhashLifetime, assertTransactionIsFullySigned, compileMessage, createTransaction, decompileTransaction, getBase64EncodedWireTransaction, getCompiledMessageCodec, getCompiledMessageDecoder, getCompiledMessageEncoder, getCompiledTransactionDecoder, getSignatureFromTransaction, getTransactionCodec, getTransactionDecoder, getTransactionEncoder, getUnsignedTransaction, isAdvanceNonceAccountInstruction, partiallySignTransaction, prependTransactionInstruction, prependTransactionInstructions, setTransactionFeePayer, setTransactionLifetimeUsingBlockhash, setTransactionLifetimeUsingDurableNonce, signTransaction };
905
962
  //# sourceMappingURL=out.js.map
906
963
  //# sourceMappingURL=index.browser.js.map