@solana/transactions 2.0.0-experimental.76cf6b4 → 2.0.0-experimental.7712fc3

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.
@@ -1,27 +1,12 @@
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__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
- // src/blockhash.ts
10
-
11
- // src/unsigned-transaction.ts
12
- function getUnsignedTransaction(transaction) {
13
- if ("signatures" in transaction) {
14
- const {
15
- signatures: _,
16
- // eslint-disable-line @typescript-eslint/no-unused-vars
17
- ...unsignedTransaction
18
- } = transaction;
19
- return unsignedTransaction;
20
- } else {
21
- return transaction;
22
- }
23
- }
24
-
25
10
  // src/blockhash.ts
26
11
  var base58Encoder;
27
12
  function assertIsBlockhash(putativeBlockhash) {
@@ -46,6 +31,22 @@ function assertIsBlockhash(putativeBlockhash) {
46
31
  });
47
32
  }
48
33
  }
34
+
35
+ // src/unsigned-transaction.ts
36
+ function getUnsignedTransaction(transaction) {
37
+ if ("signatures" in transaction) {
38
+ const {
39
+ signatures: _,
40
+ // eslint-disable-line @typescript-eslint/no-unused-vars
41
+ ...unsignedTransaction
42
+ } = transaction;
43
+ return unsignedTransaction;
44
+ } else {
45
+ return transaction;
46
+ }
47
+ }
48
+
49
+ // src/blockhash.ts
49
50
  function isTransactionWithBlockhashLifetime(transaction) {
50
51
  const lifetimeConstraintShapeMatches = "lifetimeConstraint" in transaction && typeof transaction.lifetimeConstraint.blockhash === "string" && typeof transaction.lifetimeConstraint.lastValidBlockHeight === "bigint";
51
52
  if (!lifetimeConstraintShapeMatches)
@@ -59,7 +60,7 @@ function isTransactionWithBlockhashLifetime(transaction) {
59
60
  }
60
61
  function assertIsTransactionWithBlockhashLifetime(transaction) {
61
62
  if (!isTransactionWithBlockhashLifetime(transaction)) {
62
- throw new Error("Transaction does not have a blockhash lifetime");
63
+ throw new SolanaError(SOLANA_ERROR__TRANSACTION_EXPECTED_BLOCKHASH_LIFETIME);
63
64
  }
64
65
  }
65
66
  function setTransactionLifetimeUsingBlockhash(blockhashLifetimeConstraint, transaction) {
@@ -108,13 +109,11 @@ function isWritableRole(role) {
108
109
  function mergeRoles(roleA, roleB) {
109
110
  return roleA | roleB;
110
111
  }
111
-
112
- // src/durable-nonce.ts
113
112
  var RECENT_BLOCKHASHES_SYSVAR_ADDRESS = "SysvarRecentB1ockHashes11111111111111111111";
114
113
  var SYSTEM_PROGRAM_ADDRESS = "11111111111111111111111111111111";
115
114
  function assertIsDurableNonceTransaction(transaction) {
116
115
  if (!isDurableNonceTransaction(transaction)) {
117
- throw new Error("Transaction is not a durable nonce transaction");
116
+ throw new SolanaError(SOLANA_ERROR__TRANSACTION_EXPECTED_NONCE_LIFETIME);
118
117
  }
119
118
  }
120
119
  function createAdvanceNonceAccountInstruction(nonceAccountAddress, nonceAuthorityAddress) {
@@ -200,21 +199,181 @@ function setTransactionFeePayer(feePayer, transaction) {
200
199
 
201
200
  // src/instructions.ts
202
201
  function appendTransactionInstruction(instruction, transaction) {
202
+ return appendTransactionInstructions([instruction], transaction);
203
+ }
204
+ function appendTransactionInstructions(instructions, transaction) {
203
205
  const out = {
204
206
  ...getUnsignedTransaction(transaction),
205
- instructions: [...transaction.instructions, instruction]
207
+ instructions: [...transaction.instructions, ...instructions]
206
208
  };
207
209
  Object.freeze(out);
208
210
  return out;
209
211
  }
210
212
  function prependTransactionInstruction(instruction, transaction) {
213
+ return prependTransactionInstructions([instruction], transaction);
214
+ }
215
+ function prependTransactionInstructions(instructions, transaction) {
211
216
  const out = {
212
217
  ...getUnsignedTransaction(transaction),
213
- instructions: [instruction, ...transaction.instructions]
218
+ instructions: [...instructions, ...transaction.instructions]
214
219
  };
215
220
  Object.freeze(out);
216
221
  return out;
217
222
  }
223
+
224
+ // src/decompile-transaction.ts
225
+ function getAccountMetas(message) {
226
+ const { header } = message;
227
+ const numWritableSignerAccounts = header.numSignerAccounts - header.numReadonlySignerAccounts;
228
+ const numWritableNonSignerAccounts = message.staticAccounts.length - header.numSignerAccounts - header.numReadonlyNonSignerAccounts;
229
+ const accountMetas = [];
230
+ let accountIndex = 0;
231
+ for (let i = 0; i < numWritableSignerAccounts; i++) {
232
+ accountMetas.push({
233
+ address: message.staticAccounts[accountIndex],
234
+ role: AccountRole.WRITABLE_SIGNER
235
+ });
236
+ accountIndex++;
237
+ }
238
+ for (let i = 0; i < header.numReadonlySignerAccounts; i++) {
239
+ accountMetas.push({
240
+ address: message.staticAccounts[accountIndex],
241
+ role: AccountRole.READONLY_SIGNER
242
+ });
243
+ accountIndex++;
244
+ }
245
+ for (let i = 0; i < numWritableNonSignerAccounts; i++) {
246
+ accountMetas.push({
247
+ address: message.staticAccounts[accountIndex],
248
+ role: AccountRole.WRITABLE
249
+ });
250
+ accountIndex++;
251
+ }
252
+ for (let i = 0; i < header.numReadonlyNonSignerAccounts; i++) {
253
+ accountMetas.push({
254
+ address: message.staticAccounts[accountIndex],
255
+ role: AccountRole.READONLY
256
+ });
257
+ accountIndex++;
258
+ }
259
+ return accountMetas;
260
+ }
261
+ function getAddressLookupMetas(compiledAddressTableLookups, addressesByLookupTableAddress) {
262
+ const compiledAddressTableLookupAddresses = compiledAddressTableLookups.map((l) => l.lookupTableAddress);
263
+ const missing = compiledAddressTableLookupAddresses.filter((a) => addressesByLookupTableAddress[a] === void 0);
264
+ if (missing.length > 0) {
265
+ throw new SolanaError(SOLANA_ERROR__TRANSACTION_FAILED_TO_DECOMPILE_ADDRESS_LOOKUP_TABLE_CONTENTS_MISSING, {
266
+ lookupTableAddresses: missing
267
+ });
268
+ }
269
+ const readOnlyMetas = [];
270
+ const writableMetas = [];
271
+ for (const lookup of compiledAddressTableLookups) {
272
+ const addresses = addressesByLookupTableAddress[lookup.lookupTableAddress];
273
+ const highestIndex = Math.max(...lookup.readableIndices, ...lookup.writableIndices);
274
+ if (highestIndex >= addresses.length) {
275
+ throw new SolanaError(
276
+ SOLANA_ERROR__TRANSACTION_FAILED_TO_DECOMPILE_ADDRESS_LOOKUP_TABLE_INDEX_OUT_OF_RANGE,
277
+ {
278
+ highestKnownIndex: addresses.length - 1,
279
+ highestRequestedIndex: highestIndex,
280
+ lookupTableAddress: lookup.lookupTableAddress
281
+ }
282
+ );
283
+ }
284
+ const readOnlyForLookup = lookup.readableIndices.map((r) => ({
285
+ address: addresses[r],
286
+ addressIndex: r,
287
+ lookupTableAddress: lookup.lookupTableAddress,
288
+ role: AccountRole.READONLY
289
+ }));
290
+ readOnlyMetas.push(...readOnlyForLookup);
291
+ const writableForLookup = lookup.writableIndices.map((w) => ({
292
+ address: addresses[w],
293
+ addressIndex: w,
294
+ lookupTableAddress: lookup.lookupTableAddress,
295
+ role: AccountRole.WRITABLE
296
+ }));
297
+ writableMetas.push(...writableForLookup);
298
+ }
299
+ return [...writableMetas, ...readOnlyMetas];
300
+ }
301
+ function convertInstruction(instruction, accountMetas) {
302
+ const programAddress = accountMetas[instruction.programAddressIndex]?.address;
303
+ if (!programAddress) {
304
+ throw new SolanaError(SOLANA_ERROR__TRANSACTION_FAILED_TO_DECOMPILE_INSTRUCTION_PROGRAM_ADDRESS_NOT_FOUND, {
305
+ index: instruction.programAddressIndex
306
+ });
307
+ }
308
+ const accounts = instruction.accountIndices?.map((accountIndex) => accountMetas[accountIndex]);
309
+ const { data } = instruction;
310
+ return {
311
+ programAddress,
312
+ ...accounts && accounts.length ? { accounts } : {},
313
+ ...data && data.length ? { data } : {}
314
+ };
315
+ }
316
+ function getLifetimeConstraint(messageLifetimeToken, firstInstruction, lastValidBlockHeight) {
317
+ if (!firstInstruction || !isAdvanceNonceAccountInstruction(firstInstruction)) {
318
+ return {
319
+ blockhash: messageLifetimeToken,
320
+ lastValidBlockHeight: lastValidBlockHeight ?? 2n ** 64n - 1n
321
+ // U64 MAX
322
+ };
323
+ } else {
324
+ const nonceAccountAddress = firstInstruction.accounts[0].address;
325
+ assertIsAddress(nonceAccountAddress);
326
+ const nonceAuthorityAddress = firstInstruction.accounts[2].address;
327
+ assertIsAddress(nonceAuthorityAddress);
328
+ return {
329
+ nonce: messageLifetimeToken,
330
+ nonceAccountAddress,
331
+ nonceAuthorityAddress
332
+ };
333
+ }
334
+ }
335
+ function convertSignatures(compiledTransaction) {
336
+ const {
337
+ compiledMessage: { staticAccounts },
338
+ signatures
339
+ } = compiledTransaction;
340
+ return signatures.reduce((acc, sig, index) => {
341
+ const allZeros = sig.every((byte) => byte === 0);
342
+ if (allZeros)
343
+ return acc;
344
+ const address = staticAccounts[index];
345
+ return { ...acc, [address]: sig };
346
+ }, {});
347
+ }
348
+ function decompileTransaction(compiledTransaction, config) {
349
+ const { compiledMessage } = compiledTransaction;
350
+ const feePayer = compiledMessage.staticAccounts[0];
351
+ if (!feePayer) {
352
+ throw new SolanaError(SOLANA_ERROR__TRANSACTION_FAILED_TO_DECOMPILE_FEE_PAYER_MISSING);
353
+ }
354
+ const accountMetas = getAccountMetas(compiledMessage);
355
+ const accountLookupMetas = "addressTableLookups" in compiledMessage && compiledMessage.addressTableLookups !== void 0 && compiledMessage.addressTableLookups.length > 0 ? getAddressLookupMetas(compiledMessage.addressTableLookups, config?.addressesByLookupTableAddress ?? {}) : [];
356
+ const transactionMetas = [...accountMetas, ...accountLookupMetas];
357
+ const instructions = compiledMessage.instructions.map(
358
+ (compiledInstruction) => convertInstruction(compiledInstruction, transactionMetas)
359
+ );
360
+ const firstInstruction = instructions[0];
361
+ const lifetimeConstraint = getLifetimeConstraint(
362
+ compiledMessage.lifetimeToken,
363
+ firstInstruction,
364
+ config?.lastValidBlockHeight
365
+ );
366
+ const signatures = convertSignatures(compiledTransaction);
367
+ return pipe(
368
+ createTransaction({ version: compiledMessage.version }),
369
+ (tx) => setTransactionFeePayer(feePayer, tx),
370
+ (tx) => instructions.reduce((acc, instruction) => {
371
+ return appendTransactionInstruction(instruction, acc);
372
+ }, tx),
373
+ (tx) => "blockhash" in lifetimeConstraint ? setTransactionLifetimeUsingBlockhash(lifetimeConstraint, tx) : setTransactionLifetimeUsingDurableNonce(lifetimeConstraint, tx),
374
+ (tx) => Object.keys(signatures).length > 0 ? { ...tx, signatures } : tx
375
+ );
376
+ }
218
377
  function upsert(addressMap, address, update) {
219
378
  addressMap[address] = update(addressMap[address] ?? { role: AccountRole.READONLY });
220
379
  }
@@ -231,13 +390,13 @@ function getAddressMapFromInstructions(feePayer, instructions) {
231
390
  if (isWritableRole(entry.role)) {
232
391
  switch (entry[TYPE]) {
233
392
  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
- );
393
+ throw new SolanaError(SOLANA_ERROR__TRANSACTION_INVOKED_PROGRAMS_CANNOT_PAY_FEES, {
394
+ programAddress: instruction.programAddress
395
+ });
237
396
  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
- );
397
+ throw new SolanaError(SOLANA_ERROR__TRANSACTION_INVOKED_PROGRAMS_MUST_NOT_BE_WRITABLE, {
398
+ programAddress: instruction.programAddress
399
+ });
241
400
  }
242
401
  }
243
402
  if (entry[TYPE] === 2 /* STATIC */) {
@@ -302,8 +461,11 @@ function getAddressMapFromInstructions(feePayer, instructions) {
302
461
  addressesOfInvokedPrograms.has(account.address)
303
462
  ) {
304
463
  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.`
464
+ throw new SolanaError(
465
+ SOLANA_ERROR__TRANSACTION_INVOKED_PROGRAMS_MUST_NOT_BE_WRITABLE,
466
+ {
467
+ programAddress: account.address
468
+ }
307
469
  );
308
470
  }
309
471
  if (entry.role !== nextRole) {
@@ -593,7 +755,9 @@ function getTransactionVersionEncoder() {
593
755
  return offset;
594
756
  }
595
757
  if (value < 0 || value > 127) {
596
- throw new Error(`Transaction version must be in the range [0, 127]. \`${value}\` given.`);
758
+ throw new SolanaError(SOLANA_ERROR__TRANSACTION_VERSION_NUMBER_OUT_OF_RANGE, {
759
+ actualVersion: value
760
+ });
597
761
  }
598
762
  bytes.set([value | VERSION_FLAG_MASK], offset);
599
763
  return offset + 1;
@@ -680,12 +844,15 @@ function getCompiledMessageEncoder() {
680
844
  });
681
845
  }
682
846
  function getCompiledMessageDecoder() {
683
- return mapDecoder(getStructDecoder(getPreludeStructDecoderTuple()), ({ addressTableLookups, ...restOfMessage }) => {
684
- if (restOfMessage.version === "legacy" || !addressTableLookups?.length) {
685
- return restOfMessage;
847
+ return mapDecoder(
848
+ getStructDecoder(getPreludeStructDecoderTuple()),
849
+ ({ addressTableLookups, ...restOfMessage }) => {
850
+ if (restOfMessage.version === "legacy" || !addressTableLookups?.length) {
851
+ return restOfMessage;
852
+ }
853
+ return { ...restOfMessage, addressTableLookups };
686
854
  }
687
- return { ...restOfMessage, addressTableLookups };
688
- });
855
+ );
689
856
  }
690
857
  function getCompiledMessageCodec() {
691
858
  return combineCodec(getCompiledMessageEncoder(), getCompiledMessageDecoder());
@@ -708,116 +875,6 @@ function getCompiledTransaction(transaction) {
708
875
  signatures
709
876
  };
710
877
  }
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
878
 
822
879
  // src/serializers/transaction.ts
823
880
  function getCompiledTransactionEncoder() {
@@ -840,14 +897,14 @@ function getCompiledTransactionDecoder() {
840
897
  function getTransactionEncoder() {
841
898
  return mapEncoder(getCompiledTransactionEncoder(), getCompiledTransaction);
842
899
  }
843
- function getTransactionDecoder(lastValidBlockHeight) {
900
+ function getTransactionDecoder(config) {
844
901
  return mapDecoder(
845
902
  getCompiledTransactionDecoder(),
846
- (compiledTransaction) => decompileTransaction(compiledTransaction, lastValidBlockHeight)
903
+ (compiledTransaction) => decompileTransaction(compiledTransaction, config)
847
904
  );
848
905
  }
849
- function getTransactionCodec(lastValidBlockHeight) {
850
- return combineCodec(getTransactionEncoder(), getTransactionDecoder(lastValidBlockHeight));
906
+ function getTransactionCodec(config) {
907
+ return combineCodec(getTransactionEncoder(), getTransactionDecoder(config));
851
908
  }
852
909
  var base58Decoder;
853
910
  function getSignatureFromTransaction(transaction) {
@@ -855,9 +912,7 @@ function getSignatureFromTransaction(transaction) {
855
912
  base58Decoder = getBase58Decoder();
856
913
  const signatureBytes = transaction.signatures[transaction.feePayer];
857
914
  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
- );
915
+ throw new SolanaError(SOLANA_ERROR__TRANSACTION_SIGNATURE_NOT_COMPUTABLE);
861
916
  }
862
917
  const transactionSignature = base58Decoder.decode(signatureBytes);
863
918
  return transactionSignature;
@@ -890,17 +945,23 @@ async function signTransaction(keyPairs, transaction) {
890
945
  function assertTransactionIsFullySigned(transaction) {
891
946
  const signerAddressesFromInstructions = transaction.instructions.flatMap((i) => i.accounts?.filter((a) => isSignerRole(a.role)) ?? []).map((a) => a.address);
892
947
  const requiredSigners = /* @__PURE__ */ new Set([transaction.feePayer, ...signerAddressesFromInstructions]);
948
+ const missingSigs = [];
893
949
  requiredSigners.forEach((address) => {
894
950
  if (!transaction.signatures[address]) {
895
- throw new Error(`Transaction is missing signature for address \`${address}\``);
951
+ missingSigs.push(address);
896
952
  }
897
953
  });
954
+ if (missingSigs.length > 0) {
955
+ throw new SolanaError(SOLANA_ERROR__TRANSACTION_MISSING_SIGNATURES, {
956
+ addresses: missingSigs
957
+ });
958
+ }
898
959
  }
899
960
  function getBase64EncodedWireTransaction(transaction) {
900
961
  const wireTransactionBytes = getTransactionEncoder().encode(transaction);
901
962
  return getBase64Decoder().decode(wireTransactionBytes);
902
963
  }
903
964
 
904
- export { appendTransactionInstruction, assertIsBlockhash, assertIsDurableNonceTransaction, assertIsTransactionWithBlockhashLifetime, assertTransactionIsFullySigned, compileMessage, createTransaction, getBase64EncodedWireTransaction, getCompiledMessageCodec, getCompiledMessageDecoder, getCompiledMessageEncoder, getSignatureFromTransaction, getTransactionCodec, getTransactionDecoder, getTransactionEncoder, isAdvanceNonceAccountInstruction, partiallySignTransaction, prependTransactionInstruction, setTransactionFeePayer, setTransactionLifetimeUsingBlockhash, setTransactionLifetimeUsingDurableNonce, signTransaction };
965
+ 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
966
  //# sourceMappingURL=out.js.map
906
967
  //# sourceMappingURL=index.browser.js.map