@orbinum/sdk 0.11.0 → 0.13.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 +174 -579
- package/dist/index.d.ts +174 -579
- package/dist/index.js +186 -460
- package/dist/index.mjs +190 -462
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -26,14 +26,15 @@ __export(index_exports, {
|
|
|
26
26
|
BABYJUB_SUBORDER: () => BABYJUB_SUBORDER,
|
|
27
27
|
BN254_R: () => BN254_R,
|
|
28
28
|
Blake2256: () => import_substrate_bindings3.Blake2256,
|
|
29
|
+
CURRENT_CIRCUIT_VERSION: () => CURRENT_CIRCUIT_VERSION,
|
|
29
30
|
CircuitId: () => CircuitId,
|
|
30
|
-
CircuitType: () =>
|
|
31
|
+
CircuitType: () => import_proof_generator2.CircuitType,
|
|
32
|
+
CircuitVersionResolver: () => CircuitVersionResolver,
|
|
31
33
|
CryptoPrecompiles: () => CryptoPrecompiles,
|
|
32
34
|
ENCRYPTED_MEMO_SIZE: () => ENCRYPTED_MEMO_SIZE,
|
|
33
35
|
EncryptedMemo: () => EncryptedMemo,
|
|
34
36
|
EvmClient: () => EvmClient,
|
|
35
37
|
EvmExplorer: () => EvmExplorer,
|
|
36
|
-
IndexerClient: () => IndexerClient,
|
|
37
38
|
KNOWN_PRECOMPILES: () => KNOWN_PRECOMPILES,
|
|
38
39
|
Keccak256: () => import_substrate_bindings3.Keccak256,
|
|
39
40
|
NoteBuilder: () => NoteBuilder,
|
|
@@ -50,7 +51,7 @@ __export(index_exports, {
|
|
|
50
51
|
Storage: () => import_substrate_bindings3.Storage,
|
|
51
52
|
SubstrateClient: () => SubstrateClient,
|
|
52
53
|
VaultLockedError: () => VaultLockedError,
|
|
53
|
-
WebArtifactProvider: () =>
|
|
54
|
+
WebArtifactProvider: () => import_proof_generator2.WebArtifactProvider,
|
|
54
55
|
ZkVerifierModule: () => ZkVerifierModule,
|
|
55
56
|
accountIdHexToSs58: () => accountIdHexToSs58,
|
|
56
57
|
addressToAccountIdHex: () => addressToAccountIdHex,
|
|
@@ -1149,397 +1150,6 @@ var EvmExplorer = class _EvmExplorer {
|
|
|
1149
1150
|
}
|
|
1150
1151
|
};
|
|
1151
1152
|
|
|
1152
|
-
// src/indexer/IndexerClient.ts
|
|
1153
|
-
var LOCAL_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "[::1]", "::1"]);
|
|
1154
|
-
function normalizeBaseUrl(baseUrl) {
|
|
1155
|
-
let url;
|
|
1156
|
-
try {
|
|
1157
|
-
url = new URL(baseUrl);
|
|
1158
|
-
} catch {
|
|
1159
|
-
throw new Error(`IndexerClient: invalid baseUrl ${JSON.stringify(baseUrl)}`);
|
|
1160
|
-
}
|
|
1161
|
-
const isLocal = LOCAL_HOSTS.has(url.hostname);
|
|
1162
|
-
if (url.protocol !== "https:" && !(url.protocol === "http:" && isLocal)) {
|
|
1163
|
-
throw new Error(
|
|
1164
|
-
`IndexerClient: baseUrl must use https:// (got ${url.protocol}//${url.hostname}). Plain http is only allowed for localhost.`
|
|
1165
|
-
);
|
|
1166
|
-
}
|
|
1167
|
-
return baseUrl.replace(/\/$/, "");
|
|
1168
|
-
}
|
|
1169
|
-
var IndexerClient = class _IndexerClient {
|
|
1170
|
-
baseUrl;
|
|
1171
|
-
timeoutMs;
|
|
1172
|
-
constructor(config) {
|
|
1173
|
-
this.baseUrl = normalizeBaseUrl(config.baseUrl);
|
|
1174
|
-
this.timeoutMs = config.timeoutMs ?? 1e4;
|
|
1175
|
-
}
|
|
1176
|
-
// ─── Internal helpers ──────────────────────────────────────────────────────
|
|
1177
|
-
async _fetchResponse(path) {
|
|
1178
|
-
const controller = new AbortController();
|
|
1179
|
-
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
1180
|
-
try {
|
|
1181
|
-
return await fetch(`${this.baseUrl}${path}`, { signal: controller.signal });
|
|
1182
|
-
} finally {
|
|
1183
|
-
clearTimeout(timer);
|
|
1184
|
-
}
|
|
1185
|
-
}
|
|
1186
|
-
async get(path) {
|
|
1187
|
-
const res = await this._fetchResponse(path);
|
|
1188
|
-
if (!res.ok) {
|
|
1189
|
-
throw new Error(`IndexerClient: HTTP ${res.status} for ${path}`);
|
|
1190
|
-
}
|
|
1191
|
-
return res.json();
|
|
1192
|
-
}
|
|
1193
|
-
async getOrNull(path) {
|
|
1194
|
-
const res = await this._fetchResponse(path);
|
|
1195
|
-
if (res.status === 404) return null;
|
|
1196
|
-
if (!res.ok) {
|
|
1197
|
-
throw new Error(`IndexerClient: HTTP ${res.status} for ${path}`);
|
|
1198
|
-
}
|
|
1199
|
-
return res.json();
|
|
1200
|
-
}
|
|
1201
|
-
buildQuery(params) {
|
|
1202
|
-
const entries = Object.entries(params).filter(([, v]) => v !== void 0);
|
|
1203
|
-
if (entries.length === 0) return "";
|
|
1204
|
-
const qs = entries.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`).join("&");
|
|
1205
|
-
return `?${qs}`;
|
|
1206
|
-
}
|
|
1207
|
-
// ─── Commitments ───────────────────────────────────────────────────────────
|
|
1208
|
-
/** Returns the total count of shielded commitments. */
|
|
1209
|
-
async getCommitmentsCount() {
|
|
1210
|
-
const res = await this.get("/shielded/commitments/count");
|
|
1211
|
-
return res.total;
|
|
1212
|
-
}
|
|
1213
|
-
/** Returns a paginated list of shielded commitments. */
|
|
1214
|
-
async getCommitments(params) {
|
|
1215
|
-
const qs = this.buildQuery({
|
|
1216
|
-
page: params?.page,
|
|
1217
|
-
limit: params?.limit,
|
|
1218
|
-
since_leaf_index: params?.sinceLeafIndex
|
|
1219
|
-
});
|
|
1220
|
-
return this.get(`/shielded/commitments${qs}`);
|
|
1221
|
-
}
|
|
1222
|
-
/** Returns a single commitment by its hex string, or null if not found. */
|
|
1223
|
-
async getCommitmentByHex(hex) {
|
|
1224
|
-
return this.getOrNull(
|
|
1225
|
-
`/shielded/commitments/${encodeURIComponent(hex)}`
|
|
1226
|
-
);
|
|
1227
|
-
}
|
|
1228
|
-
/**
|
|
1229
|
-
* Returns a paginated list of stealth scan hints ordered ascending by leafIndex.
|
|
1230
|
-
* Each hint contains only the fields required for ECDH triage and decryption:
|
|
1231
|
-
* leafIndex, commitmentHex, assetId, ephPkHex, encryptedMemo.
|
|
1232
|
-
*
|
|
1233
|
-
* Use `sinceLeafIndex` for incremental scans (cursor = last seen leafIndex + 1).
|
|
1234
|
-
*/
|
|
1235
|
-
async getScanHints(params) {
|
|
1236
|
-
const qs = this.buildQuery({
|
|
1237
|
-
page: params?.page,
|
|
1238
|
-
limit: params?.limit,
|
|
1239
|
-
since_leaf_index: params?.sinceLeafIndex
|
|
1240
|
-
});
|
|
1241
|
-
return this.get(`/shielded/scan-hints${qs}`);
|
|
1242
|
-
}
|
|
1243
|
-
// ─── Nullifiers ────────────────────────────────────────────────────────────
|
|
1244
|
-
/** Returns a paginated list of spent nullifiers. */
|
|
1245
|
-
async getNullifiers(params) {
|
|
1246
|
-
const qs = this.buildQuery({ page: params?.page, limit: params?.limit });
|
|
1247
|
-
return this.get(`/shielded/nullifiers${qs}`);
|
|
1248
|
-
}
|
|
1249
|
-
/** Returns the spent/unspent status of a nullifier. */
|
|
1250
|
-
async getNullifierStatus(hex) {
|
|
1251
|
-
return this.get(
|
|
1252
|
-
`/shielded/nullifier/${encodeURIComponent(hex)}/status`
|
|
1253
|
-
);
|
|
1254
|
-
}
|
|
1255
|
-
/**
|
|
1256
|
-
* Downloads the full spent nullifier set and returns it as a Set of lowercase hex strings.
|
|
1257
|
-
*
|
|
1258
|
-
* The server sees an identical GET request regardless of which notes the wallet holds —
|
|
1259
|
-
* the intersection is computed locally (PIR-A privacy model).
|
|
1260
|
-
*
|
|
1261
|
-
* Kept as the FALLBACK for readers that don't serve sealed chunks yet (and for
|
|
1262
|
-
* small sets). New integrations should prefer the incremental chunk flow:
|
|
1263
|
-
* `getNullifierManifest` → `getNullifierChunk` for missing chunks →
|
|
1264
|
-
* `getNullifierTail`, persisting the set locally between rescans.
|
|
1265
|
-
*/
|
|
1266
|
-
async getAllSpentNullifiers() {
|
|
1267
|
-
const res = await this.get("/shielded/nullifiers/all");
|
|
1268
|
-
return new Set(res.data.map((h) => h.toLowerCase()));
|
|
1269
|
-
}
|
|
1270
|
-
/**
|
|
1271
|
-
* Universal index of the sealed nullifier chunks — identical request and
|
|
1272
|
-
* response for every caller (no client-supplied position: PIR-A preserved).
|
|
1273
|
-
*
|
|
1274
|
-
* Returns `null` when the reader does not serve chunks yet (404) — the
|
|
1275
|
-
* caller should fall back to `getAllSpentNullifiers`.
|
|
1276
|
-
*/
|
|
1277
|
-
async getNullifierManifest() {
|
|
1278
|
-
return this.getOrNull("/shielded/nullifiers/manifest");
|
|
1279
|
-
}
|
|
1280
|
-
/**
|
|
1281
|
-
* One sealed, immutable chunk of the spent-nullifier set (ascending hex,
|
|
1282
|
-
* lowercased). The digest comes from the manifest and lives in the URL, so
|
|
1283
|
-
* a corrected chunk is a different URL — safe to cache forever client-side.
|
|
1284
|
-
*/
|
|
1285
|
-
async getNullifierChunk(idx, digest) {
|
|
1286
|
-
const res = await this.get(
|
|
1287
|
-
`/shielded/nullifiers/chunks/${idx}/${encodeURIComponent(digest)}`
|
|
1288
|
-
);
|
|
1289
|
-
return res.data.map((h) => h.toLowerCase());
|
|
1290
|
-
}
|
|
1291
|
-
/**
|
|
1292
|
-
* The mutable remainder of the nullifier set after the last sealed chunk.
|
|
1293
|
-
* Identical request for every caller (no input). `afterChunks` lets the
|
|
1294
|
-
* client detect a chunk sealed between its manifest fetch and this one.
|
|
1295
|
-
*/
|
|
1296
|
-
async getNullifierTail() {
|
|
1297
|
-
const res = await this.get("/shielded/nullifiers/tail");
|
|
1298
|
-
return { afterChunks: res.afterChunks, data: res.data.map((h) => h.toLowerCase()) };
|
|
1299
|
-
}
|
|
1300
|
-
// ─── Private transfers ─────────────────────────────────────────────────────
|
|
1301
|
-
/** Server-enforced max items per by-nullifiers / by-commitments request. */
|
|
1302
|
-
static TRANSFER_LOOKUP_CHUNK = 50;
|
|
1303
|
-
/**
|
|
1304
|
-
* Chunked fetch for the transfer timestamp lookups. The reader silently
|
|
1305
|
-
* truncates each request to 50 items, so larger inputs MUST be split or
|
|
1306
|
-
* results are silently lost. Responses are merged per extrinsic
|
|
1307
|
-
* (`blockNumber:extrinsicIndex`), concatenating the matched arrays, and
|
|
1308
|
-
* sorted by block descending.
|
|
1309
|
-
*
|
|
1310
|
-
* Privacy note: these lookups send the wallet's own note identifiers to
|
|
1311
|
-
* the indexer — a bounded, documented linkage tradeoff for timestamp
|
|
1312
|
-
* recovery. They are NEVER used for spent-STATUS checks (PIR-A: status
|
|
1313
|
-
* comes from the anonymous full-set `/shielded/nullifiers/all` download).
|
|
1314
|
-
*/
|
|
1315
|
-
async fetchTransfersChunked(path, param, items, matchedField) {
|
|
1316
|
-
if (items.length === 0) return [];
|
|
1317
|
-
const normalized = items.map((i) => i.toLowerCase());
|
|
1318
|
-
const chunks = [];
|
|
1319
|
-
for (let i = 0; i < normalized.length; i += _IndexerClient.TRANSFER_LOOKUP_CHUNK) {
|
|
1320
|
-
chunks.push(normalized.slice(i, i + _IndexerClient.TRANSFER_LOOKUP_CHUNK));
|
|
1321
|
-
}
|
|
1322
|
-
const responses = await Promise.all(
|
|
1323
|
-
chunks.map((chunk) => {
|
|
1324
|
-
const qs = this.buildQuery({ [param]: chunk.join(",") });
|
|
1325
|
-
return this.get(
|
|
1326
|
-
`${path}${qs}`
|
|
1327
|
-
);
|
|
1328
|
-
})
|
|
1329
|
-
);
|
|
1330
|
-
const byExtrinsic = /* @__PURE__ */ new Map();
|
|
1331
|
-
for (const res of responses) {
|
|
1332
|
-
for (const transfer of res.data) {
|
|
1333
|
-
const key = `${transfer.blockNumber}:${transfer.extrinsicIndex ?? "null"}`;
|
|
1334
|
-
const existing = byExtrinsic.get(key);
|
|
1335
|
-
if (!existing) {
|
|
1336
|
-
byExtrinsic.set(key, transfer);
|
|
1337
|
-
continue;
|
|
1338
|
-
}
|
|
1339
|
-
const merged = /* @__PURE__ */ new Set([
|
|
1340
|
-
...existing[matchedField] ?? [],
|
|
1341
|
-
...transfer[matchedField] ?? []
|
|
1342
|
-
]);
|
|
1343
|
-
existing[matchedField] = [...merged];
|
|
1344
|
-
}
|
|
1345
|
-
}
|
|
1346
|
-
return [...byExtrinsic.values()].sort((a, b) => b.blockNumber - a.blockNumber);
|
|
1347
|
-
}
|
|
1348
|
-
/**
|
|
1349
|
-
* Returns temporal metadata for private transfers that spent any of the given nullifiers.
|
|
1350
|
-
* Only blockNumber, extrinsicIndex, timestampMs, and hash are returned — no cross-link
|
|
1351
|
-
* between inputs and outputs to prevent graph reconstruction.
|
|
1352
|
-
* Inputs of any size are transparently chunked into requests of 50 (the server cap)
|
|
1353
|
-
* and merged per extrinsic.
|
|
1354
|
-
*/
|
|
1355
|
-
async getTransfersByNullifiers(nullifiers) {
|
|
1356
|
-
return this.fetchTransfersChunked(
|
|
1357
|
-
"/shielded/transfers/by-nullifiers",
|
|
1358
|
-
"nullifiers",
|
|
1359
|
-
nullifiers,
|
|
1360
|
-
"matchedNullifiers"
|
|
1361
|
-
);
|
|
1362
|
-
}
|
|
1363
|
-
/**
|
|
1364
|
-
* Returns temporal metadata for private transfers that produced any of the given commitments.
|
|
1365
|
-
* Only blockNumber, extrinsicIndex, timestampMs, and hash are returned — no cross-link
|
|
1366
|
-
* between outputs and inputs to prevent graph reconstruction.
|
|
1367
|
-
* Inputs of any size are transparently chunked into requests of 50 (the server cap)
|
|
1368
|
-
* and merged per extrinsic.
|
|
1369
|
-
*/
|
|
1370
|
-
async getTransfersByCommitments(commitments) {
|
|
1371
|
-
return this.fetchTransfersChunked(
|
|
1372
|
-
"/shielded/transfers/by-commitments",
|
|
1373
|
-
"commitments",
|
|
1374
|
-
commitments,
|
|
1375
|
-
"matchedCommitments"
|
|
1376
|
-
);
|
|
1377
|
-
}
|
|
1378
|
-
// ─── Unshields ─────────────────────────────────────────────────────────────
|
|
1379
|
-
/** Returns a paginated list of unshield events. */
|
|
1380
|
-
async getUnshields(params) {
|
|
1381
|
-
const qs = this.buildQuery({ page: params?.page, limit: params?.limit });
|
|
1382
|
-
return this.get(`/shielded/unshields${qs}`);
|
|
1383
|
-
}
|
|
1384
|
-
// ─── Merkle roots ──────────────────────────────────────────────────────────
|
|
1385
|
-
/** Returns a paginated list of Merkle root checkpoints. */
|
|
1386
|
-
async getMerkleRoots(params) {
|
|
1387
|
-
const qs = this.buildQuery({ page: params?.page, limit: params?.limit });
|
|
1388
|
-
return this.get(`/shielded/merkle-roots${qs}`);
|
|
1389
|
-
}
|
|
1390
|
-
/** Returns the latest Merkle root, or null if none exists. */
|
|
1391
|
-
async getLatestMerkleRoot() {
|
|
1392
|
-
return this.getOrNull("/shielded/merkle-roots/latest");
|
|
1393
|
-
}
|
|
1394
|
-
// ─── Address activity ──────────────────────────────────────────────────────
|
|
1395
|
-
/** Returns a paginated list of extrinsics signed by the given address. */
|
|
1396
|
-
async getAddressExtrinsics(address, params) {
|
|
1397
|
-
const qs = this.buildQuery({ page: params?.page, limit: params?.limit });
|
|
1398
|
-
return this.get(
|
|
1399
|
-
`/address/${encodeURIComponent(address.toLowerCase())}/extrinsics${qs}`
|
|
1400
|
-
);
|
|
1401
|
-
}
|
|
1402
|
-
/** Returns a paginated list of EVM transactions filtered by address and/or block number. */
|
|
1403
|
-
async getEvmTransactions(params) {
|
|
1404
|
-
const qs = this.buildQuery({
|
|
1405
|
-
page: params?.page,
|
|
1406
|
-
limit: params?.limit,
|
|
1407
|
-
address: params?.address?.toLowerCase(),
|
|
1408
|
-
blockNumber: params?.blockNumber
|
|
1409
|
-
});
|
|
1410
|
-
return this.get(`/evm/transactions${qs}`);
|
|
1411
|
-
}
|
|
1412
|
-
/** Returns a single EVM transaction by hash, or null if not found. */
|
|
1413
|
-
async getEvmTransactionByHash(hash) {
|
|
1414
|
-
return this.getOrNull(
|
|
1415
|
-
`/evm/transactions/${encodeURIComponent(hash.toLowerCase())}`
|
|
1416
|
-
);
|
|
1417
|
-
}
|
|
1418
|
-
// ─── Blocks ────────────────────────────────────────────────────────────────
|
|
1419
|
-
/** Returns a paginated list of indexed blocks. */
|
|
1420
|
-
async getBlocks(params) {
|
|
1421
|
-
const qs = this.buildQuery({ page: params?.page, limit: params?.limit });
|
|
1422
|
-
return this.get(`/blocks${qs}`);
|
|
1423
|
-
}
|
|
1424
|
-
/** Returns a single block by number or hash, or null if not found. */
|
|
1425
|
-
async getBlock(numberOrHash) {
|
|
1426
|
-
return this.getOrNull(`/blocks/${encodeURIComponent(String(numberOrHash))}`);
|
|
1427
|
-
}
|
|
1428
|
-
// ─── Address-scoped shielded activity ─────────────────────────────────────
|
|
1429
|
-
/**
|
|
1430
|
-
* Returns a paginated list of unshield events where the given address is the recipient.
|
|
1431
|
-
* Accepts a 0x-prefixed EVM address or an SS58 Substrate address.
|
|
1432
|
-
*/
|
|
1433
|
-
async getAddressUnshields(address, params) {
|
|
1434
|
-
const qs = this.buildQuery({ page: params?.page, limit: params?.limit });
|
|
1435
|
-
return this.get(
|
|
1436
|
-
`/address/${encodeURIComponent(address)}/unshields${qs}`
|
|
1437
|
-
);
|
|
1438
|
-
}
|
|
1439
|
-
/**
|
|
1440
|
-
* Returns the shielded-pool BOUNDARY activity for an address: shields it
|
|
1441
|
-
* deposited and unshields it received, tagged `kind: 'shield' | 'unshield'`.
|
|
1442
|
-
* Only boundary fields are returned (block, asset, amount for unshields,
|
|
1443
|
-
* timestamp, tx hash) — note internals are never served per-address, and
|
|
1444
|
-
* private transfers carry no address at all (PIR-A).
|
|
1445
|
-
*/
|
|
1446
|
-
async getAddressShieldedActivity(address, params) {
|
|
1447
|
-
const qs = this.buildQuery({ page: params?.page, limit: params?.limit });
|
|
1448
|
-
return this.get(
|
|
1449
|
-
`/shielded/address/${encodeURIComponent(address)}${qs}`
|
|
1450
|
-
);
|
|
1451
|
-
}
|
|
1452
|
-
// ─── Relayers ──────────────────────────────────────────────────────────────
|
|
1453
|
-
/** Returns a paginated list of relayers. Filter by active status with `active`. */
|
|
1454
|
-
async getRelayers(params) {
|
|
1455
|
-
const qs = this.buildQuery({
|
|
1456
|
-
page: params?.page,
|
|
1457
|
-
limit: params?.limit,
|
|
1458
|
-
active: params?.active === void 0 ? void 0 : params.active ? "true" : "false"
|
|
1459
|
-
});
|
|
1460
|
-
return this.get(`/relayers${qs}`);
|
|
1461
|
-
}
|
|
1462
|
-
/** Returns a single relayer by EVM address, or null if not found. */
|
|
1463
|
-
async getRelayer(evmAddress) {
|
|
1464
|
-
return this.getOrNull(`/relayers/${encodeURIComponent(evmAddress.toLowerCase())}`);
|
|
1465
|
-
}
|
|
1466
|
-
/** Returns a paginated list of relay fee events. */
|
|
1467
|
-
async getRelayFees(params) {
|
|
1468
|
-
const qs = this.buildQuery({
|
|
1469
|
-
page: params?.page,
|
|
1470
|
-
limit: params?.limit,
|
|
1471
|
-
relayer: params?.relayer,
|
|
1472
|
-
type: params?.type
|
|
1473
|
-
});
|
|
1474
|
-
return this.get(`/relayers/fees${qs}`);
|
|
1475
|
-
}
|
|
1476
|
-
/** Returns aggregated relay fee balances per asset for a given relayer account. */
|
|
1477
|
-
async getRelayFeesSummary(relayer) {
|
|
1478
|
-
return this.get(
|
|
1479
|
-
`/relayers/fees/summary/${encodeURIComponent(relayer)}`
|
|
1480
|
-
);
|
|
1481
|
-
}
|
|
1482
|
-
// ─── Registered assets ─────────────────────────────────────────────────────
|
|
1483
|
-
/** Returns a paginated list of assets registered via register_asset. */
|
|
1484
|
-
async getRegisteredAssets(params) {
|
|
1485
|
-
const qs = this.buildQuery({ page: params?.page, limit: params?.limit });
|
|
1486
|
-
return this.get(`/shielded/assets${qs}`);
|
|
1487
|
-
}
|
|
1488
|
-
/** Returns a single registered asset by its ID, or null if not found. */
|
|
1489
|
-
async getRegisteredAsset(assetId) {
|
|
1490
|
-
return this.getOrNull(`/shielded/assets/${encodeURIComponent(assetId)}`);
|
|
1491
|
-
}
|
|
1492
|
-
// ─── Validators ────────────────────────────────────────────────────────────
|
|
1493
|
-
/** Returns a paginated list of validators. Filter by lifecycle status with `status`. */
|
|
1494
|
-
async getValidators(params) {
|
|
1495
|
-
const qs = this.buildQuery({
|
|
1496
|
-
page: params?.page,
|
|
1497
|
-
limit: params?.limit,
|
|
1498
|
-
status: params?.status
|
|
1499
|
-
});
|
|
1500
|
-
return this.get(`/validators${qs}`);
|
|
1501
|
-
}
|
|
1502
|
-
/** Returns a single validator by account address, or null if not found. */
|
|
1503
|
-
async getValidator(account) {
|
|
1504
|
-
return this.getOrNull(`/validators/${encodeURIComponent(account)}`);
|
|
1505
|
-
}
|
|
1506
|
-
// ─── Sessions ──────────────────────────────────────────────────────────────
|
|
1507
|
-
/** Returns a paginated list of session rotations, ordered by most recent first. */
|
|
1508
|
-
async getSessions(params) {
|
|
1509
|
-
const qs = this.buildQuery({ page: params?.page, limit: params?.limit });
|
|
1510
|
-
return this.get(`/sessions${qs}`);
|
|
1511
|
-
}
|
|
1512
|
-
// ─── Stats & Health ────────────────────────────────────────────────────────
|
|
1513
|
-
/** Returns aggregated indexer statistics. */
|
|
1514
|
-
async getStats() {
|
|
1515
|
-
return this.get("/stats");
|
|
1516
|
-
}
|
|
1517
|
-
/**
|
|
1518
|
-
* Returns transaction activity bucketed per hour over the last `hours` hours
|
|
1519
|
-
* of chain time (default 24, max 168). For sparklines / activity charts.
|
|
1520
|
-
*/
|
|
1521
|
-
async getActivity(hours = 24) {
|
|
1522
|
-
return this.get(`/stats/activity?hours=${hours}`);
|
|
1523
|
-
}
|
|
1524
|
-
/** Returns true if the indexer health endpoint responds OK. */
|
|
1525
|
-
async isHealthy() {
|
|
1526
|
-
try {
|
|
1527
|
-
const controller = new AbortController();
|
|
1528
|
-
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
1529
|
-
try {
|
|
1530
|
-
const res = await fetch(`${this.baseUrl}/health`, {
|
|
1531
|
-
signal: controller.signal
|
|
1532
|
-
});
|
|
1533
|
-
return res.ok;
|
|
1534
|
-
} finally {
|
|
1535
|
-
clearTimeout(timer);
|
|
1536
|
-
}
|
|
1537
|
-
} catch {
|
|
1538
|
-
return false;
|
|
1539
|
-
}
|
|
1540
|
-
}
|
|
1541
|
-
};
|
|
1542
|
-
|
|
1543
1153
|
// src/shielded-pool/pallet/ShieldedPoolModule.ts
|
|
1544
1154
|
var import_polkadot_api2 = require("polkadot-api");
|
|
1545
1155
|
|
|
@@ -1648,8 +1258,8 @@ var BABYJUB_SUBORDER = 273603035897990940278080071815715938607681397215856725920
|
|
|
1648
1258
|
// src/shielded-pool/protocol/memo.ts
|
|
1649
1259
|
var import_sha2 = require("@noble/hashes/sha2.js");
|
|
1650
1260
|
var KEY_DOMAIN = new TextEncoder().encode("orbinum-note-encryption-v1");
|
|
1651
|
-
var MEMO_PLAINTEXT_SIZE =
|
|
1652
|
-
function serializeMemo(value, ownerPk, blinding, assetId, counterpartyPk) {
|
|
1261
|
+
var MEMO_PLAINTEXT_SIZE = 120;
|
|
1262
|
+
function serializeMemo(value, ownerPk, blinding, assetId, counterpartyPk, circuitVersion) {
|
|
1653
1263
|
const buf = new Uint8Array(MEMO_PLAINTEXT_SIZE);
|
|
1654
1264
|
const view = new DataView(buf.buffer);
|
|
1655
1265
|
view.setBigUint64(0, value & 0xffffffffffffffffn, true);
|
|
@@ -1658,6 +1268,7 @@ function serializeMemo(value, ownerPk, blinding, assetId, counterpartyPk) {
|
|
|
1658
1268
|
buf.set(blinding.slice(0, 32), 48);
|
|
1659
1269
|
view.setUint32(80, assetId >>> 0, true);
|
|
1660
1270
|
buf.set(counterpartyPk.slice(0, 32), 84);
|
|
1271
|
+
view.setUint32(116, circuitVersion >>> 0, true);
|
|
1661
1272
|
return buf;
|
|
1662
1273
|
}
|
|
1663
1274
|
function deriveEncryptionKey(sharedSecret, commitment) {
|
|
@@ -1670,7 +1281,7 @@ function deriveEncryptionKey(sharedSecret, commitment) {
|
|
|
1670
1281
|
|
|
1671
1282
|
// src/shielded-pool/protocol/EncryptedMemo.ts
|
|
1672
1283
|
var NONCE_SIZE = 12;
|
|
1673
|
-
var CIPHERTEXT_SIZE =
|
|
1284
|
+
var CIPHERTEXT_SIZE = 136;
|
|
1674
1285
|
var EPH_PK_SIZE = 32;
|
|
1675
1286
|
var ENCRYPTED_MEMO_SIZE = NONCE_SIZE + CIPHERTEXT_SIZE + EPH_PK_SIZE;
|
|
1676
1287
|
function bytesToBjjScalar(bytes) {
|
|
@@ -1689,14 +1300,15 @@ function parsePlaintext(nonce, ciphertextWithMac, encKey) {
|
|
|
1689
1300
|
const blinding = bytesToBigintLE(plaintext.slice(48, 80));
|
|
1690
1301
|
const assetId = BigInt(view.getUint32(80, true));
|
|
1691
1302
|
const counterpartyPk = bytesToBigintLE(plaintext.slice(84, 116));
|
|
1692
|
-
|
|
1303
|
+
const circuitVersion = view.getUint32(116, true);
|
|
1304
|
+
return { value, ownerPk, blinding, assetId, counterpartyPk, circuitVersion };
|
|
1693
1305
|
} catch {
|
|
1694
1306
|
return null;
|
|
1695
1307
|
}
|
|
1696
1308
|
}
|
|
1697
1309
|
var EncryptedMemo = {
|
|
1698
1310
|
/**
|
|
1699
|
-
* Build and encrypt a memo for a note using ECDH (v2,
|
|
1311
|
+
* Build and encrypt a memo for a note using ECDH (v2, 180 bytes).
|
|
1700
1312
|
*
|
|
1701
1313
|
* @param value Note value in planck.
|
|
1702
1314
|
* @param ownerPk 32-byte owner public key (LE).
|
|
@@ -1708,11 +1320,20 @@ var EncryptedMemo = {
|
|
|
1708
1320
|
* decoded from a privacy address).
|
|
1709
1321
|
* Pass `new Uint8Array(32)` (all zeros) for a publicly-readable memo.
|
|
1710
1322
|
* @param counterpartyPk 32-byte counterparty BJJ Ax. Default: all zeros.
|
|
1711
|
-
* @
|
|
1323
|
+
* @param circuitVersion ZK circuit version the note is spent under. Default: 0.
|
|
1324
|
+
* @param ephSkOverride 32-byte ephemeral secret key (stealth coordination). Optional.
|
|
1325
|
+
* @returns 180-byte encrypted memo: nonce(12) || ciphertext+MAC(136) || ephPk(32).
|
|
1712
1326
|
*/
|
|
1713
|
-
encrypt(value, ownerPk, blinding, assetId, commitment, recipientIvkPacked, counterpartyPk = new Uint8Array(32), ephSkOverride) {
|
|
1327
|
+
encrypt(value, ownerPk, blinding, assetId, commitment, recipientIvkPacked, counterpartyPk = new Uint8Array(32), circuitVersion = 0, ephSkOverride) {
|
|
1714
1328
|
const nonce = (0, import_utils.randomBytes)(NONCE_SIZE);
|
|
1715
|
-
const plaintext = serializeMemo(
|
|
1329
|
+
const plaintext = serializeMemo(
|
|
1330
|
+
value,
|
|
1331
|
+
ownerPk,
|
|
1332
|
+
blinding,
|
|
1333
|
+
assetId,
|
|
1334
|
+
counterpartyPk,
|
|
1335
|
+
circuitVersion
|
|
1336
|
+
);
|
|
1716
1337
|
const isZeroKey = recipientIvkPacked.every((b) => b === 0);
|
|
1717
1338
|
let sharedSecret;
|
|
1718
1339
|
let ephPkPackedBytes;
|
|
@@ -1743,29 +1364,31 @@ var EncryptedMemo = {
|
|
|
1743
1364
|
return result;
|
|
1744
1365
|
},
|
|
1745
1366
|
/**
|
|
1746
|
-
* Returns a
|
|
1367
|
+
* Returns a 180-byte public memo encrypted with a zero viewing key.
|
|
1747
1368
|
* Decryptable by anyone with `decrypt(memo, commitment, new Uint8Array(32))`.
|
|
1748
1369
|
* Convenience alias for `encrypt(..., new Uint8Array(32))`.
|
|
1749
1370
|
*/
|
|
1750
|
-
encryptPublic(value, ownerPk, blinding, assetId, commitment) {
|
|
1371
|
+
encryptPublic(value, ownerPk, blinding, assetId, commitment, circuitVersion = 0) {
|
|
1751
1372
|
return EncryptedMemo.encrypt(
|
|
1752
1373
|
value,
|
|
1753
1374
|
ownerPk,
|
|
1754
1375
|
blinding,
|
|
1755
1376
|
assetId,
|
|
1756
1377
|
commitment,
|
|
1757
|
-
new Uint8Array(32)
|
|
1378
|
+
new Uint8Array(32),
|
|
1379
|
+
new Uint8Array(32),
|
|
1380
|
+
circuitVersion
|
|
1758
1381
|
);
|
|
1759
1382
|
},
|
|
1760
1383
|
/**
|
|
1761
|
-
* Returns a
|
|
1384
|
+
* Returns a 180-byte zeroed dummy memo (no information, always valid on-chain).
|
|
1762
1385
|
*/
|
|
1763
1386
|
dummy() {
|
|
1764
1387
|
return new Uint8Array(ENCRYPTED_MEMO_SIZE);
|
|
1765
1388
|
},
|
|
1766
1389
|
/**
|
|
1767
1390
|
* Validates that `bytes` is a properly-sized encrypted memo.
|
|
1768
|
-
* Throws an Error if the length is not ENCRYPTED_MEMO_SIZE (
|
|
1391
|
+
* Throws an Error if the length is not ENCRYPTED_MEMO_SIZE (180 bytes).
|
|
1769
1392
|
*
|
|
1770
1393
|
* Call this at system boundaries (extrinsic builders, precompile encoders)
|
|
1771
1394
|
* to catch malformed memos before they reach the chain and fail on-chain.
|
|
@@ -1786,7 +1409,7 @@ var EncryptedMemo = {
|
|
|
1786
1409
|
* Returns null if decryption fails — wrong key, bad MAC, or malformed memo.
|
|
1787
1410
|
* Never throws; safe for scan loops.
|
|
1788
1411
|
*
|
|
1789
|
-
* @param memoBytes
|
|
1412
|
+
* @param memoBytes 180-byte encrypted memo.
|
|
1790
1413
|
* @param commitment 32-byte note commitment (LE).
|
|
1791
1414
|
* @param viewingSecretKey 32-byte HKDF viewing secret key from deriveViewingSecretKey().
|
|
1792
1415
|
*/
|
|
@@ -1798,13 +1421,13 @@ var EncryptedMemo = {
|
|
|
1798
1421
|
* Extract the ECDH shared secret from an encrypted memo using the recipient's viewing secret key.
|
|
1799
1422
|
*
|
|
1800
1423
|
* Used by NoteDecryptor to obtain the shared secret needed for stealth address derivation
|
|
1801
|
-
* without re-running the full decrypt path. Safe to call on any
|
|
1424
|
+
* without re-running the full decrypt path. Safe to call on any 180-byte memo.
|
|
1802
1425
|
*
|
|
1803
1426
|
* Returns `new Uint8Array(32)` (all zeros) for public/dummy memos (zero ephPk).
|
|
1804
1427
|
* Returns `null` if the memo is malformed or the ephPk is not a valid BJJ point.
|
|
1805
1428
|
* Never throws; safe for scan loops.
|
|
1806
1429
|
*
|
|
1807
|
-
* @param memoBytes
|
|
1430
|
+
* @param memoBytes 180-byte encrypted memo.
|
|
1808
1431
|
* @param viewingSecretKey 32-byte HKDF viewing secret key from deriveViewingSecretKey().
|
|
1809
1432
|
*/
|
|
1810
1433
|
extractSharedSecret(memoBytes, viewingSecretKey) {
|
|
@@ -2008,7 +1631,7 @@ var ShieldedPoolModule = class {
|
|
|
2008
1631
|
* Withdraws tokens from the shielded pool to a public address.
|
|
2009
1632
|
* Submits as an UNSIGNED (gasless) transaction — fee is embedded in the ZK proof.
|
|
2010
1633
|
* Pass a `signer` to fall back to signed submission (e.g. for testing).
|
|
2011
|
-
* Extrinsic: shieldedPool.unshield(proof, merkleRoot, nullifier, assetId, amount, recipient, fee)
|
|
1634
|
+
* Extrinsic: shieldedPool.unshield(proof, merkleRoot, nullifier, assetId, amount, recipient, fee, changeCommitment, changeEncryptedMemo, relayer, circuitVersion)
|
|
2012
1635
|
*/
|
|
2013
1636
|
async unshield(params, signer, txOptions) {
|
|
2014
1637
|
const entry = resolveTx(this.substrate.unsafe, "ShieldedPool", "unshield");
|
|
@@ -2032,8 +1655,9 @@ var ShieldedPoolModule = class {
|
|
|
2032
1655
|
fee: params.fee ?? 0n,
|
|
2033
1656
|
change_commitment: changeCommitment,
|
|
2034
1657
|
change_encrypted_memo: changeEncryptedMemo,
|
|
2035
|
-
relayer: void 0
|
|
1658
|
+
relayer: void 0,
|
|
2036
1659
|
// Option<H160> — None for direct Substrate submissions
|
|
1660
|
+
circuit_version: params.circuitVersion
|
|
2037
1661
|
});
|
|
2038
1662
|
if (signer) {
|
|
2039
1663
|
return toTxResult(
|
|
@@ -2046,7 +1670,7 @@ var ShieldedPoolModule = class {
|
|
|
2046
1670
|
* Performs a private (shielded) transfer between two notes.
|
|
2047
1671
|
* Submits as an UNSIGNED (gasless) transaction — fee is embedded in the ZK proof.
|
|
2048
1672
|
* Pass a `signer` to fall back to signed submission (e.g. for testing).
|
|
2049
|
-
* Extrinsic: shieldedPool.privateTransfer(proof, merkleRoot, nullifiers, commitments, memos, assetId, fee)
|
|
1673
|
+
* Extrinsic: shieldedPool.privateTransfer(proof, merkleRoot, nullifiers, commitments, memos, assetId, fee, relayer, circuitVersion)
|
|
2050
1674
|
*/
|
|
2051
1675
|
async privateTransfer(params, signer, txOptions) {
|
|
2052
1676
|
const nullifiers = params.inputs.map((inp) => inp.nullifier);
|
|
@@ -2067,8 +1691,9 @@ var ShieldedPoolModule = class {
|
|
|
2067
1691
|
encrypted_memos: memos,
|
|
2068
1692
|
asset_id: params.assetId,
|
|
2069
1693
|
fee: params.fee ?? 0n,
|
|
2070
|
-
relayer: void 0
|
|
1694
|
+
relayer: void 0,
|
|
2071
1695
|
// Option<H160> — None for direct Substrate submissions
|
|
1696
|
+
circuit_version: params.circuitVersion
|
|
2072
1697
|
});
|
|
2073
1698
|
if (signer) {
|
|
2074
1699
|
return toTxResult(
|
|
@@ -2102,7 +1727,7 @@ var ShieldedPoolModule = class {
|
|
|
2102
1727
|
* This is a SIGNED transaction — the relayer must sign it with their wallet.
|
|
2103
1728
|
* Before calling this, generate a ZK value proof with generateFeeClaimProof() (not yet implemented).
|
|
2104
1729
|
*
|
|
2105
|
-
* Extrinsic: shieldedPool.claim_shielded_fees(commitment, amount, asset_id, memo, proof, public_signals)
|
|
1730
|
+
* Extrinsic: shieldedPool.claim_shielded_fees(commitment, amount, asset_id, memo, proof, public_signals, circuit_version)
|
|
2106
1731
|
*/
|
|
2107
1732
|
async claimShieldedFees(params, signer, txOptions) {
|
|
2108
1733
|
EncryptedMemo.validate(params.encryptedMemo, "claimShieldedFees.encryptedMemo");
|
|
@@ -2113,7 +1738,8 @@ var ShieldedPoolModule = class {
|
|
|
2113
1738
|
asset_id: params.assetId,
|
|
2114
1739
|
encrypted_memo: params.encryptedMemo,
|
|
2115
1740
|
proof: params.proof,
|
|
2116
|
-
public_signals: params.publicSignals
|
|
1741
|
+
public_signals: params.publicSignals,
|
|
1742
|
+
circuit_version: params.circuitVersion
|
|
2117
1743
|
});
|
|
2118
1744
|
return toTxResult(
|
|
2119
1745
|
await (txOptions !== void 0 ? tx.signAndSubmit(signer, txOptions) : tx.signAndSubmit(signer))
|
|
@@ -2631,6 +2257,74 @@ var ZkVerifierModule = class {
|
|
|
2631
2257
|
}
|
|
2632
2258
|
};
|
|
2633
2259
|
|
|
2260
|
+
// src/shielded-pool/CircuitVersionResolver.ts
|
|
2261
|
+
var import_proof_generator = require("@orbinum/proof-generator");
|
|
2262
|
+
var CircuitVersionResolver = class {
|
|
2263
|
+
constructor(zkVerifier, baseUrl, providerFactory) {
|
|
2264
|
+
this.zkVerifier = zkVerifier;
|
|
2265
|
+
this.makeProvider = providerFactory ?? ((circuit, noteVersion) => new import_proof_generator.WebArtifactProvider({
|
|
2266
|
+
...baseUrl ? { baseUrl } : {},
|
|
2267
|
+
circuitVersions: { [circuit]: noteVersion }
|
|
2268
|
+
}));
|
|
2269
|
+
}
|
|
2270
|
+
zkVerifier;
|
|
2271
|
+
makeProvider;
|
|
2272
|
+
/**
|
|
2273
|
+
* Resolves the prover + on-chain version for spending a note of `circuit`
|
|
2274
|
+
* created under `noteVersion`. Fail-closed: throws on unsupported version or
|
|
2275
|
+
* VK-hash mismatch (CDN vs chain), before generating any proof.
|
|
2276
|
+
*/
|
|
2277
|
+
async resolve(circuit, noteVersion) {
|
|
2278
|
+
if (!Number.isInteger(noteVersion) || noteVersion <= 0) {
|
|
2279
|
+
throw new Error(
|
|
2280
|
+
`CircuitVersionResolver: invalid note circuitVersion ${noteVersion} for ${circuit}`
|
|
2281
|
+
);
|
|
2282
|
+
}
|
|
2283
|
+
const provider = this.makeProvider(circuit, noteVersion);
|
|
2284
|
+
const resolved = await provider.getResolvedVersion(circuit);
|
|
2285
|
+
if (resolved.version !== noteVersion) {
|
|
2286
|
+
throw new Error(
|
|
2287
|
+
`CircuitVersionResolver: prover resolved ${circuit} to v${resolved.version}, expected v${noteVersion}`
|
|
2288
|
+
);
|
|
2289
|
+
}
|
|
2290
|
+
const circuitId = (0, import_proof_generator.circuitTypeToId)(circuit);
|
|
2291
|
+
const info = await this.zkVerifier.getCircuitVersionInfo(circuitId);
|
|
2292
|
+
if (!info) {
|
|
2293
|
+
throw new Error(
|
|
2294
|
+
`CircuitVersionResolver: chain reports no version info for ${circuit} (id ${circuitId})`
|
|
2295
|
+
);
|
|
2296
|
+
}
|
|
2297
|
+
if (!info.supportedVersions.includes(noteVersion)) {
|
|
2298
|
+
throw new Error(
|
|
2299
|
+
`CircuitVersionResolver: chain does not support ${circuit} v${noteVersion} (supported: ${info.supportedVersions.join(", ")})`
|
|
2300
|
+
);
|
|
2301
|
+
}
|
|
2302
|
+
const chainVk = info.vkHashes.find((h) => h.version === noteVersion);
|
|
2303
|
+
if (!chainVk) {
|
|
2304
|
+
throw new Error(
|
|
2305
|
+
`CircuitVersionResolver: chain has no VK hash for ${circuit} v${noteVersion}`
|
|
2306
|
+
);
|
|
2307
|
+
}
|
|
2308
|
+
if (!vkHashEquals(chainVk.vkHash, resolved.vkHash)) {
|
|
2309
|
+
throw new Error(
|
|
2310
|
+
`CircuitVersionResolver: VK hash mismatch for ${circuit} v${noteVersion} \u2014 prover ${resolved.vkHash}, chain ${chainVk.vkHash}`
|
|
2311
|
+
);
|
|
2312
|
+
}
|
|
2313
|
+
return { provider, version: noteVersion };
|
|
2314
|
+
}
|
|
2315
|
+
};
|
|
2316
|
+
function vkHashEquals(a, b) {
|
|
2317
|
+
const na = normalizeHash(a);
|
|
2318
|
+
const nb = normalizeHash(b);
|
|
2319
|
+
if (na === null || nb === null) return false;
|
|
2320
|
+
return na === nb;
|
|
2321
|
+
}
|
|
2322
|
+
function normalizeHash(h) {
|
|
2323
|
+
if (typeof h !== "string") return null;
|
|
2324
|
+
const s = h.toLowerCase().startsWith("0x") ? h.toLowerCase().slice(2) : h.toLowerCase();
|
|
2325
|
+
return /^[0-9a-f]{64}$/.test(s) ? s : null;
|
|
2326
|
+
}
|
|
2327
|
+
|
|
2634
2328
|
// src/relayer/RelayerStatusModule.ts
|
|
2635
2329
|
var RelayerStatusModule = class {
|
|
2636
2330
|
constructor(substrate) {
|
|
@@ -2876,12 +2570,12 @@ var AM_SEL = {
|
|
|
2876
2570
|
var SP_SEL = {
|
|
2877
2571
|
// shield(uint32,bytes32,bytes) → 0x9feb22ea (payable, amount = msg.value)
|
|
2878
2572
|
SHIELD: new Uint8Array([159, 235, 34, 234]),
|
|
2879
|
-
// privateTransfer(bytes,bytes32,bytes32[],bytes32[],bytes[],uint32,uint256)
|
|
2880
|
-
PRIVATE_TRANSFER: new Uint8Array([
|
|
2881
|
-
// unshield(bytes,bytes32,bytes32,uint32,uint256,bytes32,uint256,bytes32)
|
|
2882
|
-
UNSHIELD: new Uint8Array([
|
|
2883
|
-
// claimShieldedFees(bytes32,uint256,uint32,bytes,bytes,bytes)
|
|
2884
|
-
CLAIM_SHIELDED_FEES: new Uint8Array([
|
|
2573
|
+
// privateTransfer(bytes,bytes32,bytes32[],bytes32[],bytes[],uint32,uint256,uint32) → 0x66ed2cd4
|
|
2574
|
+
PRIVATE_TRANSFER: new Uint8Array([102, 237, 44, 212]),
|
|
2575
|
+
// unshield(bytes,bytes32,bytes32,uint32,uint256,bytes32,uint256,bytes32,bytes,uint32) → 0x4e505348
|
|
2576
|
+
UNSHIELD: new Uint8Array([78, 80, 83, 72]),
|
|
2577
|
+
// claimShieldedFees(bytes32,uint256,uint32,bytes,bytes,bytes,uint32) → 0x88d9deba
|
|
2578
|
+
CLAIM_SHIELDED_FEES: new Uint8Array([136, 217, 222, 186])
|
|
2885
2579
|
};
|
|
2886
2580
|
var KNOWN_PRECOMPILES = {
|
|
2887
2581
|
// ── Ethereum standard (EIP) ─────────────────────────────────────────────
|
|
@@ -2925,9 +2619,9 @@ var KNOWN_PRECOMPILES = {
|
|
|
2925
2619
|
name: "ShieldedPool",
|
|
2926
2620
|
functions: {
|
|
2927
2621
|
"9feb22ea": "shield(uint32,bytes32,bytes)",
|
|
2928
|
-
"
|
|
2929
|
-
|
|
2930
|
-
"
|
|
2622
|
+
"66ed2cd4": "privateTransfer(bytes,bytes32,bytes32[],bytes32[],bytes[],uint32,uint256,uint32)",
|
|
2623
|
+
"4e505348": "unshield(bytes,bytes32,bytes32,uint32,uint256,bytes32,uint256,bytes32,bytes,uint32)",
|
|
2624
|
+
"88d9deba": "claimShieldedFees(bytes32,uint256,uint32,bytes,bytes,bytes,uint32)"
|
|
2931
2625
|
}
|
|
2932
2626
|
}
|
|
2933
2627
|
};
|
|
@@ -2981,7 +2675,8 @@ var ShieldedPoolPrecompile = class {
|
|
|
2981
2675
|
// ─── privateTransfer ───────────────────────────────────────────────────────
|
|
2982
2676
|
/**
|
|
2983
2677
|
* Returns the ABI-encoded calldata for
|
|
2984
|
-
* `privateTransfer(bytes, bytes32, bytes32[], bytes32[], bytes[], uint32, uint256)`.
|
|
2678
|
+
* `privateTransfer(bytes, bytes32, bytes32[], bytes32[], bytes[], uint32, uint256, uint32)`.
|
|
2679
|
+
* The trailing `uint32` is the circuit version the input notes were created under.
|
|
2985
2680
|
*/
|
|
2986
2681
|
buildPrivateTransferCalldata(params) {
|
|
2987
2682
|
const nullifiers = params.inputs.map((i) => fromHex(i.nullifier));
|
|
@@ -3002,7 +2697,8 @@ var ShieldedPoolPrecompile = class {
|
|
|
3002
2697
|
{ type: "bytes32[]", value: commitments },
|
|
3003
2698
|
{ type: "bytes[]", value: memos },
|
|
3004
2699
|
{ type: "uint", value: BigInt(params.assetId) },
|
|
3005
|
-
{ type: "uint", value: params.fee ?? 0n }
|
|
2700
|
+
{ type: "uint", value: params.fee ?? 0n },
|
|
2701
|
+
{ type: "uint", value: BigInt(params.circuitVersion) }
|
|
3006
2702
|
);
|
|
3007
2703
|
}
|
|
3008
2704
|
/**
|
|
@@ -3043,7 +2739,8 @@ var ShieldedPoolPrecompile = class {
|
|
|
3043
2739
|
{ type: "bytes32", value: recipientBytes },
|
|
3044
2740
|
{ type: "uint", value: params.fee ?? 0n },
|
|
3045
2741
|
{ type: "bytes32", value: changeCommitment },
|
|
3046
|
-
{ type: "bytes", value: changeEncryptedMemo }
|
|
2742
|
+
{ type: "bytes", value: changeEncryptedMemo },
|
|
2743
|
+
{ type: "uint", value: BigInt(params.circuitVersion) }
|
|
3047
2744
|
);
|
|
3048
2745
|
}
|
|
3049
2746
|
/**
|
|
@@ -3093,7 +2790,7 @@ var ShieldedPoolPrecompile = class {
|
|
|
3093
2790
|
// ─── claimShieldedFees ───────────────────────────────────────────────────────────────────
|
|
3094
2791
|
/**
|
|
3095
2792
|
* Returns the ABI-encoded calldata for
|
|
3096
|
-
* `claimShieldedFees(bytes32,uint256,uint32,bytes,bytes,bytes)`.
|
|
2793
|
+
* `claimShieldedFees(bytes32,uint256,uint32,bytes,bytes,bytes,uint32)`.
|
|
3097
2794
|
*
|
|
3098
2795
|
* ABI layout (params after selector):
|
|
3099
2796
|
* - `commitment` — bytes32 (fixed)
|
|
@@ -3102,6 +2799,7 @@ var ShieldedPoolPrecompile = class {
|
|
|
3102
2799
|
* - `memo` — bytes (dynamic)
|
|
3103
2800
|
* - `proof` — bytes (dynamic, 128 bytes Groth16)
|
|
3104
2801
|
* - `publicSignals` — bytes (dynamic, 76 bytes)
|
|
2802
|
+
* - `circuitVersion` — uint32 (fixed, right-aligned)
|
|
3105
2803
|
*
|
|
3106
2804
|
* The validator identity is derived from `msg.sender` in the precompile —
|
|
3107
2805
|
* do NOT include it in the calldata.
|
|
@@ -3127,7 +2825,8 @@ var ShieldedPoolPrecompile = class {
|
|
|
3127
2825
|
{ type: "uint", value: BigInt(params.assetId) },
|
|
3128
2826
|
{ type: "bytes", value: params.encryptedMemo },
|
|
3129
2827
|
{ type: "bytes", value: params.proof },
|
|
3130
|
-
{ type: "bytes", value: params.publicSignals }
|
|
2828
|
+
{ type: "bytes", value: params.publicSignals },
|
|
2829
|
+
{ type: "uint", value: BigInt(params.circuitVersion) }
|
|
3131
2830
|
);
|
|
3132
2831
|
}
|
|
3133
2832
|
/**
|
|
@@ -3549,12 +3248,6 @@ var OrbinumClient = class _OrbinumClient {
|
|
|
3549
3248
|
* `null` when `evmRpc` is not configured.
|
|
3550
3249
|
*/
|
|
3551
3250
|
evmExplorer;
|
|
3552
|
-
/**
|
|
3553
|
-
* HTTP client for the Orbinum indexer REST API.
|
|
3554
|
-
* Provides paginated access to indexed blocks, extrinsics, shielded events, and nullifiers.
|
|
3555
|
-
* `null` when `indexerUrl` is not configured.
|
|
3556
|
-
*/
|
|
3557
|
-
indexer;
|
|
3558
3251
|
/** Shielded-pool extrinsics and Merkle tree queries (`shield`, `unshield`, `privateTransfer`, …). */
|
|
3559
3252
|
shieldedPool;
|
|
3560
3253
|
/** Account-mapping extrinsics: aliases, chain links, metadata, marketplace, and identity. */
|
|
@@ -3565,6 +3258,11 @@ var OrbinumClient = class _OrbinumClient {
|
|
|
3565
3258
|
chain;
|
|
3566
3259
|
/** Typed access to `zkVerifier_*` custom RPC endpoints. */
|
|
3567
3260
|
zkVerifier;
|
|
3261
|
+
/**
|
|
3262
|
+
* Resolves a note's circuit version to a pinned prover + on-chain version
|
|
3263
|
+
* before spending it (fail-closed: throws on unsupported version / VK mismatch).
|
|
3264
|
+
*/
|
|
3265
|
+
circuitVersionResolver;
|
|
3568
3266
|
/** Typed access to `relayer_*` RPC endpoints (registry lookup and pending fee queries). */
|
|
3569
3267
|
relayerStatus;
|
|
3570
3268
|
/**
|
|
@@ -3573,16 +3271,16 @@ var OrbinumClient = class _OrbinumClient {
|
|
|
3573
3271
|
*/
|
|
3574
3272
|
precompiles;
|
|
3575
3273
|
/** @internal Use `OrbinumClient.connect()` to obtain an instance. */
|
|
3576
|
-
constructor(substrate, evm,
|
|
3274
|
+
constructor(substrate, evm, circuitsBaseUrl) {
|
|
3577
3275
|
this.substrate = substrate;
|
|
3578
3276
|
this.evm = evm;
|
|
3579
3277
|
this.evmExplorer = evm ? new EvmExplorer(evm) : null;
|
|
3580
|
-
this.indexer = indexer;
|
|
3581
3278
|
this.shieldedPool = new ShieldedPoolModule(substrate);
|
|
3582
3279
|
this.accountMapping = new AccountMappingModule(substrate);
|
|
3583
3280
|
this.privacy = new PrivacyModule(substrate);
|
|
3584
3281
|
this.chain = new ChainModule(substrate);
|
|
3585
3282
|
this.zkVerifier = new ZkVerifierModule(substrate);
|
|
3283
|
+
this.circuitVersionResolver = new CircuitVersionResolver(this.zkVerifier, circuitsBaseUrl);
|
|
3586
3284
|
this.relayerStatus = new RelayerStatusModule(substrate);
|
|
3587
3285
|
this.precompiles = evm ? {
|
|
3588
3286
|
shieldedPool: new ShieldedPoolPrecompile(evm),
|
|
@@ -3602,8 +3300,7 @@ var OrbinumClient = class _OrbinumClient {
|
|
|
3602
3300
|
config.connectTimeoutMs ?? 15e3
|
|
3603
3301
|
);
|
|
3604
3302
|
const evm = config.evmRpc ? new EvmClient(config.evmRpc) : null;
|
|
3605
|
-
|
|
3606
|
-
return new _OrbinumClient(substrate, evm, indexer);
|
|
3303
|
+
return new _OrbinumClient(substrate, evm, config.circuitsBaseUrl);
|
|
3607
3304
|
}
|
|
3608
3305
|
/** Closes the underlying Substrate WebSocket connection and releases all resources. */
|
|
3609
3306
|
destroy() {
|
|
@@ -3724,7 +3421,8 @@ var OrbinumClientProvider = class {
|
|
|
3724
3421
|
substrateWs: this.config.substrateWs
|
|
3725
3422
|
};
|
|
3726
3423
|
if (this.config.evmRpc) connectConfig.evmRpc = this.config.evmRpc;
|
|
3727
|
-
if (this.config.
|
|
3424
|
+
if (this.config.circuitsBaseUrl)
|
|
3425
|
+
connectConfig.circuitsBaseUrl = this.config.circuitsBaseUrl;
|
|
3728
3426
|
const clientPromise = OrbinumClient.connect(connectConfig);
|
|
3729
3427
|
const client = await Promise.race([clientPromise, timeoutPromise]);
|
|
3730
3428
|
orphanClient = client;
|
|
@@ -3911,6 +3609,9 @@ var OrbinumClientProvider = class {
|
|
|
3911
3609
|
}
|
|
3912
3610
|
};
|
|
3913
3611
|
|
|
3612
|
+
// src/shielded-pool/protocol/types.ts
|
|
3613
|
+
var CURRENT_CIRCUIT_VERSION = 1;
|
|
3614
|
+
|
|
3914
3615
|
// src/utils/stealth.ts
|
|
3915
3616
|
var import_sha22 = require("@noble/hashes/sha2.js");
|
|
3916
3617
|
var import_hkdf = require("@noble/hashes/hkdf.js");
|
|
@@ -4019,6 +3720,7 @@ var NoteBuilder = class {
|
|
|
4019
3720
|
const blinding = input.blinding ?? BigInt(Date.now());
|
|
4020
3721
|
const spendingKey = input.spendingKey ?? 0n;
|
|
4021
3722
|
const counterpartyPk = input.counterpartyPk ?? 0n;
|
|
3723
|
+
const circuitVersion = input.circuitVersion ?? CURRENT_CIRCUIT_VERSION;
|
|
4022
3724
|
const useStealth = input.viewingPublicKey !== void 0 && input.recipientOwnerPk !== void 0;
|
|
4023
3725
|
let memo;
|
|
4024
3726
|
if (useStealth) {
|
|
@@ -4053,6 +3755,7 @@ var NoteBuilder = class {
|
|
|
4053
3755
|
stealthCommitmentBytes,
|
|
4054
3756
|
recipientIvkPacked,
|
|
4055
3757
|
bigintTo32Le(counterpartyPk),
|
|
3758
|
+
circuitVersion,
|
|
4056
3759
|
ephSk
|
|
4057
3760
|
)
|
|
4058
3761
|
);
|
|
@@ -4070,6 +3773,7 @@ var NoteBuilder = class {
|
|
|
4070
3773
|
ownerPk: effectiveOwnerPk,
|
|
4071
3774
|
blinding,
|
|
4072
3775
|
spendingKey,
|
|
3776
|
+
circuitVersion,
|
|
4073
3777
|
spent: false,
|
|
4074
3778
|
spentAt: null,
|
|
4075
3779
|
commitment: commitment2,
|
|
@@ -4092,7 +3796,8 @@ var NoteBuilder = class {
|
|
|
4092
3796
|
Number(assetId),
|
|
4093
3797
|
commitmentBytes,
|
|
4094
3798
|
input.viewingPublicKey,
|
|
4095
|
-
bigintTo32Le(counterpartyPk)
|
|
3799
|
+
bigintTo32Le(counterpartyPk),
|
|
3800
|
+
circuitVersion
|
|
4096
3801
|
)
|
|
4097
3802
|
) : Array.from(EncryptedMemo.dummy());
|
|
4098
3803
|
if (memo.length !== ENCRYPTED_MEMO_SIZE)
|
|
@@ -4105,6 +3810,7 @@ var NoteBuilder = class {
|
|
|
4105
3810
|
ownerPk,
|
|
4106
3811
|
blinding,
|
|
4107
3812
|
spendingKey,
|
|
3813
|
+
circuitVersion,
|
|
4108
3814
|
spent: false,
|
|
4109
3815
|
spentAt: null,
|
|
4110
3816
|
commitment,
|
|
@@ -4116,7 +3822,7 @@ var NoteBuilder = class {
|
|
|
4116
3822
|
};
|
|
4117
3823
|
}
|
|
4118
3824
|
/**
|
|
4119
|
-
* Build the
|
|
3825
|
+
* Build the 180-byte ECDH-encrypted memo for a note.
|
|
4120
3826
|
*
|
|
4121
3827
|
* Pure TypeScript implementation — no WASM dependency.
|
|
4122
3828
|
* Uses ChaCha20-Poly1305 with ECDH key agreement (BabyJubJub ephemeral keypair).
|
|
@@ -4135,7 +3841,8 @@ var NoteBuilder = class {
|
|
|
4135
3841
|
Number(note.assetId),
|
|
4136
3842
|
bigintTo32Le(note.commitment),
|
|
4137
3843
|
recipientIvkPacked ?? new Uint8Array(32),
|
|
4138
|
-
counterpartyPk ?? bigintTo32Le(note.counterpartyPk ?? 0n)
|
|
3844
|
+
counterpartyPk ?? bigintTo32Le(note.counterpartyPk ?? 0n),
|
|
3845
|
+
note.circuitVersion
|
|
4139
3846
|
);
|
|
4140
3847
|
}
|
|
4141
3848
|
};
|
|
@@ -4197,6 +3904,7 @@ function tryDecryptNoteVerbose(commitment, viewingSecretKey, spendingKey, ownOwn
|
|
|
4197
3904
|
ownerPk: effectiveOwnerPk,
|
|
4198
3905
|
blinding: plaintext.blinding,
|
|
4199
3906
|
spendingKey: effectiveSpendingKey,
|
|
3907
|
+
circuitVersion: plaintext.circuitVersion,
|
|
4200
3908
|
spent: false,
|
|
4201
3909
|
spentAt: null,
|
|
4202
3910
|
commitment: recomputed,
|
|
@@ -4270,7 +3978,7 @@ function selectNotes(notes, needed) {
|
|
|
4270
3978
|
for (let j = i + 1; j < sorted.length; j++) {
|
|
4271
3979
|
const a = sorted[i];
|
|
4272
3980
|
const b = sorted[j];
|
|
4273
|
-
if (a !== void 0 && b !== void 0 && a.value + b.value >= needed) {
|
|
3981
|
+
if (a !== void 0 && b !== void 0 && a.circuitVersion === b.circuitVersion && a.value + b.value >= needed) {
|
|
4274
3982
|
return [a, b];
|
|
4275
3983
|
}
|
|
4276
3984
|
}
|
|
@@ -4627,6 +4335,11 @@ async function encryptNote(key, blindKey, note) {
|
|
|
4627
4335
|
}
|
|
4628
4336
|
async function decryptNoteRecord(key, rec) {
|
|
4629
4337
|
const note = await decryptJson(key, rec.iv, rec.ciphertext);
|
|
4338
|
+
if (typeof note.circuitVersion !== "number") {
|
|
4339
|
+
throw new Error(
|
|
4340
|
+
"decryptNoteRecord: note is missing circuitVersion (invalid or corrupt record)"
|
|
4341
|
+
);
|
|
4342
|
+
}
|
|
4630
4343
|
return applyNoteStatus(note, {
|
|
4631
4344
|
...rec.spent !== void 0 && { spent: rec.spent },
|
|
4632
4345
|
spentAt: rec.spentAt ?? null
|
|
@@ -4637,7 +4350,7 @@ async function noteBlindTag(blindKey, hex) {
|
|
|
4637
4350
|
}
|
|
4638
4351
|
|
|
4639
4352
|
// src/proof-generator/unshield.ts
|
|
4640
|
-
var
|
|
4353
|
+
var import_proof_generator2 = require("@orbinum/proof-generator");
|
|
4641
4354
|
var import_utils3 = require("@noble/ciphers/utils.js");
|
|
4642
4355
|
var import_baby_jubjub6 = require("@zk-kit/baby-jubjub");
|
|
4643
4356
|
var import_poseidon_lite4 = require("poseidon-lite");
|
|
@@ -4683,15 +4396,15 @@ async function generateUnshieldProof(inputs, options = {}) {
|
|
|
4683
4396
|
change_blinding: changeBlinding.toString(),
|
|
4684
4397
|
change_owner_pubkey: changeOwnerPubkey.toString()
|
|
4685
4398
|
};
|
|
4686
|
-
const provider = options.provider ?? new
|
|
4399
|
+
const provider = options.provider ?? new import_proof_generator2.WebArtifactProvider();
|
|
4687
4400
|
const opts = { provider };
|
|
4688
4401
|
if (options.verbose !== void 0) opts.verbose = options.verbose;
|
|
4689
|
-
const proofResult = await (0,
|
|
4402
|
+
const proofResult = await (0, import_proof_generator2.generateProof)(import_proof_generator2.CircuitType.Unshield, circuitInputs, opts);
|
|
4690
4403
|
return { ...proofResult, changeCommitment, changeValue, changeBlinding, changeOwnerPubkey };
|
|
4691
4404
|
}
|
|
4692
4405
|
|
|
4693
4406
|
// src/proof-generator/transfer.ts
|
|
4694
|
-
var
|
|
4407
|
+
var import_proof_generator3 = require("@orbinum/proof-generator");
|
|
4695
4408
|
async function generateTransferProof(params, options = {}) {
|
|
4696
4409
|
const root = leHexToBigint(params.merkleRoot).toString();
|
|
4697
4410
|
const [i0, i1] = params.inputs;
|
|
@@ -4716,14 +4429,14 @@ async function generateTransferProof(params, options = {}) {
|
|
|
4716
4429
|
output_owner_pubkeys: [o0.ownerPk.toString(), o1.ownerPk.toString()],
|
|
4717
4430
|
output_blindings: [o0.blinding.toString(), o1.blinding.toString()]
|
|
4718
4431
|
};
|
|
4719
|
-
const provider = options.provider ?? new
|
|
4432
|
+
const provider = options.provider ?? new import_proof_generator3.WebArtifactProvider();
|
|
4720
4433
|
const opts = { provider };
|
|
4721
4434
|
if (options.verbose !== void 0) opts.verbose = options.verbose;
|
|
4722
|
-
return (0,
|
|
4435
|
+
return (0, import_proof_generator3.generateProof)(import_proof_generator3.CircuitType.Transfer, circuitInputs, opts);
|
|
4723
4436
|
}
|
|
4724
4437
|
|
|
4725
4438
|
// src/proof-generator/fee-claim.ts
|
|
4726
|
-
var
|
|
4439
|
+
var import_proof_generator4 = require("@orbinum/proof-generator");
|
|
4727
4440
|
async function generateFeeClaimProof(inputs, options = {}) {
|
|
4728
4441
|
if (inputs.amount <= 0n) {
|
|
4729
4442
|
throw new Error("Fee claim amount must be greater than zero.");
|
|
@@ -4735,10 +4448,10 @@ async function generateFeeClaimProof(inputs, options = {}) {
|
|
|
4735
4448
|
owner_pubkey: inputs.ownerPubkey.toString(),
|
|
4736
4449
|
blinding: inputs.blinding.toString()
|
|
4737
4450
|
};
|
|
4738
|
-
const provider = options.provider ?? new
|
|
4451
|
+
const provider = options.provider ?? new import_proof_generator4.WebArtifactProvider();
|
|
4739
4452
|
const opts = { provider };
|
|
4740
4453
|
if (options.verbose !== void 0) opts.verbose = options.verbose;
|
|
4741
|
-
const proofResult = await (0,
|
|
4454
|
+
const proofResult = await (0, import_proof_generator4.generateProof)(import_proof_generator4.CircuitType.ValueProof, circuitInputs, opts);
|
|
4742
4455
|
const [sigCommitment, sigValue, sigAssetId, sigOwnerHash] = proofResult.publicSignals.map(BigInt);
|
|
4743
4456
|
const buf = new Uint8Array(76);
|
|
4744
4457
|
buf.set(bigintTo32Le(sigCommitment), 0);
|
|
@@ -4794,9 +4507,19 @@ function decodePrecompileCalldata(address, input) {
|
|
|
4794
4507
|
const recipient = toHex(data.slice(160, 192));
|
|
4795
4508
|
const fee = decodeUint(data, 192);
|
|
4796
4509
|
const changeCommitment = toHex(data.slice(224, 256));
|
|
4510
|
+
const circuitVersion = decodeUint(data, 288);
|
|
4797
4511
|
return {
|
|
4798
4512
|
fnSig,
|
|
4799
|
-
args: {
|
|
4513
|
+
args: {
|
|
4514
|
+
root,
|
|
4515
|
+
nullifier,
|
|
4516
|
+
assetId,
|
|
4517
|
+
amount,
|
|
4518
|
+
recipient,
|
|
4519
|
+
fee,
|
|
4520
|
+
changeCommitment,
|
|
4521
|
+
circuitVersion
|
|
4522
|
+
}
|
|
4800
4523
|
};
|
|
4801
4524
|
} catch {
|
|
4802
4525
|
return { fnSig, args: {} };
|
|
@@ -4812,7 +4535,8 @@ function decodePrecompileCalldata(address, input) {
|
|
|
4812
4535
|
const commitments = Number(decodeUint(data, commOffset));
|
|
4813
4536
|
const assetId = decodeUint(data, 160);
|
|
4814
4537
|
const fee = decodeUint(data, 192);
|
|
4815
|
-
|
|
4538
|
+
const circuitVersion = decodeUint(data, 224);
|
|
4539
|
+
return { fnSig, args: { root, nullifiers, commitments, assetId, fee, circuitVersion } };
|
|
4816
4540
|
} catch {
|
|
4817
4541
|
return { fnSig, args: {} };
|
|
4818
4542
|
}
|
|
@@ -4823,7 +4547,8 @@ function decodePrecompileCalldata(address, input) {
|
|
|
4823
4547
|
const commitment = toHex(data.slice(0, 32));
|
|
4824
4548
|
const amount = decodeUint(data, 32);
|
|
4825
4549
|
const assetId = decodeUint(data, 64);
|
|
4826
|
-
|
|
4550
|
+
const circuitVersion = decodeUint(data, 192);
|
|
4551
|
+
return { fnSig, args: { commitment, amount, assetId, circuitVersion } };
|
|
4827
4552
|
} catch {
|
|
4828
4553
|
return { fnSig, args: {} };
|
|
4829
4554
|
}
|
|
@@ -4835,8 +4560,8 @@ function decodePrecompileCalldata(address, input) {
|
|
|
4835
4560
|
var CircuitId = {
|
|
4836
4561
|
Transfer: 1,
|
|
4837
4562
|
Unshield: 2,
|
|
4838
|
-
|
|
4839
|
-
|
|
4563
|
+
PrivateLink: 5,
|
|
4564
|
+
ValueProof: 6
|
|
4840
4565
|
};
|
|
4841
4566
|
|
|
4842
4567
|
// src/utils/string.ts
|
|
@@ -5508,14 +5233,15 @@ var import_polkadot_api4 = require("polkadot-api");
|
|
|
5508
5233
|
BABYJUB_SUBORDER,
|
|
5509
5234
|
BN254_R,
|
|
5510
5235
|
Blake2256,
|
|
5236
|
+
CURRENT_CIRCUIT_VERSION,
|
|
5511
5237
|
CircuitId,
|
|
5512
5238
|
CircuitType,
|
|
5239
|
+
CircuitVersionResolver,
|
|
5513
5240
|
CryptoPrecompiles,
|
|
5514
5241
|
ENCRYPTED_MEMO_SIZE,
|
|
5515
5242
|
EncryptedMemo,
|
|
5516
5243
|
EvmClient,
|
|
5517
5244
|
EvmExplorer,
|
|
5518
|
-
IndexerClient,
|
|
5519
5245
|
KNOWN_PRECOMPILES,
|
|
5520
5246
|
Keccak256,
|
|
5521
5247
|
NoteBuilder,
|