@solana/web3.js 0.0.0-next → 0.0.0-pr-29130

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 (74) hide show
  1. package/README.md +24 -25
  2. package/lib/index.browser.cjs.js +4583 -4238
  3. package/lib/index.browser.cjs.js.map +1 -1
  4. package/lib/index.browser.esm.js +4565 -4238
  5. package/lib/index.browser.esm.js.map +1 -1
  6. package/lib/index.cjs.js +7072 -3604
  7. package/lib/index.cjs.js.map +1 -1
  8. package/lib/index.d.ts +3516 -2420
  9. package/lib/index.esm.js +7046 -3601
  10. package/lib/index.esm.js.map +1 -1
  11. package/lib/index.iife.js +22171 -27053
  12. package/lib/index.iife.js.map +1 -1
  13. package/lib/index.iife.min.js +8 -33
  14. package/lib/index.iife.min.js.map +1 -1
  15. package/lib/index.native.js +10407 -0
  16. package/lib/index.native.js.map +1 -0
  17. package/package.json +36 -36
  18. package/src/__forks__/browser/fetch-impl.ts +4 -0
  19. package/src/__forks__/react-native/fetch-impl.ts +4 -0
  20. package/src/account-data.ts +39 -0
  21. package/src/account.ts +20 -11
  22. package/src/bpf-loader.ts +2 -2
  23. package/src/connection.ts +2303 -635
  24. package/src/epoch-schedule.ts +1 -1
  25. package/src/errors.ts +41 -0
  26. package/src/fee-calculator.ts +2 -0
  27. package/src/fetch-impl.ts +13 -0
  28. package/src/index.ts +3 -10
  29. package/src/keypair.ts +20 -25
  30. package/src/layout.ts +45 -4
  31. package/src/loader.ts +3 -3
  32. package/src/message/account-keys.ts +79 -0
  33. package/src/message/compiled-keys.ts +165 -0
  34. package/src/message/index.ts +47 -0
  35. package/src/{message.ts → message/legacy.ts} +95 -40
  36. package/src/message/v0.ts +496 -0
  37. package/src/message/versioned.ts +36 -0
  38. package/src/nonce-account.ts +8 -4
  39. package/src/programs/address-lookup-table/index.ts +435 -0
  40. package/src/programs/address-lookup-table/state.ts +84 -0
  41. package/src/programs/compute-budget.ts +281 -0
  42. package/src/{ed25519-program.ts → programs/ed25519.ts} +6 -6
  43. package/src/programs/index.ts +7 -0
  44. package/src/{secp256k1-program.ts → programs/secp256k1.ts} +15 -16
  45. package/src/{stake-program.ts → programs/stake.ts} +7 -7
  46. package/src/{system-program.ts → programs/system.ts} +55 -18
  47. package/src/{vote-program.ts → programs/vote.ts} +137 -9
  48. package/src/publickey.ts +37 -79
  49. package/src/transaction/constants.ts +12 -0
  50. package/src/transaction/expiry-custom-errors.ts +48 -0
  51. package/src/transaction/index.ts +5 -0
  52. package/src/{transaction.ts → transaction/legacy.ts} +162 -67
  53. package/src/transaction/message.ts +140 -0
  54. package/src/transaction/versioned.ts +126 -0
  55. package/src/{util → utils}/assert.ts +0 -0
  56. package/src/utils/bigint.ts +43 -0
  57. package/src/{util → utils}/borsh-schema.ts +0 -0
  58. package/src/{util → utils}/cluster.ts +0 -0
  59. package/src/utils/ed25519.ts +46 -0
  60. package/src/utils/index.ts +5 -0
  61. package/src/utils/makeWebsocketUrl.ts +26 -0
  62. package/src/{util → utils}/promise-timeout.ts +0 -0
  63. package/src/utils/secp256k1.ts +18 -0
  64. package/src/utils/send-and-confirm-raw-transaction.ts +105 -0
  65. package/src/utils/send-and-confirm-transaction.ts +98 -0
  66. package/src/{util → utils}/shortvec-encoding.ts +0 -0
  67. package/src/{util → utils}/sleep.ts +0 -0
  68. package/src/{util → utils}/to-buffer.ts +0 -0
  69. package/src/validator-info.ts +4 -6
  70. package/src/vote-account.ts +1 -1
  71. package/src/agent-manager.ts +0 -44
  72. package/src/util/send-and-confirm-raw-transaction.ts +0 -46
  73. package/src/util/send-and-confirm-transaction.ts +0 -50
  74. package/src/util/url.ts +0 -18
@@ -1,39 +1,34 @@
1
- import nacl from 'tweetnacl';
2
1
  import bs58 from 'bs58';
3
2
  import {Buffer} from 'buffer';
4
3
 
5
- import {Connection} from './connection';
6
- import {Message} from './message';
7
- import {PublicKey} from './publickey';
8
- import * as shortvec from './util/shortvec-encoding';
9
- import {toBuffer} from './util/to-buffer';
10
- import invariant from './util/assert';
11
- import type {Signer} from './keypair';
12
- import type {Blockhash} from './blockhash';
13
- import type {CompiledInstruction} from './message';
4
+ import {PACKET_DATA_SIZE, SIGNATURE_LENGTH_IN_BYTES} from './constants';
5
+ import {Connection} from '../connection';
6
+ import {Message} from '../message';
7
+ import {PublicKey} from '../publickey';
8
+ import * as shortvec from '../utils/shortvec-encoding';
9
+ import {toBuffer} from '../utils/to-buffer';
10
+ import invariant from '../utils/assert';
11
+ import type {Signer} from '../keypair';
12
+ import type {Blockhash} from '../blockhash';
13
+ import type {CompiledInstruction} from '../message';
14
+ import {sign, verify} from '../utils/ed25519';
14
15
 
15
16
  /**
16
17
  * Transaction signature as base-58 encoded string
17
18
  */
18
19
  export type TransactionSignature = string;
19
20
 
20
- /**
21
- * Default (empty) signature
22
- *
23
- * Signatures are 64 bytes in length
24
- */
25
- const DEFAULT_SIGNATURE = Buffer.alloc(64).fill(0);
21
+ export const enum TransactionStatus {
22
+ BLOCKHEIGHT_EXCEEDED,
23
+ PROCESSED,
24
+ TIMED_OUT,
25
+ NONCE_INVALID,
26
+ }
26
27
 
27
28
  /**
28
- * Maximum over-the-wire size of a Transaction
29
- *
30
- * 1280 is IPv6 minimum MTU
31
- * 40 bytes is the size of the IPv6 header
32
- * 8 bytes is the size of the fragment header
29
+ * Default (empty) signature
33
30
  */
34
- export const PACKET_DATA_SIZE = 1280 - 40 - 8;
35
-
36
- const SIGNATURE_LENGTH = 64;
31
+ const DEFAULT_SIGNATURE = Buffer.alloc(SIGNATURE_LENGTH_IN_BYTES).fill(0);
37
32
 
38
33
  /**
39
34
  * Account metadata used to define instructions
@@ -133,17 +128,49 @@ export type SignaturePubkeyPair = {
133
128
 
134
129
  /**
135
130
  * List of Transaction object fields that may be initialized at construction
136
- *
137
131
  */
138
- export type TransactionCtorFields = {
139
- /** A recent blockhash */
140
- recentBlockhash?: Blockhash | null;
132
+ export type TransactionCtorFields_DEPRECATED = {
141
133
  /** Optional nonce information used for offline nonce'd transactions */
142
134
  nonceInfo?: NonceInformation | null;
143
135
  /** The transaction fee payer */
144
136
  feePayer?: PublicKey | null;
145
137
  /** One or more signatures */
146
138
  signatures?: Array<SignaturePubkeyPair>;
139
+ /** A recent blockhash */
140
+ recentBlockhash?: Blockhash;
141
+ };
142
+
143
+ // For backward compatibility; an unfortunate consequence of being
144
+ // forced to over-export types by the documentation generator.
145
+ // See https://github.com/solana-labs/solana/pull/25820
146
+ export type TransactionCtorFields = TransactionCtorFields_DEPRECATED;
147
+
148
+ /**
149
+ * Blockhash-based transactions have a lifetime that are defined by
150
+ * the blockhash they include. Any transaction whose blockhash is
151
+ * too old will be rejected.
152
+ */
153
+ export type TransactionBlockhashCtor = {
154
+ /** The transaction fee payer */
155
+ feePayer?: PublicKey | null;
156
+ /** One or more signatures */
157
+ signatures?: Array<SignaturePubkeyPair>;
158
+ /** A recent blockhash */
159
+ blockhash: Blockhash;
160
+ /** the last block chain can advance to before tx is declared expired */
161
+ lastValidBlockHeight: number;
162
+ };
163
+
164
+ /**
165
+ * Use these options to construct a durable nonce transaction.
166
+ */
167
+ export type TransactionNonceCtor = {
168
+ /** The transaction fee payer */
169
+ feePayer?: PublicKey | null;
170
+ minContextSlot: number;
171
+ nonceInfo: NonceInformation;
172
+ /** One or more signatures */
173
+ signatures?: Array<SignaturePubkeyPair>;
147
174
  };
148
175
 
149
176
  /**
@@ -205,12 +232,26 @@ export class Transaction {
205
232
  */
206
233
  recentBlockhash?: Blockhash;
207
234
 
235
+ /**
236
+ * the last block chain can advance to before tx is declared expired
237
+ * */
238
+ lastValidBlockHeight?: number;
239
+
208
240
  /**
209
241
  * Optional Nonce information. If populated, transaction will use a durable
210
242
  * Nonce hash instead of a recentBlockhash. Must be populated by the caller
211
243
  */
212
244
  nonceInfo?: NonceInformation;
213
245
 
246
+ /**
247
+ * If this is a nonce transaction this represents the minimum slot from which
248
+ * to evaluate if the nonce has advanced when attempting to confirm the
249
+ * transaction. This protects against a case where the transaction confirmation
250
+ * logic loads the nonce account from an old slot and assumes the mismatch in
251
+ * nonce value implies that the nonce has been advanced.
252
+ */
253
+ minNonceContextSlot?: number;
254
+
214
255
  /**
215
256
  * @internal
216
257
  */
@@ -221,11 +262,55 @@ export class Transaction {
221
262
  */
222
263
  _json?: TransactionJSON;
223
264
 
265
+ // Construct a transaction with a blockhash and lastValidBlockHeight
266
+ constructor(opts?: TransactionBlockhashCtor);
267
+
268
+ // Construct a transaction using a durable nonce
269
+ constructor(opts?: TransactionNonceCtor);
270
+
271
+ /**
272
+ * @deprecated `TransactionCtorFields` has been deprecated and will be removed in a future version.
273
+ * Please supply a `TransactionBlockhashCtor` instead.
274
+ */
275
+ constructor(opts?: TransactionCtorFields_DEPRECATED);
276
+
224
277
  /**
225
278
  * Construct an empty Transaction
226
279
  */
227
- constructor(opts?: TransactionCtorFields) {
228
- opts && Object.assign(this, opts);
280
+ constructor(
281
+ opts?:
282
+ | TransactionBlockhashCtor
283
+ | TransactionNonceCtor
284
+ | TransactionCtorFields_DEPRECATED,
285
+ ) {
286
+ if (!opts) {
287
+ return;
288
+ }
289
+ if (opts.feePayer) {
290
+ this.feePayer = opts.feePayer;
291
+ }
292
+ if (opts.signatures) {
293
+ this.signatures = opts.signatures;
294
+ }
295
+ if (Object.prototype.hasOwnProperty.call(opts, 'nonceInfo')) {
296
+ const {minContextSlot, nonceInfo} = opts as TransactionNonceCtor;
297
+ this.minNonceContextSlot = minContextSlot;
298
+ this.nonceInfo = nonceInfo;
299
+ } else if (
300
+ Object.prototype.hasOwnProperty.call(opts, 'lastValidBlockHeight')
301
+ ) {
302
+ const {blockhash, lastValidBlockHeight} =
303
+ opts as TransactionBlockhashCtor;
304
+ this.recentBlockhash = blockhash;
305
+ this.lastValidBlockHeight = lastValidBlockHeight;
306
+ } else {
307
+ const {recentBlockhash, nonceInfo} =
308
+ opts as TransactionCtorFields_DEPRECATED;
309
+ if (nonceInfo) {
310
+ this.nonceInfo = nonceInfo;
311
+ }
312
+ this.recentBlockhash = recentBlockhash;
313
+ }
229
314
  }
230
315
 
231
316
  /**
@@ -276,26 +361,31 @@ export class Transaction {
276
361
  * Compile transaction data
277
362
  */
278
363
  compileMessage(): Message {
279
- if (this._message) {
280
- if (JSON.stringify(this.toJSON()) !== JSON.stringify(this._json)) {
281
- throw new Error(
282
- 'Transaction message mutated after being populated from Message',
283
- );
284
- }
364
+ if (
365
+ this._message &&
366
+ JSON.stringify(this.toJSON()) === JSON.stringify(this._json)
367
+ ) {
285
368
  return this._message;
286
369
  }
287
370
 
288
- const {nonceInfo} = this;
289
- if (nonceInfo && this.instructions[0] != nonceInfo.nonceInstruction) {
290
- this.recentBlockhash = nonceInfo.nonce;
291
- this.instructions.unshift(nonceInfo.nonceInstruction);
371
+ let recentBlockhash;
372
+ let instructions: TransactionInstruction[];
373
+ if (this.nonceInfo) {
374
+ recentBlockhash = this.nonceInfo.nonce;
375
+ if (this.instructions[0] != this.nonceInfo.nonceInstruction) {
376
+ instructions = [this.nonceInfo.nonceInstruction, ...this.instructions];
377
+ } else {
378
+ instructions = this.instructions;
379
+ }
380
+ } else {
381
+ recentBlockhash = this.recentBlockhash;
382
+ instructions = this.instructions;
292
383
  }
293
- const {recentBlockhash} = this;
294
384
  if (!recentBlockhash) {
295
385
  throw new Error('Transaction recentBlockhash required');
296
386
  }
297
387
 
298
- if (this.instructions.length < 1) {
388
+ if (instructions.length < 1) {
299
389
  console.warn('No instructions provided');
300
390
  }
301
391
 
@@ -309,8 +399,8 @@ export class Transaction {
309
399
  throw new Error('Transaction fee payer required');
310
400
  }
311
401
 
312
- for (let i = 0; i < this.instructions.length; i++) {
313
- if (this.instructions[i].programId === undefined) {
402
+ for (let i = 0; i < instructions.length; i++) {
403
+ if (instructions[i].programId === undefined) {
314
404
  throw new Error(
315
405
  `Transaction instruction index ${i} has undefined program id`,
316
406
  );
@@ -319,7 +409,7 @@ export class Transaction {
319
409
 
320
410
  const programIds: string[] = [];
321
411
  const accountMetas: AccountMeta[] = [];
322
- this.instructions.forEach(instruction => {
412
+ instructions.forEach(instruction => {
323
413
  instruction.keys.forEach(accountMeta => {
324
414
  accountMetas.push({...accountMeta});
325
415
  });
@@ -339,17 +429,6 @@ export class Transaction {
339
429
  });
340
430
  });
341
431
 
342
- // Sort. Prioritizing first by signer, then by writable
343
- accountMetas.sort(function (x, y) {
344
- const pubkeySorting = x.pubkey
345
- .toBase58()
346
- .localeCompare(y.pubkey.toBase58());
347
- const checkSigner = x.isSigner === y.isSigner ? 0 : x.isSigner ? -1 : 1;
348
- const checkWritable =
349
- x.isWritable === y.isWritable ? pubkeySorting : x.isWritable ? -1 : 1;
350
- return checkSigner || checkWritable;
351
- });
352
-
353
432
  // Cull duplicate account metas
354
433
  const uniqueMetas: AccountMeta[] = [];
355
434
  accountMetas.forEach(accountMeta => {
@@ -360,11 +439,27 @@ export class Transaction {
360
439
  if (uniqueIndex > -1) {
361
440
  uniqueMetas[uniqueIndex].isWritable =
362
441
  uniqueMetas[uniqueIndex].isWritable || accountMeta.isWritable;
442
+ uniqueMetas[uniqueIndex].isSigner =
443
+ uniqueMetas[uniqueIndex].isSigner || accountMeta.isSigner;
363
444
  } else {
364
445
  uniqueMetas.push(accountMeta);
365
446
  }
366
447
  });
367
448
 
449
+ // Sort. Prioritizing first by signer, then by writable
450
+ uniqueMetas.sort(function (x, y) {
451
+ if (x.isSigner !== y.isSigner) {
452
+ // Signers always come before non-signers
453
+ return x.isSigner ? -1 : 1;
454
+ }
455
+ if (x.isWritable !== y.isWritable) {
456
+ // Writable accounts always come before read-only accounts
457
+ return x.isWritable ? -1 : 1;
458
+ }
459
+ // Otherwise, sort by pubkey, stringwise.
460
+ return x.pubkey.toBase58().localeCompare(y.pubkey.toBase58());
461
+ });
462
+
368
463
  // Move fee payer to the front
369
464
  const feePayerIndex = uniqueMetas.findIndex(x => {
370
465
  return x.pubkey.equals(feePayer);
@@ -424,7 +519,7 @@ export class Transaction {
424
519
  });
425
520
 
426
521
  const accountKeys = signedKeys.concat(unsignedKeys);
427
- const instructions: CompiledInstruction[] = this.instructions.map(
522
+ const compiledInstructions: CompiledInstruction[] = instructions.map(
428
523
  instruction => {
429
524
  const {data, programId} = instruction;
430
525
  return {
@@ -437,7 +532,7 @@ export class Transaction {
437
532
  },
438
533
  );
439
534
 
440
- instructions.forEach(instruction => {
535
+ compiledInstructions.forEach(instruction => {
441
536
  invariant(instruction.programIdIndex >= 0);
442
537
  instruction.accounts.forEach(keyIndex => invariant(keyIndex >= 0));
443
538
  });
@@ -450,7 +545,7 @@ export class Transaction {
450
545
  },
451
546
  accountKeys,
452
547
  recentBlockhash,
453
- instructions,
548
+ instructions: compiledInstructions,
454
549
  });
455
550
  }
456
551
 
@@ -562,7 +657,6 @@ export class Transaction {
562
657
 
563
658
  const message = this._compile();
564
659
  this._partialSign(message, ...uniqueSigners);
565
- this._verifySignatures(message.serialize(), true);
566
660
  }
567
661
 
568
662
  /**
@@ -600,7 +694,7 @@ export class Transaction {
600
694
  _partialSign(message: Message, ...signers: Array<Signer>) {
601
695
  const signData = message.serialize();
602
696
  signers.forEach(signer => {
603
- const signature = nacl.sign.detached(signData, signer.secretKey);
697
+ const signature = sign(signData, signer.secretKey);
604
698
  this._addSignature(signer.publicKey, toBuffer(signature));
605
699
  });
606
700
  }
@@ -641,16 +735,17 @@ export class Transaction {
641
735
  /**
642
736
  * @internal
643
737
  */
644
- _verifySignatures(signData: Buffer, requireAllSignatures: boolean): boolean {
738
+ _verifySignatures(
739
+ signData: Uint8Array,
740
+ requireAllSignatures: boolean,
741
+ ): boolean {
645
742
  for (const {signature, publicKey} of this.signatures) {
646
743
  if (signature === null) {
647
744
  if (requireAllSignatures) {
648
745
  return false;
649
746
  }
650
747
  } else {
651
- if (
652
- !nacl.sign.detached.verify(signData, signature, publicKey.toBuffer())
653
- ) {
748
+ if (!verify(signature, signData, publicKey.toBytes())) {
654
749
  return false;
655
750
  }
656
751
  }
@@ -747,8 +842,8 @@ export class Transaction {
747
842
  const signatureCount = shortvec.decodeLength(byteArray);
748
843
  let signatures = [];
749
844
  for (let i = 0; i < signatureCount; i++) {
750
- const signature = byteArray.slice(0, SIGNATURE_LENGTH);
751
- byteArray = byteArray.slice(SIGNATURE_LENGTH);
845
+ const signature = byteArray.slice(0, SIGNATURE_LENGTH_IN_BYTES);
846
+ byteArray = byteArray.slice(SIGNATURE_LENGTH_IN_BYTES);
752
847
  signatures.push(bs58.encode(Buffer.from(signature)));
753
848
  }
754
849
 
@@ -0,0 +1,140 @@
1
+ import {AccountKeysFromLookups} from '../message/account-keys';
2
+ import assert from '../utils/assert';
3
+ import {toBuffer} from '../utils/to-buffer';
4
+ import {Blockhash} from '../blockhash';
5
+ import {Message, MessageV0, VersionedMessage} from '../message';
6
+ import {PublicKey} from '../publickey';
7
+ import {AddressLookupTableAccount} from '../programs';
8
+ import {AccountMeta, TransactionInstruction} from './legacy';
9
+
10
+ export type TransactionMessageArgs = {
11
+ payerKey: PublicKey;
12
+ instructions: Array<TransactionInstruction>;
13
+ recentBlockhash: Blockhash;
14
+ };
15
+
16
+ export type DecompileArgs =
17
+ | {
18
+ accountKeysFromLookups: AccountKeysFromLookups;
19
+ }
20
+ | {
21
+ addressLookupTableAccounts: AddressLookupTableAccount[];
22
+ };
23
+
24
+ export class TransactionMessage {
25
+ payerKey: PublicKey;
26
+ instructions: Array<TransactionInstruction>;
27
+ recentBlockhash: Blockhash;
28
+
29
+ constructor(args: TransactionMessageArgs) {
30
+ this.payerKey = args.payerKey;
31
+ this.instructions = args.instructions;
32
+ this.recentBlockhash = args.recentBlockhash;
33
+ }
34
+
35
+ static decompile(
36
+ message: VersionedMessage,
37
+ args?: DecompileArgs,
38
+ ): TransactionMessage {
39
+ const {header, compiledInstructions, recentBlockhash} = message;
40
+
41
+ const {
42
+ numRequiredSignatures,
43
+ numReadonlySignedAccounts,
44
+ numReadonlyUnsignedAccounts,
45
+ } = header;
46
+
47
+ const numWritableSignedAccounts =
48
+ numRequiredSignatures - numReadonlySignedAccounts;
49
+ assert(numWritableSignedAccounts > 0, 'Message header is invalid');
50
+
51
+ const numWritableUnsignedAccounts =
52
+ message.staticAccountKeys.length -
53
+ numRequiredSignatures -
54
+ numReadonlyUnsignedAccounts;
55
+ assert(numWritableUnsignedAccounts >= 0, 'Message header is invalid');
56
+
57
+ const accountKeys = message.getAccountKeys(args);
58
+ const payerKey = accountKeys.get(0);
59
+ if (payerKey === undefined) {
60
+ throw new Error(
61
+ 'Failed to decompile message because no account keys were found',
62
+ );
63
+ }
64
+
65
+ const instructions: TransactionInstruction[] = [];
66
+ for (const compiledIx of compiledInstructions) {
67
+ const keys: AccountMeta[] = [];
68
+
69
+ for (const keyIndex of compiledIx.accountKeyIndexes) {
70
+ const pubkey = accountKeys.get(keyIndex);
71
+ if (pubkey === undefined) {
72
+ throw new Error(
73
+ `Failed to find key for account key index ${keyIndex}`,
74
+ );
75
+ }
76
+
77
+ const isSigner = keyIndex < numRequiredSignatures;
78
+
79
+ let isWritable;
80
+ if (isSigner) {
81
+ isWritable = keyIndex < numWritableSignedAccounts;
82
+ } else if (keyIndex < accountKeys.staticAccountKeys.length) {
83
+ isWritable =
84
+ keyIndex - numRequiredSignatures < numWritableUnsignedAccounts;
85
+ } else {
86
+ isWritable =
87
+ keyIndex - accountKeys.staticAccountKeys.length <
88
+ // accountKeysFromLookups cannot be undefined because we already found a pubkey for this index above
89
+ accountKeys.accountKeysFromLookups!.writable.length;
90
+ }
91
+
92
+ keys.push({
93
+ pubkey,
94
+ isSigner: keyIndex < header.numRequiredSignatures,
95
+ isWritable,
96
+ });
97
+ }
98
+
99
+ const programId = accountKeys.get(compiledIx.programIdIndex);
100
+ if (programId === undefined) {
101
+ throw new Error(
102
+ `Failed to find program id for program id index ${compiledIx.programIdIndex}`,
103
+ );
104
+ }
105
+
106
+ instructions.push(
107
+ new TransactionInstruction({
108
+ programId,
109
+ data: toBuffer(compiledIx.data),
110
+ keys,
111
+ }),
112
+ );
113
+ }
114
+
115
+ return new TransactionMessage({
116
+ payerKey,
117
+ instructions,
118
+ recentBlockhash,
119
+ });
120
+ }
121
+
122
+ compileToLegacyMessage(): Message {
123
+ return Message.compile({
124
+ payerKey: this.payerKey,
125
+ recentBlockhash: this.recentBlockhash,
126
+ instructions: this.instructions,
127
+ });
128
+ }
129
+
130
+ compileToV0Message(
131
+ addressLookupTableAccounts?: AddressLookupTableAccount[],
132
+ ): MessageV0 {
133
+ return MessageV0.compile({
134
+ payerKey: this.payerKey,
135
+ recentBlockhash: this.recentBlockhash,
136
+ instructions: this.instructions,
137
+ addressLookupTableAccounts,
138
+ });
139
+ }
140
+ }
@@ -0,0 +1,126 @@
1
+ import * as BufferLayout from '@solana/buffer-layout';
2
+
3
+ import {Signer} from '../keypair';
4
+ import assert from '../utils/assert';
5
+ import {VersionedMessage} from '../message/versioned';
6
+ import {SIGNATURE_LENGTH_IN_BYTES} from './constants';
7
+ import * as shortvec from '../utils/shortvec-encoding';
8
+ import * as Layout from '../layout';
9
+ import {sign} from '../utils/ed25519';
10
+ import {PublicKey} from '../publickey';
11
+
12
+ export type TransactionVersion = 'legacy' | 0;
13
+
14
+ /**
15
+ * Versioned transaction class
16
+ */
17
+ export class VersionedTransaction {
18
+ signatures: Array<Uint8Array>;
19
+ message: VersionedMessage;
20
+
21
+ get version(): TransactionVersion {
22
+ return this.message.version;
23
+ }
24
+
25
+ constructor(message: VersionedMessage, signatures?: Array<Uint8Array>) {
26
+ if (signatures !== undefined) {
27
+ assert(
28
+ signatures.length === message.header.numRequiredSignatures,
29
+ 'Expected signatures length to be equal to the number of required signatures',
30
+ );
31
+ this.signatures = signatures;
32
+ } else {
33
+ const defaultSignatures = [];
34
+ for (let i = 0; i < message.header.numRequiredSignatures; i++) {
35
+ defaultSignatures.push(new Uint8Array(SIGNATURE_LENGTH_IN_BYTES));
36
+ }
37
+ this.signatures = defaultSignatures;
38
+ }
39
+ this.message = message;
40
+ }
41
+
42
+ serialize(): Uint8Array {
43
+ const serializedMessage = this.message.serialize();
44
+
45
+ const encodedSignaturesLength = Array<number>();
46
+ shortvec.encodeLength(encodedSignaturesLength, this.signatures.length);
47
+
48
+ const transactionLayout = BufferLayout.struct<{
49
+ encodedSignaturesLength: Uint8Array;
50
+ signatures: Array<Uint8Array>;
51
+ serializedMessage: Uint8Array;
52
+ }>([
53
+ BufferLayout.blob(
54
+ encodedSignaturesLength.length,
55
+ 'encodedSignaturesLength',
56
+ ),
57
+ BufferLayout.seq(
58
+ Layout.signature(),
59
+ this.signatures.length,
60
+ 'signatures',
61
+ ),
62
+ BufferLayout.blob(serializedMessage.length, 'serializedMessage'),
63
+ ]);
64
+
65
+ const serializedTransaction = new Uint8Array(2048);
66
+ const serializedTransactionLength = transactionLayout.encode(
67
+ {
68
+ encodedSignaturesLength: new Uint8Array(encodedSignaturesLength),
69
+ signatures: this.signatures,
70
+ serializedMessage,
71
+ },
72
+ serializedTransaction,
73
+ );
74
+
75
+ return serializedTransaction.slice(0, serializedTransactionLength);
76
+ }
77
+
78
+ static deserialize(serializedTransaction: Uint8Array): VersionedTransaction {
79
+ let byteArray = [...serializedTransaction];
80
+
81
+ const signatures = [];
82
+ const signaturesLength = shortvec.decodeLength(byteArray);
83
+ for (let i = 0; i < signaturesLength; i++) {
84
+ signatures.push(
85
+ new Uint8Array(byteArray.splice(0, SIGNATURE_LENGTH_IN_BYTES)),
86
+ );
87
+ }
88
+
89
+ const message = VersionedMessage.deserialize(new Uint8Array(byteArray));
90
+ return new VersionedTransaction(message, signatures);
91
+ }
92
+
93
+ sign(signers: Array<Signer>) {
94
+ const messageData = this.message.serialize();
95
+ const signerPubkeys = this.message.staticAccountKeys.slice(
96
+ 0,
97
+ this.message.header.numRequiredSignatures,
98
+ );
99
+ for (const signer of signers) {
100
+ const signerIndex = signerPubkeys.findIndex(pubkey =>
101
+ pubkey.equals(signer.publicKey),
102
+ );
103
+ assert(
104
+ signerIndex >= 0,
105
+ `Cannot sign with non signer key ${signer.publicKey.toBase58()}`,
106
+ );
107
+ this.signatures[signerIndex] = sign(messageData, signer.secretKey);
108
+ }
109
+ }
110
+
111
+ addSignature(publicKey: PublicKey, signature: Uint8Array) {
112
+ assert(signature.byteLength === 64, 'Signature must be 64 bytes long');
113
+ const signerPubkeys = this.message.staticAccountKeys.slice(
114
+ 0,
115
+ this.message.header.numRequiredSignatures,
116
+ );
117
+ const signerIndex = signerPubkeys.findIndex(pubkey =>
118
+ pubkey.equals(publicKey),
119
+ );
120
+ assert(
121
+ signerIndex >= 0,
122
+ `Can not add signature; \`${publicKey.toBase58()}\` is not required to sign this transaction`,
123
+ );
124
+ this.signatures[signerIndex] = signature;
125
+ }
126
+ }
File without changes
@@ -0,0 +1,43 @@
1
+ import {Buffer} from 'buffer';
2
+ import {blob, Layout} from '@solana/buffer-layout';
3
+ import {toBigIntLE, toBufferLE} from 'bigint-buffer';
4
+
5
+ interface EncodeDecode<T> {
6
+ decode(buffer: Buffer, offset?: number): T;
7
+ encode(src: T, buffer: Buffer, offset?: number): number;
8
+ }
9
+
10
+ const encodeDecode = <T>(layout: Layout<T>): EncodeDecode<T> => {
11
+ const decode = layout.decode.bind(layout);
12
+ const encode = layout.encode.bind(layout);
13
+ return {decode, encode};
14
+ };
15
+
16
+ const bigInt =
17
+ (length: number) =>
18
+ (property?: string): Layout<bigint> => {
19
+ const layout = blob(length, property);
20
+ const {encode, decode} = encodeDecode(layout);
21
+
22
+ const bigIntLayout = layout as Layout<unknown> as Layout<bigint>;
23
+
24
+ bigIntLayout.decode = (buffer: Buffer, offset: number) => {
25
+ const src = decode(buffer, offset);
26
+ return toBigIntLE(Buffer.from(src));
27
+ };
28
+
29
+ bigIntLayout.encode = (bigInt: bigint, buffer: Buffer, offset: number) => {
30
+ const src = toBufferLE(bigInt, length);
31
+ return encode(src, buffer, offset);
32
+ };
33
+
34
+ return bigIntLayout;
35
+ };
36
+
37
+ export const u64 = bigInt(8);
38
+
39
+ export const u128 = bigInt(16);
40
+
41
+ export const u192 = bigInt(24);
42
+
43
+ export const u256 = bigInt(32);
File without changes
File without changes