@usearete/adapter-kit 0.15.0 → 0.17.0
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 +38 -2
- package/dist/index.cjs +557 -80
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +132 -9
- package/dist/index.js +545 -83
- package/dist/index.js.map +1 -1
- package/package.json +7 -6
package/dist/index.js
CHANGED
|
@@ -1,5 +1,110 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { resolveTransactionBuildOptions, TransactionTransportError } from '@usearete/sdk';
|
|
1
|
+
import { getTransactionVersionDecoder, getTransactionEncoder, getTransactionDecoder, AccountRole, address, getTransactionSize, getBase64EncodedWireTransaction, compileTransaction, signTransactionMessageWithSigners, assertIsTransactionWithBlockhashLifetime, getSignatureFromTransaction, sendAndConfirmTransactionFactory, isSolanaError, SOLANA_ERROR__JSON_RPC__SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE, createNoopSigner, getBase64Decoder, addSignersToTransactionMessage, setTransactionMessageConfig, createTransactionMessage, setTransactionMessageFeePayerSigner, setTransactionMessageLifetimeUsingBlockhash, appendTransactionMessageInstructions, setTransactionMessageComputeUnitLimit, setTransactionMessageComputeUnitPrice, setTransactionMessageHeapSize, setTransactionMessageLoadedAccountsDataSizeLimit } from '@solana/kit';
|
|
2
|
+
import { resolveTransactionBuildOptions, TransactionTransportError, toWireResourceOptions } from '@usearete/sdk';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Wallet Standard → kit signer bridge.
|
|
6
|
+
*
|
|
7
|
+
* The handoff is byte-oriented on purpose: the app picks the transaction
|
|
8
|
+
* format, the wallet decodes the version out of the bytes it is given, and
|
|
9
|
+
* Arete forwards whatever came back without converting versions. Nothing
|
|
10
|
+
* here routes a transaction through a web3.js 1.x wrapper, which cannot
|
|
11
|
+
* represent V1 at all.
|
|
12
|
+
*
|
|
13
|
+
* The capability check is a local fact about the wallet, not proof the
|
|
14
|
+
* cluster has activated a version: the account must advertise
|
|
15
|
+
* `solana:signTransaction`, and *that* feature must advertise the version
|
|
16
|
+
* being signed. Sign-and-send support is never read as sign-only support —
|
|
17
|
+
* a wallet that can only sign-and-send cannot hand bytes back for Arete to
|
|
18
|
+
* relay.
|
|
19
|
+
*
|
|
20
|
+
* As of Kit 8, the upstream Wallet Standard `signTransaction` feature still
|
|
21
|
+
* types `supportedTransactionVersions` as `legacy | 0`. The interfaces below
|
|
22
|
+
* are therefore declared structurally rather than imported, so a wallet that
|
|
23
|
+
* does advertise `1` is usable without a cast and one that does not is
|
|
24
|
+
* refused before it is ever prompted.
|
|
25
|
+
*/
|
|
26
|
+
/** The feature name a sign-only handoff requires. */
|
|
27
|
+
const SOLANA_SIGN_TRANSACTION = 'solana:signTransaction';
|
|
28
|
+
/** The feature name that is explicitly *not* evidence of sign-only support. */
|
|
29
|
+
const SOLANA_SIGN_AND_SEND_TRANSACTION = 'solana:signAndSendTransaction';
|
|
30
|
+
/** Typed refusal from the Wallet Standard bridge. */
|
|
31
|
+
class WalletStandardSignerError extends Error {
|
|
32
|
+
constructor(code, message) {
|
|
33
|
+
super(message);
|
|
34
|
+
this.name = 'WalletStandardSignerError';
|
|
35
|
+
this.code = code;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function readSignTransactionFeature(config) {
|
|
39
|
+
if (!config.account.features.includes(SOLANA_SIGN_TRANSACTION)) {
|
|
40
|
+
const signAndSendOnly = config.account.features.includes(SOLANA_SIGN_AND_SEND_TRANSACTION);
|
|
41
|
+
throw new WalletStandardSignerError('sign_transaction_unsupported', `Account ${config.account.address} does not support '${SOLANA_SIGN_TRANSACTION}'`
|
|
42
|
+
+ (signAndSendOnly
|
|
43
|
+
? `; it advertises only '${SOLANA_SIGN_AND_SEND_TRANSACTION}', which cannot return `
|
|
44
|
+
+ 'signed bytes for Arete to relay'
|
|
45
|
+
: ''));
|
|
46
|
+
}
|
|
47
|
+
const feature = config.wallet.features[SOLANA_SIGN_TRANSACTION];
|
|
48
|
+
if (!feature || typeof feature.signTransaction !== 'function') {
|
|
49
|
+
throw new WalletStandardSignerError('sign_transaction_unsupported', `Wallet does not implement the '${SOLANA_SIGN_TRANSACTION}' feature`);
|
|
50
|
+
}
|
|
51
|
+
return feature;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* A kit {@link TransactionSigner} backed by a Wallet Standard account.
|
|
55
|
+
*
|
|
56
|
+
* The feature is resolved once, at creation, so a wallet that cannot sign at
|
|
57
|
+
* all fails before any transaction is built. The per-version check happens
|
|
58
|
+
* per transaction, against the version decoded from the bytes about to be
|
|
59
|
+
* handed over — so a v0-only wallet refuses a V1 transaction without ever
|
|
60
|
+
* showing the user a prompt.
|
|
61
|
+
*/
|
|
62
|
+
function createWalletStandardSigner(config) {
|
|
63
|
+
const feature = readSignTransactionFeature(config);
|
|
64
|
+
const address = config.account.address;
|
|
65
|
+
return {
|
|
66
|
+
address,
|
|
67
|
+
async signTransactions(transactions) {
|
|
68
|
+
if (transactions.length === 0)
|
|
69
|
+
return [];
|
|
70
|
+
const inputs = transactions.map((transaction) => {
|
|
71
|
+
const version = getTransactionVersionDecoder().decode(transaction.messageBytes);
|
|
72
|
+
if (!feature.supportedTransactionVersions.includes(version)) {
|
|
73
|
+
throw new WalletStandardSignerError('transaction_version_unsupported', `Wallet does not support signing transaction version ${JSON.stringify(version)} `
|
|
74
|
+
+ `(supported: ${feature.supportedTransactionVersions
|
|
75
|
+
.map((supported) => JSON.stringify(supported))
|
|
76
|
+
.join(', ')})`);
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
account: config.account,
|
|
80
|
+
chain: config.chain,
|
|
81
|
+
transaction: getTransactionEncoder().encode(transaction),
|
|
82
|
+
};
|
|
83
|
+
});
|
|
84
|
+
const outputs = await feature.signTransaction(...inputs);
|
|
85
|
+
if (outputs.length !== transactions.length) {
|
|
86
|
+
throw new WalletStandardSignerError('wallet_returned_invalid_transaction', `Wallet returned ${outputs.length} signed transactions for ${transactions.length} `
|
|
87
|
+
+ 'inputs');
|
|
88
|
+
}
|
|
89
|
+
return outputs.map((output, index) => {
|
|
90
|
+
const returned = getTransactionDecoder().decode(output.signedTransaction);
|
|
91
|
+
// A wallet may add signatures; it may not rewrite the message the
|
|
92
|
+
// app compiled. Anything else and the bytes Arete would relay are
|
|
93
|
+
// not the bytes the caller approved.
|
|
94
|
+
const original = transactions[index].messageBytes;
|
|
95
|
+
if (returned.messageBytes.length !== original.length
|
|
96
|
+
|| returned.messageBytes.some((byte, offset) => byte !== original[offset])) {
|
|
97
|
+
throw new WalletStandardSignerError('wallet_returned_invalid_transaction', 'Wallet returned a transaction whose message differs from the one it was given');
|
|
98
|
+
}
|
|
99
|
+
const signature = returned.signatures[address];
|
|
100
|
+
if (!signature) {
|
|
101
|
+
throw new WalletStandardSignerError('wallet_returned_invalid_transaction', `Wallet returned no signature for ${address}`);
|
|
102
|
+
}
|
|
103
|
+
return { [address]: signature };
|
|
104
|
+
});
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
}
|
|
3
108
|
|
|
4
109
|
/**
|
|
5
110
|
* @usearete/adapter-kit
|
|
@@ -12,13 +117,58 @@ import { resolveTransactionBuildOptions, TransactionTransportError } from '@usea
|
|
|
12
117
|
* sending, and confirmation.
|
|
13
118
|
*/
|
|
14
119
|
/**
|
|
15
|
-
* Kit
|
|
16
|
-
*
|
|
120
|
+
* Kit 8 is the first release that constructs transaction V1 messages and
|
|
121
|
+
* carries the resource budget as a typed message config, so this adapter
|
|
122
|
+
* builds every version the contract defines and honours every typed
|
|
123
|
+
* resource option.
|
|
17
124
|
*/
|
|
18
125
|
const CAPABILITY = {
|
|
19
|
-
supportedTransactionVersions: [0],
|
|
20
|
-
supportedResourceOptions: [
|
|
126
|
+
supportedTransactionVersions: ['legacy', 0, 1],
|
|
127
|
+
supportedResourceOptions: [
|
|
128
|
+
'computeUnitLimit',
|
|
129
|
+
'loadedAccountsDataSizeLimit',
|
|
130
|
+
'heapSize',
|
|
131
|
+
'priorityFeeLamports',
|
|
132
|
+
'computeUnitPriceMicroLamports',
|
|
133
|
+
],
|
|
21
134
|
};
|
|
135
|
+
/** Wire ceiling for legacy and v0 transactions, in bytes (one UDP packet). */
|
|
136
|
+
const LEGACY_TRANSACTION_SIZE_LIMIT = 1232;
|
|
137
|
+
/** Wire ceiling for V1 transactions, in bytes (SIMD-0385). */
|
|
138
|
+
const V1_TRANSACTION_SIZE_LIMIT = 4096;
|
|
139
|
+
/** Structural V1 caps enforced before a signer is reached (SIMD-0385). */
|
|
140
|
+
const V1_MAX_SIGNATURES = 12;
|
|
141
|
+
const V1_MAX_ACCOUNTS = 64;
|
|
142
|
+
const V1_MAX_INSTRUCTIONS = 64;
|
|
143
|
+
/**
|
|
144
|
+
* Per-transaction compute-unit maximum. Also the limit a *provisional* V1
|
|
145
|
+
* message declares for a budget the caller omitted: SIMD-0385 makes an unset
|
|
146
|
+
* V1 config field request the **minimum**, not a default, so a provisional
|
|
147
|
+
* message carrying nothing could never produce an ordinary successful
|
|
148
|
+
* execution estimate.
|
|
149
|
+
*/
|
|
150
|
+
const MAX_COMPUTE_UNIT_LIMIT = 1400000;
|
|
151
|
+
/** Loaded-account-data maximum (64 MiB), used the same way. */
|
|
152
|
+
const MAX_LOADED_ACCOUNTS_DATA_SIZE = 64 * 1024 * 1024;
|
|
153
|
+
/**
|
|
154
|
+
* Headroom added to measured compute consumption, in percent: a simulation
|
|
155
|
+
* is one slot's view of the chain and the transaction that lands may take a
|
|
156
|
+
* slightly different branch.
|
|
157
|
+
*/
|
|
158
|
+
const ESTIMATED_COMPUTE_UNIT_HEADROOM_PERCENT = 20n;
|
|
159
|
+
/**
|
|
160
|
+
* The runtime accounts for loaded data in pages of this size, so an
|
|
161
|
+
* estimated data budget is rounded up to a whole page plus one page of
|
|
162
|
+
* headroom for account growth between simulation and execution.
|
|
163
|
+
*/
|
|
164
|
+
const LOADED_ACCOUNTS_DATA_PAGE_BYTES = 32 * 1024;
|
|
165
|
+
const COMPUTE_BUDGET_PROGRAM_ADDRESS = 'ComputeBudget111111111111111111111111111111';
|
|
166
|
+
/** Option keys that would carry address lookup tables. */
|
|
167
|
+
const LOOKUP_TABLE_OPTION_KEYS = [
|
|
168
|
+
'addressLookupTables',
|
|
169
|
+
'addressLookupTableAccounts',
|
|
170
|
+
'lookupTables',
|
|
171
|
+
];
|
|
22
172
|
/** Structured adapter error consumed by the Arete transaction outcome APIs. */
|
|
23
173
|
class KitTransactionExecutionError extends Error {
|
|
24
174
|
constructor(outcome) {
|
|
@@ -91,14 +241,72 @@ function resolveTransport(selection, rpc, rpcSubscriptions, context) {
|
|
|
91
241
|
return { kind: 'direct', rpc, rpcSubscriptions };
|
|
92
242
|
throw new Error('No transaction transport is available; connect through Arete or configure direct RPC');
|
|
93
243
|
}
|
|
94
|
-
|
|
95
|
-
|
|
244
|
+
/**
|
|
245
|
+
* Report, never adopt, a relay signature that is not the one signed here.
|
|
246
|
+
*
|
|
247
|
+
* The adapter signs the final bytes and derives the signature from them, so
|
|
248
|
+
* the local one is authoritative. Reconciling an echoed signature would
|
|
249
|
+
* poll a transaction this adapter never submitted: it can report another
|
|
250
|
+
* transaction's status as this one's, or never confirm the one that went
|
|
251
|
+
* out.
|
|
252
|
+
*/
|
|
253
|
+
function reportSignatureMismatch(local, echoed) {
|
|
254
|
+
if (echoed && echoed !== local) {
|
|
255
|
+
// eslint-disable-next-line no-console -- the adapter has no logger, and
|
|
256
|
+
// this runs after the transaction may already be on the wire, so it
|
|
257
|
+
// must not interrupt the signature-bearing outcome.
|
|
258
|
+
console.warn(`Arete relay reported signature ${echoed} for a transaction signed as ${local}; `
|
|
259
|
+
+ 'the locally derived signature is authoritative and is the one being reconciled');
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
/** Thrown by {@link withDeadline} when the shared deadline passes first. */
|
|
263
|
+
class DeadlineExpiredError extends Error {
|
|
264
|
+
constructor() {
|
|
265
|
+
super('The configured confirmation deadline expired');
|
|
266
|
+
this.name = 'DeadlineExpiredError';
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
const DEADLINE_EXPIRED = Symbol('deadline-expired');
|
|
270
|
+
/**
|
|
271
|
+
* Await `work`, or give up at `deadline`.
|
|
272
|
+
*
|
|
273
|
+
* A racing timer rather than an abort signal: `TransactionTransport` takes
|
|
274
|
+
* none, and no HTTP client under it imposes a request timeout, so a backend
|
|
275
|
+
* that stops answering would otherwise hold the caller forever. The
|
|
276
|
+
* abandoned request is left with a no-op rejection handler — it may still be
|
|
277
|
+
* in flight, which is exactly why the outcome is *unknown* rather than
|
|
278
|
+
* failed.
|
|
279
|
+
*/
|
|
280
|
+
async function withDeadline(deadline, work) {
|
|
281
|
+
work.catch(() => { });
|
|
282
|
+
let timer;
|
|
283
|
+
// Executor form, not `Promise.withResolvers`: that landed in Node 22 and
|
|
284
|
+
// this package's floor is the Node 20.18 that Kit 8 requires.
|
|
285
|
+
const expiry = new Promise((resolve) => {
|
|
286
|
+
timer = setTimeout(() => resolve(DEADLINE_EXPIRED), Math.max(deadline - Date.now(), 0));
|
|
287
|
+
});
|
|
288
|
+
try {
|
|
289
|
+
const result = await Promise.race([work, expiry]);
|
|
290
|
+
if (result === DEADLINE_EXPIRED)
|
|
291
|
+
throw new DeadlineExpiredError();
|
|
292
|
+
return result;
|
|
293
|
+
}
|
|
294
|
+
finally {
|
|
295
|
+
clearTimeout(timer);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Poll until the signature reaches `commitment`, its lifetime expires, or
|
|
300
|
+
* `deadline` passes — including while a request is in flight. Never
|
|
301
|
+
* resubmits, and only ever polls the signature the caller signed.
|
|
302
|
+
*/
|
|
303
|
+
async function pollAreteStatus(transport, signature, commitment, lastValidBlockHeight, deadline, options) {
|
|
96
304
|
const interval = options?.statusPollIntervalMs ?? 500;
|
|
97
305
|
let emptyStatusPolls = 0;
|
|
98
306
|
while (Date.now() <= deadline) {
|
|
99
|
-
const status = await transport.getSignatureStatus(signature, {
|
|
307
|
+
const status = await withDeadline(deadline, transport.getSignatureStatus(signature, {
|
|
100
308
|
commitment, searchTransactionHistory: true,
|
|
101
|
-
});
|
|
309
|
+
}));
|
|
102
310
|
if (status?.err) {
|
|
103
311
|
throw new KitTransactionExecutionError({
|
|
104
312
|
status: 'chain-failed', phase: 'confirmation', signature,
|
|
@@ -108,7 +316,8 @@ async function pollAreteStatus(transport, signature, commitment, lastValidBlockH
|
|
|
108
316
|
if (status && hasReachedCommitment(status.confirmationStatus, commitment)) {
|
|
109
317
|
return { signature, slot: status.slot === null ? undefined : Number(status.slot) };
|
|
110
318
|
}
|
|
111
|
-
if (await transport.getBlockHeight({ commitment })
|
|
319
|
+
if (await withDeadline(deadline, transport.getBlockHeight({ commitment }))
|
|
320
|
+
> lastValidBlockHeight) {
|
|
112
321
|
throw new KitTransactionExecutionError({
|
|
113
322
|
status: 'submitted-unknown', phase: 'confirmation', signature,
|
|
114
323
|
slot: status?.slot === null ? undefined : Number(status?.slot),
|
|
@@ -116,7 +325,8 @@ async function pollAreteStatus(transport, signature, commitment, lastValidBlockH
|
|
|
116
325
|
});
|
|
117
326
|
}
|
|
118
327
|
emptyStatusPolls = status ? 0 : emptyStatusPolls + 1;
|
|
119
|
-
const
|
|
328
|
+
const backoffMs = Math.min(interval * (2 ** Math.min(emptyStatusPolls, 3)), 4000);
|
|
329
|
+
const delayMs = Math.min(backoffMs, Math.max(deadline - Date.now(), 0));
|
|
120
330
|
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
121
331
|
}
|
|
122
332
|
throw new KitTransactionExecutionError({
|
|
@@ -174,7 +384,7 @@ function fromAccountRole(role) {
|
|
|
174
384
|
return { isSigner: false, isWritable: false };
|
|
175
385
|
}
|
|
176
386
|
}
|
|
177
|
-
/** Convert an Arete BuiltInstruction to a kit
|
|
387
|
+
/** Convert an Arete BuiltInstruction to a kit Instruction. */
|
|
178
388
|
function toKitInstruction(ix) {
|
|
179
389
|
const accounts = ix.keys.map((k) => ({
|
|
180
390
|
address: address(k.pubkey),
|
|
@@ -186,7 +396,7 @@ function toKitInstruction(ix) {
|
|
|
186
396
|
data: ix.data,
|
|
187
397
|
};
|
|
188
398
|
}
|
|
189
|
-
/** Convert a kit
|
|
399
|
+
/** Convert a kit Instruction to an Arete BuiltInstruction. */
|
|
190
400
|
function fromKitInstruction(ix) {
|
|
191
401
|
return {
|
|
192
402
|
programId: ix.programAddress,
|
|
@@ -197,6 +407,232 @@ function fromKitInstruction(ix) {
|
|
|
197
407
|
data: ix.data ? new Uint8Array(ix.data) : new Uint8Array(0),
|
|
198
408
|
};
|
|
199
409
|
}
|
|
410
|
+
// ---------------------------------------------------------------------------
|
|
411
|
+
// One version/configuration planner, shared by send and unsigned inspection
|
|
412
|
+
// ---------------------------------------------------------------------------
|
|
413
|
+
/**
|
|
414
|
+
* Refuse the inputs the typed configuration owns.
|
|
415
|
+
*
|
|
416
|
+
* A hand-built `ComputeBudget` instruction is not an effective V1
|
|
417
|
+
* configuration — the runtime reads V1 budgets from the message config — and
|
|
418
|
+
* silently accepting one would apply a budget on legacy/v0 and none on V1
|
|
419
|
+
* from the same caller code. Address lookup tables have no V1 encoding at
|
|
420
|
+
* all, and this adapter compiles none for any version.
|
|
421
|
+
*/
|
|
422
|
+
function assertBuildableInputs(instructions, options, version) {
|
|
423
|
+
if (instructions.length === 0) {
|
|
424
|
+
throw new Error('A transaction requires at least one instruction');
|
|
425
|
+
}
|
|
426
|
+
if (instructions.some((ix) => ix.programId === COMPUTE_BUDGET_PROGRAM_ADDRESS)) {
|
|
427
|
+
throw new Error('Caller-supplied ComputeBudget instructions are rejected: request compute units, heap '
|
|
428
|
+
+ 'size, loaded-accounts size and fees through the typed `resources` options so one '
|
|
429
|
+
+ 'contract covers every transaction version');
|
|
430
|
+
}
|
|
431
|
+
const lookupTableKey = LOOKUP_TABLE_OPTION_KEYS.find((key) => options?.[key] !== undefined);
|
|
432
|
+
if (lookupTableKey) {
|
|
433
|
+
throw new Error(`Address lookup tables are not supported by this adapter (rejected option `
|
|
434
|
+
+ `'${lookupTableKey}')`
|
|
435
|
+
+ (version === 1 ? '; transaction version 1 has no lookup tables at all (SIMD-0385)' : ''));
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
function toUnits(value) {
|
|
439
|
+
return value === undefined ? undefined : Number(value);
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* A V1 message: the resource budget is the message's typed config, and the
|
|
443
|
+
* runtime reads it from there — which is why a caller-supplied
|
|
444
|
+
* `ComputeBudget` instruction is rejected rather than merged.
|
|
445
|
+
*/
|
|
446
|
+
function planV1Message(input) {
|
|
447
|
+
const { resources } = input;
|
|
448
|
+
const configured = setTransactionMessageConfig({
|
|
449
|
+
computeUnitLimit: toUnits(resources.computeUnitLimit),
|
|
450
|
+
heapSize: toUnits(resources.heapSize),
|
|
451
|
+
loadedAccountsDataSizeLimit: toUnits(resources.loadedAccountsDataSizeLimit),
|
|
452
|
+
priorityFeeLamports: resources.priorityFeeLamports,
|
|
453
|
+
}, createTransactionMessage({ version: 1 }));
|
|
454
|
+
const withFeePayer = setTransactionMessageFeePayerSigner(input.feePayer, configured);
|
|
455
|
+
const withLifetime = setTransactionMessageLifetimeUsingBlockhash(input.latestBlockhash, withFeePayer);
|
|
456
|
+
return appendTransactionMessageInstructions(input.instructions.map(toKitInstruction), withLifetime);
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* A legacy or v0 message: the same budget, rendered as the canonical
|
|
460
|
+
* `ComputeBudget` instructions kit prepends.
|
|
461
|
+
*/
|
|
462
|
+
function planLegacyMessage(input) {
|
|
463
|
+
const { resources } = input;
|
|
464
|
+
const created = input.version === 0
|
|
465
|
+
? createTransactionMessage({ version: 0 })
|
|
466
|
+
: createTransactionMessage({ version: 'legacy' });
|
|
467
|
+
const withFeePayer = setTransactionMessageFeePayerSigner(input.feePayer, created);
|
|
468
|
+
const withLifetime = setTransactionMessageLifetimeUsingBlockhash(input.latestBlockhash, withFeePayer);
|
|
469
|
+
let message = appendTransactionMessageInstructions(input.instructions.map(toKitInstruction), withLifetime);
|
|
470
|
+
if (resources.computeUnitLimit !== undefined) {
|
|
471
|
+
message = setTransactionMessageComputeUnitLimit(Number(resources.computeUnitLimit), message);
|
|
472
|
+
}
|
|
473
|
+
if (resources.computeUnitPriceMicroLamports !== undefined) {
|
|
474
|
+
message = setTransactionMessageComputeUnitPrice(resources.computeUnitPriceMicroLamports, message);
|
|
475
|
+
}
|
|
476
|
+
if (resources.heapSize !== undefined) {
|
|
477
|
+
message = setTransactionMessageHeapSize(Number(resources.heapSize), message);
|
|
478
|
+
}
|
|
479
|
+
if (resources.loadedAccountsDataSizeLimit !== undefined) {
|
|
480
|
+
message = setTransactionMessageLoadedAccountsDataSizeLimit(Number(resources.loadedAccountsDataSizeLimit), message);
|
|
481
|
+
}
|
|
482
|
+
return message;
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* One version/configuration planner for send and unsigned inspection: the
|
|
486
|
+
* only difference between the two is whether the fee payer can sign.
|
|
487
|
+
*/
|
|
488
|
+
function planMessage(input) {
|
|
489
|
+
const message = input.version === 1 ? planV1Message(input) : planLegacyMessage(input);
|
|
490
|
+
return input.attachedSigners?.length
|
|
491
|
+
? addSignersToTransactionMessage([...input.attachedSigners], message)
|
|
492
|
+
: message;
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* The version's wire ceiling, checked on the compiled bytes before a signer
|
|
496
|
+
* is ever reached.
|
|
497
|
+
*
|
|
498
|
+
* The V1 structural caps ({@link V1_MAX_SIGNATURES} signatures,
|
|
499
|
+
* {@link V1_MAX_ACCOUNTS} accounts, {@link V1_MAX_INSTRUCTIONS} top-level
|
|
500
|
+
* instructions) are enforced by `compileTransaction` itself, so they fail
|
|
501
|
+
* here too — one compile earlier than this check, and equally before
|
|
502
|
+
* signing. Re-implementing them would only risk disagreeing with the codec.
|
|
503
|
+
*/
|
|
504
|
+
function assertWithinSizeLimit(version, size) {
|
|
505
|
+
const limit = version === 1 ? V1_TRANSACTION_SIZE_LIMIT : LEGACY_TRANSACTION_SIZE_LIMIT;
|
|
506
|
+
if (size > limit) {
|
|
507
|
+
throw new Error(`Transaction is ${size} bytes, over the ${limit}-byte limit for version `
|
|
508
|
+
+ `${JSON.stringify(version)}`);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
/** Measured consumption plus headroom, positive and protocol-bounded. */
|
|
512
|
+
function estimatedComputeUnitLimit(unitsConsumed) {
|
|
513
|
+
const padded = (unitsConsumed * (100n + ESTIMATED_COMPUTE_UNIT_HEADROOM_PERCENT) + 99n) / 100n;
|
|
514
|
+
if (padded < 1n)
|
|
515
|
+
return 1n;
|
|
516
|
+
return padded > BigInt(MAX_COMPUTE_UNIT_LIMIT) ? BigInt(MAX_COMPUTE_UNIT_LIMIT) : padded;
|
|
517
|
+
}
|
|
518
|
+
/** Measured loaded data rounded up to a whole page plus one page of headroom. */
|
|
519
|
+
function estimatedLoadedAccountsDataSize(loadedBytes) {
|
|
520
|
+
const page = BigInt(LOADED_ACCOUNTS_DATA_PAGE_BYTES);
|
|
521
|
+
const padded = ((loadedBytes + page - 1n) / page + 1n) * page;
|
|
522
|
+
const max = BigInt(MAX_LOADED_ACCOUNTS_DATA_SIZE);
|
|
523
|
+
return padded > max ? max : padded;
|
|
524
|
+
}
|
|
525
|
+
async function fetchLatestBlockhash(resolved, commitment, minContextSlot) {
|
|
526
|
+
const value = resolved.kind === 'arete'
|
|
527
|
+
? await resolved.transport.getLatestBlockhash({ commitment, minContextSlot })
|
|
528
|
+
: (await resolved.rpc.getLatestBlockhash({ commitment, minContextSlot }).send()).value;
|
|
529
|
+
return {
|
|
530
|
+
blockhash: value.blockhash,
|
|
531
|
+
lastValidBlockHeight: BigInt(value.lastValidBlockHeight),
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* Simulate a transaction carrying placeholder signatures. Verification is
|
|
536
|
+
* off on both backends: nothing on this path has touched a signer.
|
|
537
|
+
*/
|
|
538
|
+
async function simulateUnsigned(resolved, wireTransaction, commitment, minContextSlot) {
|
|
539
|
+
if (resolved.kind === 'arete') {
|
|
540
|
+
const simulation = await resolved.transport.simulateTransaction(wireTransaction, {
|
|
541
|
+
commitment,
|
|
542
|
+
minContextSlot,
|
|
543
|
+
});
|
|
544
|
+
return {
|
|
545
|
+
contextSlot: simulation.contextSlot,
|
|
546
|
+
err: simulation.err ?? undefined,
|
|
547
|
+
logs: simulation.logs ?? undefined,
|
|
548
|
+
unitsConsumed: simulation.unitsConsumed,
|
|
549
|
+
loadedAccountsDataSize: simulation.loadedAccountsDataSize,
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
const simulation = await resolved.rpc.simulateTransaction(wireTransaction, { commitment, encoding: 'base64', minContextSlot, sigVerify: false }).send();
|
|
553
|
+
// The loaded-accounts budget is optional upstream and comes back as null
|
|
554
|
+
// when the node did not measure it; `Number(null)` would invent a measured
|
|
555
|
+
// zero and destroy the distinction estimation depends on.
|
|
556
|
+
const { loadedAccountsDataSize } = simulation.value;
|
|
557
|
+
return {
|
|
558
|
+
contextSlot: simulation.context.slot,
|
|
559
|
+
err: simulation.value.err ?? undefined,
|
|
560
|
+
logs: simulation.value.logs ?? undefined,
|
|
561
|
+
unitsConsumed: simulation.value.unitsConsumed == null
|
|
562
|
+
? undefined
|
|
563
|
+
: BigInt(simulation.value.unitsConsumed),
|
|
564
|
+
loadedAccountsDataSize: loadedAccountsDataSize == null
|
|
565
|
+
? undefined
|
|
566
|
+
: BigInt(loadedAccountsDataSize),
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
async function estimateFee(resolved, encodedMessage, commitment, minContextSlot) {
|
|
570
|
+
if (resolved.kind === 'arete') {
|
|
571
|
+
const fee = await resolved.transport.getFeeForMessage(encodedMessage, {
|
|
572
|
+
commitment,
|
|
573
|
+
minContextSlot,
|
|
574
|
+
});
|
|
575
|
+
return {
|
|
576
|
+
feeLamports: fee.feeLamports === null ? undefined : fee.feeLamports,
|
|
577
|
+
contextSlot: fee.contextSlot,
|
|
578
|
+
};
|
|
579
|
+
}
|
|
580
|
+
const fee = await resolved.rpc
|
|
581
|
+
.getFeeForMessage(encodedMessage, { commitment, minContextSlot })
|
|
582
|
+
.send();
|
|
583
|
+
return {
|
|
584
|
+
feeLamports: fee.value === null ? undefined : fee.value,
|
|
585
|
+
contextSlot: fee.context.slot,
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
/**
|
|
589
|
+
* The V1 message a simulation can actually execute: the caller's own budgets
|
|
590
|
+
* where they gave them, the protocol maxima where they did not.
|
|
591
|
+
*/
|
|
592
|
+
function provisionalV1Resources(resources) {
|
|
593
|
+
return {
|
|
594
|
+
...resources,
|
|
595
|
+
computeUnitLimit: resources.computeUnitLimit ?? BigInt(MAX_COMPUTE_UNIT_LIMIT),
|
|
596
|
+
loadedAccountsDataSizeLimit: resources.loadedAccountsDataSizeLimit ?? BigInt(MAX_LOADED_ACCOUNTS_DATA_SIZE),
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
/**
|
|
600
|
+
* Resolve the two budgets a signable V1 message must declare.
|
|
601
|
+
*
|
|
602
|
+
* An unset V1 budget requests the *minimum* rather than a default
|
|
603
|
+
* (SIMD-0385), so a final message missing either could only fail on chain.
|
|
604
|
+
* An explicit caller budget is used verbatim and never raised; a missing one
|
|
605
|
+
* is measured by simulating the provisional message and derived with
|
|
606
|
+
* headroom; and a metric the simulation never reported is refused by name.
|
|
607
|
+
*/
|
|
608
|
+
async function resolveV1Budgets(resolved, compileProvisional, resources, commitment, minContextSlot) {
|
|
609
|
+
if (resources.computeUnitLimit !== undefined
|
|
610
|
+
&& resources.loadedAccountsDataSizeLimit !== undefined) {
|
|
611
|
+
return resources;
|
|
612
|
+
}
|
|
613
|
+
const simulation = await simulateUnsigned(resolved, compileProvisional(provisionalV1Resources(resources)), commitment, minContextSlot);
|
|
614
|
+
if (simulation.err) {
|
|
615
|
+
throw new Error(`Budget estimation simulation reported ${JSON.stringify(simulation.err)}; `
|
|
616
|
+
+ 'nothing was submitted');
|
|
617
|
+
}
|
|
618
|
+
const unestimable = (option, metric) => new Error(`${option} is required for transaction version 1 and could not be estimated: the `
|
|
619
|
+
+ `simulation reported no ${metric}. Pass an explicit ${option}, or use a backend whose `
|
|
620
|
+
+ `simulation reports ${metric}.`);
|
|
621
|
+
let { computeUnitLimit, loadedAccountsDataSizeLimit } = resources;
|
|
622
|
+
if (computeUnitLimit === undefined) {
|
|
623
|
+
if (simulation.unitsConsumed === undefined) {
|
|
624
|
+
throw unestimable('computeUnitLimit', 'unitsConsumed');
|
|
625
|
+
}
|
|
626
|
+
computeUnitLimit = estimatedComputeUnitLimit(simulation.unitsConsumed);
|
|
627
|
+
}
|
|
628
|
+
if (loadedAccountsDataSizeLimit === undefined) {
|
|
629
|
+
if (simulation.loadedAccountsDataSize === undefined) {
|
|
630
|
+
throw unestimable('loadedAccountsDataSizeLimit', 'loadedAccountsDataSize');
|
|
631
|
+
}
|
|
632
|
+
loadedAccountsDataSizeLimit = estimatedLoadedAccountsDataSize(simulation.loadedAccountsDataSize);
|
|
633
|
+
}
|
|
634
|
+
return { ...resources, computeUnitLimit, loadedAccountsDataSizeLimit };
|
|
635
|
+
}
|
|
200
636
|
/**
|
|
201
637
|
* Create a {@link WalletAdapter} from a kit RPC pair and a signer.
|
|
202
638
|
*/
|
|
@@ -210,17 +646,21 @@ function createWalletAdapter(config) {
|
|
|
210
646
|
signerAddresses: [...new Set(signerAddresses)],
|
|
211
647
|
supportedTransactionVersions: CAPABILITY.supportedTransactionVersions,
|
|
212
648
|
async signAndSend(instructions, options, context) {
|
|
213
|
-
// Rejects an explicit
|
|
214
|
-
//
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
649
|
+
// Rejects an unsupported explicit version and any version-bound fee
|
|
650
|
+
// used against the wrong version, before a wallet is ever prompted.
|
|
651
|
+
let plan;
|
|
652
|
+
try {
|
|
653
|
+
plan = resolveTransactionBuildOptions(options, CAPABILITY);
|
|
654
|
+
assertBuildableInputs(instructions, options, plan.transactionVersion);
|
|
655
|
+
}
|
|
656
|
+
catch (cause) {
|
|
218
657
|
throw new KitTransactionExecutionError({
|
|
219
658
|
status: 'not-submitted',
|
|
220
659
|
phase: 'build',
|
|
221
660
|
cause,
|
|
222
661
|
});
|
|
223
662
|
}
|
|
663
|
+
const version = plan.transactionVersion;
|
|
224
664
|
const sendOptions = options;
|
|
225
665
|
const feePayer = sendOptions?.feePayer ?? signer;
|
|
226
666
|
const commitment = toCommitment(options?.confirmationLevel, fallbackCommitment);
|
|
@@ -249,12 +689,30 @@ function createWalletAdapter(config) {
|
|
|
249
689
|
const attachedSigners = [...requiredSignerAddresses]
|
|
250
690
|
.filter((requiredAddress) => requiredAddress !== feePayer.address)
|
|
251
691
|
.map((requiredAddress) => localSignerMap.get(requiredAddress));
|
|
252
|
-
const latestBlockhash = resolved
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
692
|
+
const latestBlockhash = await fetchLatestBlockhash(resolved, commitment);
|
|
693
|
+
lastValidBlockHeight = latestBlockhash.lastValidBlockHeight;
|
|
694
|
+
const compile = (resources) => planMessage({
|
|
695
|
+
version,
|
|
696
|
+
resources,
|
|
697
|
+
instructions,
|
|
698
|
+
latestBlockhash,
|
|
699
|
+
feePayer,
|
|
700
|
+
attachedSigners,
|
|
701
|
+
});
|
|
702
|
+
// The budgets are resolved before the final message exists, so the
|
|
703
|
+
// configuration that is compiled, sized, preflighted and signed is
|
|
704
|
+
// one configuration. The provisional message is sized too: an
|
|
705
|
+
// oversized payload is a build failure with an exact byte count,
|
|
706
|
+
// not a backend-specific simulation error.
|
|
707
|
+
const encodeSized = (compiled) => {
|
|
708
|
+
assertWithinSizeLimit(version, getTransactionSize(compiled));
|
|
709
|
+
return getBase64EncodedWireTransaction(compiled);
|
|
710
|
+
};
|
|
711
|
+
const resources = version === 1
|
|
712
|
+
? await resolveV1Budgets(resolved, (provisional) => encodeSized(compileTransaction(compile(provisional))), plan.resources, commitment)
|
|
713
|
+
: plan.resources;
|
|
714
|
+
message = compile(resources);
|
|
715
|
+
assertWithinSizeLimit(version, getTransactionSize(compileTransaction(message)));
|
|
258
716
|
}
|
|
259
717
|
catch (cause) {
|
|
260
718
|
throw new KitTransactionExecutionError({
|
|
@@ -267,6 +725,7 @@ function createWalletAdapter(config) {
|
|
|
267
725
|
let signature;
|
|
268
726
|
try {
|
|
269
727
|
signedTransaction = await signTransactionMessageWithSigners(message);
|
|
728
|
+
assertIsTransactionWithBlockhashLifetime(signedTransaction);
|
|
270
729
|
signature = getSignatureFromTransaction(signedTransaction);
|
|
271
730
|
}
|
|
272
731
|
catch (cause) {
|
|
@@ -277,31 +736,45 @@ function createWalletAdapter(config) {
|
|
|
277
736
|
});
|
|
278
737
|
}
|
|
279
738
|
if (resolved.kind === 'arete') {
|
|
280
|
-
|
|
739
|
+
// One deadline over submission *and* confirmation: a relay that
|
|
740
|
+
// takes the transaction and then stalls would otherwise hold the
|
|
741
|
+
// caller forever, because no HTTP client here imposes a request
|
|
742
|
+
// timeout. Expiry on either side is submitted-unknown under the
|
|
743
|
+
// signature these bytes carry — never a rebuild, re-sign or resend.
|
|
744
|
+
const deadline = Date.now() + (sendOptions?.confirmationTimeoutMs ?? 60000);
|
|
281
745
|
try {
|
|
282
|
-
const sent = await resolved.transport.sendTransaction(getBase64EncodedWireTransaction(signedTransaction), { skipPreflight: options?.skipPreflight ?? false, preflightCommitment: commitment });
|
|
283
|
-
|
|
746
|
+
const sent = await withDeadline(deadline, resolved.transport.sendTransaction(getBase64EncodedWireTransaction(signedTransaction), { skipPreflight: options?.skipPreflight ?? false, preflightCommitment: commitment }));
|
|
747
|
+
reportSignatureMismatch(signature, sent.signature);
|
|
284
748
|
}
|
|
285
749
|
catch (cause) {
|
|
750
|
+
if (cause instanceof DeadlineExpiredError) {
|
|
751
|
+
throw new KitTransactionExecutionError({
|
|
752
|
+
status: 'submitted-unknown', phase: 'send', signature,
|
|
753
|
+
cause: new Error('The relay did not acknowledge the submission before the confirmation '
|
|
754
|
+
+ 'deadline; the transaction was dispatched once and may still land'),
|
|
755
|
+
});
|
|
756
|
+
}
|
|
286
757
|
if (cause instanceof TransactionTransportError && cause.submissionState === 'not_submitted') {
|
|
287
758
|
throw new KitTransactionExecutionError({ status: 'not-submitted', phase: 'send', cause });
|
|
288
759
|
}
|
|
760
|
+
if (cause instanceof TransactionTransportError) {
|
|
761
|
+
reportSignatureMismatch(signature, cause.signature);
|
|
762
|
+
}
|
|
763
|
+
// The locally derived signature stays authoritative: it is the one
|
|
764
|
+
// these bytes carry, so a relay reporting another identifies some
|
|
765
|
+
// other transaction and cannot be what the caller reconciles.
|
|
289
766
|
throw new KitTransactionExecutionError({
|
|
290
|
-
status: 'submitted-unknown', phase: 'send',
|
|
291
|
-
signature: cause instanceof TransactionTransportError && cause.signature
|
|
292
|
-
? cause.signature : signature,
|
|
293
|
-
cause,
|
|
767
|
+
status: 'submitted-unknown', phase: 'send', signature, cause,
|
|
294
768
|
});
|
|
295
769
|
}
|
|
296
770
|
try {
|
|
297
|
-
return await pollAreteStatus(resolved.transport,
|
|
771
|
+
return await pollAreteStatus(resolved.transport, signature, commitment, lastValidBlockHeight, deadline, sendOptions);
|
|
298
772
|
}
|
|
299
773
|
catch (cause) {
|
|
300
774
|
if (cause instanceof KitTransactionExecutionError)
|
|
301
775
|
throw cause;
|
|
302
776
|
throw new KitTransactionExecutionError({
|
|
303
|
-
status: 'submitted-unknown', phase: 'confirmation',
|
|
304
|
-
signature: submittedSignature, cause,
|
|
777
|
+
status: 'submitted-unknown', phase: 'confirmation', signature, cause,
|
|
305
778
|
});
|
|
306
779
|
}
|
|
307
780
|
}
|
|
@@ -375,68 +848,57 @@ function createWalletAdapter(config) {
|
|
|
375
848
|
}
|
|
376
849
|
},
|
|
377
850
|
async inspectTransaction(instructions, options, context) {
|
|
378
|
-
resolveTransactionBuildOptions(options, CAPABILITY);
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
}
|
|
851
|
+
const plan = resolveTransactionBuildOptions(options, CAPABILITY);
|
|
852
|
+
const version = plan.transactionVersion;
|
|
853
|
+
assertBuildableInputs(instructions, options, version);
|
|
382
854
|
const inspectionOptions = options;
|
|
383
855
|
const commitment = inspectionOptions?.commitment ?? fallbackCommitment;
|
|
384
856
|
const minContextSlot = inspectionOptions?.minContextSlot;
|
|
385
857
|
const resolved = resolveTransport(config.transport, rpc, rpcSubscriptions, context);
|
|
386
|
-
const latestBlockhash = resolved
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
858
|
+
const latestBlockhash = await fetchLatestBlockhash(resolved, commitment, minContextSlot);
|
|
859
|
+
// Inspection is where you discover the budgets to pin, so an omitted
|
|
860
|
+
// V1 ceiling becomes the protocol maximum rather than the SIMD-0385
|
|
861
|
+
// minimum: that provisional message is what the reported metrics
|
|
862
|
+
// describe. Nothing here touches a signer — the fee payer is a bare
|
|
863
|
+
// address and the compiled transaction carries placeholder signatures.
|
|
864
|
+
const resources = version === 1 ? provisionalV1Resources(plan.resources) : plan.resources;
|
|
865
|
+
const message = planMessage({
|
|
866
|
+
version,
|
|
867
|
+
resources,
|
|
868
|
+
instructions,
|
|
869
|
+
latestBlockhash,
|
|
870
|
+
feePayer: createNoopSigner(address(inspectionOptions?.feePayer ?? signer.address)),
|
|
871
|
+
});
|
|
390
872
|
const unsignedTransaction = compileTransaction(message);
|
|
873
|
+
// The same ceiling the send path enforces: a payload that could only
|
|
874
|
+
// be rejected on submission is refused here with its byte count,
|
|
875
|
+
// rather than answered with inspection results or a backend-specific
|
|
876
|
+
// simulation error.
|
|
877
|
+
assertWithinSizeLimit(version, getTransactionSize(unsignedTransaction));
|
|
391
878
|
const wireTransaction = getBase64EncodedWireTransaction(unsignedTransaction);
|
|
392
879
|
const encodedMessage = getBase64Decoder().decode(unsignedTransaction.messageBytes);
|
|
393
|
-
if (resolved.kind === 'arete') {
|
|
394
|
-
const [fee, simulation] = await Promise.all([
|
|
395
|
-
resolved.transport.getFeeForMessage(encodedMessage, { commitment, minContextSlot }),
|
|
396
|
-
resolved.transport.simulateTransaction(wireTransaction, { commitment, minContextSlot }),
|
|
397
|
-
]);
|
|
398
|
-
return {
|
|
399
|
-
feeLamports: fee.feeLamports === null ? undefined : toNumber(fee.feeLamports),
|
|
400
|
-
logs: simulation.logs ?? undefined,
|
|
401
|
-
computeUnitsConsumed: simulation.unitsConsumed === undefined
|
|
402
|
-
? undefined : toNumber(simulation.unitsConsumed),
|
|
403
|
-
contextSlot: toNumber(simulation.contextSlot),
|
|
404
|
-
error: simulation.err ?? undefined,
|
|
405
|
-
loadedAccountsDataSize: simulation.loadedAccountsDataSize === undefined
|
|
406
|
-
? undefined : toNumber(simulation.loadedAccountsDataSize),
|
|
407
|
-
feeContextSlot: toNumber(fee.contextSlot),
|
|
408
|
-
};
|
|
409
|
-
}
|
|
410
880
|
const [fee, simulation] = await Promise.all([
|
|
411
|
-
resolved
|
|
412
|
-
resolved
|
|
413
|
-
commitment,
|
|
414
|
-
encoding: 'base64',
|
|
415
|
-
minContextSlot,
|
|
416
|
-
sigVerify: false,
|
|
417
|
-
}).send(),
|
|
881
|
+
estimateFee(resolved, encodedMessage, commitment, minContextSlot),
|
|
882
|
+
simulateUnsigned(resolved, wireTransaction, commitment, minContextSlot),
|
|
418
883
|
]);
|
|
419
|
-
// The RPC reports the loaded-accounts budget that @solana/kit 2.3 does
|
|
420
|
-
// not yet type. It is optional upstream and comes back as null when the
|
|
421
|
-
// node did not measure it — `Number(null)` would invent a measured zero
|
|
422
|
-
// and destroy the missing-versus-zero distinction budget estimation
|
|
423
|
-
// depends on.
|
|
424
|
-
const { loadedAccountsDataSize: directLoadedAccountsDataSize } = simulation.value;
|
|
425
884
|
return {
|
|
426
|
-
feeLamports: fee.
|
|
427
|
-
logs: simulation.
|
|
428
|
-
computeUnitsConsumed: simulation.
|
|
885
|
+
feeLamports: fee.feeLamports === undefined ? undefined : toNumber(fee.feeLamports),
|
|
886
|
+
logs: simulation.logs === undefined ? undefined : [...simulation.logs],
|
|
887
|
+
computeUnitsConsumed: simulation.unitsConsumed === undefined
|
|
888
|
+
? undefined
|
|
889
|
+
: toNumber(simulation.unitsConsumed),
|
|
890
|
+
contextSlot: toNumber(simulation.contextSlot),
|
|
891
|
+
error: simulation.err ?? undefined,
|
|
892
|
+
loadedAccountsDataSize: simulation.loadedAccountsDataSize === undefined
|
|
429
893
|
? undefined
|
|
430
|
-
: toNumber(simulation.
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
? undefined : Number(directLoadedAccountsDataSize),
|
|
435
|
-
feeContextSlot: toNumber(fee.context.slot),
|
|
894
|
+
: toNumber(simulation.loadedAccountsDataSize),
|
|
895
|
+
feeContextSlot: toNumber(fee.contextSlot),
|
|
896
|
+
transactionVersion: version,
|
|
897
|
+
resources: toWireResourceOptions(resources),
|
|
436
898
|
};
|
|
437
899
|
},
|
|
438
900
|
};
|
|
439
901
|
}
|
|
440
902
|
|
|
441
|
-
export { KitTransactionExecutionError, createWalletAdapter, fromAccountRole, fromKitInstruction, toAccountRole, toKitInstruction };
|
|
903
|
+
export { ESTIMATED_COMPUTE_UNIT_HEADROOM_PERCENT, KitTransactionExecutionError, LEGACY_TRANSACTION_SIZE_LIMIT, LOADED_ACCOUNTS_DATA_PAGE_BYTES, MAX_COMPUTE_UNIT_LIMIT, MAX_LOADED_ACCOUNTS_DATA_SIZE, SOLANA_SIGN_AND_SEND_TRANSACTION, SOLANA_SIGN_TRANSACTION, V1_MAX_ACCOUNTS, V1_MAX_INSTRUCTIONS, V1_MAX_SIGNATURES, V1_TRANSACTION_SIZE_LIMIT, WalletStandardSignerError, createWalletAdapter, createWalletStandardSigner, estimatedComputeUnitLimit, estimatedLoadedAccountsDataSize, fromAccountRole, fromKitInstruction, toAccountRole, toKitInstruction };
|
|
442
904
|
//# sourceMappingURL=index.js.map
|