@orbinum/sdk 0.12.0 → 0.14.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/dist/index.d.mts +1 -532
- package/dist/index.d.ts +1 -532
- package/dist/index.js +62 -447
- package/dist/index.mjs +62 -446
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -35,7 +35,6 @@ __export(index_exports, {
|
|
|
35
35
|
EncryptedMemo: () => EncryptedMemo,
|
|
36
36
|
EvmClient: () => EvmClient,
|
|
37
37
|
EvmExplorer: () => EvmExplorer,
|
|
38
|
-
IndexerClient: () => IndexerClient,
|
|
39
38
|
KNOWN_PRECOMPILES: () => KNOWN_PRECOMPILES,
|
|
40
39
|
Keccak256: () => import_substrate_bindings3.Keccak256,
|
|
41
40
|
NoteBuilder: () => NoteBuilder,
|
|
@@ -173,18 +172,10 @@ var DEFAULT_MAX_RETRIES = 5;
|
|
|
173
172
|
var DEFAULT_BASE_BACKOFF_MS = 250;
|
|
174
173
|
var DEFAULT_MAX_BACKOFF_MS = 4e3;
|
|
175
174
|
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
176
|
-
async function
|
|
177
|
-
if (calls.length === 0) return [];
|
|
175
|
+
async function postJsonWithRetry(httpUrl, payload, options = {}) {
|
|
178
176
|
const maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
179
177
|
const baseBackoffMs = options.baseBackoffMs ?? DEFAULT_BASE_BACKOFF_MS;
|
|
180
178
|
const maxBackoffMs = options.maxBackoffMs ?? DEFAULT_MAX_BACKOFF_MS;
|
|
181
|
-
const body = calls.map((c, i) => ({
|
|
182
|
-
id: i,
|
|
183
|
-
jsonrpc: "2.0",
|
|
184
|
-
method: c.method,
|
|
185
|
-
params: c.params ?? []
|
|
186
|
-
}));
|
|
187
|
-
const payload = JSON.stringify(body);
|
|
188
179
|
let attempt = 0;
|
|
189
180
|
for (; ; ) {
|
|
190
181
|
const res = await fetch(httpUrl, {
|
|
@@ -192,21 +183,28 @@ async function jsonRpcBatch(httpUrl, calls, options = {}) {
|
|
|
192
183
|
headers: { "Content-Type": "application/json" },
|
|
193
184
|
body: payload
|
|
194
185
|
});
|
|
195
|
-
if (res.ok) {
|
|
196
|
-
const arr = await res.json();
|
|
197
|
-
const byId = new Map(arr.map((r) => [r.id, r]));
|
|
198
|
-
return calls.map((_, i) => byId.get(i)?.result ?? null);
|
|
199
|
-
}
|
|
200
186
|
const retryable = res.status === 429 || res.status === 503;
|
|
201
|
-
if (!retryable || attempt >= maxRetries)
|
|
202
|
-
throw new Error(`JSON-RPC HTTP ${res.status}: ${res.statusText}`);
|
|
203
|
-
}
|
|
187
|
+
if (!retryable || attempt >= maxRetries) return res;
|
|
204
188
|
const retryAfter = Number(res.headers.get("retry-after"));
|
|
205
189
|
const delayMs = Number.isFinite(retryAfter) && retryAfter > 0 ? retryAfter * 1e3 : Math.min(baseBackoffMs * 2 ** attempt, maxBackoffMs);
|
|
206
190
|
await sleep(delayMs);
|
|
207
191
|
attempt++;
|
|
208
192
|
}
|
|
209
193
|
}
|
|
194
|
+
async function jsonRpcBatch(httpUrl, calls, options = {}) {
|
|
195
|
+
if (calls.length === 0) return [];
|
|
196
|
+
const body = calls.map((c, i) => ({
|
|
197
|
+
id: i,
|
|
198
|
+
jsonrpc: "2.0",
|
|
199
|
+
method: c.method,
|
|
200
|
+
params: c.params ?? []
|
|
201
|
+
}));
|
|
202
|
+
const res = await postJsonWithRetry(httpUrl, JSON.stringify(body), options);
|
|
203
|
+
if (!res.ok) throw new Error(`JSON-RPC HTTP ${res.status}: ${res.statusText}`);
|
|
204
|
+
const arr = await res.json();
|
|
205
|
+
const byId = new Map(arr.map((r) => [r.id, r]));
|
|
206
|
+
return calls.map((_, i) => byId.get(i)?.result ?? null);
|
|
207
|
+
}
|
|
210
208
|
function wsUrlToHttp(wsUrl) {
|
|
211
209
|
if (wsUrl.startsWith("wss://")) return "https://" + wsUrl.slice("wss://".length);
|
|
212
210
|
if (wsUrl.startsWith("ws://")) return "http://" + wsUrl.slice("ws://".length);
|
|
@@ -230,15 +228,26 @@ var SubstrateClient = class _SubstrateClient {
|
|
|
230
228
|
static async connect(wsUrl, timeoutMs = 15e3) {
|
|
231
229
|
const provider = (0, import_ws.getWsProvider)(wsUrl);
|
|
232
230
|
const papi = (0, import_polkadot_api.createClient)(provider);
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
(
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
231
|
+
let timer;
|
|
232
|
+
try {
|
|
233
|
+
await Promise.race([
|
|
234
|
+
papi._request("system_name", []),
|
|
235
|
+
new Promise((_, reject) => {
|
|
236
|
+
timer = setTimeout(
|
|
237
|
+
() => reject(new Error(`Connection timeout (${timeoutMs}ms) to ${wsUrl}`)),
|
|
238
|
+
timeoutMs
|
|
239
|
+
);
|
|
240
|
+
})
|
|
241
|
+
]);
|
|
242
|
+
} catch (err) {
|
|
243
|
+
try {
|
|
244
|
+
papi.destroy();
|
|
245
|
+
} catch {
|
|
246
|
+
}
|
|
247
|
+
throw err;
|
|
248
|
+
} finally {
|
|
249
|
+
clearTimeout(timer);
|
|
250
|
+
}
|
|
242
251
|
return new _SubstrateClient(papi, wsUrlToHttp(wsUrl));
|
|
243
252
|
}
|
|
244
253
|
/**
|
|
@@ -613,11 +622,10 @@ var EvmClient = class {
|
|
|
613
622
|
* Throws on HTTP errors, RPC-level errors, or a `null` result.
|
|
614
623
|
*/
|
|
615
624
|
async request(method, params = []) {
|
|
616
|
-
const res = await
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
});
|
|
625
|
+
const res = await postJsonWithRetry(
|
|
626
|
+
this.rpcUrl,
|
|
627
|
+
JSON.stringify({ id: 1, jsonrpc: "2.0", method, params })
|
|
628
|
+
);
|
|
621
629
|
if (!res.ok) throw new Error(`EVM HTTP ${res.status}: ${res.statusText}`);
|
|
622
630
|
const json = await res.json();
|
|
623
631
|
if (json.error) {
|
|
@@ -639,11 +647,7 @@ var EvmClient = class {
|
|
|
639
647
|
method: c.method,
|
|
640
648
|
params: c.params ?? []
|
|
641
649
|
}));
|
|
642
|
-
const res = await
|
|
643
|
-
method: "POST",
|
|
644
|
-
headers: { "Content-Type": "application/json" },
|
|
645
|
-
body: JSON.stringify(body)
|
|
646
|
-
});
|
|
650
|
+
const res = await postJsonWithRetry(this.rpcUrl, JSON.stringify(body));
|
|
647
651
|
if (!res.ok) throw new Error(`EVM HTTP ${res.status}: ${res.statusText}`);
|
|
648
652
|
const arr = await res.json();
|
|
649
653
|
arr.sort((a, b) => (a.id ?? 0) - (b.id ?? 0));
|
|
@@ -692,16 +696,15 @@ var EvmClient = class {
|
|
|
692
696
|
}
|
|
693
697
|
/** Returns a transaction receipt by hash, or `null` if the transaction has not been mined yet. */
|
|
694
698
|
async getTransactionReceipt(txHash) {
|
|
695
|
-
const res = await
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
body: JSON.stringify({
|
|
699
|
+
const res = await postJsonWithRetry(
|
|
700
|
+
this.rpcUrl,
|
|
701
|
+
JSON.stringify({
|
|
699
702
|
id: 1,
|
|
700
703
|
jsonrpc: "2.0",
|
|
701
704
|
method: "eth_getTransactionReceipt",
|
|
702
705
|
params: [txHash]
|
|
703
706
|
})
|
|
704
|
-
|
|
707
|
+
);
|
|
705
708
|
if (!res.ok) throw new Error(`EVM HTTP ${res.status}: ${res.statusText}`);
|
|
706
709
|
const json = await res.json();
|
|
707
710
|
if (json.error) {
|
|
@@ -1151,397 +1154,6 @@ var EvmExplorer = class _EvmExplorer {
|
|
|
1151
1154
|
}
|
|
1152
1155
|
};
|
|
1153
1156
|
|
|
1154
|
-
// src/indexer/IndexerClient.ts
|
|
1155
|
-
var LOCAL_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "[::1]", "::1"]);
|
|
1156
|
-
function normalizeBaseUrl(baseUrl) {
|
|
1157
|
-
let url;
|
|
1158
|
-
try {
|
|
1159
|
-
url = new URL(baseUrl);
|
|
1160
|
-
} catch {
|
|
1161
|
-
throw new Error(`IndexerClient: invalid baseUrl ${JSON.stringify(baseUrl)}`);
|
|
1162
|
-
}
|
|
1163
|
-
const isLocal = LOCAL_HOSTS.has(url.hostname);
|
|
1164
|
-
if (url.protocol !== "https:" && !(url.protocol === "http:" && isLocal)) {
|
|
1165
|
-
throw new Error(
|
|
1166
|
-
`IndexerClient: baseUrl must use https:// (got ${url.protocol}//${url.hostname}). Plain http is only allowed for localhost.`
|
|
1167
|
-
);
|
|
1168
|
-
}
|
|
1169
|
-
return baseUrl.replace(/\/$/, "");
|
|
1170
|
-
}
|
|
1171
|
-
var IndexerClient = class _IndexerClient {
|
|
1172
|
-
baseUrl;
|
|
1173
|
-
timeoutMs;
|
|
1174
|
-
constructor(config) {
|
|
1175
|
-
this.baseUrl = normalizeBaseUrl(config.baseUrl);
|
|
1176
|
-
this.timeoutMs = config.timeoutMs ?? 1e4;
|
|
1177
|
-
}
|
|
1178
|
-
// ─── Internal helpers ──────────────────────────────────────────────────────
|
|
1179
|
-
async _fetchResponse(path) {
|
|
1180
|
-
const controller = new AbortController();
|
|
1181
|
-
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
1182
|
-
try {
|
|
1183
|
-
return await fetch(`${this.baseUrl}${path}`, { signal: controller.signal });
|
|
1184
|
-
} finally {
|
|
1185
|
-
clearTimeout(timer);
|
|
1186
|
-
}
|
|
1187
|
-
}
|
|
1188
|
-
async get(path) {
|
|
1189
|
-
const res = await this._fetchResponse(path);
|
|
1190
|
-
if (!res.ok) {
|
|
1191
|
-
throw new Error(`IndexerClient: HTTP ${res.status} for ${path}`);
|
|
1192
|
-
}
|
|
1193
|
-
return res.json();
|
|
1194
|
-
}
|
|
1195
|
-
async getOrNull(path) {
|
|
1196
|
-
const res = await this._fetchResponse(path);
|
|
1197
|
-
if (res.status === 404) return null;
|
|
1198
|
-
if (!res.ok) {
|
|
1199
|
-
throw new Error(`IndexerClient: HTTP ${res.status} for ${path}`);
|
|
1200
|
-
}
|
|
1201
|
-
return res.json();
|
|
1202
|
-
}
|
|
1203
|
-
buildQuery(params) {
|
|
1204
|
-
const entries = Object.entries(params).filter(([, v]) => v !== void 0);
|
|
1205
|
-
if (entries.length === 0) return "";
|
|
1206
|
-
const qs = entries.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`).join("&");
|
|
1207
|
-
return `?${qs}`;
|
|
1208
|
-
}
|
|
1209
|
-
// ─── Commitments ───────────────────────────────────────────────────────────
|
|
1210
|
-
/** Returns the total count of shielded commitments. */
|
|
1211
|
-
async getCommitmentsCount() {
|
|
1212
|
-
const res = await this.get("/shielded/commitments/count");
|
|
1213
|
-
return res.total;
|
|
1214
|
-
}
|
|
1215
|
-
/** Returns a paginated list of shielded commitments. */
|
|
1216
|
-
async getCommitments(params) {
|
|
1217
|
-
const qs = this.buildQuery({
|
|
1218
|
-
page: params?.page,
|
|
1219
|
-
limit: params?.limit,
|
|
1220
|
-
since_leaf_index: params?.sinceLeafIndex
|
|
1221
|
-
});
|
|
1222
|
-
return this.get(`/shielded/commitments${qs}`);
|
|
1223
|
-
}
|
|
1224
|
-
/** Returns a single commitment by its hex string, or null if not found. */
|
|
1225
|
-
async getCommitmentByHex(hex) {
|
|
1226
|
-
return this.getOrNull(
|
|
1227
|
-
`/shielded/commitments/${encodeURIComponent(hex)}`
|
|
1228
|
-
);
|
|
1229
|
-
}
|
|
1230
|
-
/**
|
|
1231
|
-
* Returns a paginated list of stealth scan hints ordered ascending by leafIndex.
|
|
1232
|
-
* Each hint contains only the fields required for ECDH triage and decryption:
|
|
1233
|
-
* leafIndex, commitmentHex, assetId, ephPkHex, encryptedMemo, circuitVersion.
|
|
1234
|
-
*
|
|
1235
|
-
* Use `sinceLeafIndex` for incremental scans (cursor = last seen leafIndex + 1).
|
|
1236
|
-
*/
|
|
1237
|
-
async getScanHints(params) {
|
|
1238
|
-
const qs = this.buildQuery({
|
|
1239
|
-
page: params?.page,
|
|
1240
|
-
limit: params?.limit,
|
|
1241
|
-
since_leaf_index: params?.sinceLeafIndex
|
|
1242
|
-
});
|
|
1243
|
-
return this.get(`/shielded/scan-hints${qs}`);
|
|
1244
|
-
}
|
|
1245
|
-
// ─── Nullifiers ────────────────────────────────────────────────────────────
|
|
1246
|
-
/** Returns a paginated list of spent nullifiers. */
|
|
1247
|
-
async getNullifiers(params) {
|
|
1248
|
-
const qs = this.buildQuery({ page: params?.page, limit: params?.limit });
|
|
1249
|
-
return this.get(`/shielded/nullifiers${qs}`);
|
|
1250
|
-
}
|
|
1251
|
-
/** Returns the spent/unspent status of a nullifier. */
|
|
1252
|
-
async getNullifierStatus(hex) {
|
|
1253
|
-
return this.get(
|
|
1254
|
-
`/shielded/nullifier/${encodeURIComponent(hex)}/status`
|
|
1255
|
-
);
|
|
1256
|
-
}
|
|
1257
|
-
/**
|
|
1258
|
-
* Downloads the full spent nullifier set and returns it as a Set of lowercase hex strings.
|
|
1259
|
-
*
|
|
1260
|
-
* The server sees an identical GET request regardless of which notes the wallet holds —
|
|
1261
|
-
* the intersection is computed locally (PIR-A privacy model).
|
|
1262
|
-
*
|
|
1263
|
-
* Kept as the FALLBACK for readers that don't serve sealed chunks yet (and for
|
|
1264
|
-
* small sets). New integrations should prefer the incremental chunk flow:
|
|
1265
|
-
* `getNullifierManifest` → `getNullifierChunk` for missing chunks →
|
|
1266
|
-
* `getNullifierTail`, persisting the set locally between rescans.
|
|
1267
|
-
*/
|
|
1268
|
-
async getAllSpentNullifiers() {
|
|
1269
|
-
const res = await this.get("/shielded/nullifiers/all");
|
|
1270
|
-
return new Set(res.data.map((h) => h.toLowerCase()));
|
|
1271
|
-
}
|
|
1272
|
-
/**
|
|
1273
|
-
* Universal index of the sealed nullifier chunks — identical request and
|
|
1274
|
-
* response for every caller (no client-supplied position: PIR-A preserved).
|
|
1275
|
-
*
|
|
1276
|
-
* Returns `null` when the reader does not serve chunks yet (404) — the
|
|
1277
|
-
* caller should fall back to `getAllSpentNullifiers`.
|
|
1278
|
-
*/
|
|
1279
|
-
async getNullifierManifest() {
|
|
1280
|
-
return this.getOrNull("/shielded/nullifiers/manifest");
|
|
1281
|
-
}
|
|
1282
|
-
/**
|
|
1283
|
-
* One sealed, immutable chunk of the spent-nullifier set (ascending hex,
|
|
1284
|
-
* lowercased). The digest comes from the manifest and lives in the URL, so
|
|
1285
|
-
* a corrected chunk is a different URL — safe to cache forever client-side.
|
|
1286
|
-
*/
|
|
1287
|
-
async getNullifierChunk(idx, digest) {
|
|
1288
|
-
const res = await this.get(
|
|
1289
|
-
`/shielded/nullifiers/chunks/${idx}/${encodeURIComponent(digest)}`
|
|
1290
|
-
);
|
|
1291
|
-
return res.data.map((h) => h.toLowerCase());
|
|
1292
|
-
}
|
|
1293
|
-
/**
|
|
1294
|
-
* The mutable remainder of the nullifier set after the last sealed chunk.
|
|
1295
|
-
* Identical request for every caller (no input). `afterChunks` lets the
|
|
1296
|
-
* client detect a chunk sealed between its manifest fetch and this one.
|
|
1297
|
-
*/
|
|
1298
|
-
async getNullifierTail() {
|
|
1299
|
-
const res = await this.get("/shielded/nullifiers/tail");
|
|
1300
|
-
return { afterChunks: res.afterChunks, data: res.data.map((h) => h.toLowerCase()) };
|
|
1301
|
-
}
|
|
1302
|
-
// ─── Private transfers ─────────────────────────────────────────────────────
|
|
1303
|
-
/** Server-enforced max items per by-nullifiers / by-commitments request. */
|
|
1304
|
-
static TRANSFER_LOOKUP_CHUNK = 50;
|
|
1305
|
-
/**
|
|
1306
|
-
* Chunked fetch for the transfer timestamp lookups. The reader silently
|
|
1307
|
-
* truncates each request to 50 items, so larger inputs MUST be split or
|
|
1308
|
-
* results are silently lost. Responses are merged per extrinsic
|
|
1309
|
-
* (`blockNumber:extrinsicIndex`), concatenating the matched arrays, and
|
|
1310
|
-
* sorted by block descending.
|
|
1311
|
-
*
|
|
1312
|
-
* Privacy note: these lookups send the wallet's own note identifiers to
|
|
1313
|
-
* the indexer — a bounded, documented linkage tradeoff for timestamp
|
|
1314
|
-
* recovery. They are NEVER used for spent-STATUS checks (PIR-A: status
|
|
1315
|
-
* comes from the anonymous full-set `/shielded/nullifiers/all` download).
|
|
1316
|
-
*/
|
|
1317
|
-
async fetchTransfersChunked(path, param, items, matchedField) {
|
|
1318
|
-
if (items.length === 0) return [];
|
|
1319
|
-
const normalized = items.map((i) => i.toLowerCase());
|
|
1320
|
-
const chunks = [];
|
|
1321
|
-
for (let i = 0; i < normalized.length; i += _IndexerClient.TRANSFER_LOOKUP_CHUNK) {
|
|
1322
|
-
chunks.push(normalized.slice(i, i + _IndexerClient.TRANSFER_LOOKUP_CHUNK));
|
|
1323
|
-
}
|
|
1324
|
-
const responses = await Promise.all(
|
|
1325
|
-
chunks.map((chunk) => {
|
|
1326
|
-
const qs = this.buildQuery({ [param]: chunk.join(",") });
|
|
1327
|
-
return this.get(
|
|
1328
|
-
`${path}${qs}`
|
|
1329
|
-
);
|
|
1330
|
-
})
|
|
1331
|
-
);
|
|
1332
|
-
const byExtrinsic = /* @__PURE__ */ new Map();
|
|
1333
|
-
for (const res of responses) {
|
|
1334
|
-
for (const transfer of res.data) {
|
|
1335
|
-
const key = `${transfer.blockNumber}:${transfer.extrinsicIndex ?? "null"}`;
|
|
1336
|
-
const existing = byExtrinsic.get(key);
|
|
1337
|
-
if (!existing) {
|
|
1338
|
-
byExtrinsic.set(key, transfer);
|
|
1339
|
-
continue;
|
|
1340
|
-
}
|
|
1341
|
-
const merged = /* @__PURE__ */ new Set([
|
|
1342
|
-
...existing[matchedField] ?? [],
|
|
1343
|
-
...transfer[matchedField] ?? []
|
|
1344
|
-
]);
|
|
1345
|
-
existing[matchedField] = [...merged];
|
|
1346
|
-
}
|
|
1347
|
-
}
|
|
1348
|
-
return [...byExtrinsic.values()].sort((a, b) => b.blockNumber - a.blockNumber);
|
|
1349
|
-
}
|
|
1350
|
-
/**
|
|
1351
|
-
* Returns temporal metadata for private transfers that spent any of the given nullifiers.
|
|
1352
|
-
* Only blockNumber, extrinsicIndex, timestampMs, and hash are returned — no cross-link
|
|
1353
|
-
* between inputs and outputs to prevent graph reconstruction.
|
|
1354
|
-
* Inputs of any size are transparently chunked into requests of 50 (the server cap)
|
|
1355
|
-
* and merged per extrinsic.
|
|
1356
|
-
*/
|
|
1357
|
-
async getTransfersByNullifiers(nullifiers) {
|
|
1358
|
-
return this.fetchTransfersChunked(
|
|
1359
|
-
"/shielded/transfers/by-nullifiers",
|
|
1360
|
-
"nullifiers",
|
|
1361
|
-
nullifiers,
|
|
1362
|
-
"matchedNullifiers"
|
|
1363
|
-
);
|
|
1364
|
-
}
|
|
1365
|
-
/**
|
|
1366
|
-
* Returns temporal metadata for private transfers that produced any of the given commitments.
|
|
1367
|
-
* Only blockNumber, extrinsicIndex, timestampMs, and hash are returned — no cross-link
|
|
1368
|
-
* between outputs and inputs to prevent graph reconstruction.
|
|
1369
|
-
* Inputs of any size are transparently chunked into requests of 50 (the server cap)
|
|
1370
|
-
* and merged per extrinsic.
|
|
1371
|
-
*/
|
|
1372
|
-
async getTransfersByCommitments(commitments) {
|
|
1373
|
-
return this.fetchTransfersChunked(
|
|
1374
|
-
"/shielded/transfers/by-commitments",
|
|
1375
|
-
"commitments",
|
|
1376
|
-
commitments,
|
|
1377
|
-
"matchedCommitments"
|
|
1378
|
-
);
|
|
1379
|
-
}
|
|
1380
|
-
// ─── Unshields ─────────────────────────────────────────────────────────────
|
|
1381
|
-
/** Returns a paginated list of unshield events. */
|
|
1382
|
-
async getUnshields(params) {
|
|
1383
|
-
const qs = this.buildQuery({ page: params?.page, limit: params?.limit });
|
|
1384
|
-
return this.get(`/shielded/unshields${qs}`);
|
|
1385
|
-
}
|
|
1386
|
-
// ─── Merkle roots ──────────────────────────────────────────────────────────
|
|
1387
|
-
/** Returns a paginated list of Merkle root checkpoints. */
|
|
1388
|
-
async getMerkleRoots(params) {
|
|
1389
|
-
const qs = this.buildQuery({ page: params?.page, limit: params?.limit });
|
|
1390
|
-
return this.get(`/shielded/merkle-roots${qs}`);
|
|
1391
|
-
}
|
|
1392
|
-
/** Returns the latest Merkle root, or null if none exists. */
|
|
1393
|
-
async getLatestMerkleRoot() {
|
|
1394
|
-
return this.getOrNull("/shielded/merkle-roots/latest");
|
|
1395
|
-
}
|
|
1396
|
-
// ─── Address activity ──────────────────────────────────────────────────────
|
|
1397
|
-
/** Returns a paginated list of extrinsics signed by the given address. */
|
|
1398
|
-
async getAddressExtrinsics(address, params) {
|
|
1399
|
-
const qs = this.buildQuery({ page: params?.page, limit: params?.limit });
|
|
1400
|
-
return this.get(
|
|
1401
|
-
`/address/${encodeURIComponent(address.toLowerCase())}/extrinsics${qs}`
|
|
1402
|
-
);
|
|
1403
|
-
}
|
|
1404
|
-
/** Returns a paginated list of EVM transactions filtered by address and/or block number. */
|
|
1405
|
-
async getEvmTransactions(params) {
|
|
1406
|
-
const qs = this.buildQuery({
|
|
1407
|
-
page: params?.page,
|
|
1408
|
-
limit: params?.limit,
|
|
1409
|
-
address: params?.address?.toLowerCase(),
|
|
1410
|
-
blockNumber: params?.blockNumber
|
|
1411
|
-
});
|
|
1412
|
-
return this.get(`/evm/transactions${qs}`);
|
|
1413
|
-
}
|
|
1414
|
-
/** Returns a single EVM transaction by hash, or null if not found. */
|
|
1415
|
-
async getEvmTransactionByHash(hash) {
|
|
1416
|
-
return this.getOrNull(
|
|
1417
|
-
`/evm/transactions/${encodeURIComponent(hash.toLowerCase())}`
|
|
1418
|
-
);
|
|
1419
|
-
}
|
|
1420
|
-
// ─── Blocks ────────────────────────────────────────────────────────────────
|
|
1421
|
-
/** Returns a paginated list of indexed blocks. */
|
|
1422
|
-
async getBlocks(params) {
|
|
1423
|
-
const qs = this.buildQuery({ page: params?.page, limit: params?.limit });
|
|
1424
|
-
return this.get(`/blocks${qs}`);
|
|
1425
|
-
}
|
|
1426
|
-
/** Returns a single block by number or hash, or null if not found. */
|
|
1427
|
-
async getBlock(numberOrHash) {
|
|
1428
|
-
return this.getOrNull(`/blocks/${encodeURIComponent(String(numberOrHash))}`);
|
|
1429
|
-
}
|
|
1430
|
-
// ─── Address-scoped shielded activity ─────────────────────────────────────
|
|
1431
|
-
/**
|
|
1432
|
-
* Returns a paginated list of unshield events where the given address is the recipient.
|
|
1433
|
-
* Accepts a 0x-prefixed EVM address or an SS58 Substrate address.
|
|
1434
|
-
*/
|
|
1435
|
-
async getAddressUnshields(address, params) {
|
|
1436
|
-
const qs = this.buildQuery({ page: params?.page, limit: params?.limit });
|
|
1437
|
-
return this.get(
|
|
1438
|
-
`/address/${encodeURIComponent(address)}/unshields${qs}`
|
|
1439
|
-
);
|
|
1440
|
-
}
|
|
1441
|
-
/**
|
|
1442
|
-
* Returns the shielded-pool BOUNDARY activity for an address: shields it
|
|
1443
|
-
* deposited and unshields it received, tagged `kind: 'shield' | 'unshield'`.
|
|
1444
|
-
* Only boundary fields are returned (block, asset, amount for unshields,
|
|
1445
|
-
* timestamp, tx hash) — note internals are never served per-address, and
|
|
1446
|
-
* private transfers carry no address at all (PIR-A).
|
|
1447
|
-
*/
|
|
1448
|
-
async getAddressShieldedActivity(address, params) {
|
|
1449
|
-
const qs = this.buildQuery({ page: params?.page, limit: params?.limit });
|
|
1450
|
-
return this.get(
|
|
1451
|
-
`/shielded/address/${encodeURIComponent(address)}${qs}`
|
|
1452
|
-
);
|
|
1453
|
-
}
|
|
1454
|
-
// ─── Relayers ──────────────────────────────────────────────────────────────
|
|
1455
|
-
/** Returns a paginated list of relayers. Filter by active status with `active`. */
|
|
1456
|
-
async getRelayers(params) {
|
|
1457
|
-
const qs = this.buildQuery({
|
|
1458
|
-
page: params?.page,
|
|
1459
|
-
limit: params?.limit,
|
|
1460
|
-
active: params?.active === void 0 ? void 0 : params.active ? "true" : "false"
|
|
1461
|
-
});
|
|
1462
|
-
return this.get(`/relayers${qs}`);
|
|
1463
|
-
}
|
|
1464
|
-
/** Returns a single relayer by EVM address, or null if not found. */
|
|
1465
|
-
async getRelayer(evmAddress) {
|
|
1466
|
-
return this.getOrNull(`/relayers/${encodeURIComponent(evmAddress.toLowerCase())}`);
|
|
1467
|
-
}
|
|
1468
|
-
/** Returns a paginated list of relay fee events. */
|
|
1469
|
-
async getRelayFees(params) {
|
|
1470
|
-
const qs = this.buildQuery({
|
|
1471
|
-
page: params?.page,
|
|
1472
|
-
limit: params?.limit,
|
|
1473
|
-
relayer: params?.relayer,
|
|
1474
|
-
type: params?.type
|
|
1475
|
-
});
|
|
1476
|
-
return this.get(`/relayers/fees${qs}`);
|
|
1477
|
-
}
|
|
1478
|
-
/** Returns aggregated relay fee balances per asset for a given relayer account. */
|
|
1479
|
-
async getRelayFeesSummary(relayer) {
|
|
1480
|
-
return this.get(
|
|
1481
|
-
`/relayers/fees/summary/${encodeURIComponent(relayer)}`
|
|
1482
|
-
);
|
|
1483
|
-
}
|
|
1484
|
-
// ─── Registered assets ─────────────────────────────────────────────────────
|
|
1485
|
-
/** Returns a paginated list of assets registered via register_asset. */
|
|
1486
|
-
async getRegisteredAssets(params) {
|
|
1487
|
-
const qs = this.buildQuery({ page: params?.page, limit: params?.limit });
|
|
1488
|
-
return this.get(`/shielded/assets${qs}`);
|
|
1489
|
-
}
|
|
1490
|
-
/** Returns a single registered asset by its ID, or null if not found. */
|
|
1491
|
-
async getRegisteredAsset(assetId) {
|
|
1492
|
-
return this.getOrNull(`/shielded/assets/${encodeURIComponent(assetId)}`);
|
|
1493
|
-
}
|
|
1494
|
-
// ─── Validators ────────────────────────────────────────────────────────────
|
|
1495
|
-
/** Returns a paginated list of validators. Filter by lifecycle status with `status`. */
|
|
1496
|
-
async getValidators(params) {
|
|
1497
|
-
const qs = this.buildQuery({
|
|
1498
|
-
page: params?.page,
|
|
1499
|
-
limit: params?.limit,
|
|
1500
|
-
status: params?.status
|
|
1501
|
-
});
|
|
1502
|
-
return this.get(`/validators${qs}`);
|
|
1503
|
-
}
|
|
1504
|
-
/** Returns a single validator by account address, or null if not found. */
|
|
1505
|
-
async getValidator(account) {
|
|
1506
|
-
return this.getOrNull(`/validators/${encodeURIComponent(account)}`);
|
|
1507
|
-
}
|
|
1508
|
-
// ─── Sessions ──────────────────────────────────────────────────────────────
|
|
1509
|
-
/** Returns a paginated list of session rotations, ordered by most recent first. */
|
|
1510
|
-
async getSessions(params) {
|
|
1511
|
-
const qs = this.buildQuery({ page: params?.page, limit: params?.limit });
|
|
1512
|
-
return this.get(`/sessions${qs}`);
|
|
1513
|
-
}
|
|
1514
|
-
// ─── Stats & Health ────────────────────────────────────────────────────────
|
|
1515
|
-
/** Returns aggregated indexer statistics. */
|
|
1516
|
-
async getStats() {
|
|
1517
|
-
return this.get("/stats");
|
|
1518
|
-
}
|
|
1519
|
-
/**
|
|
1520
|
-
* Returns transaction activity bucketed per hour over the last `hours` hours
|
|
1521
|
-
* of chain time (default 24, max 168). For sparklines / activity charts.
|
|
1522
|
-
*/
|
|
1523
|
-
async getActivity(hours = 24) {
|
|
1524
|
-
return this.get(`/stats/activity?hours=${hours}`);
|
|
1525
|
-
}
|
|
1526
|
-
/** Returns true if the indexer health endpoint responds OK. */
|
|
1527
|
-
async isHealthy() {
|
|
1528
|
-
try {
|
|
1529
|
-
const controller = new AbortController();
|
|
1530
|
-
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
1531
|
-
try {
|
|
1532
|
-
const res = await fetch(`${this.baseUrl}/health`, {
|
|
1533
|
-
signal: controller.signal
|
|
1534
|
-
});
|
|
1535
|
-
return res.ok;
|
|
1536
|
-
} finally {
|
|
1537
|
-
clearTimeout(timer);
|
|
1538
|
-
}
|
|
1539
|
-
} catch {
|
|
1540
|
-
return false;
|
|
1541
|
-
}
|
|
1542
|
-
}
|
|
1543
|
-
};
|
|
1544
|
-
|
|
1545
1157
|
// src/shielded-pool/pallet/ShieldedPoolModule.ts
|
|
1546
1158
|
var import_polkadot_api2 = require("polkadot-api");
|
|
1547
1159
|
|
|
@@ -3640,12 +3252,6 @@ var OrbinumClient = class _OrbinumClient {
|
|
|
3640
3252
|
* `null` when `evmRpc` is not configured.
|
|
3641
3253
|
*/
|
|
3642
3254
|
evmExplorer;
|
|
3643
|
-
/**
|
|
3644
|
-
* HTTP client for the Orbinum indexer REST API.
|
|
3645
|
-
* Provides paginated access to indexed blocks, extrinsics, shielded events, and nullifiers.
|
|
3646
|
-
* `null` when `indexerUrl` is not configured.
|
|
3647
|
-
*/
|
|
3648
|
-
indexer;
|
|
3649
3255
|
/** Shielded-pool extrinsics and Merkle tree queries (`shield`, `unshield`, `privateTransfer`, …). */
|
|
3650
3256
|
shieldedPool;
|
|
3651
3257
|
/** Account-mapping extrinsics: aliases, chain links, metadata, marketplace, and identity. */
|
|
@@ -3669,11 +3275,10 @@ var OrbinumClient = class _OrbinumClient {
|
|
|
3669
3275
|
*/
|
|
3670
3276
|
precompiles;
|
|
3671
3277
|
/** @internal Use `OrbinumClient.connect()` to obtain an instance. */
|
|
3672
|
-
constructor(substrate, evm,
|
|
3278
|
+
constructor(substrate, evm, circuitsBaseUrl) {
|
|
3673
3279
|
this.substrate = substrate;
|
|
3674
3280
|
this.evm = evm;
|
|
3675
3281
|
this.evmExplorer = evm ? new EvmExplorer(evm) : null;
|
|
3676
|
-
this.indexer = indexer;
|
|
3677
3282
|
this.shieldedPool = new ShieldedPoolModule(substrate);
|
|
3678
3283
|
this.accountMapping = new AccountMappingModule(substrate);
|
|
3679
3284
|
this.privacy = new PrivacyModule(substrate);
|
|
@@ -3699,8 +3304,7 @@ var OrbinumClient = class _OrbinumClient {
|
|
|
3699
3304
|
config.connectTimeoutMs ?? 15e3
|
|
3700
3305
|
);
|
|
3701
3306
|
const evm = config.evmRpc ? new EvmClient(config.evmRpc) : null;
|
|
3702
|
-
|
|
3703
|
-
return new _OrbinumClient(substrate, evm, indexer, config.circuitsBaseUrl);
|
|
3307
|
+
return new _OrbinumClient(substrate, evm, config.circuitsBaseUrl);
|
|
3704
3308
|
}
|
|
3705
3309
|
/** Closes the underlying Substrate WebSocket connection and releases all resources. */
|
|
3706
3310
|
destroy() {
|
|
@@ -3816,15 +3420,16 @@ var OrbinumClientProvider = class {
|
|
|
3816
3420
|
this.connectTimeoutMs
|
|
3817
3421
|
);
|
|
3818
3422
|
});
|
|
3423
|
+
let clientPromise = null;
|
|
3819
3424
|
try {
|
|
3820
3425
|
const connectConfig = {
|
|
3821
|
-
substrateWs: this.config.substrateWs
|
|
3426
|
+
substrateWs: this.config.substrateWs,
|
|
3427
|
+
connectTimeoutMs: this.connectTimeoutMs
|
|
3822
3428
|
};
|
|
3823
3429
|
if (this.config.evmRpc) connectConfig.evmRpc = this.config.evmRpc;
|
|
3824
|
-
if (this.config.indexerUrl) connectConfig.indexerUrl = this.config.indexerUrl;
|
|
3825
3430
|
if (this.config.circuitsBaseUrl)
|
|
3826
3431
|
connectConfig.circuitsBaseUrl = this.config.circuitsBaseUrl;
|
|
3827
|
-
|
|
3432
|
+
clientPromise = OrbinumClient.connect(connectConfig);
|
|
3828
3433
|
const client = await Promise.race([clientPromise, timeoutPromise]);
|
|
3829
3434
|
orphanClient = client;
|
|
3830
3435
|
clearTimeout(timeoutId);
|
|
@@ -3842,6 +3447,17 @@ var OrbinumClientProvider = class {
|
|
|
3842
3447
|
orphanClient.destroy();
|
|
3843
3448
|
} catch {
|
|
3844
3449
|
}
|
|
3450
|
+
} else if (clientPromise) {
|
|
3451
|
+
clientPromise.then(
|
|
3452
|
+
(c) => {
|
|
3453
|
+
try {
|
|
3454
|
+
c.destroy();
|
|
3455
|
+
} catch {
|
|
3456
|
+
}
|
|
3457
|
+
},
|
|
3458
|
+
() => {
|
|
3459
|
+
}
|
|
3460
|
+
);
|
|
3845
3461
|
}
|
|
3846
3462
|
this._connectingPromise = null;
|
|
3847
3463
|
this.setStatus(
|
|
@@ -5643,7 +5259,6 @@ var import_polkadot_api4 = require("polkadot-api");
|
|
|
5643
5259
|
EncryptedMemo,
|
|
5644
5260
|
EvmClient,
|
|
5645
5261
|
EvmExplorer,
|
|
5646
|
-
IndexerClient,
|
|
5647
5262
|
KNOWN_PRECOMPILES,
|
|
5648
5263
|
Keccak256,
|
|
5649
5264
|
NoteBuilder,
|