@bounded-sh/server 0.0.16 → 0.0.18
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 +9 -4
- package/dist/auth/index.d.ts +0 -3
- package/dist/auth/providers/solana-keypair-provider.d.ts +5 -3
- package/dist/index.js +271 -49
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +273 -51
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -2
package/dist/index.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import { convertRemainingAccounts, buildSetDocumentsTransaction, getConfig, init
|
|
|
2
2
|
export * from '@bounded-sh/core';
|
|
3
3
|
export { FunctionInvokeError, InsufficientBalanceError, LiveIntentError, aggregate, count, functions, get, getConfig, getFiles, getMany, invokeFunction, live, liveIntent, liveStatus, queryAggregate, runExpression, runExpressionMany, runQuery, runQueryMany, search, set, setFile, setMany, signAndSubmitTransaction, signMessage, signTransaction, subscribe } from '@bounded-sh/core';
|
|
4
4
|
import { Buffer as Buffer$1 } from 'buffer';
|
|
5
|
-
import { Connection, VersionedTransaction, ComputeBudgetProgram, Keypair } from '@solana/web3.js';
|
|
5
|
+
import { Connection, VersionedTransaction, ComputeBudgetProgram, Keypair, SystemProgram } from '@solana/web3.js';
|
|
6
6
|
import * as anchor from '@coral-xyz/anchor';
|
|
7
7
|
import nacl from 'tweetnacl';
|
|
8
8
|
import bs58 from 'bs58';
|
|
@@ -16,6 +16,25 @@ import * as crypto from 'crypto';
|
|
|
16
16
|
* is the canonical name (it matches the CLI's `BOUNDED_PRIVATE_KEY`).
|
|
17
17
|
* ------------------------------------------------------------- */
|
|
18
18
|
const ENV_VARS = ['BOUNDED_PRIVATE_KEY']; // base-58 or JSON array
|
|
19
|
+
const PRESIGNED_BLOCKHASH_ERROR = 'Server signedTransaction blockhash is stale or expired';
|
|
20
|
+
const BOUNDED_PROGRAM_MAINNET = 'poof4b5pk1L9tmThvBmaABjcyjfhFGbMbQP5BXk2QZp';
|
|
21
|
+
const BOUNDED_PROGRAM_DEVNET = 'taro6CvKqwrYrDc16ufYgzQ2NZcyyVKStffbtudrhRu';
|
|
22
|
+
const COMPUTE_BUDGET_PROGRAM = 'ComputeBudget111111111111111111111111111111';
|
|
23
|
+
const SYSTEM_PROGRAM_ID = '11111111111111111111111111111111';
|
|
24
|
+
const SUPPORTED_SOLANA_NETWORKS = ['solana_devnet', 'solana_mainnet', 'surfnet'];
|
|
25
|
+
const SUPPORTED_SOLANA_NETWORK_SET = new Set(SUPPORTED_SOLANA_NETWORKS);
|
|
26
|
+
const ALLOWED_SERVER_TX_PROGRAMS = new Set([
|
|
27
|
+
BOUNDED_PROGRAM_MAINNET,
|
|
28
|
+
BOUNDED_PROGRAM_DEVNET,
|
|
29
|
+
COMPUTE_BUDGET_PROGRAM,
|
|
30
|
+
SYSTEM_PROGRAM_ID,
|
|
31
|
+
]);
|
|
32
|
+
const SET_DOCUMENTS_DISCRIMINATOR = '79,46,72,73,24,79,66,245';
|
|
33
|
+
const SET_DOCUMENTS_V2_DISCRIMINATOR = '22,236,242,185,145,61,26,39';
|
|
34
|
+
const ALLOWED_BOUNDED_SET_DISCRIMINATORS = new Set([
|
|
35
|
+
SET_DOCUMENTS_DISCRIMINATOR,
|
|
36
|
+
SET_DOCUMENTS_V2_DISCRIMINATOR,
|
|
37
|
+
]);
|
|
19
38
|
function loadKeypairFromEnv() {
|
|
20
39
|
var _a;
|
|
21
40
|
let secret;
|
|
@@ -64,23 +83,214 @@ async function fetchPriorityFee(rpcEndpoint, rawUnsignedTxBuffer) {
|
|
|
64
83
|
});
|
|
65
84
|
const data = await res.json();
|
|
66
85
|
const fee = (_b = (_a = data === null || data === void 0 ? void 0 : data.result) === null || _a === void 0 ? void 0 : _a.priorityFeeEstimate) !== null && _b !== void 0 ? _b : null;
|
|
67
|
-
console.log("Got fee from Helius", fee);
|
|
68
86
|
if (fee != null && fee > 0) {
|
|
69
87
|
return Math.ceil(Number(fee) * 1.2);
|
|
70
88
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
return Math.ceil(10000 * 1.2);
|
|
74
|
-
}
|
|
89
|
+
console.warn("Priority-fee estimate returned no positive fee; skipping priority fee instruction");
|
|
90
|
+
return null;
|
|
75
91
|
}
|
|
76
92
|
catch (err) {
|
|
77
|
-
console.warn("Priority
|
|
78
|
-
return
|
|
93
|
+
console.warn("Priority-fee estimate failed; skipping priority fee instruction:", err);
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function isPresignedBlockhashError(error) {
|
|
98
|
+
return typeof (error === null || error === void 0 ? void 0 : error.message) === 'string' && error.message.includes(PRESIGNED_BLOCKHASH_ERROR);
|
|
99
|
+
}
|
|
100
|
+
function isPermanentPresignedTransactionError(error) {
|
|
101
|
+
return isPresignedBlockhashError(error) ||
|
|
102
|
+
(typeof (error === null || error === void 0 ? void 0 : error.message) === 'string' && (error.message.startsWith('Server signedTransaction') ||
|
|
103
|
+
error.message.startsWith('Server preInstruction')));
|
|
104
|
+
}
|
|
105
|
+
class BorshCursor {
|
|
106
|
+
constructor(data, offset) {
|
|
107
|
+
this.data = data;
|
|
108
|
+
this.offset = offset;
|
|
109
|
+
}
|
|
110
|
+
requireBytes(length, field) {
|
|
111
|
+
if (this.offset + length > this.data.length) {
|
|
112
|
+
throw new Error(`Server signedTransaction has malformed Bounded instruction data while reading ${field}`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
readU8(field) {
|
|
116
|
+
this.requireBytes(1, field);
|
|
117
|
+
return this.data[this.offset++];
|
|
118
|
+
}
|
|
119
|
+
readU32(field) {
|
|
120
|
+
this.requireBytes(4, field);
|
|
121
|
+
const value = this.data[this.offset] |
|
|
122
|
+
(this.data[this.offset + 1] << 8) |
|
|
123
|
+
(this.data[this.offset + 2] << 16) |
|
|
124
|
+
(this.data[this.offset + 3] << 24);
|
|
125
|
+
this.offset += 4;
|
|
126
|
+
return value >>> 0;
|
|
127
|
+
}
|
|
128
|
+
skip(length, field) {
|
|
129
|
+
this.requireBytes(length, field);
|
|
130
|
+
this.offset += length;
|
|
131
|
+
}
|
|
132
|
+
readString(field) {
|
|
133
|
+
const length = this.readU32(`${field} length`);
|
|
134
|
+
this.requireBytes(length, field);
|
|
135
|
+
const raw = this.data.slice(this.offset, this.offset + length);
|
|
136
|
+
this.offset += length;
|
|
137
|
+
return Buffer$1.from(raw).toString('utf8');
|
|
138
|
+
}
|
|
139
|
+
skipBytes(field) {
|
|
140
|
+
const length = this.readU32(`${field} length`);
|
|
141
|
+
this.skip(length, field);
|
|
142
|
+
}
|
|
143
|
+
isAtEnd() {
|
|
144
|
+
return this.offset === this.data.length;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
function normalizeOnchainPath(path) {
|
|
148
|
+
let normalized = path.startsWith('/') ? path.slice(1) : path;
|
|
149
|
+
if (normalized.endsWith('*') && normalized.length > 1) {
|
|
150
|
+
normalized = normalized.slice(0, -1);
|
|
151
|
+
}
|
|
152
|
+
if (normalized.endsWith('/')) {
|
|
153
|
+
normalized = normalized.slice(0, -1);
|
|
154
|
+
}
|
|
155
|
+
return normalized;
|
|
156
|
+
}
|
|
157
|
+
function skipBoundedFieldValue(cursor) {
|
|
158
|
+
const option = cursor.readU8('operation value option');
|
|
159
|
+
if (option === 0)
|
|
160
|
+
return;
|
|
161
|
+
if (option !== 1) {
|
|
162
|
+
throw new Error('Server signedTransaction has malformed Bounded field value option');
|
|
163
|
+
}
|
|
164
|
+
const variant = cursor.readU8('operation value variant');
|
|
165
|
+
switch (variant) {
|
|
166
|
+
case 0:
|
|
167
|
+
case 1:
|
|
168
|
+
cursor.skip(8, 'operation numeric value');
|
|
169
|
+
return;
|
|
170
|
+
case 2:
|
|
171
|
+
cursor.skip(1, 'operation bool value');
|
|
172
|
+
return;
|
|
173
|
+
case 3:
|
|
174
|
+
cursor.readString('operation string value');
|
|
175
|
+
return;
|
|
176
|
+
case 4:
|
|
177
|
+
cursor.skip(32, 'operation address value');
|
|
178
|
+
return;
|
|
179
|
+
default:
|
|
180
|
+
throw new Error(`Server signedTransaction has unsupported Bounded field value variant: ${variant}`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
function skipBoundedFieldOperation(cursor) {
|
|
184
|
+
cursor.readString('operation key');
|
|
185
|
+
skipBoundedFieldValue(cursor);
|
|
186
|
+
cursor.skip(1, 'operation kind');
|
|
187
|
+
}
|
|
188
|
+
function skipBoundedTxData(cursor, isV2) {
|
|
189
|
+
cursor.readString('txData plugin function key');
|
|
190
|
+
cursor.skipBytes('txData bytes');
|
|
191
|
+
if (isV2) {
|
|
192
|
+
cursor.skipBytes('txData raIndices');
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
const raIndexCount = cursor.readU32('txData raIndices length');
|
|
196
|
+
cursor.skip(raIndexCount * 8, 'txData raIndices');
|
|
197
|
+
}
|
|
198
|
+
function parseBoundedSetDocumentsInstruction(data) {
|
|
199
|
+
if (data.length < 8) {
|
|
200
|
+
throw new Error('Server signedTransaction has malformed Bounded instruction data');
|
|
201
|
+
}
|
|
202
|
+
const discriminator = Array.from(data.slice(0, 8)).join(',');
|
|
203
|
+
if (!ALLOWED_BOUNDED_SET_DISCRIMINATORS.has(discriminator)) {
|
|
204
|
+
throw new Error('Server signedTransaction contains unsupported Bounded instruction');
|
|
205
|
+
}
|
|
206
|
+
const isV2 = discriminator === SET_DOCUMENTS_V2_DISCRIMINATOR;
|
|
207
|
+
const cursor = new BorshCursor(data, 8);
|
|
208
|
+
const appId = cursor.readString('appId');
|
|
209
|
+
const documentPaths = [];
|
|
210
|
+
const documentCount = cursor.readU32('documents length');
|
|
211
|
+
for (let i = 0; i < documentCount; i++) {
|
|
212
|
+
documentPaths.push(normalizeOnchainPath(cursor.readString('document path')));
|
|
213
|
+
const operationCount = cursor.readU32('operations length');
|
|
214
|
+
for (let j = 0; j < operationCount; j++) {
|
|
215
|
+
skipBoundedFieldOperation(cursor);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
const deletePaths = [];
|
|
219
|
+
const deleteCount = cursor.readU32('delete paths length');
|
|
220
|
+
for (let i = 0; i < deleteCount; i++) {
|
|
221
|
+
deletePaths.push(normalizeOnchainPath(cursor.readString('delete path')));
|
|
222
|
+
}
|
|
223
|
+
const txDataCount = cursor.readU32('txData length');
|
|
224
|
+
for (let i = 0; i < txDataCount; i++) {
|
|
225
|
+
skipBoundedTxData(cursor, isV2);
|
|
226
|
+
}
|
|
227
|
+
const simulate = cursor.readU8('simulate');
|
|
228
|
+
if (simulate !== 0 && simulate !== 1) {
|
|
229
|
+
throw new Error('Server signedTransaction has malformed Bounded simulate flag');
|
|
230
|
+
}
|
|
231
|
+
if (!cursor.isAtEnd()) {
|
|
232
|
+
throw new Error('Server signedTransaction has trailing Bounded instruction data');
|
|
233
|
+
}
|
|
234
|
+
return { appId, documentPaths, deletePaths };
|
|
235
|
+
}
|
|
236
|
+
function assertSamePathSet(expectedPaths, actualPaths) {
|
|
237
|
+
const expected = new Set(expectedPaths.map(normalizeOnchainPath).filter(Boolean));
|
|
238
|
+
const actual = new Set(actualPaths.map(normalizeOnchainPath).filter(Boolean));
|
|
239
|
+
const missing = [...expected].filter(path => !actual.has(path));
|
|
240
|
+
const extra = [...actual].filter(path => !expected.has(path));
|
|
241
|
+
if (missing.length > 0 || extra.length > 0) {
|
|
242
|
+
const details = [
|
|
243
|
+
missing.length ? `missing paths: ${missing.join(', ')}` : '',
|
|
244
|
+
extra.length ? `unexpected paths: ${extra.join(', ')}` : '',
|
|
245
|
+
].filter(Boolean).join('; ');
|
|
246
|
+
throw new Error(`Server signedTransaction Bounded instruction paths do not match requested write paths (${details})`);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
function validatePresignedSignedTransaction(transaction, sol) {
|
|
250
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
251
|
+
const accountKeys = transaction.message.staticAccountKeys;
|
|
252
|
+
let boundedInstructionCount = 0;
|
|
253
|
+
const actualWritePaths = [];
|
|
254
|
+
for (const ix of transaction.message.compiledInstructions) {
|
|
255
|
+
if (ix.programIdIndex >= accountKeys.length) {
|
|
256
|
+
throw new Error('Server signedTransaction has program ID in lookup table (not allowed)');
|
|
257
|
+
}
|
|
258
|
+
const programId = accountKeys[ix.programIdIndex].toBase58();
|
|
259
|
+
if (!ALLOWED_SERVER_TX_PROGRAMS.has(programId)) {
|
|
260
|
+
throw new Error(`Server signedTransaction contains unauthorized program: ${programId}`);
|
|
261
|
+
}
|
|
262
|
+
const data = ix.data instanceof Uint8Array ? ix.data : Buffer$1.from((_a = ix.data) !== null && _a !== void 0 ? _a : []);
|
|
263
|
+
if (programId === SYSTEM_PROGRAM_ID) {
|
|
264
|
+
throw new Error('Server signedTransaction contains unauthorized System Program instruction');
|
|
265
|
+
}
|
|
266
|
+
if (programId === BOUNDED_PROGRAM_MAINNET || programId === BOUNDED_PROGRAM_DEVNET) {
|
|
267
|
+
boundedInstructionCount += 1;
|
|
268
|
+
const parsed = parseBoundedSetDocumentsInstruction(data);
|
|
269
|
+
if (parsed.appId !== sol.appId) {
|
|
270
|
+
throw new Error('Server signedTransaction Bounded instruction appId does not match configured appId');
|
|
271
|
+
}
|
|
272
|
+
actualWritePaths.push(...parsed.documentPaths, ...parsed.deletePaths);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
if (boundedInstructionCount !== 1) {
|
|
276
|
+
throw new Error('Server signedTransaction must contain exactly one Bounded set-documents instruction');
|
|
277
|
+
}
|
|
278
|
+
const expectedWritePaths = [
|
|
279
|
+
...(((_d = (_c = (_b = sol.txArgs) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.setDocumentData) !== null && _d !== void 0 ? _d : []).map((doc) => doc.path)),
|
|
280
|
+
...((_g = (_f = (_e = sol.txArgs) === null || _e === void 0 ? void 0 : _e[0]) === null || _f === void 0 ? void 0 : _f.deletePaths) !== null && _g !== void 0 ? _g : []),
|
|
281
|
+
];
|
|
282
|
+
assertSamePathSet(expectedWritePaths, actualWritePaths);
|
|
283
|
+
}
|
|
284
|
+
function assertAllowedServerPreInstructions(preInstructions) {
|
|
285
|
+
for (const [index, ix] of (preInstructions !== null && preInstructions !== void 0 ? preInstructions : []).entries()) {
|
|
286
|
+
if (ix.programId.equals(SystemProgram.programId)) {
|
|
287
|
+
throw new Error(`Server preInstruction[${index}] contains unauthorized System Program instruction`);
|
|
288
|
+
}
|
|
79
289
|
}
|
|
80
290
|
}
|
|
81
291
|
class SolanaKeypairProvider {
|
|
82
|
-
constructor(
|
|
83
|
-
this.
|
|
292
|
+
constructor(rpcUrl = null, keypair) {
|
|
293
|
+
this.rpcUrl = rpcUrl;
|
|
84
294
|
this.explicitKeypair = keypair;
|
|
85
295
|
}
|
|
86
296
|
get keypair() {
|
|
@@ -119,7 +329,8 @@ class SolanaKeypairProvider {
|
|
|
119
329
|
*
|
|
120
330
|
* This method handles blockhash and transaction confirmation automatically - you do NOT need to
|
|
121
331
|
* set recentBlockhash or lastValidBlockHeight on the transaction before calling this method.
|
|
122
|
-
*
|
|
332
|
+
* Requires an explicit RPC URL configured on the provider because this method has no
|
|
333
|
+
* Solana transaction payload network to resolve.
|
|
123
334
|
*
|
|
124
335
|
* @param transaction - The transaction to sign and submit (Transaction or VersionedTransaction)
|
|
125
336
|
* @param feePayer - Optional fee payer public key. If not provided and the transaction doesn't
|
|
@@ -203,20 +414,23 @@ class SolanaKeypairProvider {
|
|
|
203
414
|
}
|
|
204
415
|
return { signature: sig, txInfo };
|
|
205
416
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
if (network === 'solana_devnet') {
|
|
211
|
-
return SOLANA_DEVNET_RPC_URL;
|
|
417
|
+
requireSupportedNetwork(network) {
|
|
418
|
+
const networkName = network === null || network === void 0 ? void 0 : network.trim();
|
|
419
|
+
if (!networkName) {
|
|
420
|
+
throw new Error(`Supported sol.network is required for Solana transaction submission; expected one of: ${SUPPORTED_SOLANA_NETWORKS.join(', ')}.`);
|
|
212
421
|
}
|
|
213
|
-
|
|
214
|
-
|
|
422
|
+
if (!SUPPORTED_SOLANA_NETWORK_SET.has(networkName)) {
|
|
423
|
+
throw new Error(`Unsupported Solana network "${networkName}". Supported networks: ${SUPPORTED_SOLANA_NETWORKS.join(', ')}.`);
|
|
215
424
|
}
|
|
216
|
-
|
|
217
|
-
|
|
425
|
+
return networkName;
|
|
426
|
+
}
|
|
427
|
+
getRpcUrl() {
|
|
428
|
+
var _a;
|
|
429
|
+
const explicitRpcUrl = (_a = this.rpcUrl) === null || _a === void 0 ? void 0 : _a.trim();
|
|
430
|
+
if (explicitRpcUrl) {
|
|
431
|
+
return explicitRpcUrl;
|
|
218
432
|
}
|
|
219
|
-
|
|
433
|
+
throw new Error('Solana RPC URL is required for server keypair transaction submission; pass an explicit rpcUrl when creating SolanaKeypairProvider.');
|
|
220
434
|
}
|
|
221
435
|
/* ----------------------------------------------------------- *
|
|
222
436
|
* Transaction runner
|
|
@@ -225,6 +439,8 @@ class SolanaKeypairProvider {
|
|
|
225
439
|
if (!sol)
|
|
226
440
|
throw new Error('Solana transaction data required');
|
|
227
441
|
const kp = this.keypair;
|
|
442
|
+
this.requireSupportedNetwork(sol.network);
|
|
443
|
+
const rpcUrl = this.getRpcUrl();
|
|
228
444
|
// Helper for duck typing - checks if transaction is legacy Transaction vs VersionedTransaction
|
|
229
445
|
const isLegacyTx = (tx) => {
|
|
230
446
|
return 'recentBlockhash' in tx && !('message' in tx && 'staticAccountKeys' in tx.message);
|
|
@@ -273,11 +489,14 @@ class SolanaKeypairProvider {
|
|
|
273
489
|
let errorMessage = "";
|
|
274
490
|
while (retries < 5) {
|
|
275
491
|
try {
|
|
276
|
-
toReturn = await this.runTransactionInner(sol, opts, wallet, kp, deduped);
|
|
492
|
+
toReturn = await this.runTransactionInner(sol, opts, wallet, kp, deduped, rpcUrl);
|
|
277
493
|
didPass = true;
|
|
278
494
|
break;
|
|
279
495
|
}
|
|
280
496
|
catch (error) {
|
|
497
|
+
if (isPermanentPresignedTransactionError(error)) {
|
|
498
|
+
throw error;
|
|
499
|
+
}
|
|
281
500
|
console.log("Error building and sending transaction on retry:", retries, error);
|
|
282
501
|
await new Promise(resolve => setTimeout(resolve, delay));
|
|
283
502
|
// Exponential backoff
|
|
@@ -291,9 +510,8 @@ class SolanaKeypairProvider {
|
|
|
291
510
|
}
|
|
292
511
|
return toReturn;
|
|
293
512
|
}
|
|
294
|
-
async runTransactionInner(sol, opts, wallet, kp, deduped) {
|
|
513
|
+
async runTransactionInner(sol, opts, wallet, kp, deduped, rpcUrl) {
|
|
295
514
|
var _a, _b, _c;
|
|
296
|
-
let rpcUrl = this.getRpcUrl(sol.network);
|
|
297
515
|
const connection = new Connection(rpcUrl, 'confirmed');
|
|
298
516
|
const anchorProvider = new anchor.AnchorProvider(connection, wallet, anchor.AnchorProvider.defaultOptions());
|
|
299
517
|
const app_id = sol.appId;
|
|
@@ -307,11 +525,16 @@ class SolanaKeypairProvider {
|
|
|
307
525
|
let lastValidBlockHeight;
|
|
308
526
|
if (sol.signedTransaction) {
|
|
309
527
|
tx = VersionedTransaction.deserialize(Buffer$1.from(sol.signedTransaction, 'base64'));
|
|
310
|
-
|
|
311
|
-
blockhash =
|
|
312
|
-
|
|
528
|
+
validatePresignedSignedTransaction(tx, sol);
|
|
529
|
+
blockhash = tx.message.recentBlockhash;
|
|
530
|
+
const isValid = await connection.isBlockhashValid(blockhash, { commitment: 'confirmed' });
|
|
531
|
+
if (!isValid.value) {
|
|
532
|
+
throw new Error(`${PRESIGNED_BLOCKHASH_ERROR}; request a fresh server-built transaction.`);
|
|
533
|
+
}
|
|
534
|
+
lastValidBlockHeight = 0;
|
|
313
535
|
}
|
|
314
536
|
else {
|
|
537
|
+
assertAllowedServerPreInstructions(sol.preInstructions);
|
|
315
538
|
const result = await buildSetDocumentsTransaction(connection, sol.txArgs[0].idl, anchorProvider, kp.publicKey, {
|
|
316
539
|
app_id,
|
|
317
540
|
documents: sol.txArgs[0].setDocumentData,
|
|
@@ -326,31 +549,33 @@ class SolanaKeypairProvider {
|
|
|
326
549
|
const isLegacyTx = (t) => {
|
|
327
550
|
return 'recentBlockhash' in t && !('message' in t && 'staticAccountKeys' in t.message);
|
|
328
551
|
};
|
|
329
|
-
// Refresh blockhash and lastValidBlockHeight
|
|
330
552
|
// Use duck typing instead of instanceof to handle multiple @solana/web3.js versions
|
|
331
553
|
if (isLegacyTx(tx)) {
|
|
554
|
+
if (!sol.signedTransaction) {
|
|
555
|
+
tx.recentBlockhash = blockhash;
|
|
556
|
+
tx.lastValidBlockHeight = lastValidBlockHeight;
|
|
557
|
+
tx.feePayer = kp.publicKey;
|
|
558
|
+
}
|
|
332
559
|
tx.partialSign(kp);
|
|
333
|
-
tx.recentBlockhash = blockhash;
|
|
334
|
-
tx.lastValidBlockHeight = lastValidBlockHeight;
|
|
335
|
-
tx.feePayer = kp.publicKey;
|
|
336
560
|
}
|
|
337
561
|
else {
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
}
|
|
341
|
-
// 3️⃣ Optional priority‑fee instruction
|
|
342
|
-
const rawUnsigned = tx.serialize({ requireAllSignatures: false });
|
|
343
|
-
const microLamports = await fetchPriorityFee(connection.rpcEndpoint, rawUnsigned);
|
|
344
|
-
if (microLamports != null) {
|
|
345
|
-
// Use duck typing instead of instanceof to handle multiple @solana/web3.js versions
|
|
346
|
-
if (isLegacyTx(tx)) {
|
|
347
|
-
tx.instructions.unshift(ComputeBudgetProgram.setComputeUnitPrice({ microLamports }));
|
|
348
|
-
tx.partialSign(kp);
|
|
562
|
+
if (!sol.signedTransaction) {
|
|
563
|
+
tx.message.recentBlockhash = blockhash;
|
|
349
564
|
}
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
565
|
+
tx.sign([kp]);
|
|
566
|
+
}
|
|
567
|
+
// 3️⃣ Optional priority-fee instruction. Pre-signed transactions are
|
|
568
|
+
// immutable at this point; preserve their exact message and skip estimation.
|
|
569
|
+
if (!sol.signedTransaction) {
|
|
570
|
+
const rawUnsigned = tx.serialize({ requireAllSignatures: false });
|
|
571
|
+
const microLamports = await fetchPriorityFee(connection.rpcEndpoint, rawUnsigned);
|
|
572
|
+
if (microLamports != null) {
|
|
573
|
+
// Use duck typing instead of instanceof to handle multiple @solana/web3.js versions
|
|
574
|
+
if (isLegacyTx(tx)) {
|
|
575
|
+
tx.instructions.unshift(ComputeBudgetProgram.setComputeUnitPrice({ microLamports }));
|
|
576
|
+
tx.partialSign(kp);
|
|
577
|
+
}
|
|
578
|
+
else {
|
|
354
579
|
const roundTwo = await buildSetDocumentsTransaction(connection, sol.txArgs[0].idl, anchorProvider, kp.publicKey, {
|
|
355
580
|
app_id,
|
|
356
581
|
documents: sol.txArgs[0].setDocumentData,
|
|
@@ -360,8 +585,8 @@ class SolanaKeypairProvider {
|
|
|
360
585
|
tx = roundTwo.tx;
|
|
361
586
|
blockhash = roundTwo.blockhash;
|
|
362
587
|
lastValidBlockHeight = roundTwo.lastValidBlockHeight;
|
|
363
|
-
tx.sign([kp]);
|
|
364
588
|
tx.message.recentBlockhash = blockhash;
|
|
589
|
+
tx.sign([kp]);
|
|
365
590
|
}
|
|
366
591
|
}
|
|
367
592
|
}
|
|
@@ -432,9 +657,6 @@ class OffchainAuthProvider {
|
|
|
432
657
|
}
|
|
433
658
|
|
|
434
659
|
let currentAuthProvider = null;
|
|
435
|
-
const SOLANA_DEVNET_RPC_URL = "https://idelle-8nxsep-fast-devnet.helius-rpc.com";
|
|
436
|
-
const SOLANA_MAINNET_RPC_URL = "https://celestia-cegncv-fast-mainnet.helius-rpc.com";
|
|
437
|
-
const SURFNET_RPC_URL = "https://surfpool.fly.dev";
|
|
438
660
|
async function getAuthProvider() {
|
|
439
661
|
var _a;
|
|
440
662
|
const config = await getConfig();
|