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