@bounded-sh/server 0.0.17 → 0.0.19
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 +44 -65
- package/dist/auth/index.d.ts +0 -4
- package/dist/auth/providers/solana-keypair-provider.d.ts +6 -5
- package/dist/explicit-client-only.d.ts +28 -0
- package/dist/index.d.ts +6 -12
- package/dist/index.js +351 -227
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +347 -153
- package/dist/index.mjs.map +1 -1
- package/dist/wallet-client.d.ts +4 -6
- package/package.json +3 -2
package/dist/index.mjs
CHANGED
|
@@ -1,47 +1,76 @@
|
|
|
1
|
-
import { convertRemainingAccounts, buildSetDocumentsTransaction,
|
|
2
|
-
export
|
|
3
|
-
|
|
1
|
+
import { init as init$1, convertRemainingAccounts, buildSetDocumentsTransaction, live as live$1, set as set$1, setMany as setMany$1, setFile as setFile$1, get as get$1, getMany as getMany$1, getFiles as getFiles$1, search as search$1, runQuery as runQuery$1, runQueryMany as runQueryMany$1, runExpression as runExpression$1, runExpressionMany as runExpressionMany$1, count as count$1, aggregate as aggregate$1, queryAggregate as queryAggregate$1, subscribe as subscribe$1, functions as functions$1, getConfig, ServerSessionManager, getWebhookKeysUrl } from '@bounded-sh/core';
|
|
2
|
+
export { FunctionInvokeError, InsufficientBalanceError, LiveIntentError, getConfig, getWebhookKeysUrl } from '@bounded-sh/core';
|
|
3
|
+
import { Connection, VersionedTransaction, ComputeBudgetProgram, SystemProgram, Keypair } from '@solana/web3.js';
|
|
4
|
+
import bs58 from 'bs58';
|
|
4
5
|
import { Buffer as Buffer$1 } from 'buffer';
|
|
5
|
-
import { Connection, VersionedTransaction, ComputeBudgetProgram, Keypair } from '@solana/web3.js';
|
|
6
6
|
import * as anchor from '@coral-xyz/anchor';
|
|
7
7
|
import nacl from 'tweetnacl';
|
|
8
|
-
import bs58 from 'bs58';
|
|
9
8
|
import * as crypto from 'crypto';
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
function
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
10
|
+
async function init(newConfig) {
|
|
11
|
+
await init$1(Object.assign(Object.assign({}, newConfig), { authProvider: null, isServer: true, skipBackendInit: true }));
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async function getAuthProvider() {
|
|
15
|
+
throw new Error('Server auth providers are not process-global. Use createWalletClient({ keypair }) and call that client\'s methods.');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function explicitClientOnly(name) {
|
|
19
|
+
throw new Error(`${name} is not available as a tarobase-server top-level auth operation. ` +
|
|
20
|
+
'Use createWalletClient({ keypair }) and call the returned client method.');
|
|
21
|
+
}
|
|
22
|
+
async function get(..._args) { explicitClientOnly('get'); }
|
|
23
|
+
async function getMany(..._args) { explicitClientOnly('getMany'); }
|
|
24
|
+
async function set(..._args) { explicitClientOnly('set'); }
|
|
25
|
+
async function setMany(..._args) { explicitClientOnly('setMany'); }
|
|
26
|
+
async function setFile(..._args) { explicitClientOnly('setFile'); }
|
|
27
|
+
async function getFiles(..._args) { explicitClientOnly('getFiles'); }
|
|
28
|
+
async function search(..._args) { explicitClientOnly('search'); }
|
|
29
|
+
async function queryAggregate(..._args) { explicitClientOnly('queryAggregate'); }
|
|
30
|
+
async function runQuery(..._args) { explicitClientOnly('runQuery'); }
|
|
31
|
+
async function runQueryMany(..._args) { explicitClientOnly('runQueryMany'); }
|
|
32
|
+
async function runExpression(..._args) { explicitClientOnly('runExpression'); }
|
|
33
|
+
async function runExpressionMany(..._args) { explicitClientOnly('runExpressionMany'); }
|
|
34
|
+
async function signMessage(..._args) { explicitClientOnly('signMessage'); }
|
|
35
|
+
async function signTransaction(..._args) { explicitClientOnly('signTransaction'); }
|
|
36
|
+
async function signAndSubmitTransaction(..._args) { explicitClientOnly('signAndSubmitTransaction'); }
|
|
37
|
+
async function count(..._args) { explicitClientOnly('count'); }
|
|
38
|
+
async function aggregate(..._args) { explicitClientOnly('aggregate'); }
|
|
39
|
+
async function subscribe(..._args) { explicitClientOnly('subscribe'); }
|
|
40
|
+
async function invokeFunction(..._args) { explicitClientOnly('invokeFunction'); }
|
|
41
|
+
async function liveIntent(..._args) { explicitClientOnly('liveIntent'); }
|
|
42
|
+
async function liveStatus(..._args) { explicitClientOnly('liveStatus'); }
|
|
43
|
+
const functions = {
|
|
44
|
+
invoke: async (..._args) => explicitClientOnly('functions.invoke'),
|
|
45
|
+
};
|
|
46
|
+
const live = {
|
|
47
|
+
intent: async (..._args) => explicitClientOnly('live.intent'),
|
|
48
|
+
status: async (..._args) => explicitClientOnly('live.status'),
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
async function getIdToken() {
|
|
52
|
+
throw new Error('getIdToken is not available as a tarobase-server top-level auth operation. Use createWalletClient({ keypair }) and call the returned client methods.');
|
|
44
53
|
}
|
|
54
|
+
|
|
55
|
+
const PRESIGNED_BLOCKHASH_ERROR = 'Server signedTransaction blockhash is stale or expired';
|
|
56
|
+
const BOUNDED_PROGRAM_MAINNET = 'poof4b5pk1L9tmThvBmaABjcyjfhFGbMbQP5BXk2QZp';
|
|
57
|
+
const BOUNDED_PROGRAM_DEVNET = 'taro6CvKqwrYrDc16ufYgzQ2NZcyyVKStffbtudrhRu';
|
|
58
|
+
const COMPUTE_BUDGET_PROGRAM = 'ComputeBudget111111111111111111111111111111';
|
|
59
|
+
const SYSTEM_PROGRAM_ID = '11111111111111111111111111111111';
|
|
60
|
+
const SUPPORTED_SOLANA_NETWORKS = ['solana_devnet', 'solana_mainnet', 'surfnet'];
|
|
61
|
+
const SUPPORTED_SOLANA_NETWORK_SET = new Set(SUPPORTED_SOLANA_NETWORKS);
|
|
62
|
+
const ALLOWED_SERVER_TX_PROGRAMS = new Set([
|
|
63
|
+
BOUNDED_PROGRAM_MAINNET,
|
|
64
|
+
BOUNDED_PROGRAM_DEVNET,
|
|
65
|
+
COMPUTE_BUDGET_PROGRAM,
|
|
66
|
+
SYSTEM_PROGRAM_ID,
|
|
67
|
+
]);
|
|
68
|
+
const SET_DOCUMENTS_DISCRIMINATOR = '79,46,72,73,24,79,66,245';
|
|
69
|
+
const SET_DOCUMENTS_V2_DISCRIMINATOR = '22,236,242,185,145,61,26,39';
|
|
70
|
+
const ALLOWED_BOUNDED_SET_DISCRIMINATORS = new Set([
|
|
71
|
+
SET_DOCUMENTS_DISCRIMINATOR,
|
|
72
|
+
SET_DOCUMENTS_V2_DISCRIMINATOR,
|
|
73
|
+
]);
|
|
45
74
|
/* ──────────────────────────────────────────────────────────
|
|
46
75
|
* Helper – fetch getPriorityFeeEstimate
|
|
47
76
|
* ──────────────────────────────────────────────────────── */
|
|
@@ -64,28 +93,221 @@ async function fetchPriorityFee(rpcEndpoint, rawUnsignedTxBuffer) {
|
|
|
64
93
|
});
|
|
65
94
|
const data = await res.json();
|
|
66
95
|
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
96
|
if (fee != null && fee > 0) {
|
|
69
97
|
return Math.ceil(Number(fee) * 1.2);
|
|
70
98
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
return Math.ceil(10000 * 1.2);
|
|
74
|
-
}
|
|
99
|
+
console.warn("Priority-fee estimate returned no positive fee; skipping priority fee instruction");
|
|
100
|
+
return null;
|
|
75
101
|
}
|
|
76
102
|
catch (err) {
|
|
77
|
-
console.warn("Priority
|
|
78
|
-
return
|
|
103
|
+
console.warn("Priority-fee estimate failed; skipping priority fee instruction:", err);
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function isPresignedBlockhashError(error) {
|
|
108
|
+
return typeof (error === null || error === void 0 ? void 0 : error.message) === 'string' && error.message.includes(PRESIGNED_BLOCKHASH_ERROR);
|
|
109
|
+
}
|
|
110
|
+
function isPermanentPresignedTransactionError(error) {
|
|
111
|
+
return isPresignedBlockhashError(error) ||
|
|
112
|
+
(typeof (error === null || error === void 0 ? void 0 : error.message) === 'string' && (error.message.startsWith('Server signedTransaction') ||
|
|
113
|
+
error.message.startsWith('Server preInstruction')));
|
|
114
|
+
}
|
|
115
|
+
class BorshCursor {
|
|
116
|
+
constructor(data, offset) {
|
|
117
|
+
this.data = data;
|
|
118
|
+
this.offset = offset;
|
|
119
|
+
}
|
|
120
|
+
requireBytes(length, field) {
|
|
121
|
+
if (this.offset + length > this.data.length) {
|
|
122
|
+
throw new Error(`Server signedTransaction has malformed Bounded instruction data while reading ${field}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
readU8(field) {
|
|
126
|
+
this.requireBytes(1, field);
|
|
127
|
+
return this.data[this.offset++];
|
|
128
|
+
}
|
|
129
|
+
readU32(field) {
|
|
130
|
+
this.requireBytes(4, field);
|
|
131
|
+
const value = this.data[this.offset] |
|
|
132
|
+
(this.data[this.offset + 1] << 8) |
|
|
133
|
+
(this.data[this.offset + 2] << 16) |
|
|
134
|
+
(this.data[this.offset + 3] << 24);
|
|
135
|
+
this.offset += 4;
|
|
136
|
+
return value >>> 0;
|
|
137
|
+
}
|
|
138
|
+
skip(length, field) {
|
|
139
|
+
this.requireBytes(length, field);
|
|
140
|
+
this.offset += length;
|
|
141
|
+
}
|
|
142
|
+
readString(field) {
|
|
143
|
+
const length = this.readU32(`${field} length`);
|
|
144
|
+
this.requireBytes(length, field);
|
|
145
|
+
const raw = this.data.slice(this.offset, this.offset + length);
|
|
146
|
+
this.offset += length;
|
|
147
|
+
return Buffer$1.from(raw).toString('utf8');
|
|
148
|
+
}
|
|
149
|
+
skipBytes(field) {
|
|
150
|
+
const length = this.readU32(`${field} length`);
|
|
151
|
+
this.skip(length, field);
|
|
152
|
+
}
|
|
153
|
+
isAtEnd() {
|
|
154
|
+
return this.offset === this.data.length;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
function normalizeOnchainPath(path) {
|
|
158
|
+
let normalized = path.startsWith('/') ? path.slice(1) : path;
|
|
159
|
+
if (normalized.endsWith('*') && normalized.length > 1) {
|
|
160
|
+
normalized = normalized.slice(0, -1);
|
|
161
|
+
}
|
|
162
|
+
if (normalized.endsWith('/')) {
|
|
163
|
+
normalized = normalized.slice(0, -1);
|
|
164
|
+
}
|
|
165
|
+
return normalized;
|
|
166
|
+
}
|
|
167
|
+
function skipBoundedFieldValue(cursor) {
|
|
168
|
+
const option = cursor.readU8('operation value option');
|
|
169
|
+
if (option === 0)
|
|
170
|
+
return;
|
|
171
|
+
if (option !== 1) {
|
|
172
|
+
throw new Error('Server signedTransaction has malformed Bounded field value option');
|
|
173
|
+
}
|
|
174
|
+
const variant = cursor.readU8('operation value variant');
|
|
175
|
+
switch (variant) {
|
|
176
|
+
case 0:
|
|
177
|
+
case 1:
|
|
178
|
+
cursor.skip(8, 'operation numeric value');
|
|
179
|
+
return;
|
|
180
|
+
case 2:
|
|
181
|
+
cursor.skip(1, 'operation bool value');
|
|
182
|
+
return;
|
|
183
|
+
case 3:
|
|
184
|
+
cursor.readString('operation string value');
|
|
185
|
+
return;
|
|
186
|
+
case 4:
|
|
187
|
+
cursor.skip(32, 'operation address value');
|
|
188
|
+
return;
|
|
189
|
+
default:
|
|
190
|
+
throw new Error(`Server signedTransaction has unsupported Bounded field value variant: ${variant}`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
function skipBoundedFieldOperation(cursor) {
|
|
194
|
+
cursor.readString('operation key');
|
|
195
|
+
skipBoundedFieldValue(cursor);
|
|
196
|
+
cursor.skip(1, 'operation kind');
|
|
197
|
+
}
|
|
198
|
+
function skipBoundedTxData(cursor, isV2) {
|
|
199
|
+
cursor.readString('txData plugin function key');
|
|
200
|
+
cursor.skipBytes('txData bytes');
|
|
201
|
+
if (isV2) {
|
|
202
|
+
cursor.skipBytes('txData raIndices');
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
const raIndexCount = cursor.readU32('txData raIndices length');
|
|
206
|
+
cursor.skip(raIndexCount * 8, 'txData raIndices');
|
|
207
|
+
}
|
|
208
|
+
function parseBoundedSetDocumentsInstruction(data) {
|
|
209
|
+
if (data.length < 8) {
|
|
210
|
+
throw new Error('Server signedTransaction has malformed Bounded instruction data');
|
|
211
|
+
}
|
|
212
|
+
const discriminator = Array.from(data.slice(0, 8)).join(',');
|
|
213
|
+
if (!ALLOWED_BOUNDED_SET_DISCRIMINATORS.has(discriminator)) {
|
|
214
|
+
throw new Error('Server signedTransaction contains unsupported Bounded instruction');
|
|
215
|
+
}
|
|
216
|
+
const isV2 = discriminator === SET_DOCUMENTS_V2_DISCRIMINATOR;
|
|
217
|
+
const cursor = new BorshCursor(data, 8);
|
|
218
|
+
const appId = cursor.readString('appId');
|
|
219
|
+
const documentPaths = [];
|
|
220
|
+
const documentCount = cursor.readU32('documents length');
|
|
221
|
+
for (let i = 0; i < documentCount; i++) {
|
|
222
|
+
documentPaths.push(normalizeOnchainPath(cursor.readString('document path')));
|
|
223
|
+
const operationCount = cursor.readU32('operations length');
|
|
224
|
+
for (let j = 0; j < operationCount; j++) {
|
|
225
|
+
skipBoundedFieldOperation(cursor);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
const deletePaths = [];
|
|
229
|
+
const deleteCount = cursor.readU32('delete paths length');
|
|
230
|
+
for (let i = 0; i < deleteCount; i++) {
|
|
231
|
+
deletePaths.push(normalizeOnchainPath(cursor.readString('delete path')));
|
|
232
|
+
}
|
|
233
|
+
const txDataCount = cursor.readU32('txData length');
|
|
234
|
+
for (let i = 0; i < txDataCount; i++) {
|
|
235
|
+
skipBoundedTxData(cursor, isV2);
|
|
236
|
+
}
|
|
237
|
+
const simulate = cursor.readU8('simulate');
|
|
238
|
+
if (simulate !== 0 && simulate !== 1) {
|
|
239
|
+
throw new Error('Server signedTransaction has malformed Bounded simulate flag');
|
|
240
|
+
}
|
|
241
|
+
if (!cursor.isAtEnd()) {
|
|
242
|
+
throw new Error('Server signedTransaction has trailing Bounded instruction data');
|
|
243
|
+
}
|
|
244
|
+
return { appId, documentPaths, deletePaths };
|
|
245
|
+
}
|
|
246
|
+
function assertSamePathSet(expectedPaths, actualPaths) {
|
|
247
|
+
const expected = new Set(expectedPaths.map(normalizeOnchainPath).filter(Boolean));
|
|
248
|
+
const actual = new Set(actualPaths.map(normalizeOnchainPath).filter(Boolean));
|
|
249
|
+
const missing = [...expected].filter(path => !actual.has(path));
|
|
250
|
+
const extra = [...actual].filter(path => !expected.has(path));
|
|
251
|
+
if (missing.length > 0 || extra.length > 0) {
|
|
252
|
+
const details = [
|
|
253
|
+
missing.length ? `missing paths: ${missing.join(', ')}` : '',
|
|
254
|
+
extra.length ? `unexpected paths: ${extra.join(', ')}` : '',
|
|
255
|
+
].filter(Boolean).join('; ');
|
|
256
|
+
throw new Error(`Server signedTransaction Bounded instruction paths do not match requested write paths (${details})`);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
function validatePresignedSignedTransaction(transaction, sol) {
|
|
260
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
261
|
+
const accountKeys = transaction.message.staticAccountKeys;
|
|
262
|
+
let boundedInstructionCount = 0;
|
|
263
|
+
const actualWritePaths = [];
|
|
264
|
+
for (const ix of transaction.message.compiledInstructions) {
|
|
265
|
+
if (ix.programIdIndex >= accountKeys.length) {
|
|
266
|
+
throw new Error('Server signedTransaction has program ID in lookup table (not allowed)');
|
|
267
|
+
}
|
|
268
|
+
const programId = accountKeys[ix.programIdIndex].toBase58();
|
|
269
|
+
if (!ALLOWED_SERVER_TX_PROGRAMS.has(programId)) {
|
|
270
|
+
throw new Error(`Server signedTransaction contains unauthorized program: ${programId}`);
|
|
271
|
+
}
|
|
272
|
+
const data = ix.data instanceof Uint8Array ? ix.data : Buffer$1.from((_a = ix.data) !== null && _a !== void 0 ? _a : []);
|
|
273
|
+
if (programId === SYSTEM_PROGRAM_ID) {
|
|
274
|
+
throw new Error('Server signedTransaction contains unauthorized System Program instruction');
|
|
275
|
+
}
|
|
276
|
+
if (programId === BOUNDED_PROGRAM_MAINNET || programId === BOUNDED_PROGRAM_DEVNET) {
|
|
277
|
+
boundedInstructionCount += 1;
|
|
278
|
+
const parsed = parseBoundedSetDocumentsInstruction(data);
|
|
279
|
+
if (parsed.appId !== sol.appId) {
|
|
280
|
+
throw new Error('Server signedTransaction Bounded instruction appId does not match configured appId');
|
|
281
|
+
}
|
|
282
|
+
actualWritePaths.push(...parsed.documentPaths, ...parsed.deletePaths);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
if (boundedInstructionCount !== 1) {
|
|
286
|
+
throw new Error('Server signedTransaction must contain exactly one Bounded set-documents instruction');
|
|
287
|
+
}
|
|
288
|
+
const expectedWritePaths = [
|
|
289
|
+
...(((_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)),
|
|
290
|
+
...((_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 : []),
|
|
291
|
+
];
|
|
292
|
+
assertSamePathSet(expectedWritePaths, actualWritePaths);
|
|
293
|
+
}
|
|
294
|
+
function assertAllowedServerPreInstructions(preInstructions) {
|
|
295
|
+
for (const [index, ix] of (preInstructions !== null && preInstructions !== void 0 ? preInstructions : []).entries()) {
|
|
296
|
+
if (ix.programId.equals(SystemProgram.programId)) {
|
|
297
|
+
throw new Error(`Server preInstruction[${index}] contains unauthorized System Program instruction`);
|
|
298
|
+
}
|
|
79
299
|
}
|
|
80
300
|
}
|
|
81
301
|
class SolanaKeypairProvider {
|
|
82
|
-
constructor(
|
|
83
|
-
this.
|
|
84
|
-
this.
|
|
302
|
+
constructor(rpcUrl, serverKeypair) {
|
|
303
|
+
this.rpcUrl = rpcUrl;
|
|
304
|
+
this.serverKeypair = serverKeypair;
|
|
85
305
|
}
|
|
86
306
|
get keypair() {
|
|
87
|
-
|
|
88
|
-
|
|
307
|
+
if (!this.serverKeypair) {
|
|
308
|
+
throw new Error('Server keypair is required; use createWalletClient({ keypair }) or pass a Keypair to SolanaKeypairProvider.');
|
|
309
|
+
}
|
|
310
|
+
return this.serverKeypair;
|
|
89
311
|
}
|
|
90
312
|
/* ----------------------------------------------------------- *
|
|
91
313
|
* (Auth stubs – fill in later if needed)
|
|
@@ -119,7 +341,8 @@ class SolanaKeypairProvider {
|
|
|
119
341
|
*
|
|
120
342
|
* This method handles blockhash and transaction confirmation automatically - you do NOT need to
|
|
121
343
|
* set recentBlockhash or lastValidBlockHeight on the transaction before calling this method.
|
|
122
|
-
*
|
|
344
|
+
* Requires an explicit RPC URL configured on the provider because this method has no
|
|
345
|
+
* Solana transaction payload network to resolve.
|
|
123
346
|
*
|
|
124
347
|
* @param transaction - The transaction to sign and submit (Transaction or VersionedTransaction)
|
|
125
348
|
* @param feePayer - Optional fee payer public key. If not provided and the transaction doesn't
|
|
@@ -203,20 +426,23 @@ class SolanaKeypairProvider {
|
|
|
203
426
|
}
|
|
204
427
|
return { signature: sig, txInfo };
|
|
205
428
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
if (network === 'solana_devnet') {
|
|
211
|
-
return SOLANA_DEVNET_RPC_URL;
|
|
429
|
+
requireSupportedNetwork(network) {
|
|
430
|
+
const networkName = network === null || network === void 0 ? void 0 : network.trim();
|
|
431
|
+
if (!networkName) {
|
|
432
|
+
throw new Error(`Supported sol.network is required for Solana transaction submission; expected one of: ${SUPPORTED_SOLANA_NETWORKS.join(', ')}.`);
|
|
212
433
|
}
|
|
213
|
-
|
|
214
|
-
|
|
434
|
+
if (!SUPPORTED_SOLANA_NETWORK_SET.has(networkName)) {
|
|
435
|
+
throw new Error(`Unsupported Solana network "${networkName}". Supported networks: ${SUPPORTED_SOLANA_NETWORKS.join(', ')}.`);
|
|
215
436
|
}
|
|
216
|
-
|
|
217
|
-
|
|
437
|
+
return networkName;
|
|
438
|
+
}
|
|
439
|
+
getRpcUrl() {
|
|
440
|
+
var _a;
|
|
441
|
+
const explicitRpcUrl = (_a = this.rpcUrl) === null || _a === void 0 ? void 0 : _a.trim();
|
|
442
|
+
if (explicitRpcUrl) {
|
|
443
|
+
return explicitRpcUrl;
|
|
218
444
|
}
|
|
219
|
-
|
|
445
|
+
throw new Error('Solana RPC URL is required for server keypair transaction submission; pass an explicit rpcUrl when creating SolanaKeypairProvider.');
|
|
220
446
|
}
|
|
221
447
|
/* ----------------------------------------------------------- *
|
|
222
448
|
* Transaction runner
|
|
@@ -225,6 +451,8 @@ class SolanaKeypairProvider {
|
|
|
225
451
|
if (!sol)
|
|
226
452
|
throw new Error('Solana transaction data required');
|
|
227
453
|
const kp = this.keypair;
|
|
454
|
+
this.requireSupportedNetwork(sol.network);
|
|
455
|
+
const rpcUrl = this.getRpcUrl();
|
|
228
456
|
// Helper for duck typing - checks if transaction is legacy Transaction vs VersionedTransaction
|
|
229
457
|
const isLegacyTx = (tx) => {
|
|
230
458
|
return 'recentBlockhash' in tx && !('message' in tx && 'staticAccountKeys' in tx.message);
|
|
@@ -273,11 +501,14 @@ class SolanaKeypairProvider {
|
|
|
273
501
|
let errorMessage = "";
|
|
274
502
|
while (retries < 5) {
|
|
275
503
|
try {
|
|
276
|
-
toReturn = await this.runTransactionInner(sol, opts, wallet, kp, deduped);
|
|
504
|
+
toReturn = await this.runTransactionInner(sol, opts, wallet, kp, deduped, rpcUrl);
|
|
277
505
|
didPass = true;
|
|
278
506
|
break;
|
|
279
507
|
}
|
|
280
508
|
catch (error) {
|
|
509
|
+
if (isPermanentPresignedTransactionError(error)) {
|
|
510
|
+
throw error;
|
|
511
|
+
}
|
|
281
512
|
console.log("Error building and sending transaction on retry:", retries, error);
|
|
282
513
|
await new Promise(resolve => setTimeout(resolve, delay));
|
|
283
514
|
// Exponential backoff
|
|
@@ -291,9 +522,8 @@ class SolanaKeypairProvider {
|
|
|
291
522
|
}
|
|
292
523
|
return toReturn;
|
|
293
524
|
}
|
|
294
|
-
async runTransactionInner(sol, opts, wallet, kp, deduped) {
|
|
525
|
+
async runTransactionInner(sol, opts, wallet, kp, deduped, rpcUrl) {
|
|
295
526
|
var _a, _b, _c;
|
|
296
|
-
let rpcUrl = this.getRpcUrl(sol.network);
|
|
297
527
|
const connection = new Connection(rpcUrl, 'confirmed');
|
|
298
528
|
const anchorProvider = new anchor.AnchorProvider(connection, wallet, anchor.AnchorProvider.defaultOptions());
|
|
299
529
|
const app_id = sol.appId;
|
|
@@ -307,11 +537,16 @@ class SolanaKeypairProvider {
|
|
|
307
537
|
let lastValidBlockHeight;
|
|
308
538
|
if (sol.signedTransaction) {
|
|
309
539
|
tx = VersionedTransaction.deserialize(Buffer$1.from(sol.signedTransaction, 'base64'));
|
|
310
|
-
|
|
311
|
-
blockhash =
|
|
312
|
-
|
|
540
|
+
validatePresignedSignedTransaction(tx, sol);
|
|
541
|
+
blockhash = tx.message.recentBlockhash;
|
|
542
|
+
const isValid = await connection.isBlockhashValid(blockhash, { commitment: 'confirmed' });
|
|
543
|
+
if (!isValid.value) {
|
|
544
|
+
throw new Error(`${PRESIGNED_BLOCKHASH_ERROR}; request a fresh server-built transaction.`);
|
|
545
|
+
}
|
|
546
|
+
lastValidBlockHeight = 0;
|
|
313
547
|
}
|
|
314
548
|
else {
|
|
549
|
+
assertAllowedServerPreInstructions(sol.preInstructions);
|
|
315
550
|
const result = await buildSetDocumentsTransaction(connection, sol.txArgs[0].idl, anchorProvider, kp.publicKey, {
|
|
316
551
|
app_id,
|
|
317
552
|
documents: sol.txArgs[0].setDocumentData,
|
|
@@ -326,31 +561,33 @@ class SolanaKeypairProvider {
|
|
|
326
561
|
const isLegacyTx = (t) => {
|
|
327
562
|
return 'recentBlockhash' in t && !('message' in t && 'staticAccountKeys' in t.message);
|
|
328
563
|
};
|
|
329
|
-
// Refresh blockhash and lastValidBlockHeight
|
|
330
564
|
// Use duck typing instead of instanceof to handle multiple @solana/web3.js versions
|
|
331
565
|
if (isLegacyTx(tx)) {
|
|
566
|
+
if (!sol.signedTransaction) {
|
|
567
|
+
tx.recentBlockhash = blockhash;
|
|
568
|
+
tx.lastValidBlockHeight = lastValidBlockHeight;
|
|
569
|
+
tx.feePayer = kp.publicKey;
|
|
570
|
+
}
|
|
332
571
|
tx.partialSign(kp);
|
|
333
|
-
tx.recentBlockhash = blockhash;
|
|
334
|
-
tx.lastValidBlockHeight = lastValidBlockHeight;
|
|
335
|
-
tx.feePayer = kp.publicKey;
|
|
336
572
|
}
|
|
337
573
|
else {
|
|
574
|
+
if (!sol.signedTransaction) {
|
|
575
|
+
tx.message.recentBlockhash = blockhash;
|
|
576
|
+
}
|
|
338
577
|
tx.sign([kp]);
|
|
339
|
-
tx.message.recentBlockhash = blockhash;
|
|
340
578
|
}
|
|
341
|
-
// 3️⃣ Optional priority
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
if (
|
|
347
|
-
|
|
348
|
-
tx
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
// Rebuild the transaction with the new priority fee
|
|
579
|
+
// 3️⃣ Optional priority-fee instruction. Pre-signed transactions are
|
|
580
|
+
// immutable at this point; preserve their exact message and skip estimation.
|
|
581
|
+
if (!sol.signedTransaction) {
|
|
582
|
+
const rawUnsigned = tx.serialize({ requireAllSignatures: false });
|
|
583
|
+
const microLamports = await fetchPriorityFee(connection.rpcEndpoint, rawUnsigned);
|
|
584
|
+
if (microLamports != null) {
|
|
585
|
+
// Use duck typing instead of instanceof to handle multiple @solana/web3.js versions
|
|
586
|
+
if (isLegacyTx(tx)) {
|
|
587
|
+
tx.instructions.unshift(ComputeBudgetProgram.setComputeUnitPrice({ microLamports }));
|
|
588
|
+
tx.partialSign(kp);
|
|
589
|
+
}
|
|
590
|
+
else {
|
|
354
591
|
const roundTwo = await buildSetDocumentsTransaction(connection, sol.txArgs[0].idl, anchorProvider, kp.publicKey, {
|
|
355
592
|
app_id,
|
|
356
593
|
documents: sol.txArgs[0].setDocumentData,
|
|
@@ -360,8 +597,8 @@ class SolanaKeypairProvider {
|
|
|
360
597
|
tx = roundTwo.tx;
|
|
361
598
|
blockhash = roundTwo.blockhash;
|
|
362
599
|
lastValidBlockHeight = roundTwo.lastValidBlockHeight;
|
|
363
|
-
tx.sign([kp]);
|
|
364
600
|
tx.message.recentBlockhash = blockhash;
|
|
601
|
+
tx.sign([kp]);
|
|
365
602
|
}
|
|
366
603
|
}
|
|
367
604
|
}
|
|
@@ -431,47 +668,6 @@ class OffchainAuthProvider {
|
|
|
431
668
|
}
|
|
432
669
|
}
|
|
433
670
|
|
|
434
|
-
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
|
-
async function getAuthProvider() {
|
|
439
|
-
var _a;
|
|
440
|
-
const config = await getConfig();
|
|
441
|
-
if (currentAuthProvider) {
|
|
442
|
-
// If provider exists but chain is "offchain" and it's not already wrapped, rewrap it
|
|
443
|
-
if (config.chain === "offchain" && !(currentAuthProvider instanceof OffchainAuthProvider)) {
|
|
444
|
-
currentAuthProvider = new OffchainAuthProvider(currentAuthProvider);
|
|
445
|
-
}
|
|
446
|
-
return currentAuthProvider;
|
|
447
|
-
}
|
|
448
|
-
currentAuthProvider = await matchAuthProvider((_a = config.rpcUrl) !== null && _a !== void 0 ? _a : null);
|
|
449
|
-
// Wrap with OffchainAuthProvider for offchain chain
|
|
450
|
-
if (config.chain === "offchain") {
|
|
451
|
-
currentAuthProvider = new OffchainAuthProvider(currentAuthProvider);
|
|
452
|
-
}
|
|
453
|
-
return currentAuthProvider;
|
|
454
|
-
}
|
|
455
|
-
async function matchAuthProvider(rpcUrl) {
|
|
456
|
-
return new SolanaKeypairProvider(rpcUrl);
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
let authProviderInstance = null;
|
|
460
|
-
async function init(newConfig) {
|
|
461
|
-
// Initialize config first so getAuthProvider can access it
|
|
462
|
-
// Server-side skips backend init since it already has all needed config
|
|
463
|
-
await init$1(Object.assign(Object.assign({}, newConfig), { isServer: true, skipBackendInit: true }));
|
|
464
|
-
// Get the auth provider (which will wrap it if chain is "offchain")
|
|
465
|
-
authProviderInstance = await getAuthProvider();
|
|
466
|
-
// Update config with the wrapped provider
|
|
467
|
-
await init$1(Object.assign(Object.assign({}, newConfig), { authProvider: authProviderInstance, isServer: true, skipBackendInit: true }));
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
// Wrapper for getIdToken - passes isServer=true for server-side usage
|
|
471
|
-
async function getIdToken() {
|
|
472
|
-
return getIdToken$1(true);
|
|
473
|
-
}
|
|
474
|
-
|
|
475
671
|
var __rest = (undefined && undefined.__rest) || function (s, e) {
|
|
476
672
|
var t = {};
|
|
477
673
|
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
@@ -528,8 +724,8 @@ class WalletClient {
|
|
|
528
724
|
this.sessionManager = sessionManager;
|
|
529
725
|
this.address = address;
|
|
530
726
|
this.live = {
|
|
531
|
-
intent: (roomPath, intent, opts = {}) => live.intent(roomPath, intent, this.mergeLiveIntentOptions(opts)),
|
|
532
|
-
status: (roomPath, opts = {}) => live.status(roomPath, Object.assign(Object.assign({}, opts), { headers: this.sanitizeHeaders(opts.headers) })),
|
|
727
|
+
intent: (roomPath, intent, opts = {}) => live$1.intent(roomPath, intent, this.mergeLiveIntentOptions(opts)),
|
|
728
|
+
status: (roomPath, opts = {}) => live$1.status(roomPath, Object.assign(Object.assign({}, opts), { headers: this.sanitizeHeaders(opts.headers) })),
|
|
533
729
|
};
|
|
534
730
|
}
|
|
535
731
|
/* ---- Auth override helpers ---- */
|
|
@@ -578,51 +774,50 @@ class WalletClient {
|
|
|
578
774
|
}
|
|
579
775
|
/* ---- Data operations ---- */
|
|
580
776
|
async set(path, document, options) {
|
|
581
|
-
return set(path, document, this.mergeSetOverrides(options));
|
|
777
|
+
return set$1(path, document, this.mergeSetOverrides(options));
|
|
582
778
|
}
|
|
583
779
|
async setMany(many, options) {
|
|
584
|
-
return setMany(many, this.mergeSetOverrides(options));
|
|
780
|
+
return setMany$1(many, this.mergeSetOverrides(options));
|
|
585
781
|
}
|
|
586
782
|
async setFile(path, file, options) {
|
|
587
|
-
return setFile(path, file, { _overrides: this.mergeReadOverrides(options === null || options === void 0 ? void 0 : options._overrides) });
|
|
783
|
+
return setFile$1(path, file, { _overrides: this.mergeReadOverrides(options === null || options === void 0 ? void 0 : options._overrides) });
|
|
588
784
|
}
|
|
589
785
|
async get(path, opts) {
|
|
590
|
-
return get(path, Object.assign(Object.assign({}, opts), { _overrides: this.mergeReadOverrides(opts === null || opts === void 0 ? void 0 : opts._overrides) }));
|
|
786
|
+
return get$1(path, Object.assign(Object.assign({}, opts), { _overrides: this.mergeReadOverrides(opts === null || opts === void 0 ? void 0 : opts._overrides) }));
|
|
591
787
|
}
|
|
592
788
|
async getMany(paths, opts) {
|
|
593
|
-
return getMany(paths, Object.assign(Object.assign({}, opts), { _overrides: this.mergeReadOverrides() }));
|
|
789
|
+
return getMany$1(paths, Object.assign(Object.assign({}, opts), { _overrides: this.mergeReadOverrides() }));
|
|
594
790
|
}
|
|
595
791
|
async getFiles(path, options) {
|
|
596
|
-
return getFiles(path, { _overrides: this.mergeReadOverrides(options === null || options === void 0 ? void 0 : options._overrides) });
|
|
792
|
+
return getFiles$1(path, { _overrides: this.mergeReadOverrides(options === null || options === void 0 ? void 0 : options._overrides) });
|
|
597
793
|
}
|
|
598
794
|
async search(path, query, opts = {}) {
|
|
599
|
-
return search(path, query, Object.assign(Object.assign({}, opts), { _overrides: this.mergeReadOverrides(opts === null || opts === void 0 ? void 0 : opts._overrides) }));
|
|
795
|
+
return search$1(path, query, Object.assign(Object.assign({}, opts), { _overrides: this.mergeReadOverrides(opts === null || opts === void 0 ? void 0 : opts._overrides) }));
|
|
600
796
|
}
|
|
601
797
|
async runQuery(absolutePath, queryName, queryArgs, opts) {
|
|
602
|
-
return runQuery(absolutePath, queryName, queryArgs, Object.assign(Object.assign({}, opts), { _overrides: this.mergeReadOverrides(opts === null || opts === void 0 ? void 0 : opts._overrides) }));
|
|
798
|
+
return runQuery$1(absolutePath, queryName, queryArgs, Object.assign(Object.assign({}, opts), { _overrides: this.mergeReadOverrides(opts === null || opts === void 0 ? void 0 : opts._overrides) }));
|
|
603
799
|
}
|
|
604
800
|
async runQueryMany(many, opts) {
|
|
605
|
-
return runQueryMany(many, Object.assign(Object.assign({}, opts), { _overrides: this.mergeReadOverrides(opts === null || opts === void 0 ? void 0 : opts._overrides) }));
|
|
801
|
+
return runQueryMany$1(many, Object.assign(Object.assign({}, opts), { _overrides: this.mergeReadOverrides(opts === null || opts === void 0 ? void 0 : opts._overrides) }));
|
|
606
802
|
}
|
|
607
803
|
async runExpression(expression, queryArgs, options) {
|
|
608
|
-
return runExpression(expression, queryArgs, Object.assign(Object.assign({}, options), { _overrides: this.mergeReadOverrides(options === null || options === void 0 ? void 0 : options._overrides) }));
|
|
804
|
+
return runExpression$1(expression, queryArgs, Object.assign(Object.assign({}, options), { _overrides: this.mergeReadOverrides(options === null || options === void 0 ? void 0 : options._overrides) }));
|
|
609
805
|
}
|
|
610
806
|
async runExpressionMany(many) {
|
|
611
807
|
const ovr = this.mergeReadOverrides();
|
|
612
|
-
return runExpressionMany(many.map(m => (Object.assign(Object.assign({}, m), { _overrides: ovr }))));
|
|
808
|
+
return runExpressionMany$1(many.map(m => (Object.assign(Object.assign({}, m), { _overrides: ovr }))));
|
|
613
809
|
}
|
|
614
810
|
async count(path, opts = {}) {
|
|
615
|
-
return count(path, Object.assign(Object.assign({}, opts), { _overrides: this.mergeReadOverrides(opts === null || opts === void 0 ? void 0 : opts._overrides) }));
|
|
811
|
+
return count$1(path, Object.assign(Object.assign({}, opts), { _overrides: this.mergeReadOverrides(opts === null || opts === void 0 ? void 0 : opts._overrides) }));
|
|
616
812
|
}
|
|
617
813
|
async aggregate(path, operation, opts = {}) {
|
|
618
|
-
return aggregate(path, operation, Object.assign(Object.assign({}, opts), { _overrides: this.mergeReadOverrides(opts === null || opts === void 0 ? void 0 : opts._overrides) }));
|
|
814
|
+
return aggregate$1(path, operation, Object.assign(Object.assign({}, opts), { _overrides: this.mergeReadOverrides(opts === null || opts === void 0 ? void 0 : opts._overrides) }));
|
|
619
815
|
}
|
|
620
816
|
async queryAggregate(path, spec, opts = {}) {
|
|
621
|
-
return queryAggregate(path, spec, Object.assign(Object.assign({}, opts), { _overrides: this.mergeReadOverrides(opts === null || opts === void 0 ? void 0 : opts._overrides) }));
|
|
817
|
+
return queryAggregate$1(path, spec, Object.assign(Object.assign({}, opts), { _overrides: this.mergeReadOverrides(opts === null || opts === void 0 ? void 0 : opts._overrides) }));
|
|
622
818
|
}
|
|
623
819
|
/**
|
|
624
|
-
* Subscribe to real-time updates as THIS wallet's identity.
|
|
625
|
-
* `subscribe`, this needs no `BOUNDED_PRIVATE_KEY` env var — the WS connection
|
|
820
|
+
* Subscribe to real-time updates as THIS wallet's identity. The WS connection
|
|
626
821
|
* authenticates with the wallet's own session (so read rules see the right
|
|
627
822
|
* principal), and is scoped to its own connection so it never crosses another
|
|
628
823
|
* identity. Accepts a bare callback or `{ onData, onError, filter, ... }`.
|
|
@@ -631,22 +826,21 @@ class WalletClient {
|
|
|
631
826
|
async subscribe(path, options) {
|
|
632
827
|
const opts = typeof options === 'function' ? { onData: options } : Object.assign({}, options);
|
|
633
828
|
const ovr = this.buildOverrides();
|
|
634
|
-
return subscribe(path, Object.assign(Object.assign({}, opts), { _overrides: {
|
|
829
|
+
return subscribe$1(path, Object.assign(Object.assign({}, opts), { _overrides: {
|
|
635
830
|
_getAuthHeaders: ovr._getAuthHeaders,
|
|
636
831
|
_clearAuth: ovr._clearAuth,
|
|
637
832
|
_walletAddress: ovr._walletAddress,
|
|
638
833
|
} }));
|
|
639
834
|
}
|
|
640
835
|
/**
|
|
641
|
-
* Invoke a deployed Bounded Function AS this wallet's identity.
|
|
642
|
-
*
|
|
643
|
-
*
|
|
644
|
-
* rule + `ctx.user` reflect this client. Returns the function's JSON; throws
|
|
836
|
+
* Invoke a deployed Bounded Function AS this wallet's identity. The dispatcher
|
|
837
|
+
* sees the wallet's verified session, so the function's `auth` rule +
|
|
838
|
+
* `ctx.user` reflect this client. Returns the function's JSON; throws
|
|
645
839
|
* `FunctionInvokeError` on 401/403/404/503.
|
|
646
840
|
*/
|
|
647
841
|
async invoke(name, args = {}, opts = {}) {
|
|
648
842
|
const ovr = this.buildOverrides();
|
|
649
|
-
return functions.invoke(name, args, Object.assign(Object.assign({}, opts), { _overrides: { _getAuthHeaders: ovr._getAuthHeaders } }));
|
|
843
|
+
return functions$1.invoke(name, args, Object.assign(Object.assign({}, opts), { _overrides: { _getAuthHeaders: ovr._getAuthHeaders } }));
|
|
650
844
|
}
|
|
651
845
|
/* ---- Signing operations (use provider directly) ---- */
|
|
652
846
|
async signMessage(message) {
|
|
@@ -937,5 +1131,5 @@ async function verifyWebhook(rawBody, headers, opts = {}) {
|
|
|
937
1131
|
return payload;
|
|
938
1132
|
}
|
|
939
1133
|
|
|
940
|
-
export { DEFAULT_WEBHOOK_KEYS_URL, InMemoryReplayStore, WalletClient, WebhookVerificationError, clearWebhookKeyCache, clearWebhookReplayCache, createWalletClient, getAuthProvider, getIdToken, init, verifyWebhook };
|
|
1134
|
+
export { DEFAULT_WEBHOOK_KEYS_URL, InMemoryReplayStore, WalletClient, WebhookVerificationError, aggregate, clearWebhookKeyCache, clearWebhookReplayCache, count, createWalletClient, functions, get, getAuthProvider, getFiles, getIdToken, getMany, init, invokeFunction, live, liveIntent, liveStatus, queryAggregate, runExpression, runExpressionMany, runQuery, runQueryMany, search, set, setFile, setMany, signAndSubmitTransaction, signMessage, signTransaction, subscribe, verifyWebhook };
|
|
941
1135
|
//# sourceMappingURL=index.mjs.map
|