@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 +7 -3
- package/dist/index.browser.cjs +221 -156
- package/dist/index.browser.cjs.map +1 -1
- package/dist/index.browser.js +221 -160
- package/dist/index.browser.js.map +1 -1
- package/dist/index.native.js +223 -160
- package/dist/index.native.js.map +1 -1
- package/dist/index.node.cjs +221 -156
- package/dist/index.node.cjs.map +1 -1
- package/dist/index.node.js +223 -160
- package/dist/index.node.js.map +1 -1
- package/dist/types/accounts.d.ts.map +1 -1
- package/dist/types/blockhash.d.ts +1 -4
- package/dist/types/blockhash.d.ts.map +1 -1
- package/dist/types/decompile-transaction.d.ts +9 -1
- package/dist/types/decompile-transaction.d.ts.map +1 -1
- package/dist/types/durable-nonce.d.ts.map +1 -1
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/instructions.d.ts +2 -0
- package/dist/types/instructions.d.ts.map +1 -1
- package/dist/types/serializers/message.d.ts.map +1 -1
- package/dist/types/serializers/transaction-version.d.ts.map +1 -1
- package/dist/types/serializers/transaction.d.ts +5 -2
- package/dist/types/serializers/transaction.d.ts.map +1 -1
- package/dist/types/signatures.d.ts.map +1 -1
- package/package.json +18 -39
- package/dist/index.development.js +0 -1628
- package/dist/index.development.js.map +0 -1
- package/dist/index.production.min.js +0 -32
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
|
package/dist/index.browser.cjs
CHANGED
|
@@ -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
|
|
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
|
|
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,
|
|
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: [
|
|
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
|
|
237
|
-
|
|
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
|
|
241
|
-
|
|
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
|
|
308
|
-
|
|
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
|
|
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(
|
|
686
|
-
|
|
687
|
-
|
|
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
|
-
|
|
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(
|
|
902
|
+
function getTransactionDecoder(config) {
|
|
846
903
|
return codecsCore.mapDecoder(
|
|
847
904
|
getCompiledTransactionDecoder(),
|
|
848
|
-
(compiledTransaction) => decompileTransaction(compiledTransaction,
|
|
905
|
+
(compiledTransaction) => decompileTransaction(compiledTransaction, config)
|
|
849
906
|
);
|
|
850
907
|
}
|
|
851
|
-
function getTransactionCodec(
|
|
852
|
-
return codecsCore.combineCodec(getTransactionEncoder(), getTransactionDecoder(
|
|
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
|
|
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
|
-
|
|
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.
|
|
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;
|