@xgamesfun/solana 0.27.1 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -17
- package/bin/xsolana.mjs +72 -7
- package/js/approve/details.mjs +1 -1
- package/js/approve/keyfile.mjs +242 -0
- package/js/approve/live-check.mjs +5 -2
- package/js/approve/server.mjs +94 -13
- package/js/approve/with-key.mjs +298 -0
- package/js/chain/lookup.js +11 -0
- package/js/chain/programs.js +49 -1
- package/js/chain/rpc.js +53 -6
- package/js/client/idl/loot.js +1 -1
- package/js/client/idl/loot.json +2 -2
- package/js/economy/check.mjs +2 -2
- package/js/economy/cost.mjs +20 -4
- package/js/economy/explain.mjs +3 -3
- package/js/economy/guide.mjs +2 -2
- package/js/economy/live.mjs +1 -1
- package/js/economy/plan.mjs +34 -6
- package/js/economy/review.mjs +1 -1
- package/js/economy/schema.json +1 -1
- package/js/economy/txsize.mjs +3 -2
- package/js/mcp/server.mjs +3 -3
- package/js/mcp/tools.mjs +50 -13
- package/js/play/index.js +21 -7
- package/package.json +5 -1
- package/release.json +6 -6
- package/skills/xsolana-economy/SKILL.md +58 -19
- package/skills/xsolana-economy/references/after-launch.md +43 -12
- package/skills/xsolana-economy/references/apps.md +1 -1
- package/skills/xsolana-economy/references/sentence-to-spec.md +3 -3
- package/skills/xsolana-economy/references/telling-the-person.md +18 -11
- package/skills/xsolana-economy/references/trust-and-cheating.md +4 -2
- package/types/approve/keyfile.d.mts +36 -0
- package/types/chain/lookup.d.ts +12 -0
- package/types/chain/programs.d.ts +22 -0
- package/types/chain/rpc.d.ts +15 -1
- package/types/economy/plan.d.mts +21 -4
- package/types/mcp/server.d.mts +4 -3
- package/types/mcp/tools.d.mts +14 -5
package/js/chain/rpc.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// (devnet: about 100 requests per 10 s per IP, 40 for any one method), so 429s are retried with backoff, and so are a
|
|
3
3
|
// gateway's passing failures (502, 503, 504).
|
|
4
4
|
import { fromBase64, toBase64 } from './bytes.js';
|
|
5
|
+
import { transactionId } from './tx.js';
|
|
5
6
|
|
|
6
7
|
// The options of a call made without any: read-only, and typed so the generated declarations take any option.
|
|
7
8
|
const NO_OPTIONS = Object.freeze(/** @type {Record<string, any>} */ ({}));
|
|
@@ -21,11 +22,19 @@ const BEHIND = [-32005, -32016];
|
|
|
21
22
|
|
|
22
23
|
/** A JSON-RPC failure, with the node's own message (and logs for a failed simulation). */
|
|
23
24
|
export class RpcError extends Error {
|
|
24
|
-
|
|
25
|
+
/**
|
|
26
|
+
* `code`: the node's own error code, when a node answered. `logs`: a failed dry run's program logs. `err`: the
|
|
27
|
+
* node's own name for why a transaction was refused ("BlockhashNotFound", "AlreadyProcessed", or the program's
|
|
28
|
+
* error as the chain writes it). `maybeTaken`: a send whose earlier try got no answer, so a node may have taken the
|
|
29
|
+
* transaction although the last try was refused.
|
|
30
|
+
*/
|
|
31
|
+
constructor(message, { code, logs, err, maybeTaken } = NO_OPTIONS) {
|
|
25
32
|
super(message);
|
|
26
33
|
this.name = 'RpcError';
|
|
27
34
|
this.code = code;
|
|
28
35
|
this.logs = logs;
|
|
36
|
+
this.err = err;
|
|
37
|
+
this.maybeTaken = Boolean(maybeTaken);
|
|
29
38
|
}
|
|
30
39
|
}
|
|
31
40
|
|
|
@@ -72,10 +81,27 @@ export function programError(err, logs) {
|
|
|
72
81
|
return last ?? (typeof err === 'string' ? err : JSON.stringify(err));
|
|
73
82
|
}
|
|
74
83
|
|
|
84
|
+
/**
|
|
85
|
+
* Whether a send was refused because the cluster has this very transaction already (it was sent before): the node's
|
|
86
|
+
* own word for it (`err`), or, from a node that gives only a sentence, its sentence. Never a program's log line: a
|
|
87
|
+
* dry run that failed inside a program is that program's refusal, whatever its words.
|
|
88
|
+
*/
|
|
89
|
+
export const alreadyProcessed = error =>
|
|
90
|
+
error?.err === 'AlreadyProcessed' ||
|
|
91
|
+
(error?.err === undefined &&
|
|
92
|
+
!error?.logs?.length &&
|
|
93
|
+
/this transaction has already been processed/i.test(String(error?.message ?? '')));
|
|
94
|
+
|
|
95
|
+
/** Whether a send's refusal is the dry run not knowing the transaction's blockhash. */
|
|
96
|
+
const blockhashNotFound = error =>
|
|
97
|
+
error?.data?.err === 'BlockhashNotFound' || /blockhash not found/i.test(String(error?.message ?? ''));
|
|
98
|
+
|
|
75
99
|
/** A client for one RPC URL. `fetchImpl` and `sleepImpl` are for tests. */
|
|
76
100
|
export function rpcClient(url, { fetchImpl = globalThis.fetch, sleepImpl = sleep } = {}) {
|
|
77
101
|
let id = 0;
|
|
78
102
|
async function call(method, params = []) {
|
|
103
|
+
// A try that got no answer (the connection dropped, a gateway failed): a node may have taken what it carried.
|
|
104
|
+
let unanswered = false;
|
|
79
105
|
for (let attempt = 0; ; attempt++) {
|
|
80
106
|
let response;
|
|
81
107
|
try {
|
|
@@ -86,6 +112,7 @@ export function rpcClient(url, { fetchImpl = globalThis.fetch, sleepImpl = sleep
|
|
|
86
112
|
signal: AbortSignal.timeout(TIMEOUT_MS),
|
|
87
113
|
});
|
|
88
114
|
} catch (error) {
|
|
115
|
+
unanswered = true;
|
|
89
116
|
if (attempt < RETRIES) {
|
|
90
117
|
await sleepImpl(500 * 2 ** attempt);
|
|
91
118
|
continue;
|
|
@@ -98,6 +125,7 @@ export function rpcClient(url, { fetchImpl = globalThis.fetch, sleepImpl = sleep
|
|
|
98
125
|
continue;
|
|
99
126
|
}
|
|
100
127
|
// Sending the same signed transaction again is safe: it has one signature, so it lands once at most.
|
|
128
|
+
if (PASSING.includes(response.status)) unanswered = true;
|
|
101
129
|
if (PASSING.includes(response.status) && attempt < RETRIES) {
|
|
102
130
|
await sleepImpl(500 * 2 ** attempt);
|
|
103
131
|
continue;
|
|
@@ -115,13 +143,25 @@ export function rpcClient(url, { fetchImpl = globalThis.fetch, sleepImpl = sleep
|
|
|
115
143
|
await sleepImpl(500 * 2 ** attempt);
|
|
116
144
|
continue;
|
|
117
145
|
}
|
|
146
|
+
// A send refused in its dry run for a blockhash this node has not seen yet ("Blockhash not found"): the node
|
|
147
|
+
// that gave the blockhash was a moment ahead of this one. Asked again, it (or another) knows it. A blockhash
|
|
148
|
+
// that is truly gone is refused the same way each time, and the last refusal is the answer.
|
|
149
|
+
if (method === 'sendTransaction' && blockhashNotFound(body?.error) && attempt < RETRIES) {
|
|
150
|
+
await sleepImpl(500 * 2 ** attempt);
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
118
153
|
// A send whose preflight failed ("Transaction simulation failed: … custom program error: 0x17b5") carries the
|
|
119
154
|
// logs, where the program says why in words.
|
|
120
155
|
const logs = body?.error?.data?.logs;
|
|
121
156
|
const said = logs?.length
|
|
122
157
|
? `The network refused this in a dry run: ${programError(body.error.data.err, logs)}`
|
|
123
158
|
: message;
|
|
124
|
-
throw new RpcError(said, {
|
|
159
|
+
throw new RpcError(said, {
|
|
160
|
+
code: body?.error?.code,
|
|
161
|
+
logs,
|
|
162
|
+
err: body?.error?.data?.err,
|
|
163
|
+
maybeTaken: method === 'sendTransaction' && unanswered,
|
|
164
|
+
});
|
|
125
165
|
}
|
|
126
166
|
return body.result;
|
|
127
167
|
}
|
|
@@ -211,10 +251,17 @@ export function rpcClient(url, { fetchImpl = globalThis.fetch, sleepImpl = sleep
|
|
|
211
251
|
};
|
|
212
252
|
},
|
|
213
253
|
async send(wire) {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
254
|
+
try {
|
|
255
|
+
return await call('sendTransaction', [
|
|
256
|
+
toBase64(wire),
|
|
257
|
+
{ encoding: 'base64', preflightCommitment: 'confirmed', maxRetries: 3 },
|
|
258
|
+
]);
|
|
259
|
+
} catch (e) {
|
|
260
|
+
// A transaction the cluster has taken already is refused as already processed: that is what a second send
|
|
261
|
+
// gets when the answer to the first was lost on its way back. It was sent; its signature is its first.
|
|
262
|
+
if (alreadyProcessed(e)) return transactionId(wire);
|
|
263
|
+
throw e;
|
|
264
|
+
}
|
|
218
265
|
},
|
|
219
266
|
signatureStatus,
|
|
220
267
|
/** Wait until a sent transaction is confirmed; throws with the program's reason when it failed. */
|