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