@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.
- package/README.md +24 -25
- package/lib/index.browser.cjs.js +4583 -4238
- package/lib/index.browser.cjs.js.map +1 -1
- package/lib/index.browser.esm.js +4565 -4238
- package/lib/index.browser.esm.js.map +1 -1
- package/lib/index.cjs.js +7072 -3604
- package/lib/index.cjs.js.map +1 -1
- package/lib/index.d.ts +3516 -2420
- package/lib/index.esm.js +7046 -3601
- package/lib/index.esm.js.map +1 -1
- package/lib/index.iife.js +22171 -27053
- package/lib/index.iife.js.map +1 -1
- package/lib/index.iife.min.js +8 -33
- package/lib/index.iife.min.js.map +1 -1
- package/lib/index.native.js +10407 -0
- package/lib/index.native.js.map +1 -0
- package/package.json +36 -36
- package/src/__forks__/browser/fetch-impl.ts +4 -0
- package/src/__forks__/react-native/fetch-impl.ts +4 -0
- package/src/account-data.ts +39 -0
- package/src/account.ts +20 -11
- package/src/bpf-loader.ts +2 -2
- package/src/connection.ts +2303 -635
- package/src/epoch-schedule.ts +1 -1
- package/src/errors.ts +41 -0
- package/src/fee-calculator.ts +2 -0
- package/src/fetch-impl.ts +13 -0
- package/src/index.ts +3 -10
- package/src/keypair.ts +20 -25
- package/src/layout.ts +45 -4
- package/src/loader.ts +3 -3
- package/src/message/account-keys.ts +79 -0
- package/src/message/compiled-keys.ts +165 -0
- package/src/message/index.ts +47 -0
- package/src/{message.ts → message/legacy.ts} +95 -40
- package/src/message/v0.ts +496 -0
- package/src/message/versioned.ts +36 -0
- package/src/nonce-account.ts +8 -4
- package/src/programs/address-lookup-table/index.ts +435 -0
- package/src/programs/address-lookup-table/state.ts +84 -0
- package/src/programs/compute-budget.ts +281 -0
- package/src/{ed25519-program.ts → programs/ed25519.ts} +6 -6
- package/src/programs/index.ts +7 -0
- package/src/{secp256k1-program.ts → programs/secp256k1.ts} +15 -16
- package/src/{stake-program.ts → programs/stake.ts} +7 -7
- package/src/{system-program.ts → programs/system.ts} +55 -18
- package/src/{vote-program.ts → programs/vote.ts} +137 -9
- package/src/publickey.ts +37 -79
- package/src/transaction/constants.ts +12 -0
- package/src/transaction/expiry-custom-errors.ts +48 -0
- package/src/transaction/index.ts +5 -0
- package/src/{transaction.ts → transaction/legacy.ts} +162 -67
- package/src/transaction/message.ts +140 -0
- package/src/transaction/versioned.ts +126 -0
- package/src/{util → utils}/assert.ts +0 -0
- package/src/utils/bigint.ts +43 -0
- package/src/{util → utils}/borsh-schema.ts +0 -0
- package/src/{util → utils}/cluster.ts +0 -0
- package/src/utils/ed25519.ts +46 -0
- package/src/utils/index.ts +5 -0
- package/src/utils/makeWebsocketUrl.ts +26 -0
- package/src/{util → utils}/promise-timeout.ts +0 -0
- package/src/utils/secp256k1.ts +18 -0
- package/src/utils/send-and-confirm-raw-transaction.ts +105 -0
- package/src/utils/send-and-confirm-transaction.ts +98 -0
- package/src/{util → utils}/shortvec-encoding.ts +0 -0
- package/src/{util → utils}/sleep.ts +0 -0
- package/src/{util → utils}/to-buffer.ts +0 -0
- package/src/validator-info.ts +4 -6
- package/src/vote-account.ts +1 -1
- package/src/agent-manager.ts +0 -44
- package/src/util/send-and-confirm-raw-transaction.ts +0 -46
- package/src/util/send-and-confirm-transaction.ts +0 -50
- 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 {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
import type {
|
|
13
|
-
import type {
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
export const enum TransactionStatus {
|
|
22
|
+
BLOCKHEIGHT_EXCEEDED,
|
|
23
|
+
PROCESSED,
|
|
24
|
+
TIMED_OUT,
|
|
25
|
+
NONCE_INVALID,
|
|
26
|
+
}
|
|
26
27
|
|
|
27
28
|
/**
|
|
28
|
-
*
|
|
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
|
-
|
|
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
|
|
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(
|
|
228
|
-
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 (
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
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
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
this.
|
|
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 (
|
|
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 <
|
|
313
|
-
if (
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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 =
|
|
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(
|
|
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,
|
|
751
|
-
byteArray = byteArray.slice(
|
|
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
|