@solana/web3.js 0.0.0-next

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 (53) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +158 -0
  3. package/lib/index.browser.cjs.js +10062 -0
  4. package/lib/index.browser.cjs.js.map +1 -0
  5. package/lib/index.browser.esm.js +9976 -0
  6. package/lib/index.browser.esm.js.map +1 -0
  7. package/lib/index.cjs.js +9568 -0
  8. package/lib/index.cjs.js.map +1 -0
  9. package/lib/index.d.ts +3311 -0
  10. package/lib/index.esm.js +9479 -0
  11. package/lib/index.esm.js.map +1 -0
  12. package/lib/index.iife.js +30136 -0
  13. package/lib/index.iife.js.map +1 -0
  14. package/lib/index.iife.min.js +40 -0
  15. package/lib/index.iife.min.js.map +1 -0
  16. package/package.json +140 -0
  17. package/src/account.ts +46 -0
  18. package/src/agent-manager.ts +44 -0
  19. package/src/blockhash.ts +4 -0
  20. package/src/bpf-loader-deprecated.ts +5 -0
  21. package/src/bpf-loader.ts +45 -0
  22. package/src/connection.ts +4935 -0
  23. package/src/ed25519-program.ts +157 -0
  24. package/src/epoch-schedule.ts +102 -0
  25. package/src/errors.ts +9 -0
  26. package/src/fee-calculator.ts +16 -0
  27. package/src/index.ts +31 -0
  28. package/src/instruction.ts +58 -0
  29. package/src/keypair.ts +98 -0
  30. package/src/layout.ts +147 -0
  31. package/src/loader.ts +236 -0
  32. package/src/message.ts +271 -0
  33. package/src/nonce-account.ts +78 -0
  34. package/src/publickey.ts +296 -0
  35. package/src/secp256k1-program.ts +229 -0
  36. package/src/stake-program.ts +923 -0
  37. package/src/system-program.ts +1007 -0
  38. package/src/sysvar.ts +37 -0
  39. package/src/timing.ts +23 -0
  40. package/src/transaction.ts +808 -0
  41. package/src/util/assert.ts +8 -0
  42. package/src/util/borsh-schema.ts +38 -0
  43. package/src/util/cluster.ts +31 -0
  44. package/src/util/promise-timeout.ts +14 -0
  45. package/src/util/send-and-confirm-raw-transaction.ts +46 -0
  46. package/src/util/send-and-confirm-transaction.ts +50 -0
  47. package/src/util/shortvec-encoding.ts +28 -0
  48. package/src/util/sleep.ts +4 -0
  49. package/src/util/to-buffer.ts +11 -0
  50. package/src/util/url.ts +18 -0
  51. package/src/validator-info.ts +106 -0
  52. package/src/vote-account.ts +236 -0
  53. package/src/vote-program.ts +413 -0
@@ -0,0 +1,808 @@
1
+ import nacl from 'tweetnacl';
2
+ import bs58 from 'bs58';
3
+ import {Buffer} from 'buffer';
4
+
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';
14
+
15
+ /**
16
+ * Transaction signature as base-58 encoded string
17
+ */
18
+ export type TransactionSignature = string;
19
+
20
+ /**
21
+ * Default (empty) signature
22
+ *
23
+ * Signatures are 64 bytes in length
24
+ */
25
+ const DEFAULT_SIGNATURE = Buffer.alloc(64).fill(0);
26
+
27
+ /**
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
33
+ */
34
+ export const PACKET_DATA_SIZE = 1280 - 40 - 8;
35
+
36
+ const SIGNATURE_LENGTH = 64;
37
+
38
+ /**
39
+ * Account metadata used to define instructions
40
+ */
41
+ export type AccountMeta = {
42
+ /** An account's public key */
43
+ pubkey: PublicKey;
44
+ /** True if an instruction requires a transaction signature matching `pubkey` */
45
+ isSigner: boolean;
46
+ /** True if the `pubkey` can be loaded as a read-write account. */
47
+ isWritable: boolean;
48
+ };
49
+
50
+ /**
51
+ * List of TransactionInstruction object fields that may be initialized at construction
52
+ */
53
+ export type TransactionInstructionCtorFields = {
54
+ keys: Array<AccountMeta>;
55
+ programId: PublicKey;
56
+ data?: Buffer;
57
+ };
58
+
59
+ /**
60
+ * Configuration object for Transaction.serialize()
61
+ */
62
+ export type SerializeConfig = {
63
+ /** Require all transaction signatures be present (default: true) */
64
+ requireAllSignatures?: boolean;
65
+ /** Verify provided signatures (default: true) */
66
+ verifySignatures?: boolean;
67
+ };
68
+
69
+ /**
70
+ * @internal
71
+ */
72
+ export interface TransactionInstructionJSON {
73
+ keys: {
74
+ pubkey: string;
75
+ isSigner: boolean;
76
+ isWritable: boolean;
77
+ }[];
78
+ programId: string;
79
+ data: number[];
80
+ }
81
+
82
+ /**
83
+ * Transaction Instruction class
84
+ */
85
+ export class TransactionInstruction {
86
+ /**
87
+ * Public keys to include in this transaction
88
+ * Boolean represents whether this pubkey needs to sign the transaction
89
+ */
90
+ keys: Array<AccountMeta>;
91
+
92
+ /**
93
+ * Program Id to execute
94
+ */
95
+ programId: PublicKey;
96
+
97
+ /**
98
+ * Program input
99
+ */
100
+ data: Buffer = Buffer.alloc(0);
101
+
102
+ constructor(opts: TransactionInstructionCtorFields) {
103
+ this.programId = opts.programId;
104
+ this.keys = opts.keys;
105
+ if (opts.data) {
106
+ this.data = opts.data;
107
+ }
108
+ }
109
+
110
+ /**
111
+ * @internal
112
+ */
113
+ toJSON(): TransactionInstructionJSON {
114
+ return {
115
+ keys: this.keys.map(({pubkey, isSigner, isWritable}) => ({
116
+ pubkey: pubkey.toJSON(),
117
+ isSigner,
118
+ isWritable,
119
+ })),
120
+ programId: this.programId.toJSON(),
121
+ data: [...this.data],
122
+ };
123
+ }
124
+ }
125
+
126
+ /**
127
+ * Pair of signature and corresponding public key
128
+ */
129
+ export type SignaturePubkeyPair = {
130
+ signature: Buffer | null;
131
+ publicKey: PublicKey;
132
+ };
133
+
134
+ /**
135
+ * List of Transaction object fields that may be initialized at construction
136
+ *
137
+ */
138
+ export type TransactionCtorFields = {
139
+ /** A recent blockhash */
140
+ recentBlockhash?: Blockhash | null;
141
+ /** Optional nonce information used for offline nonce'd transactions */
142
+ nonceInfo?: NonceInformation | null;
143
+ /** The transaction fee payer */
144
+ feePayer?: PublicKey | null;
145
+ /** One or more signatures */
146
+ signatures?: Array<SignaturePubkeyPair>;
147
+ };
148
+
149
+ /**
150
+ * Nonce information to be used to build an offline Transaction.
151
+ */
152
+ export type NonceInformation = {
153
+ /** The current blockhash stored in the nonce */
154
+ nonce: Blockhash;
155
+ /** AdvanceNonceAccount Instruction */
156
+ nonceInstruction: TransactionInstruction;
157
+ };
158
+
159
+ /**
160
+ * @internal
161
+ */
162
+ export interface TransactionJSON {
163
+ recentBlockhash: string | null;
164
+ feePayer: string | null;
165
+ nonceInfo: {
166
+ nonce: string;
167
+ nonceInstruction: TransactionInstructionJSON;
168
+ } | null;
169
+ instructions: TransactionInstructionJSON[];
170
+ signers: string[];
171
+ }
172
+
173
+ /**
174
+ * Transaction class
175
+ */
176
+ export class Transaction {
177
+ /**
178
+ * Signatures for the transaction. Typically created by invoking the
179
+ * `sign()` method
180
+ */
181
+ signatures: Array<SignaturePubkeyPair> = [];
182
+
183
+ /**
184
+ * The first (payer) Transaction signature
185
+ */
186
+ get signature(): Buffer | null {
187
+ if (this.signatures.length > 0) {
188
+ return this.signatures[0].signature;
189
+ }
190
+ return null;
191
+ }
192
+
193
+ /**
194
+ * The transaction fee payer
195
+ */
196
+ feePayer?: PublicKey;
197
+
198
+ /**
199
+ * The instructions to atomically execute
200
+ */
201
+ instructions: Array<TransactionInstruction> = [];
202
+
203
+ /**
204
+ * A recent transaction id. Must be populated by the caller
205
+ */
206
+ recentBlockhash?: Blockhash;
207
+
208
+ /**
209
+ * Optional Nonce information. If populated, transaction will use a durable
210
+ * Nonce hash instead of a recentBlockhash. Must be populated by the caller
211
+ */
212
+ nonceInfo?: NonceInformation;
213
+
214
+ /**
215
+ * @internal
216
+ */
217
+ _message?: Message;
218
+
219
+ /**
220
+ * @internal
221
+ */
222
+ _json?: TransactionJSON;
223
+
224
+ /**
225
+ * Construct an empty Transaction
226
+ */
227
+ constructor(opts?: TransactionCtorFields) {
228
+ opts && Object.assign(this, opts);
229
+ }
230
+
231
+ /**
232
+ * @internal
233
+ */
234
+ toJSON(): TransactionJSON {
235
+ return {
236
+ recentBlockhash: this.recentBlockhash || null,
237
+ feePayer: this.feePayer ? this.feePayer.toJSON() : null,
238
+ nonceInfo: this.nonceInfo
239
+ ? {
240
+ nonce: this.nonceInfo.nonce,
241
+ nonceInstruction: this.nonceInfo.nonceInstruction.toJSON(),
242
+ }
243
+ : null,
244
+ instructions: this.instructions.map(instruction => instruction.toJSON()),
245
+ signers: this.signatures.map(({publicKey}) => {
246
+ return publicKey.toJSON();
247
+ }),
248
+ };
249
+ }
250
+
251
+ /**
252
+ * Add one or more instructions to this Transaction
253
+ */
254
+ add(
255
+ ...items: Array<
256
+ Transaction | TransactionInstruction | TransactionInstructionCtorFields
257
+ >
258
+ ): Transaction {
259
+ if (items.length === 0) {
260
+ throw new Error('No instructions');
261
+ }
262
+
263
+ items.forEach((item: any) => {
264
+ if ('instructions' in item) {
265
+ this.instructions = this.instructions.concat(item.instructions);
266
+ } else if ('data' in item && 'programId' in item && 'keys' in item) {
267
+ this.instructions.push(item);
268
+ } else {
269
+ this.instructions.push(new TransactionInstruction(item));
270
+ }
271
+ });
272
+ return this;
273
+ }
274
+
275
+ /**
276
+ * Compile transaction data
277
+ */
278
+ 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
+ }
285
+ return this._message;
286
+ }
287
+
288
+ const {nonceInfo} = this;
289
+ if (nonceInfo && this.instructions[0] != nonceInfo.nonceInstruction) {
290
+ this.recentBlockhash = nonceInfo.nonce;
291
+ this.instructions.unshift(nonceInfo.nonceInstruction);
292
+ }
293
+ const {recentBlockhash} = this;
294
+ if (!recentBlockhash) {
295
+ throw new Error('Transaction recentBlockhash required');
296
+ }
297
+
298
+ if (this.instructions.length < 1) {
299
+ console.warn('No instructions provided');
300
+ }
301
+
302
+ let feePayer: PublicKey;
303
+ if (this.feePayer) {
304
+ feePayer = this.feePayer;
305
+ } else if (this.signatures.length > 0 && this.signatures[0].publicKey) {
306
+ // Use implicit fee payer
307
+ feePayer = this.signatures[0].publicKey;
308
+ } else {
309
+ throw new Error('Transaction fee payer required');
310
+ }
311
+
312
+ for (let i = 0; i < this.instructions.length; i++) {
313
+ if (this.instructions[i].programId === undefined) {
314
+ throw new Error(
315
+ `Transaction instruction index ${i} has undefined program id`,
316
+ );
317
+ }
318
+ }
319
+
320
+ const programIds: string[] = [];
321
+ const accountMetas: AccountMeta[] = [];
322
+ this.instructions.forEach(instruction => {
323
+ instruction.keys.forEach(accountMeta => {
324
+ accountMetas.push({...accountMeta});
325
+ });
326
+
327
+ const programId = instruction.programId.toString();
328
+ if (!programIds.includes(programId)) {
329
+ programIds.push(programId);
330
+ }
331
+ });
332
+
333
+ // Append programID account metas
334
+ programIds.forEach(programId => {
335
+ accountMetas.push({
336
+ pubkey: new PublicKey(programId),
337
+ isSigner: false,
338
+ isWritable: false,
339
+ });
340
+ });
341
+
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
+ // Cull duplicate account metas
354
+ const uniqueMetas: AccountMeta[] = [];
355
+ accountMetas.forEach(accountMeta => {
356
+ const pubkeyString = accountMeta.pubkey.toString();
357
+ const uniqueIndex = uniqueMetas.findIndex(x => {
358
+ return x.pubkey.toString() === pubkeyString;
359
+ });
360
+ if (uniqueIndex > -1) {
361
+ uniqueMetas[uniqueIndex].isWritable =
362
+ uniqueMetas[uniqueIndex].isWritable || accountMeta.isWritable;
363
+ } else {
364
+ uniqueMetas.push(accountMeta);
365
+ }
366
+ });
367
+
368
+ // Move fee payer to the front
369
+ const feePayerIndex = uniqueMetas.findIndex(x => {
370
+ return x.pubkey.equals(feePayer);
371
+ });
372
+ if (feePayerIndex > -1) {
373
+ const [payerMeta] = uniqueMetas.splice(feePayerIndex, 1);
374
+ payerMeta.isSigner = true;
375
+ payerMeta.isWritable = true;
376
+ uniqueMetas.unshift(payerMeta);
377
+ } else {
378
+ uniqueMetas.unshift({
379
+ pubkey: feePayer,
380
+ isSigner: true,
381
+ isWritable: true,
382
+ });
383
+ }
384
+
385
+ // Disallow unknown signers
386
+ for (const signature of this.signatures) {
387
+ const uniqueIndex = uniqueMetas.findIndex(x => {
388
+ return x.pubkey.equals(signature.publicKey);
389
+ });
390
+ if (uniqueIndex > -1) {
391
+ if (!uniqueMetas[uniqueIndex].isSigner) {
392
+ uniqueMetas[uniqueIndex].isSigner = true;
393
+ console.warn(
394
+ 'Transaction references a signature that is unnecessary, ' +
395
+ 'only the fee payer and instruction signer accounts should sign a transaction. ' +
396
+ 'This behavior is deprecated and will throw an error in the next major version release.',
397
+ );
398
+ }
399
+ } else {
400
+ throw new Error(`unknown signer: ${signature.publicKey.toString()}`);
401
+ }
402
+ }
403
+
404
+ let numRequiredSignatures = 0;
405
+ let numReadonlySignedAccounts = 0;
406
+ let numReadonlyUnsignedAccounts = 0;
407
+
408
+ // Split out signing from non-signing keys and count header values
409
+ const signedKeys: string[] = [];
410
+ const unsignedKeys: string[] = [];
411
+ uniqueMetas.forEach(({pubkey, isSigner, isWritable}) => {
412
+ if (isSigner) {
413
+ signedKeys.push(pubkey.toString());
414
+ numRequiredSignatures += 1;
415
+ if (!isWritable) {
416
+ numReadonlySignedAccounts += 1;
417
+ }
418
+ } else {
419
+ unsignedKeys.push(pubkey.toString());
420
+ if (!isWritable) {
421
+ numReadonlyUnsignedAccounts += 1;
422
+ }
423
+ }
424
+ });
425
+
426
+ const accountKeys = signedKeys.concat(unsignedKeys);
427
+ const instructions: CompiledInstruction[] = this.instructions.map(
428
+ instruction => {
429
+ const {data, programId} = instruction;
430
+ return {
431
+ programIdIndex: accountKeys.indexOf(programId.toString()),
432
+ accounts: instruction.keys.map(meta =>
433
+ accountKeys.indexOf(meta.pubkey.toString()),
434
+ ),
435
+ data: bs58.encode(data),
436
+ };
437
+ },
438
+ );
439
+
440
+ instructions.forEach(instruction => {
441
+ invariant(instruction.programIdIndex >= 0);
442
+ instruction.accounts.forEach(keyIndex => invariant(keyIndex >= 0));
443
+ });
444
+
445
+ return new Message({
446
+ header: {
447
+ numRequiredSignatures,
448
+ numReadonlySignedAccounts,
449
+ numReadonlyUnsignedAccounts,
450
+ },
451
+ accountKeys,
452
+ recentBlockhash,
453
+ instructions,
454
+ });
455
+ }
456
+
457
+ /**
458
+ * @internal
459
+ */
460
+ _compile(): Message {
461
+ const message = this.compileMessage();
462
+ const signedKeys = message.accountKeys.slice(
463
+ 0,
464
+ message.header.numRequiredSignatures,
465
+ );
466
+
467
+ if (this.signatures.length === signedKeys.length) {
468
+ const valid = this.signatures.every((pair, index) => {
469
+ return signedKeys[index].equals(pair.publicKey);
470
+ });
471
+
472
+ if (valid) return message;
473
+ }
474
+
475
+ this.signatures = signedKeys.map(publicKey => ({
476
+ signature: null,
477
+ publicKey,
478
+ }));
479
+
480
+ return message;
481
+ }
482
+
483
+ /**
484
+ * Get a buffer of the Transaction data that need to be covered by signatures
485
+ */
486
+ serializeMessage(): Buffer {
487
+ return this._compile().serialize();
488
+ }
489
+
490
+ /**
491
+ * Get the estimated fee associated with a transaction
492
+ */
493
+ async getEstimatedFee(connection: Connection): Promise<number> {
494
+ return (await connection.getFeeForMessage(this.compileMessage())).value;
495
+ }
496
+
497
+ /**
498
+ * Specify the public keys which will be used to sign the Transaction.
499
+ * The first signer will be used as the transaction fee payer account.
500
+ *
501
+ * Signatures can be added with either `partialSign` or `addSignature`
502
+ *
503
+ * @deprecated Deprecated since v0.84.0. Only the fee payer needs to be
504
+ * specified and it can be set in the Transaction constructor or with the
505
+ * `feePayer` property.
506
+ */
507
+ setSigners(...signers: Array<PublicKey>) {
508
+ if (signers.length === 0) {
509
+ throw new Error('No signers');
510
+ }
511
+
512
+ const seen = new Set();
513
+ this.signatures = signers
514
+ .filter(publicKey => {
515
+ const key = publicKey.toString();
516
+ if (seen.has(key)) {
517
+ return false;
518
+ } else {
519
+ seen.add(key);
520
+ return true;
521
+ }
522
+ })
523
+ .map(publicKey => ({signature: null, publicKey}));
524
+ }
525
+
526
+ /**
527
+ * Sign the Transaction with the specified signers. Multiple signatures may
528
+ * be applied to a Transaction. The first signature is considered "primary"
529
+ * and is used identify and confirm transactions.
530
+ *
531
+ * If the Transaction `feePayer` is not set, the first signer will be used
532
+ * as the transaction fee payer account.
533
+ *
534
+ * Transaction fields should not be modified after the first call to `sign`,
535
+ * as doing so may invalidate the signature and cause the Transaction to be
536
+ * rejected.
537
+ *
538
+ * The Transaction must be assigned a valid `recentBlockhash` before invoking this method
539
+ */
540
+ sign(...signers: Array<Signer>) {
541
+ if (signers.length === 0) {
542
+ throw new Error('No signers');
543
+ }
544
+
545
+ // Dedupe signers
546
+ const seen = new Set();
547
+ const uniqueSigners = [];
548
+ for (const signer of signers) {
549
+ const key = signer.publicKey.toString();
550
+ if (seen.has(key)) {
551
+ continue;
552
+ } else {
553
+ seen.add(key);
554
+ uniqueSigners.push(signer);
555
+ }
556
+ }
557
+
558
+ this.signatures = uniqueSigners.map(signer => ({
559
+ signature: null,
560
+ publicKey: signer.publicKey,
561
+ }));
562
+
563
+ const message = this._compile();
564
+ this._partialSign(message, ...uniqueSigners);
565
+ this._verifySignatures(message.serialize(), true);
566
+ }
567
+
568
+ /**
569
+ * Partially sign a transaction with the specified accounts. All accounts must
570
+ * correspond to either the fee payer or a signer account in the transaction
571
+ * instructions.
572
+ *
573
+ * All the caveats from the `sign` method apply to `partialSign`
574
+ */
575
+ partialSign(...signers: Array<Signer>) {
576
+ if (signers.length === 0) {
577
+ throw new Error('No signers');
578
+ }
579
+
580
+ // Dedupe signers
581
+ const seen = new Set();
582
+ const uniqueSigners = [];
583
+ for (const signer of signers) {
584
+ const key = signer.publicKey.toString();
585
+ if (seen.has(key)) {
586
+ continue;
587
+ } else {
588
+ seen.add(key);
589
+ uniqueSigners.push(signer);
590
+ }
591
+ }
592
+
593
+ const message = this._compile();
594
+ this._partialSign(message, ...uniqueSigners);
595
+ }
596
+
597
+ /**
598
+ * @internal
599
+ */
600
+ _partialSign(message: Message, ...signers: Array<Signer>) {
601
+ const signData = message.serialize();
602
+ signers.forEach(signer => {
603
+ const signature = nacl.sign.detached(signData, signer.secretKey);
604
+ this._addSignature(signer.publicKey, toBuffer(signature));
605
+ });
606
+ }
607
+
608
+ /**
609
+ * Add an externally created signature to a transaction. The public key
610
+ * must correspond to either the fee payer or a signer account in the transaction
611
+ * instructions.
612
+ */
613
+ addSignature(pubkey: PublicKey, signature: Buffer) {
614
+ this._compile(); // Ensure signatures array is populated
615
+ this._addSignature(pubkey, signature);
616
+ }
617
+
618
+ /**
619
+ * @internal
620
+ */
621
+ _addSignature(pubkey: PublicKey, signature: Buffer) {
622
+ invariant(signature.length === 64);
623
+
624
+ const index = this.signatures.findIndex(sigpair =>
625
+ pubkey.equals(sigpair.publicKey),
626
+ );
627
+ if (index < 0) {
628
+ throw new Error(`unknown signer: ${pubkey.toString()}`);
629
+ }
630
+
631
+ this.signatures[index].signature = Buffer.from(signature);
632
+ }
633
+
634
+ /**
635
+ * Verify signatures of a complete, signed Transaction
636
+ */
637
+ verifySignatures(): boolean {
638
+ return this._verifySignatures(this.serializeMessage(), true);
639
+ }
640
+
641
+ /**
642
+ * @internal
643
+ */
644
+ _verifySignatures(signData: Buffer, requireAllSignatures: boolean): boolean {
645
+ for (const {signature, publicKey} of this.signatures) {
646
+ if (signature === null) {
647
+ if (requireAllSignatures) {
648
+ return false;
649
+ }
650
+ } else {
651
+ if (
652
+ !nacl.sign.detached.verify(signData, signature, publicKey.toBuffer())
653
+ ) {
654
+ return false;
655
+ }
656
+ }
657
+ }
658
+ return true;
659
+ }
660
+
661
+ /**
662
+ * Serialize the Transaction in the wire format.
663
+ */
664
+ serialize(config?: SerializeConfig): Buffer {
665
+ const {requireAllSignatures, verifySignatures} = Object.assign(
666
+ {requireAllSignatures: true, verifySignatures: true},
667
+ config,
668
+ );
669
+
670
+ const signData = this.serializeMessage();
671
+ if (
672
+ verifySignatures &&
673
+ !this._verifySignatures(signData, requireAllSignatures)
674
+ ) {
675
+ throw new Error('Signature verification failed');
676
+ }
677
+
678
+ return this._serialize(signData);
679
+ }
680
+
681
+ /**
682
+ * @internal
683
+ */
684
+ _serialize(signData: Buffer): Buffer {
685
+ const {signatures} = this;
686
+ const signatureCount: number[] = [];
687
+ shortvec.encodeLength(signatureCount, signatures.length);
688
+ const transactionLength =
689
+ signatureCount.length + signatures.length * 64 + signData.length;
690
+ const wireTransaction = Buffer.alloc(transactionLength);
691
+ invariant(signatures.length < 256);
692
+ Buffer.from(signatureCount).copy(wireTransaction, 0);
693
+ signatures.forEach(({signature}, index) => {
694
+ if (signature !== null) {
695
+ invariant(signature.length === 64, `signature has invalid length`);
696
+ Buffer.from(signature).copy(
697
+ wireTransaction,
698
+ signatureCount.length + index * 64,
699
+ );
700
+ }
701
+ });
702
+ signData.copy(
703
+ wireTransaction,
704
+ signatureCount.length + signatures.length * 64,
705
+ );
706
+ invariant(
707
+ wireTransaction.length <= PACKET_DATA_SIZE,
708
+ `Transaction too large: ${wireTransaction.length} > ${PACKET_DATA_SIZE}`,
709
+ );
710
+ return wireTransaction;
711
+ }
712
+
713
+ /**
714
+ * Deprecated method
715
+ * @internal
716
+ */
717
+ get keys(): Array<PublicKey> {
718
+ invariant(this.instructions.length === 1);
719
+ return this.instructions[0].keys.map(keyObj => keyObj.pubkey);
720
+ }
721
+
722
+ /**
723
+ * Deprecated method
724
+ * @internal
725
+ */
726
+ get programId(): PublicKey {
727
+ invariant(this.instructions.length === 1);
728
+ return this.instructions[0].programId;
729
+ }
730
+
731
+ /**
732
+ * Deprecated method
733
+ * @internal
734
+ */
735
+ get data(): Buffer {
736
+ invariant(this.instructions.length === 1);
737
+ return this.instructions[0].data;
738
+ }
739
+
740
+ /**
741
+ * Parse a wire transaction into a Transaction object.
742
+ */
743
+ static from(buffer: Buffer | Uint8Array | Array<number>): Transaction {
744
+ // Slice up wire data
745
+ let byteArray = [...buffer];
746
+
747
+ const signatureCount = shortvec.decodeLength(byteArray);
748
+ let signatures = [];
749
+ for (let i = 0; i < signatureCount; i++) {
750
+ const signature = byteArray.slice(0, SIGNATURE_LENGTH);
751
+ byteArray = byteArray.slice(SIGNATURE_LENGTH);
752
+ signatures.push(bs58.encode(Buffer.from(signature)));
753
+ }
754
+
755
+ return Transaction.populate(Message.from(byteArray), signatures);
756
+ }
757
+
758
+ /**
759
+ * Populate Transaction object from message and signatures
760
+ */
761
+ static populate(
762
+ message: Message,
763
+ signatures: Array<string> = [],
764
+ ): Transaction {
765
+ const transaction = new Transaction();
766
+ transaction.recentBlockhash = message.recentBlockhash;
767
+ if (message.header.numRequiredSignatures > 0) {
768
+ transaction.feePayer = message.accountKeys[0];
769
+ }
770
+ signatures.forEach((signature, index) => {
771
+ const sigPubkeyPair = {
772
+ signature:
773
+ signature == bs58.encode(DEFAULT_SIGNATURE)
774
+ ? null
775
+ : bs58.decode(signature),
776
+ publicKey: message.accountKeys[index],
777
+ };
778
+ transaction.signatures.push(sigPubkeyPair);
779
+ });
780
+
781
+ message.instructions.forEach(instruction => {
782
+ const keys = instruction.accounts.map(account => {
783
+ const pubkey = message.accountKeys[account];
784
+ return {
785
+ pubkey,
786
+ isSigner:
787
+ transaction.signatures.some(
788
+ keyObj => keyObj.publicKey.toString() === pubkey.toString(),
789
+ ) || message.isAccountSigner(account),
790
+ isWritable: message.isAccountWritable(account),
791
+ };
792
+ });
793
+
794
+ transaction.instructions.push(
795
+ new TransactionInstruction({
796
+ keys,
797
+ programId: message.accountKeys[instruction.programIdIndex],
798
+ data: bs58.decode(instruction.data),
799
+ }),
800
+ );
801
+ });
802
+
803
+ transaction._message = message;
804
+ transaction._json = transaction.toJSON();
805
+
806
+ return transaction;
807
+ }
808
+ }