@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.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var core = require('@bounded-sh/core');
|
|
4
|
-
var buffer = require('buffer');
|
|
5
4
|
var web3_js = require('@solana/web3.js');
|
|
5
|
+
var bs58 = require('bs58');
|
|
6
|
+
var buffer = require('buffer');
|
|
6
7
|
var anchor = require('@coral-xyz/anchor');
|
|
7
8
|
var nacl = require('tweetnacl');
|
|
8
|
-
var bs58 = require('bs58');
|
|
9
9
|
var crypto = require('crypto');
|
|
10
10
|
|
|
11
11
|
function _interopNamespaceDefault(e) {
|
|
@@ -28,40 +28,70 @@ function _interopNamespaceDefault(e) {
|
|
|
28
28
|
var anchor__namespace = /*#__PURE__*/_interopNamespaceDefault(anchor);
|
|
29
29
|
var crypto__namespace = /*#__PURE__*/_interopNamespaceDefault(crypto);
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
* ------------------------------------------------------------- */
|
|
38
|
-
const ENV_VARS = ['BOUNDED_PRIVATE_KEY']; // base-58 or JSON array
|
|
39
|
-
function loadKeypairFromEnv() {
|
|
40
|
-
var _a;
|
|
41
|
-
let secret;
|
|
42
|
-
let found;
|
|
43
|
-
for (const name of ENV_VARS) {
|
|
44
|
-
const v = (_a = process === null || process === void 0 ? void 0 : process.env) === null || _a === void 0 ? void 0 : _a[name];
|
|
45
|
-
if (v) {
|
|
46
|
-
secret = v;
|
|
47
|
-
found = name;
|
|
48
|
-
break;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
if (!secret) {
|
|
52
|
-
throw new Error(`No server keypair: set ${ENV_VARS[0]} to a base58 secret key (or JSON array), ` +
|
|
53
|
-
`or pass one explicitly via createWalletClient({ keypair }).`);
|
|
54
|
-
}
|
|
55
|
-
try {
|
|
56
|
-
const secretKey = secret.trim().startsWith('[')
|
|
57
|
-
? Uint8Array.from(JSON.parse(secret))
|
|
58
|
-
: bs58.decode(secret.trim());
|
|
59
|
-
return web3_js.Keypair.fromSecretKey(secretKey);
|
|
60
|
-
}
|
|
61
|
-
catch (err) {
|
|
62
|
-
throw new Error(`Unable to parse ${found}. Ensure it is valid base58 or a JSON array.`);
|
|
63
|
-
}
|
|
31
|
+
async function init(newConfig) {
|
|
32
|
+
await core.init(Object.assign(Object.assign({}, newConfig), { authProvider: null, isServer: true, skipBackendInit: true }));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function getAuthProvider() {
|
|
36
|
+
throw new Error('Server auth providers are not process-global. Use createWalletClient({ keypair }) and call that client\'s methods.');
|
|
64
37
|
}
|
|
38
|
+
|
|
39
|
+
function explicitClientOnly(name) {
|
|
40
|
+
throw new Error(`${name} is not available as a tarobase-server top-level auth operation. ` +
|
|
41
|
+
'Use createWalletClient({ keypair }) and call the returned client method.');
|
|
42
|
+
}
|
|
43
|
+
async function get(..._args) { explicitClientOnly('get'); }
|
|
44
|
+
async function getMany(..._args) { explicitClientOnly('getMany'); }
|
|
45
|
+
async function set(..._args) { explicitClientOnly('set'); }
|
|
46
|
+
async function setMany(..._args) { explicitClientOnly('setMany'); }
|
|
47
|
+
async function setFile(..._args) { explicitClientOnly('setFile'); }
|
|
48
|
+
async function getFiles(..._args) { explicitClientOnly('getFiles'); }
|
|
49
|
+
async function search(..._args) { explicitClientOnly('search'); }
|
|
50
|
+
async function queryAggregate(..._args) { explicitClientOnly('queryAggregate'); }
|
|
51
|
+
async function runQuery(..._args) { explicitClientOnly('runQuery'); }
|
|
52
|
+
async function runQueryMany(..._args) { explicitClientOnly('runQueryMany'); }
|
|
53
|
+
async function runExpression(..._args) { explicitClientOnly('runExpression'); }
|
|
54
|
+
async function runExpressionMany(..._args) { explicitClientOnly('runExpressionMany'); }
|
|
55
|
+
async function signMessage(..._args) { explicitClientOnly('signMessage'); }
|
|
56
|
+
async function signTransaction(..._args) { explicitClientOnly('signTransaction'); }
|
|
57
|
+
async function signAndSubmitTransaction(..._args) { explicitClientOnly('signAndSubmitTransaction'); }
|
|
58
|
+
async function count(..._args) { explicitClientOnly('count'); }
|
|
59
|
+
async function aggregate(..._args) { explicitClientOnly('aggregate'); }
|
|
60
|
+
async function subscribe(..._args) { explicitClientOnly('subscribe'); }
|
|
61
|
+
async function invokeFunction(..._args) { explicitClientOnly('invokeFunction'); }
|
|
62
|
+
async function liveIntent(..._args) { explicitClientOnly('liveIntent'); }
|
|
63
|
+
async function liveStatus(..._args) { explicitClientOnly('liveStatus'); }
|
|
64
|
+
const functions = {
|
|
65
|
+
invoke: async (..._args) => explicitClientOnly('functions.invoke'),
|
|
66
|
+
};
|
|
67
|
+
const live = {
|
|
68
|
+
intent: async (..._args) => explicitClientOnly('live.intent'),
|
|
69
|
+
status: async (..._args) => explicitClientOnly('live.status'),
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
async function getIdToken() {
|
|
73
|
+
throw new Error('getIdToken is not available as a tarobase-server top-level auth operation. Use createWalletClient({ keypair }) and call the returned client methods.');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const PRESIGNED_BLOCKHASH_ERROR = 'Server signedTransaction blockhash is stale or expired';
|
|
77
|
+
const BOUNDED_PROGRAM_MAINNET = 'poof4b5pk1L9tmThvBmaABjcyjfhFGbMbQP5BXk2QZp';
|
|
78
|
+
const BOUNDED_PROGRAM_DEVNET = 'taro6CvKqwrYrDc16ufYgzQ2NZcyyVKStffbtudrhRu';
|
|
79
|
+
const COMPUTE_BUDGET_PROGRAM = 'ComputeBudget111111111111111111111111111111';
|
|
80
|
+
const SYSTEM_PROGRAM_ID = '11111111111111111111111111111111';
|
|
81
|
+
const SUPPORTED_SOLANA_NETWORKS = ['solana_devnet', 'solana_mainnet', 'surfnet'];
|
|
82
|
+
const SUPPORTED_SOLANA_NETWORK_SET = new Set(SUPPORTED_SOLANA_NETWORKS);
|
|
83
|
+
const ALLOWED_SERVER_TX_PROGRAMS = new Set([
|
|
84
|
+
BOUNDED_PROGRAM_MAINNET,
|
|
85
|
+
BOUNDED_PROGRAM_DEVNET,
|
|
86
|
+
COMPUTE_BUDGET_PROGRAM,
|
|
87
|
+
SYSTEM_PROGRAM_ID,
|
|
88
|
+
]);
|
|
89
|
+
const SET_DOCUMENTS_DISCRIMINATOR = '79,46,72,73,24,79,66,245';
|
|
90
|
+
const SET_DOCUMENTS_V2_DISCRIMINATOR = '22,236,242,185,145,61,26,39';
|
|
91
|
+
const ALLOWED_BOUNDED_SET_DISCRIMINATORS = new Set([
|
|
92
|
+
SET_DOCUMENTS_DISCRIMINATOR,
|
|
93
|
+
SET_DOCUMENTS_V2_DISCRIMINATOR,
|
|
94
|
+
]);
|
|
65
95
|
/* ──────────────────────────────────────────────────────────
|
|
66
96
|
* Helper – fetch getPriorityFeeEstimate
|
|
67
97
|
* ──────────────────────────────────────────────────────── */
|
|
@@ -84,28 +114,221 @@ async function fetchPriorityFee(rpcEndpoint, rawUnsignedTxBuffer) {
|
|
|
84
114
|
});
|
|
85
115
|
const data = await res.json();
|
|
86
116
|
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;
|
|
87
|
-
console.log("Got fee from Helius", fee);
|
|
88
117
|
if (fee != null && fee > 0) {
|
|
89
118
|
return Math.ceil(Number(fee) * 1.2);
|
|
90
119
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
return Math.ceil(10000 * 1.2);
|
|
94
|
-
}
|
|
120
|
+
console.warn("Priority-fee estimate returned no positive fee; skipping priority fee instruction");
|
|
121
|
+
return null;
|
|
95
122
|
}
|
|
96
123
|
catch (err) {
|
|
97
|
-
console.warn("Priority
|
|
98
|
-
return
|
|
124
|
+
console.warn("Priority-fee estimate failed; skipping priority fee instruction:", err);
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
function isPresignedBlockhashError(error) {
|
|
129
|
+
return typeof (error === null || error === void 0 ? void 0 : error.message) === 'string' && error.message.includes(PRESIGNED_BLOCKHASH_ERROR);
|
|
130
|
+
}
|
|
131
|
+
function isPermanentPresignedTransactionError(error) {
|
|
132
|
+
return isPresignedBlockhashError(error) ||
|
|
133
|
+
(typeof (error === null || error === void 0 ? void 0 : error.message) === 'string' && (error.message.startsWith('Server signedTransaction') ||
|
|
134
|
+
error.message.startsWith('Server preInstruction')));
|
|
135
|
+
}
|
|
136
|
+
class BorshCursor {
|
|
137
|
+
constructor(data, offset) {
|
|
138
|
+
this.data = data;
|
|
139
|
+
this.offset = offset;
|
|
140
|
+
}
|
|
141
|
+
requireBytes(length, field) {
|
|
142
|
+
if (this.offset + length > this.data.length) {
|
|
143
|
+
throw new Error(`Server signedTransaction has malformed Bounded instruction data while reading ${field}`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
readU8(field) {
|
|
147
|
+
this.requireBytes(1, field);
|
|
148
|
+
return this.data[this.offset++];
|
|
149
|
+
}
|
|
150
|
+
readU32(field) {
|
|
151
|
+
this.requireBytes(4, field);
|
|
152
|
+
const value = this.data[this.offset] |
|
|
153
|
+
(this.data[this.offset + 1] << 8) |
|
|
154
|
+
(this.data[this.offset + 2] << 16) |
|
|
155
|
+
(this.data[this.offset + 3] << 24);
|
|
156
|
+
this.offset += 4;
|
|
157
|
+
return value >>> 0;
|
|
158
|
+
}
|
|
159
|
+
skip(length, field) {
|
|
160
|
+
this.requireBytes(length, field);
|
|
161
|
+
this.offset += length;
|
|
162
|
+
}
|
|
163
|
+
readString(field) {
|
|
164
|
+
const length = this.readU32(`${field} length`);
|
|
165
|
+
this.requireBytes(length, field);
|
|
166
|
+
const raw = this.data.slice(this.offset, this.offset + length);
|
|
167
|
+
this.offset += length;
|
|
168
|
+
return buffer.Buffer.from(raw).toString('utf8');
|
|
169
|
+
}
|
|
170
|
+
skipBytes(field) {
|
|
171
|
+
const length = this.readU32(`${field} length`);
|
|
172
|
+
this.skip(length, field);
|
|
173
|
+
}
|
|
174
|
+
isAtEnd() {
|
|
175
|
+
return this.offset === this.data.length;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
function normalizeOnchainPath(path) {
|
|
179
|
+
let normalized = path.startsWith('/') ? path.slice(1) : path;
|
|
180
|
+
if (normalized.endsWith('*') && normalized.length > 1) {
|
|
181
|
+
normalized = normalized.slice(0, -1);
|
|
182
|
+
}
|
|
183
|
+
if (normalized.endsWith('/')) {
|
|
184
|
+
normalized = normalized.slice(0, -1);
|
|
185
|
+
}
|
|
186
|
+
return normalized;
|
|
187
|
+
}
|
|
188
|
+
function skipBoundedFieldValue(cursor) {
|
|
189
|
+
const option = cursor.readU8('operation value option');
|
|
190
|
+
if (option === 0)
|
|
191
|
+
return;
|
|
192
|
+
if (option !== 1) {
|
|
193
|
+
throw new Error('Server signedTransaction has malformed Bounded field value option');
|
|
194
|
+
}
|
|
195
|
+
const variant = cursor.readU8('operation value variant');
|
|
196
|
+
switch (variant) {
|
|
197
|
+
case 0:
|
|
198
|
+
case 1:
|
|
199
|
+
cursor.skip(8, 'operation numeric value');
|
|
200
|
+
return;
|
|
201
|
+
case 2:
|
|
202
|
+
cursor.skip(1, 'operation bool value');
|
|
203
|
+
return;
|
|
204
|
+
case 3:
|
|
205
|
+
cursor.readString('operation string value');
|
|
206
|
+
return;
|
|
207
|
+
case 4:
|
|
208
|
+
cursor.skip(32, 'operation address value');
|
|
209
|
+
return;
|
|
210
|
+
default:
|
|
211
|
+
throw new Error(`Server signedTransaction has unsupported Bounded field value variant: ${variant}`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
function skipBoundedFieldOperation(cursor) {
|
|
215
|
+
cursor.readString('operation key');
|
|
216
|
+
skipBoundedFieldValue(cursor);
|
|
217
|
+
cursor.skip(1, 'operation kind');
|
|
218
|
+
}
|
|
219
|
+
function skipBoundedTxData(cursor, isV2) {
|
|
220
|
+
cursor.readString('txData plugin function key');
|
|
221
|
+
cursor.skipBytes('txData bytes');
|
|
222
|
+
if (isV2) {
|
|
223
|
+
cursor.skipBytes('txData raIndices');
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
const raIndexCount = cursor.readU32('txData raIndices length');
|
|
227
|
+
cursor.skip(raIndexCount * 8, 'txData raIndices');
|
|
228
|
+
}
|
|
229
|
+
function parseBoundedSetDocumentsInstruction(data) {
|
|
230
|
+
if (data.length < 8) {
|
|
231
|
+
throw new Error('Server signedTransaction has malformed Bounded instruction data');
|
|
232
|
+
}
|
|
233
|
+
const discriminator = Array.from(data.slice(0, 8)).join(',');
|
|
234
|
+
if (!ALLOWED_BOUNDED_SET_DISCRIMINATORS.has(discriminator)) {
|
|
235
|
+
throw new Error('Server signedTransaction contains unsupported Bounded instruction');
|
|
236
|
+
}
|
|
237
|
+
const isV2 = discriminator === SET_DOCUMENTS_V2_DISCRIMINATOR;
|
|
238
|
+
const cursor = new BorshCursor(data, 8);
|
|
239
|
+
const appId = cursor.readString('appId');
|
|
240
|
+
const documentPaths = [];
|
|
241
|
+
const documentCount = cursor.readU32('documents length');
|
|
242
|
+
for (let i = 0; i < documentCount; i++) {
|
|
243
|
+
documentPaths.push(normalizeOnchainPath(cursor.readString('document path')));
|
|
244
|
+
const operationCount = cursor.readU32('operations length');
|
|
245
|
+
for (let j = 0; j < operationCount; j++) {
|
|
246
|
+
skipBoundedFieldOperation(cursor);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
const deletePaths = [];
|
|
250
|
+
const deleteCount = cursor.readU32('delete paths length');
|
|
251
|
+
for (let i = 0; i < deleteCount; i++) {
|
|
252
|
+
deletePaths.push(normalizeOnchainPath(cursor.readString('delete path')));
|
|
253
|
+
}
|
|
254
|
+
const txDataCount = cursor.readU32('txData length');
|
|
255
|
+
for (let i = 0; i < txDataCount; i++) {
|
|
256
|
+
skipBoundedTxData(cursor, isV2);
|
|
257
|
+
}
|
|
258
|
+
const simulate = cursor.readU8('simulate');
|
|
259
|
+
if (simulate !== 0 && simulate !== 1) {
|
|
260
|
+
throw new Error('Server signedTransaction has malformed Bounded simulate flag');
|
|
261
|
+
}
|
|
262
|
+
if (!cursor.isAtEnd()) {
|
|
263
|
+
throw new Error('Server signedTransaction has trailing Bounded instruction data');
|
|
264
|
+
}
|
|
265
|
+
return { appId, documentPaths, deletePaths };
|
|
266
|
+
}
|
|
267
|
+
function assertSamePathSet(expectedPaths, actualPaths) {
|
|
268
|
+
const expected = new Set(expectedPaths.map(normalizeOnchainPath).filter(Boolean));
|
|
269
|
+
const actual = new Set(actualPaths.map(normalizeOnchainPath).filter(Boolean));
|
|
270
|
+
const missing = [...expected].filter(path => !actual.has(path));
|
|
271
|
+
const extra = [...actual].filter(path => !expected.has(path));
|
|
272
|
+
if (missing.length > 0 || extra.length > 0) {
|
|
273
|
+
const details = [
|
|
274
|
+
missing.length ? `missing paths: ${missing.join(', ')}` : '',
|
|
275
|
+
extra.length ? `unexpected paths: ${extra.join(', ')}` : '',
|
|
276
|
+
].filter(Boolean).join('; ');
|
|
277
|
+
throw new Error(`Server signedTransaction Bounded instruction paths do not match requested write paths (${details})`);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
function validatePresignedSignedTransaction(transaction, sol) {
|
|
281
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
282
|
+
const accountKeys = transaction.message.staticAccountKeys;
|
|
283
|
+
let boundedInstructionCount = 0;
|
|
284
|
+
const actualWritePaths = [];
|
|
285
|
+
for (const ix of transaction.message.compiledInstructions) {
|
|
286
|
+
if (ix.programIdIndex >= accountKeys.length) {
|
|
287
|
+
throw new Error('Server signedTransaction has program ID in lookup table (not allowed)');
|
|
288
|
+
}
|
|
289
|
+
const programId = accountKeys[ix.programIdIndex].toBase58();
|
|
290
|
+
if (!ALLOWED_SERVER_TX_PROGRAMS.has(programId)) {
|
|
291
|
+
throw new Error(`Server signedTransaction contains unauthorized program: ${programId}`);
|
|
292
|
+
}
|
|
293
|
+
const data = ix.data instanceof Uint8Array ? ix.data : buffer.Buffer.from((_a = ix.data) !== null && _a !== void 0 ? _a : []);
|
|
294
|
+
if (programId === SYSTEM_PROGRAM_ID) {
|
|
295
|
+
throw new Error('Server signedTransaction contains unauthorized System Program instruction');
|
|
296
|
+
}
|
|
297
|
+
if (programId === BOUNDED_PROGRAM_MAINNET || programId === BOUNDED_PROGRAM_DEVNET) {
|
|
298
|
+
boundedInstructionCount += 1;
|
|
299
|
+
const parsed = parseBoundedSetDocumentsInstruction(data);
|
|
300
|
+
if (parsed.appId !== sol.appId) {
|
|
301
|
+
throw new Error('Server signedTransaction Bounded instruction appId does not match configured appId');
|
|
302
|
+
}
|
|
303
|
+
actualWritePaths.push(...parsed.documentPaths, ...parsed.deletePaths);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
if (boundedInstructionCount !== 1) {
|
|
307
|
+
throw new Error('Server signedTransaction must contain exactly one Bounded set-documents instruction');
|
|
308
|
+
}
|
|
309
|
+
const expectedWritePaths = [
|
|
310
|
+
...(((_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)),
|
|
311
|
+
...((_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 : []),
|
|
312
|
+
];
|
|
313
|
+
assertSamePathSet(expectedWritePaths, actualWritePaths);
|
|
314
|
+
}
|
|
315
|
+
function assertAllowedServerPreInstructions(preInstructions) {
|
|
316
|
+
for (const [index, ix] of (preInstructions !== null && preInstructions !== void 0 ? preInstructions : []).entries()) {
|
|
317
|
+
if (ix.programId.equals(web3_js.SystemProgram.programId)) {
|
|
318
|
+
throw new Error(`Server preInstruction[${index}] contains unauthorized System Program instruction`);
|
|
319
|
+
}
|
|
99
320
|
}
|
|
100
321
|
}
|
|
101
322
|
class SolanaKeypairProvider {
|
|
102
|
-
constructor(
|
|
103
|
-
this.
|
|
104
|
-
this.
|
|
323
|
+
constructor(rpcUrl, serverKeypair) {
|
|
324
|
+
this.rpcUrl = rpcUrl;
|
|
325
|
+
this.serverKeypair = serverKeypair;
|
|
105
326
|
}
|
|
106
327
|
get keypair() {
|
|
107
|
-
|
|
108
|
-
|
|
328
|
+
if (!this.serverKeypair) {
|
|
329
|
+
throw new Error('Server keypair is required; use createWalletClient({ keypair }) or pass a Keypair to SolanaKeypairProvider.');
|
|
330
|
+
}
|
|
331
|
+
return this.serverKeypair;
|
|
109
332
|
}
|
|
110
333
|
/* ----------------------------------------------------------- *
|
|
111
334
|
* (Auth stubs – fill in later if needed)
|
|
@@ -139,7 +362,8 @@ class SolanaKeypairProvider {
|
|
|
139
362
|
*
|
|
140
363
|
* This method handles blockhash and transaction confirmation automatically - you do NOT need to
|
|
141
364
|
* set recentBlockhash or lastValidBlockHeight on the transaction before calling this method.
|
|
142
|
-
*
|
|
365
|
+
* Requires an explicit RPC URL configured on the provider because this method has no
|
|
366
|
+
* Solana transaction payload network to resolve.
|
|
143
367
|
*
|
|
144
368
|
* @param transaction - The transaction to sign and submit (Transaction or VersionedTransaction)
|
|
145
369
|
* @param feePayer - Optional fee payer public key. If not provided and the transaction doesn't
|
|
@@ -223,20 +447,23 @@ class SolanaKeypairProvider {
|
|
|
223
447
|
}
|
|
224
448
|
return { signature: sig, txInfo };
|
|
225
449
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
if (network === 'solana_devnet') {
|
|
231
|
-
return SOLANA_DEVNET_RPC_URL;
|
|
450
|
+
requireSupportedNetwork(network) {
|
|
451
|
+
const networkName = network === null || network === void 0 ? void 0 : network.trim();
|
|
452
|
+
if (!networkName) {
|
|
453
|
+
throw new Error(`Supported sol.network is required for Solana transaction submission; expected one of: ${SUPPORTED_SOLANA_NETWORKS.join(', ')}.`);
|
|
232
454
|
}
|
|
233
|
-
|
|
234
|
-
|
|
455
|
+
if (!SUPPORTED_SOLANA_NETWORK_SET.has(networkName)) {
|
|
456
|
+
throw new Error(`Unsupported Solana network "${networkName}". Supported networks: ${SUPPORTED_SOLANA_NETWORKS.join(', ')}.`);
|
|
235
457
|
}
|
|
236
|
-
|
|
237
|
-
|
|
458
|
+
return networkName;
|
|
459
|
+
}
|
|
460
|
+
getRpcUrl() {
|
|
461
|
+
var _a;
|
|
462
|
+
const explicitRpcUrl = (_a = this.rpcUrl) === null || _a === void 0 ? void 0 : _a.trim();
|
|
463
|
+
if (explicitRpcUrl) {
|
|
464
|
+
return explicitRpcUrl;
|
|
238
465
|
}
|
|
239
|
-
|
|
466
|
+
throw new Error('Solana RPC URL is required for server keypair transaction submission; pass an explicit rpcUrl when creating SolanaKeypairProvider.');
|
|
240
467
|
}
|
|
241
468
|
/* ----------------------------------------------------------- *
|
|
242
469
|
* Transaction runner
|
|
@@ -245,6 +472,8 @@ class SolanaKeypairProvider {
|
|
|
245
472
|
if (!sol)
|
|
246
473
|
throw new Error('Solana transaction data required');
|
|
247
474
|
const kp = this.keypair;
|
|
475
|
+
this.requireSupportedNetwork(sol.network);
|
|
476
|
+
const rpcUrl = this.getRpcUrl();
|
|
248
477
|
// Helper for duck typing - checks if transaction is legacy Transaction vs VersionedTransaction
|
|
249
478
|
const isLegacyTx = (tx) => {
|
|
250
479
|
return 'recentBlockhash' in tx && !('message' in tx && 'staticAccountKeys' in tx.message);
|
|
@@ -293,11 +522,14 @@ class SolanaKeypairProvider {
|
|
|
293
522
|
let errorMessage = "";
|
|
294
523
|
while (retries < 5) {
|
|
295
524
|
try {
|
|
296
|
-
toReturn = await this.runTransactionInner(sol, opts, wallet, kp, deduped);
|
|
525
|
+
toReturn = await this.runTransactionInner(sol, opts, wallet, kp, deduped, rpcUrl);
|
|
297
526
|
didPass = true;
|
|
298
527
|
break;
|
|
299
528
|
}
|
|
300
529
|
catch (error) {
|
|
530
|
+
if (isPermanentPresignedTransactionError(error)) {
|
|
531
|
+
throw error;
|
|
532
|
+
}
|
|
301
533
|
console.log("Error building and sending transaction on retry:", retries, error);
|
|
302
534
|
await new Promise(resolve => setTimeout(resolve, delay));
|
|
303
535
|
// Exponential backoff
|
|
@@ -311,9 +543,8 @@ class SolanaKeypairProvider {
|
|
|
311
543
|
}
|
|
312
544
|
return toReturn;
|
|
313
545
|
}
|
|
314
|
-
async runTransactionInner(sol, opts, wallet, kp, deduped) {
|
|
546
|
+
async runTransactionInner(sol, opts, wallet, kp, deduped, rpcUrl) {
|
|
315
547
|
var _a, _b, _c;
|
|
316
|
-
let rpcUrl = this.getRpcUrl(sol.network);
|
|
317
548
|
const connection = new web3_js.Connection(rpcUrl, 'confirmed');
|
|
318
549
|
const anchorProvider = new anchor__namespace.AnchorProvider(connection, wallet, anchor__namespace.AnchorProvider.defaultOptions());
|
|
319
550
|
const app_id = sol.appId;
|
|
@@ -327,11 +558,16 @@ class SolanaKeypairProvider {
|
|
|
327
558
|
let lastValidBlockHeight;
|
|
328
559
|
if (sol.signedTransaction) {
|
|
329
560
|
tx = web3_js.VersionedTransaction.deserialize(buffer.Buffer.from(sol.signedTransaction, 'base64'));
|
|
330
|
-
|
|
331
|
-
blockhash =
|
|
332
|
-
|
|
561
|
+
validatePresignedSignedTransaction(tx, sol);
|
|
562
|
+
blockhash = tx.message.recentBlockhash;
|
|
563
|
+
const isValid = await connection.isBlockhashValid(blockhash, { commitment: 'confirmed' });
|
|
564
|
+
if (!isValid.value) {
|
|
565
|
+
throw new Error(`${PRESIGNED_BLOCKHASH_ERROR}; request a fresh server-built transaction.`);
|
|
566
|
+
}
|
|
567
|
+
lastValidBlockHeight = 0;
|
|
333
568
|
}
|
|
334
569
|
else {
|
|
570
|
+
assertAllowedServerPreInstructions(sol.preInstructions);
|
|
335
571
|
const result = await core.buildSetDocumentsTransaction(connection, sol.txArgs[0].idl, anchorProvider, kp.publicKey, {
|
|
336
572
|
app_id,
|
|
337
573
|
documents: sol.txArgs[0].setDocumentData,
|
|
@@ -346,31 +582,33 @@ class SolanaKeypairProvider {
|
|
|
346
582
|
const isLegacyTx = (t) => {
|
|
347
583
|
return 'recentBlockhash' in t && !('message' in t && 'staticAccountKeys' in t.message);
|
|
348
584
|
};
|
|
349
|
-
// Refresh blockhash and lastValidBlockHeight
|
|
350
585
|
// Use duck typing instead of instanceof to handle multiple @solana/web3.js versions
|
|
351
586
|
if (isLegacyTx(tx)) {
|
|
587
|
+
if (!sol.signedTransaction) {
|
|
588
|
+
tx.recentBlockhash = blockhash;
|
|
589
|
+
tx.lastValidBlockHeight = lastValidBlockHeight;
|
|
590
|
+
tx.feePayer = kp.publicKey;
|
|
591
|
+
}
|
|
352
592
|
tx.partialSign(kp);
|
|
353
|
-
tx.recentBlockhash = blockhash;
|
|
354
|
-
tx.lastValidBlockHeight = lastValidBlockHeight;
|
|
355
|
-
tx.feePayer = kp.publicKey;
|
|
356
593
|
}
|
|
357
594
|
else {
|
|
595
|
+
if (!sol.signedTransaction) {
|
|
596
|
+
tx.message.recentBlockhash = blockhash;
|
|
597
|
+
}
|
|
358
598
|
tx.sign([kp]);
|
|
359
|
-
tx.message.recentBlockhash = blockhash;
|
|
360
599
|
}
|
|
361
|
-
// 3️⃣ Optional priority
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
if (
|
|
367
|
-
|
|
368
|
-
tx
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
// Rebuild the transaction with the new priority fee
|
|
600
|
+
// 3️⃣ Optional priority-fee instruction. Pre-signed transactions are
|
|
601
|
+
// immutable at this point; preserve their exact message and skip estimation.
|
|
602
|
+
if (!sol.signedTransaction) {
|
|
603
|
+
const rawUnsigned = tx.serialize({ requireAllSignatures: false });
|
|
604
|
+
const microLamports = await fetchPriorityFee(connection.rpcEndpoint, rawUnsigned);
|
|
605
|
+
if (microLamports != null) {
|
|
606
|
+
// Use duck typing instead of instanceof to handle multiple @solana/web3.js versions
|
|
607
|
+
if (isLegacyTx(tx)) {
|
|
608
|
+
tx.instructions.unshift(web3_js.ComputeBudgetProgram.setComputeUnitPrice({ microLamports }));
|
|
609
|
+
tx.partialSign(kp);
|
|
610
|
+
}
|
|
611
|
+
else {
|
|
374
612
|
const roundTwo = await core.buildSetDocumentsTransaction(connection, sol.txArgs[0].idl, anchorProvider, kp.publicKey, {
|
|
375
613
|
app_id,
|
|
376
614
|
documents: sol.txArgs[0].setDocumentData,
|
|
@@ -380,8 +618,8 @@ class SolanaKeypairProvider {
|
|
|
380
618
|
tx = roundTwo.tx;
|
|
381
619
|
blockhash = roundTwo.blockhash;
|
|
382
620
|
lastValidBlockHeight = roundTwo.lastValidBlockHeight;
|
|
383
|
-
tx.sign([kp]);
|
|
384
621
|
tx.message.recentBlockhash = blockhash;
|
|
622
|
+
tx.sign([kp]);
|
|
385
623
|
}
|
|
386
624
|
}
|
|
387
625
|
}
|
|
@@ -451,47 +689,6 @@ class OffchainAuthProvider {
|
|
|
451
689
|
}
|
|
452
690
|
}
|
|
453
691
|
|
|
454
|
-
let currentAuthProvider = null;
|
|
455
|
-
const SOLANA_DEVNET_RPC_URL = "https://idelle-8nxsep-fast-devnet.helius-rpc.com";
|
|
456
|
-
const SOLANA_MAINNET_RPC_URL = "https://celestia-cegncv-fast-mainnet.helius-rpc.com";
|
|
457
|
-
const SURFNET_RPC_URL = "https://surfpool.fly.dev";
|
|
458
|
-
async function getAuthProvider() {
|
|
459
|
-
var _a;
|
|
460
|
-
const config = await core.getConfig();
|
|
461
|
-
if (currentAuthProvider) {
|
|
462
|
-
// If provider exists but chain is "offchain" and it's not already wrapped, rewrap it
|
|
463
|
-
if (config.chain === "offchain" && !(currentAuthProvider instanceof OffchainAuthProvider)) {
|
|
464
|
-
currentAuthProvider = new OffchainAuthProvider(currentAuthProvider);
|
|
465
|
-
}
|
|
466
|
-
return currentAuthProvider;
|
|
467
|
-
}
|
|
468
|
-
currentAuthProvider = await matchAuthProvider((_a = config.rpcUrl) !== null && _a !== void 0 ? _a : null);
|
|
469
|
-
// Wrap with OffchainAuthProvider for offchain chain
|
|
470
|
-
if (config.chain === "offchain") {
|
|
471
|
-
currentAuthProvider = new OffchainAuthProvider(currentAuthProvider);
|
|
472
|
-
}
|
|
473
|
-
return currentAuthProvider;
|
|
474
|
-
}
|
|
475
|
-
async function matchAuthProvider(rpcUrl) {
|
|
476
|
-
return new SolanaKeypairProvider(rpcUrl);
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
let authProviderInstance = null;
|
|
480
|
-
async function init(newConfig) {
|
|
481
|
-
// Initialize config first so getAuthProvider can access it
|
|
482
|
-
// Server-side skips backend init since it already has all needed config
|
|
483
|
-
await core.init(Object.assign(Object.assign({}, newConfig), { isServer: true, skipBackendInit: true }));
|
|
484
|
-
// Get the auth provider (which will wrap it if chain is "offchain")
|
|
485
|
-
authProviderInstance = await getAuthProvider();
|
|
486
|
-
// Update config with the wrapped provider
|
|
487
|
-
await core.init(Object.assign(Object.assign({}, newConfig), { authProvider: authProviderInstance, isServer: true, skipBackendInit: true }));
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
// Wrapper for getIdToken - passes isServer=true for server-side usage
|
|
491
|
-
async function getIdToken() {
|
|
492
|
-
return core.getIdToken(true);
|
|
493
|
-
}
|
|
494
|
-
|
|
495
692
|
var __rest = (undefined && undefined.__rest) || function (s, e) {
|
|
496
693
|
var t = {};
|
|
497
694
|
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
@@ -641,8 +838,7 @@ class WalletClient {
|
|
|
641
838
|
return core.queryAggregate(path, spec, Object.assign(Object.assign({}, opts), { _overrides: this.mergeReadOverrides(opts === null || opts === void 0 ? void 0 : opts._overrides) }));
|
|
642
839
|
}
|
|
643
840
|
/**
|
|
644
|
-
* Subscribe to real-time updates as THIS wallet's identity.
|
|
645
|
-
* `subscribe`, this needs no `BOUNDED_PRIVATE_KEY` env var — the WS connection
|
|
841
|
+
* Subscribe to real-time updates as THIS wallet's identity. The WS connection
|
|
646
842
|
* authenticates with the wallet's own session (so read rules see the right
|
|
647
843
|
* principal), and is scoped to its own connection so it never crosses another
|
|
648
844
|
* identity. Accepts a bare callback or `{ onData, onError, filter, ... }`.
|
|
@@ -658,10 +854,9 @@ class WalletClient {
|
|
|
658
854
|
} }));
|
|
659
855
|
}
|
|
660
856
|
/**
|
|
661
|
-
* Invoke a deployed Bounded Function AS this wallet's identity.
|
|
662
|
-
*
|
|
663
|
-
*
|
|
664
|
-
* rule + `ctx.user` reflect this client. Returns the function's JSON; throws
|
|
857
|
+
* Invoke a deployed Bounded Function AS this wallet's identity. The dispatcher
|
|
858
|
+
* sees the wallet's verified session, so the function's `auth` rule +
|
|
859
|
+
* `ctx.user` reflect this client. Returns the function's JSON; throws
|
|
665
860
|
* `FunctionInvokeError` on 401/403/404/503.
|
|
666
861
|
*/
|
|
667
862
|
async invoke(name, args = {}, opts = {}) {
|
|
@@ -969,117 +1164,46 @@ Object.defineProperty(exports, "LiveIntentError", {
|
|
|
969
1164
|
enumerable: true,
|
|
970
1165
|
get: function () { return core.LiveIntentError; }
|
|
971
1166
|
});
|
|
972
|
-
Object.defineProperty(exports, "aggregate", {
|
|
973
|
-
enumerable: true,
|
|
974
|
-
get: function () { return core.aggregate; }
|
|
975
|
-
});
|
|
976
|
-
Object.defineProperty(exports, "count", {
|
|
977
|
-
enumerable: true,
|
|
978
|
-
get: function () { return core.count; }
|
|
979
|
-
});
|
|
980
|
-
Object.defineProperty(exports, "functions", {
|
|
981
|
-
enumerable: true,
|
|
982
|
-
get: function () { return core.functions; }
|
|
983
|
-
});
|
|
984
|
-
Object.defineProperty(exports, "get", {
|
|
985
|
-
enumerable: true,
|
|
986
|
-
get: function () { return core.get; }
|
|
987
|
-
});
|
|
988
1167
|
Object.defineProperty(exports, "getConfig", {
|
|
989
1168
|
enumerable: true,
|
|
990
1169
|
get: function () { return core.getConfig; }
|
|
991
1170
|
});
|
|
992
|
-
Object.defineProperty(exports, "
|
|
993
|
-
enumerable: true,
|
|
994
|
-
get: function () { return core.getFiles; }
|
|
995
|
-
});
|
|
996
|
-
Object.defineProperty(exports, "getMany", {
|
|
1171
|
+
Object.defineProperty(exports, "getWebhookKeysUrl", {
|
|
997
1172
|
enumerable: true,
|
|
998
|
-
get: function () { return core.
|
|
999
|
-
});
|
|
1000
|
-
Object.defineProperty(exports, "invokeFunction", {
|
|
1001
|
-
enumerable: true,
|
|
1002
|
-
get: function () { return core.invokeFunction; }
|
|
1003
|
-
});
|
|
1004
|
-
Object.defineProperty(exports, "live", {
|
|
1005
|
-
enumerable: true,
|
|
1006
|
-
get: function () { return core.live; }
|
|
1007
|
-
});
|
|
1008
|
-
Object.defineProperty(exports, "liveIntent", {
|
|
1009
|
-
enumerable: true,
|
|
1010
|
-
get: function () { return core.liveIntent; }
|
|
1011
|
-
});
|
|
1012
|
-
Object.defineProperty(exports, "liveStatus", {
|
|
1013
|
-
enumerable: true,
|
|
1014
|
-
get: function () { return core.liveStatus; }
|
|
1015
|
-
});
|
|
1016
|
-
Object.defineProperty(exports, "queryAggregate", {
|
|
1017
|
-
enumerable: true,
|
|
1018
|
-
get: function () { return core.queryAggregate; }
|
|
1019
|
-
});
|
|
1020
|
-
Object.defineProperty(exports, "runExpression", {
|
|
1021
|
-
enumerable: true,
|
|
1022
|
-
get: function () { return core.runExpression; }
|
|
1023
|
-
});
|
|
1024
|
-
Object.defineProperty(exports, "runExpressionMany", {
|
|
1025
|
-
enumerable: true,
|
|
1026
|
-
get: function () { return core.runExpressionMany; }
|
|
1027
|
-
});
|
|
1028
|
-
Object.defineProperty(exports, "runQuery", {
|
|
1029
|
-
enumerable: true,
|
|
1030
|
-
get: function () { return core.runQuery; }
|
|
1031
|
-
});
|
|
1032
|
-
Object.defineProperty(exports, "runQueryMany", {
|
|
1033
|
-
enumerable: true,
|
|
1034
|
-
get: function () { return core.runQueryMany; }
|
|
1035
|
-
});
|
|
1036
|
-
Object.defineProperty(exports, "search", {
|
|
1037
|
-
enumerable: true,
|
|
1038
|
-
get: function () { return core.search; }
|
|
1039
|
-
});
|
|
1040
|
-
Object.defineProperty(exports, "set", {
|
|
1041
|
-
enumerable: true,
|
|
1042
|
-
get: function () { return core.set; }
|
|
1043
|
-
});
|
|
1044
|
-
Object.defineProperty(exports, "setFile", {
|
|
1045
|
-
enumerable: true,
|
|
1046
|
-
get: function () { return core.setFile; }
|
|
1047
|
-
});
|
|
1048
|
-
Object.defineProperty(exports, "setMany", {
|
|
1049
|
-
enumerable: true,
|
|
1050
|
-
get: function () { return core.setMany; }
|
|
1051
|
-
});
|
|
1052
|
-
Object.defineProperty(exports, "signAndSubmitTransaction", {
|
|
1053
|
-
enumerable: true,
|
|
1054
|
-
get: function () { return core.signAndSubmitTransaction; }
|
|
1055
|
-
});
|
|
1056
|
-
Object.defineProperty(exports, "signMessage", {
|
|
1057
|
-
enumerable: true,
|
|
1058
|
-
get: function () { return core.signMessage; }
|
|
1059
|
-
});
|
|
1060
|
-
Object.defineProperty(exports, "signTransaction", {
|
|
1061
|
-
enumerable: true,
|
|
1062
|
-
get: function () { return core.signTransaction; }
|
|
1063
|
-
});
|
|
1064
|
-
Object.defineProperty(exports, "subscribe", {
|
|
1065
|
-
enumerable: true,
|
|
1066
|
-
get: function () { return core.subscribe; }
|
|
1173
|
+
get: function () { return core.getWebhookKeysUrl; }
|
|
1067
1174
|
});
|
|
1068
1175
|
exports.DEFAULT_WEBHOOK_KEYS_URL = DEFAULT_WEBHOOK_KEYS_URL;
|
|
1069
1176
|
exports.InMemoryReplayStore = InMemoryReplayStore;
|
|
1070
1177
|
exports.WalletClient = WalletClient;
|
|
1071
1178
|
exports.WebhookVerificationError = WebhookVerificationError;
|
|
1179
|
+
exports.aggregate = aggregate;
|
|
1072
1180
|
exports.clearWebhookKeyCache = clearWebhookKeyCache;
|
|
1073
1181
|
exports.clearWebhookReplayCache = clearWebhookReplayCache;
|
|
1182
|
+
exports.count = count;
|
|
1074
1183
|
exports.createWalletClient = createWalletClient;
|
|
1184
|
+
exports.functions = functions;
|
|
1185
|
+
exports.get = get;
|
|
1075
1186
|
exports.getAuthProvider = getAuthProvider;
|
|
1187
|
+
exports.getFiles = getFiles;
|
|
1076
1188
|
exports.getIdToken = getIdToken;
|
|
1189
|
+
exports.getMany = getMany;
|
|
1077
1190
|
exports.init = init;
|
|
1191
|
+
exports.invokeFunction = invokeFunction;
|
|
1192
|
+
exports.live = live;
|
|
1193
|
+
exports.liveIntent = liveIntent;
|
|
1194
|
+
exports.liveStatus = liveStatus;
|
|
1195
|
+
exports.queryAggregate = queryAggregate;
|
|
1196
|
+
exports.runExpression = runExpression;
|
|
1197
|
+
exports.runExpressionMany = runExpressionMany;
|
|
1198
|
+
exports.runQuery = runQuery;
|
|
1199
|
+
exports.runQueryMany = runQueryMany;
|
|
1200
|
+
exports.search = search;
|
|
1201
|
+
exports.set = set;
|
|
1202
|
+
exports.setFile = setFile;
|
|
1203
|
+
exports.setMany = setMany;
|
|
1204
|
+
exports.signAndSubmitTransaction = signAndSubmitTransaction;
|
|
1205
|
+
exports.signMessage = signMessage;
|
|
1206
|
+
exports.signTransaction = signTransaction;
|
|
1207
|
+
exports.subscribe = subscribe;
|
|
1078
1208
|
exports.verifyWebhook = verifyWebhook;
|
|
1079
|
-
Object.keys(core).forEach(function (k) {
|
|
1080
|
-
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
|
1081
|
-
enumerable: true,
|
|
1082
|
-
get: function () { return core[k]; }
|
|
1083
|
-
});
|
|
1084
|
-
});
|
|
1085
1209
|
//# sourceMappingURL=index.js.map
|