@hsuite/smart-engines-sdk 3.0.3 → 3.1.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.ts +241 -2
- package/dist/index.js +500 -0
- package/dist/index.js.map +1 -1
- package/dist/nestjs/index.d.ts +186 -1
- package/dist/nestjs/index.js +457 -0
- package/dist/nestjs/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1014,6 +1014,7 @@ zod.z.object({
|
|
|
1014
1014
|
// src/discovery/index.ts
|
|
1015
1015
|
var discovery_exports = {};
|
|
1016
1016
|
__export(discovery_exports, {
|
|
1017
|
+
ClusterDiscoveryClient: () => ClusterDiscoveryClient,
|
|
1017
1018
|
MIRROR_NODE_URLS: () => MIRROR_NODE_URLS,
|
|
1018
1019
|
MirrorNodeClient: () => MirrorNodeClient,
|
|
1019
1020
|
MirrorNodeError: () => MirrorNodeError,
|
|
@@ -1386,6 +1387,185 @@ function normalizeRegistryEntry(raw) {
|
|
|
1386
1387
|
return null;
|
|
1387
1388
|
}
|
|
1388
1389
|
|
|
1390
|
+
// src/discovery/cluster-discovery.ts
|
|
1391
|
+
var ClusterDiscoveryClient = class {
|
|
1392
|
+
bootstrap;
|
|
1393
|
+
cacheTtlMs;
|
|
1394
|
+
fetchTimeoutMs;
|
|
1395
|
+
allowInsecure;
|
|
1396
|
+
trustAnchorClient;
|
|
1397
|
+
cache = null;
|
|
1398
|
+
constructor(config) {
|
|
1399
|
+
if (!config.bootstrap || config.bootstrap.length === 0) {
|
|
1400
|
+
throw new Error("ClusterDiscoveryClient: bootstrap must list at least one seed URL");
|
|
1401
|
+
}
|
|
1402
|
+
if (!config.allowInsecure) {
|
|
1403
|
+
for (const seed of config.bootstrap) {
|
|
1404
|
+
if (!seed.startsWith("https://")) {
|
|
1405
|
+
throw new Error(
|
|
1406
|
+
`ClusterDiscoveryClient: bootstrap seed "${seed}" is not HTTPS. Set allowInsecure=true for local development only.`
|
|
1407
|
+
);
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
this.bootstrap = config.bootstrap;
|
|
1412
|
+
this.cacheTtlMs = config.cacheTtlMs ?? 6e4;
|
|
1413
|
+
this.fetchTimeoutMs = config.fetchTimeoutMs ?? 5e3;
|
|
1414
|
+
this.allowInsecure = config.allowInsecure ?? false;
|
|
1415
|
+
this.trustAnchorClient = config.trustAnchor ? new ValidatorDiscoveryClient(config.trustAnchor) : null;
|
|
1416
|
+
}
|
|
1417
|
+
/**
|
|
1418
|
+
* Fetch the active-cluster set, with caching.
|
|
1419
|
+
*
|
|
1420
|
+
* Tries each bootstrap seed in order until one returns HTTP 200 with a
|
|
1421
|
+
* parseable cluster list. If all seeds fail, throws — callers can
|
|
1422
|
+
* handle by falling back to a previously-cached value if they want,
|
|
1423
|
+
* or by initializing with multiple seeds.
|
|
1424
|
+
*/
|
|
1425
|
+
async getClusters(forceRefresh = false) {
|
|
1426
|
+
if (!forceRefresh && this.cache && this.isCacheValid()) {
|
|
1427
|
+
return this.cache.clusters;
|
|
1428
|
+
}
|
|
1429
|
+
const fetched = await this.fetchFromFirstAvailableSeed();
|
|
1430
|
+
const verified = this.trustAnchorClient ? await this.verifyAgainstTrustAnchor(fetched) : fetched;
|
|
1431
|
+
this.cache = { clusters: verified, lastUpdated: Date.now() };
|
|
1432
|
+
return verified;
|
|
1433
|
+
}
|
|
1434
|
+
/**
|
|
1435
|
+
* Random pick over the active-cluster set. Returns `null` when no
|
|
1436
|
+
* cluster passes verification (empty registry / all-rejected anchor).
|
|
1437
|
+
*
|
|
1438
|
+
* Uses `crypto.getRandomValues` when available (browsers, Deno, modern
|
|
1439
|
+
* Node) for cryptographic-strength randomness; `Math.random` is a
|
|
1440
|
+
* fallback for older runtimes where `globalThis.crypto` is undefined.
|
|
1441
|
+
* Discovery isn't cryptographically sensitive — load distribution is
|
|
1442
|
+
* the goal here.
|
|
1443
|
+
*/
|
|
1444
|
+
async getRandomCluster(forceRefresh = false) {
|
|
1445
|
+
const clusters = await this.getClusters(forceRefresh);
|
|
1446
|
+
if (clusters.length === 0) return null;
|
|
1447
|
+
return clusters[this.pickRandomIndex(clusters.length)];
|
|
1448
|
+
}
|
|
1449
|
+
/**
|
|
1450
|
+
* Convenience wrapper returning just the gateway URL of a random
|
|
1451
|
+
* active cluster. The single most-used SDK entry point — most
|
|
1452
|
+
* downstream callers want `new SmartEngineClient({ baseUrl: ... })`
|
|
1453
|
+
* and don't care about the rest of the metadata.
|
|
1454
|
+
*/
|
|
1455
|
+
async getRandomGatewayUrl(forceRefresh = false) {
|
|
1456
|
+
const cluster = await this.getRandomCluster(forceRefresh);
|
|
1457
|
+
return cluster?.endpoints.gatewayUrl ?? null;
|
|
1458
|
+
}
|
|
1459
|
+
/** Per-clusterId lookup, useful for "stick the SDK to cluster X" flows. */
|
|
1460
|
+
async getClusterById(clusterId, forceRefresh = false) {
|
|
1461
|
+
const clusters = await this.getClusters(forceRefresh);
|
|
1462
|
+
return clusters.find((c) => c.clusterId === clusterId) ?? null;
|
|
1463
|
+
}
|
|
1464
|
+
clearCache() {
|
|
1465
|
+
this.cache = null;
|
|
1466
|
+
}
|
|
1467
|
+
isCacheValid() {
|
|
1468
|
+
if (!this.cache) return false;
|
|
1469
|
+
return Date.now() - this.cache.lastUpdated < this.cacheTtlMs;
|
|
1470
|
+
}
|
|
1471
|
+
async fetchFromFirstAvailableSeed() {
|
|
1472
|
+
const errors = [];
|
|
1473
|
+
for (const seed of this.bootstrap) {
|
|
1474
|
+
try {
|
|
1475
|
+
return await this.fetchClustersFromSeed(seed);
|
|
1476
|
+
} catch (err) {
|
|
1477
|
+
errors.push(`${seed}: ${err.message}`);
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
throw new Error(
|
|
1481
|
+
`ClusterDiscoveryClient: no bootstrap seed reachable. Attempts:
|
|
1482
|
+
${errors.join("\n ")}`
|
|
1483
|
+
);
|
|
1484
|
+
}
|
|
1485
|
+
async fetchClustersFromSeed(seed) {
|
|
1486
|
+
const url = `${seed.replace(/\/$/, "")}/api/v3/discovery/clusters`;
|
|
1487
|
+
const ctrl = new AbortController();
|
|
1488
|
+
const timer = setTimeout(() => ctrl.abort(), this.fetchTimeoutMs);
|
|
1489
|
+
try {
|
|
1490
|
+
const res = await fetch(url, { signal: ctrl.signal });
|
|
1491
|
+
if (!res.ok) {
|
|
1492
|
+
throw new Error(`HTTP ${res.status}`);
|
|
1493
|
+
}
|
|
1494
|
+
const body = await res.json();
|
|
1495
|
+
if (!Array.isArray(body.clusters)) {
|
|
1496
|
+
throw new Error("response missing clusters[]");
|
|
1497
|
+
}
|
|
1498
|
+
return body.clusters.map((c) => this.normalizeClusterEntry(c)).filter((c) => c !== null);
|
|
1499
|
+
} finally {
|
|
1500
|
+
clearTimeout(timer);
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
normalizeClusterEntry(raw) {
|
|
1504
|
+
if (typeof raw.clusterId !== "string" || !raw.clusterId) return null;
|
|
1505
|
+
if (!raw.endpoints || typeof raw.endpoints !== "object") return null;
|
|
1506
|
+
const ep = raw.endpoints;
|
|
1507
|
+
if (typeof ep.gatewayUrl !== "string" || !ep.gatewayUrl) return null;
|
|
1508
|
+
if (!this.allowInsecure && !ep.gatewayUrl.startsWith("https://")) {
|
|
1509
|
+
return null;
|
|
1510
|
+
}
|
|
1511
|
+
const nodeIds = Array.isArray(raw.nodeIds) ? raw.nodeIds.filter((n) => typeof n === "string") : [];
|
|
1512
|
+
return {
|
|
1513
|
+
clusterId: raw.clusterId,
|
|
1514
|
+
endpoints: {
|
|
1515
|
+
clusterId: raw.clusterId,
|
|
1516
|
+
gatewayUrl: ep.gatewayUrl,
|
|
1517
|
+
harborUrl: typeof ep.harborUrl === "string" ? ep.harborUrl : void 0,
|
|
1518
|
+
natsUrl: typeof ep.natsUrl === "string" ? ep.natsUrl : void 0,
|
|
1519
|
+
publicIp: typeof ep.publicIp === "string" ? ep.publicIp : void 0,
|
|
1520
|
+
region: typeof ep.region === "string" ? ep.region : void 0
|
|
1521
|
+
},
|
|
1522
|
+
nodeIds
|
|
1523
|
+
};
|
|
1524
|
+
}
|
|
1525
|
+
/**
|
|
1526
|
+
* Cross-check the HTTP-fetched cluster set against the HCS validator
|
|
1527
|
+
* registry. Clusters whose nodeIds are not on-chain are dropped.
|
|
1528
|
+
*
|
|
1529
|
+
* Two failure modes are deliberately silent here:
|
|
1530
|
+
* - the HCS read itself throws → original list is returned
|
|
1531
|
+
* un-verified. Trust-anchor is advisory; a network partition
|
|
1532
|
+
* between SDK and Hedera mirror shouldn't strand the SDK.
|
|
1533
|
+
* - the HCS read returns empty (mirror node lag, wrong topicId)
|
|
1534
|
+
* → original list is returned. Better to keep working off
|
|
1535
|
+
* possibly-stale-but-real data than reject every cluster.
|
|
1536
|
+
*
|
|
1537
|
+
* Both behaviors are why this is called "trust *anchor*", not
|
|
1538
|
+
* "trust gate". Crypto-strong gating is a follow-up.
|
|
1539
|
+
*/
|
|
1540
|
+
async verifyAgainstTrustAnchor(clusters) {
|
|
1541
|
+
if (!this.trustAnchorClient || clusters.length === 0) return clusters;
|
|
1542
|
+
let onChainNodeIds;
|
|
1543
|
+
try {
|
|
1544
|
+
const validators = await this.trustAnchorClient.getValidators();
|
|
1545
|
+
if (validators.length === 0) return clusters;
|
|
1546
|
+
onChainNodeIds = new Set(validators.map((v) => v.nodeId));
|
|
1547
|
+
} catch {
|
|
1548
|
+
return clusters;
|
|
1549
|
+
}
|
|
1550
|
+
return clusters.filter((c) => {
|
|
1551
|
+
if (c.nodeIds.length === 0) {
|
|
1552
|
+
return false;
|
|
1553
|
+
}
|
|
1554
|
+
return c.nodeIds.some((id) => onChainNodeIds.has(id));
|
|
1555
|
+
});
|
|
1556
|
+
}
|
|
1557
|
+
pickRandomIndex(n) {
|
|
1558
|
+
if (n <= 1) return 0;
|
|
1559
|
+
const g = globalThis.crypto;
|
|
1560
|
+
if (g?.getRandomValues) {
|
|
1561
|
+
const buf = new Uint32Array(1);
|
|
1562
|
+
g.getRandomValues(buf);
|
|
1563
|
+
return buf[0] % n;
|
|
1564
|
+
}
|
|
1565
|
+
return Math.floor(Math.random() * n);
|
|
1566
|
+
}
|
|
1567
|
+
};
|
|
1568
|
+
|
|
1389
1569
|
// src/auth/index.ts
|
|
1390
1570
|
var auth_exports = {};
|
|
1391
1571
|
__export(auth_exports, {
|
|
@@ -1784,6 +1964,17 @@ function createHttpClient(config) {
|
|
|
1784
1964
|
function encodePathParam(param) {
|
|
1785
1965
|
return encodeURIComponent(param).replace(/%2F/gi, "");
|
|
1786
1966
|
}
|
|
1967
|
+
function isRuleRejected(err) {
|
|
1968
|
+
if (!(err instanceof SdkHttpError)) return false;
|
|
1969
|
+
if (err.statusCode !== 403) return false;
|
|
1970
|
+
const d = err.details;
|
|
1971
|
+
if (d === null || typeof d !== "object") return false;
|
|
1972
|
+
const obj = d;
|
|
1973
|
+
if (obj.error !== "rule_rejected") return false;
|
|
1974
|
+
if (typeof obj.reason !== "string") return false;
|
|
1975
|
+
if (!Array.isArray(obj.ruleAtoms)) return false;
|
|
1976
|
+
return obj.ruleAtoms.every((a) => typeof a === "string");
|
|
1977
|
+
}
|
|
1787
1978
|
|
|
1788
1979
|
// src/subscription/index.ts
|
|
1789
1980
|
var subscription_exports = {};
|
|
@@ -2207,6 +2398,167 @@ var SnapshotsClient = class {
|
|
|
2207
2398
|
}
|
|
2208
2399
|
};
|
|
2209
2400
|
|
|
2401
|
+
// src/historical-balance/historical-balance-client.ts
|
|
2402
|
+
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
2403
|
+
var ENDPOINT_PATH = "/api/v3/historical-balance/query";
|
|
2404
|
+
var HistoricalBalanceClientError = class extends Error {
|
|
2405
|
+
constructor(message, statusCode, details) {
|
|
2406
|
+
super(message);
|
|
2407
|
+
this.statusCode = statusCode;
|
|
2408
|
+
this.details = details;
|
|
2409
|
+
this.name = "HistoricalBalanceClientError";
|
|
2410
|
+
}
|
|
2411
|
+
statusCode;
|
|
2412
|
+
details;
|
|
2413
|
+
};
|
|
2414
|
+
var HistoricalBalanceClient = class _HistoricalBalanceClient {
|
|
2415
|
+
// Standalone-mode fields (set when the client builds its own fetch).
|
|
2416
|
+
baseUrl;
|
|
2417
|
+
authToken;
|
|
2418
|
+
apiKey;
|
|
2419
|
+
timeoutMs;
|
|
2420
|
+
fetchImpl;
|
|
2421
|
+
// Sub-client-mode field (set when wired via SmartEngineClient).
|
|
2422
|
+
http;
|
|
2423
|
+
constructor(config) {
|
|
2424
|
+
if ("http" in config) {
|
|
2425
|
+
this.http = config.http;
|
|
2426
|
+
this.timeoutMs = DEFAULT_TIMEOUT_MS;
|
|
2427
|
+
return;
|
|
2428
|
+
}
|
|
2429
|
+
if (!config.baseUrl) {
|
|
2430
|
+
throw new HistoricalBalanceClientError(
|
|
2431
|
+
"HistoricalBalanceClient: baseUrl is required for standalone construction",
|
|
2432
|
+
400
|
|
2433
|
+
);
|
|
2434
|
+
}
|
|
2435
|
+
this.baseUrl = config.baseUrl.replace(/\/+$/, "");
|
|
2436
|
+
this.authToken = config.authToken;
|
|
2437
|
+
this.apiKey = config.apiKey;
|
|
2438
|
+
this.timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
2439
|
+
this.fetchImpl = config.fetchImpl;
|
|
2440
|
+
}
|
|
2441
|
+
/**
|
|
2442
|
+
* Factory used by `SmartEngineClient` to wire this sub-client onto the
|
|
2443
|
+
* parent's shared `HttpClient` (which already carries auth + baseUrl + a
|
|
2444
|
+
* `/api/v3` prefix). Routes through `/historical-balance/query` since the
|
|
2445
|
+
* parent's HttpClient is rooted at `/api/v3`.
|
|
2446
|
+
*/
|
|
2447
|
+
static fromHttp(http) {
|
|
2448
|
+
return new _HistoricalBalanceClient({ http });
|
|
2449
|
+
}
|
|
2450
|
+
/**
|
|
2451
|
+
* Query a point-in-time balance.
|
|
2452
|
+
*
|
|
2453
|
+
* The validator returns the on-chain bigint as a base-10 decimal string.
|
|
2454
|
+
* Callers needing arithmetic precision should wrap the result:
|
|
2455
|
+
*
|
|
2456
|
+
* ```ts
|
|
2457
|
+
* const { balance } = await client.historicalBalance.getBalance({ ... });
|
|
2458
|
+
* const value = BigInt(balance);
|
|
2459
|
+
* ```
|
|
2460
|
+
*/
|
|
2461
|
+
async getBalance(params) {
|
|
2462
|
+
this.validateParams(params);
|
|
2463
|
+
if (this.http) {
|
|
2464
|
+
return this.http.post("/historical-balance/query", params);
|
|
2465
|
+
}
|
|
2466
|
+
return this.standaloneFetch(params);
|
|
2467
|
+
}
|
|
2468
|
+
validateParams(params) {
|
|
2469
|
+
if (!params || typeof params !== "object") {
|
|
2470
|
+
throw new HistoricalBalanceClientError(
|
|
2471
|
+
"HistoricalBalanceClient.getBalance: params object is required",
|
|
2472
|
+
400
|
|
2473
|
+
);
|
|
2474
|
+
}
|
|
2475
|
+
const { chain, entityId, account, atTimestamp } = params;
|
|
2476
|
+
if (chain !== "hedera" && chain !== "xrpl" && chain !== "polkadot") {
|
|
2477
|
+
throw new HistoricalBalanceClientError(
|
|
2478
|
+
`HistoricalBalanceClient.getBalance: unsupported chain "${String(chain)}" (expected hedera | xrpl | polkadot)`,
|
|
2479
|
+
400
|
|
2480
|
+
);
|
|
2481
|
+
}
|
|
2482
|
+
if (typeof entityId !== "string" || entityId.length === 0) {
|
|
2483
|
+
throw new HistoricalBalanceClientError(
|
|
2484
|
+
"HistoricalBalanceClient.getBalance: entityId must be a non-empty string",
|
|
2485
|
+
400
|
|
2486
|
+
);
|
|
2487
|
+
}
|
|
2488
|
+
if (typeof account !== "string" || account.length === 0) {
|
|
2489
|
+
throw new HistoricalBalanceClientError(
|
|
2490
|
+
"HistoricalBalanceClient.getBalance: account must be a non-empty string",
|
|
2491
|
+
400
|
|
2492
|
+
);
|
|
2493
|
+
}
|
|
2494
|
+
if (typeof atTimestamp !== "number" || !Number.isInteger(atTimestamp) || atTimestamp <= 0) {
|
|
2495
|
+
throw new HistoricalBalanceClientError(
|
|
2496
|
+
"HistoricalBalanceClient.getBalance: atTimestamp must be a positive integer (unix seconds)",
|
|
2497
|
+
400
|
|
2498
|
+
);
|
|
2499
|
+
}
|
|
2500
|
+
}
|
|
2501
|
+
async standaloneFetch(params) {
|
|
2502
|
+
const url = `${this.baseUrl}${ENDPOINT_PATH}`;
|
|
2503
|
+
const headers = {
|
|
2504
|
+
"Content-Type": "application/json",
|
|
2505
|
+
Accept: "application/json"
|
|
2506
|
+
};
|
|
2507
|
+
if (this.authToken) headers.Authorization = `Bearer ${this.authToken}`;
|
|
2508
|
+
if (this.apiKey) headers["X-API-Key"] = this.apiKey;
|
|
2509
|
+
const fetchFn = this.fetchImpl ?? (typeof fetch !== "undefined" ? fetch : void 0);
|
|
2510
|
+
if (!fetchFn) {
|
|
2511
|
+
throw new HistoricalBalanceClientError(
|
|
2512
|
+
"HistoricalBalanceClient: no fetch implementation available (provide fetchImpl)",
|
|
2513
|
+
500
|
|
2514
|
+
);
|
|
2515
|
+
}
|
|
2516
|
+
const controller = new AbortController();
|
|
2517
|
+
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
2518
|
+
let response;
|
|
2519
|
+
try {
|
|
2520
|
+
response = await fetchFn(url, {
|
|
2521
|
+
method: "POST",
|
|
2522
|
+
headers,
|
|
2523
|
+
body: JSON.stringify(params),
|
|
2524
|
+
signal: controller.signal
|
|
2525
|
+
});
|
|
2526
|
+
} catch (err) {
|
|
2527
|
+
clearTimeout(timer);
|
|
2528
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2529
|
+
throw new HistoricalBalanceClientError(
|
|
2530
|
+
`HistoricalBalanceClient: transport error: ${message}`,
|
|
2531
|
+
0,
|
|
2532
|
+
err
|
|
2533
|
+
);
|
|
2534
|
+
}
|
|
2535
|
+
clearTimeout(timer);
|
|
2536
|
+
let body;
|
|
2537
|
+
const text = await response.text();
|
|
2538
|
+
if (text.length > 0) {
|
|
2539
|
+
try {
|
|
2540
|
+
body = JSON.parse(text);
|
|
2541
|
+
} catch (err) {
|
|
2542
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2543
|
+
throw new HistoricalBalanceClientError(
|
|
2544
|
+
`HistoricalBalanceClient: non-JSON response (status=${response.status}): ${message}`,
|
|
2545
|
+
response.status,
|
|
2546
|
+
{ raw: text }
|
|
2547
|
+
);
|
|
2548
|
+
}
|
|
2549
|
+
}
|
|
2550
|
+
if (!response.ok) {
|
|
2551
|
+
const detail = body && typeof body === "object" && "message" in body ? String(body.message) : `HTTP ${response.status}`;
|
|
2552
|
+
throw new HistoricalBalanceClientError(
|
|
2553
|
+
`HistoricalBalanceClient: ${detail}`,
|
|
2554
|
+
response.status,
|
|
2555
|
+
body
|
|
2556
|
+
);
|
|
2557
|
+
}
|
|
2558
|
+
return body;
|
|
2559
|
+
}
|
|
2560
|
+
};
|
|
2561
|
+
|
|
2210
2562
|
// src/settlement/index.ts
|
|
2211
2563
|
var settlement_exports = {};
|
|
2212
2564
|
__export(settlement_exports, {
|
|
@@ -2245,6 +2597,75 @@ var SettlementClient = class {
|
|
|
2245
2597
|
}
|
|
2246
2598
|
};
|
|
2247
2599
|
|
|
2600
|
+
// src/governance/index.ts
|
|
2601
|
+
var governance_exports = {};
|
|
2602
|
+
__export(governance_exports, {
|
|
2603
|
+
GovernanceClient: () => GovernanceClient
|
|
2604
|
+
});
|
|
2605
|
+
|
|
2606
|
+
// src/governance/governance-client.ts
|
|
2607
|
+
var GovernanceClient = class {
|
|
2608
|
+
constructor(http) {
|
|
2609
|
+
this.http = http;
|
|
2610
|
+
}
|
|
2611
|
+
http;
|
|
2612
|
+
/**
|
|
2613
|
+
* Dry-run a governance proposal/vote/etc. against `GovernanceMolecule.validate(...)`.
|
|
2614
|
+
*
|
|
2615
|
+
* Returns whatever `ValidationResult` the molecule produced. A `false`
|
|
2616
|
+
* `isValid` is **not** an error — it is a successful "this proposal is
|
|
2617
|
+
* invalid" outcome and is surfaced via the normal return value.
|
|
2618
|
+
*
|
|
2619
|
+
* HTTP errors (400 malformed body, 500 unexpected) bubble up as
|
|
2620
|
+
* `SdkHttpError` via the shared `HttpClient` layer.
|
|
2621
|
+
*/
|
|
2622
|
+
async simulate(params) {
|
|
2623
|
+
return this.http.post("/governance/simulate", params);
|
|
2624
|
+
}
|
|
2625
|
+
};
|
|
2626
|
+
|
|
2627
|
+
// src/personhood/index.ts
|
|
2628
|
+
var personhood_exports = {};
|
|
2629
|
+
__export(personhood_exports, {
|
|
2630
|
+
PERSONHOOD_VERIFIER_NOT_CONFIGURED: () => PERSONHOOD_VERIFIER_NOT_CONFIGURED,
|
|
2631
|
+
PersonhoodClient: () => PersonhoodClient,
|
|
2632
|
+
isPersonhoodVerifierNotConfigured: () => isPersonhoodVerifierNotConfigured
|
|
2633
|
+
});
|
|
2634
|
+
|
|
2635
|
+
// src/personhood/personhood-client.ts
|
|
2636
|
+
var PERSONHOOD_VERIFIER_NOT_CONFIGURED = "personhood_verifier_not_configured";
|
|
2637
|
+
var PersonhoodClient = class {
|
|
2638
|
+
constructor(http) {
|
|
2639
|
+
this.http = http;
|
|
2640
|
+
}
|
|
2641
|
+
http;
|
|
2642
|
+
/**
|
|
2643
|
+
* Verify a personhood proof for `candidate`.
|
|
2644
|
+
*
|
|
2645
|
+
* Returns the issued cert on accept, `null` on clean rejection. All
|
|
2646
|
+
* other failure modes (validation, transport, 5xx) propagate as
|
|
2647
|
+
* `SdkHttpError`.
|
|
2648
|
+
*/
|
|
2649
|
+
async verify(params) {
|
|
2650
|
+
const result = await this.http.post(
|
|
2651
|
+
"/personhood/verify",
|
|
2652
|
+
{
|
|
2653
|
+
candidate: params.candidate,
|
|
2654
|
+
proof: params.proof
|
|
2655
|
+
}
|
|
2656
|
+
);
|
|
2657
|
+
if (result === void 0) return null;
|
|
2658
|
+
return result;
|
|
2659
|
+
}
|
|
2660
|
+
};
|
|
2661
|
+
function isPersonhoodVerifierNotConfigured(err) {
|
|
2662
|
+
if (!(err instanceof SdkHttpError)) return false;
|
|
2663
|
+
if (err.statusCode !== 503) return false;
|
|
2664
|
+
const d = err.details;
|
|
2665
|
+
if (d === null || typeof d !== "object") return false;
|
|
2666
|
+
return d.error === PERSONHOOD_VERIFIER_NOT_CONFIGURED;
|
|
2667
|
+
}
|
|
2668
|
+
|
|
2248
2669
|
// src/client.ts
|
|
2249
2670
|
var SmartEngineClient = class _SmartEngineClient {
|
|
2250
2671
|
baseUrl;
|
|
@@ -2265,8 +2686,14 @@ var SmartEngineClient = class _SmartEngineClient {
|
|
|
2265
2686
|
transactions;
|
|
2266
2687
|
/** Token holder snapshot generation and retrieval */
|
|
2267
2688
|
snapshots;
|
|
2689
|
+
/** Historical balance archive reads (chain-native bigint, returned as decimal string) */
|
|
2690
|
+
historicalBalance;
|
|
2268
2691
|
/** Cross-chain settlement operations */
|
|
2269
2692
|
settlement;
|
|
2693
|
+
/** Governance proposal dry-run (simulate-only) */
|
|
2694
|
+
governance;
|
|
2695
|
+
/** Personhood verification (HPP one-human-one-member) */
|
|
2696
|
+
personhood;
|
|
2270
2697
|
constructor(config) {
|
|
2271
2698
|
this.allowInsecure = config.allowInsecure ?? false;
|
|
2272
2699
|
this.baseUrl = validateClientUrl(config.baseUrl, this.allowInsecure);
|
|
@@ -2287,7 +2714,10 @@ var SmartEngineClient = class _SmartEngineClient {
|
|
|
2287
2714
|
this.ipfs = new IPFSClient(this.http);
|
|
2288
2715
|
this.transactions = new TransactionsClient(this.txHttp);
|
|
2289
2716
|
this.snapshots = new SnapshotsClient(this.http);
|
|
2717
|
+
this.historicalBalance = HistoricalBalanceClient.fromHttp(this.http);
|
|
2290
2718
|
this.settlement = new SettlementClient(this.http);
|
|
2719
|
+
this.governance = new GovernanceClient(this.http);
|
|
2720
|
+
this.personhood = new PersonhoodClient(this.http);
|
|
2291
2721
|
}
|
|
2292
2722
|
/**
|
|
2293
2723
|
* Connect to the smart-engines network with auto-discovery and authentication
|
|
@@ -2332,6 +2762,67 @@ var SmartEngineClient = class _SmartEngineClient {
|
|
|
2332
2762
|
});
|
|
2333
2763
|
return { client, validator, session };
|
|
2334
2764
|
}
|
|
2765
|
+
/**
|
|
2766
|
+
* Connect to the smart-engines network via the **service-registry**
|
|
2767
|
+
* (PR-1 of the cluster-discovery arc). Preferred over
|
|
2768
|
+
* {@link connectToNetwork} once the validator pods in the target network
|
|
2769
|
+
* have published their cluster endpoints — the SDK auto-balances across
|
|
2770
|
+
* the active cluster set and rides permissionless cluster join/leave
|
|
2771
|
+
* without code edits.
|
|
2772
|
+
*
|
|
2773
|
+
* Fallback ladder (per `docs/ops/HANDOFF-service-registry-distribution-layer.md` §6):
|
|
2774
|
+
* 1. HTTP fetch `/api/v3/discovery/clusters` from each bootstrap seed.
|
|
2775
|
+
* 2. (Optional) HCS trust-anchor membership cross-check.
|
|
2776
|
+
* 3. Random-pick over the verified set.
|
|
2777
|
+
*
|
|
2778
|
+
* @example
|
|
2779
|
+
* ```ts
|
|
2780
|
+
* const { client, cluster, session } = await SmartEngineClient.connectToCluster({
|
|
2781
|
+
* bootstrap: ['https://sn1.testnet.hsuite.network', 'https://sn2.testnet.hsuite.network'],
|
|
2782
|
+
* chain: 'xrpl',
|
|
2783
|
+
* address: '...',
|
|
2784
|
+
* publicKey: '...',
|
|
2785
|
+
* signFn: async (challenge) => sign(challenge),
|
|
2786
|
+
* });
|
|
2787
|
+
* ```
|
|
2788
|
+
*/
|
|
2789
|
+
static async connectToCluster(config) {
|
|
2790
|
+
const allowInsecure = config.allowInsecure ?? false;
|
|
2791
|
+
const discovery = new ClusterDiscoveryClient({
|
|
2792
|
+
bootstrap: config.bootstrap,
|
|
2793
|
+
allowInsecure,
|
|
2794
|
+
trustAnchor: config.trustAnchor ? {
|
|
2795
|
+
network: config.trustAnchor.network,
|
|
2796
|
+
registryTopicId: config.trustAnchor.registryTopicId,
|
|
2797
|
+
mirrorNodeUrl: config.trustAnchor.mirrorNodeUrl,
|
|
2798
|
+
allowInsecure
|
|
2799
|
+
} : void 0
|
|
2800
|
+
});
|
|
2801
|
+
const cluster = await discovery.getRandomCluster();
|
|
2802
|
+
if (!cluster) {
|
|
2803
|
+
throw new SmartEngineError2(
|
|
2804
|
+
"No active clusters available via bootstrap seeds. Check bootstrap URLs and network reachability.",
|
|
2805
|
+
503
|
|
2806
|
+
);
|
|
2807
|
+
}
|
|
2808
|
+
const gatewayUrl = cluster.endpoints.gatewayUrl;
|
|
2809
|
+
validateClientUrl(gatewayUrl, allowInsecure);
|
|
2810
|
+
const auth = new ValidatorAuthClient({ security: { allowInsecure } });
|
|
2811
|
+
const session = await auth.authenticateWithSigner(
|
|
2812
|
+
gatewayUrl,
|
|
2813
|
+
config.chain,
|
|
2814
|
+
config.address,
|
|
2815
|
+
config.publicKey,
|
|
2816
|
+
config.signFn,
|
|
2817
|
+
config.metadata
|
|
2818
|
+
);
|
|
2819
|
+
const client = new _SmartEngineClient({
|
|
2820
|
+
baseUrl: gatewayUrl,
|
|
2821
|
+
authToken: session.token,
|
|
2822
|
+
allowInsecure
|
|
2823
|
+
});
|
|
2824
|
+
return { client, cluster, session };
|
|
2825
|
+
}
|
|
2335
2826
|
/** Get the current validator URL */
|
|
2336
2827
|
getBaseUrl() {
|
|
2337
2828
|
return this.baseUrl;
|
|
@@ -4137,11 +4628,16 @@ exports.DnsClient = DnsClient;
|
|
|
4137
4628
|
exports.DomainsClient = DomainsClient;
|
|
4138
4629
|
exports.ErrorCode = ErrorCode;
|
|
4139
4630
|
exports.FunctionsClient = FunctionsClient;
|
|
4631
|
+
exports.GovernanceClient = GovernanceClient;
|
|
4632
|
+
exports.HistoricalBalanceClient = HistoricalBalanceClient;
|
|
4633
|
+
exports.HistoricalBalanceClientError = HistoricalBalanceClientError;
|
|
4140
4634
|
exports.IPFSClient = IPFSClient;
|
|
4141
4635
|
exports.MIRROR_NODE_URLS = MIRROR_NODE_URLS;
|
|
4142
4636
|
exports.MessagingClient = MessagingClient;
|
|
4143
4637
|
exports.MirrorNodeClient = MirrorNodeClient;
|
|
4144
4638
|
exports.MirrorNodeError = MirrorNodeError;
|
|
4639
|
+
exports.PERSONHOOD_VERIFIER_NOT_CONFIGURED = PERSONHOOD_VERIFIER_NOT_CONFIGURED;
|
|
4640
|
+
exports.PersonhoodClient = PersonhoodClient;
|
|
4145
4641
|
exports.RateLimiter = RateLimiter;
|
|
4146
4642
|
exports.RoutingClient = RoutingClient;
|
|
4147
4643
|
exports.SdkHttpError = SdkHttpError;
|
|
@@ -4165,6 +4661,10 @@ exports.createHttpClient = createHttpClient;
|
|
|
4165
4661
|
exports.createResilientFetchWithBreaker = createResilientFetchWithBreaker;
|
|
4166
4662
|
exports.discovery = discovery_exports;
|
|
4167
4663
|
exports.encodePathParam = encodePathParam;
|
|
4664
|
+
exports.governance = governance_exports;
|
|
4665
|
+
exports.isPersonhoodVerifierNotConfigured = isPersonhoodVerifierNotConfigured;
|
|
4666
|
+
exports.isRuleRejected = isRuleRejected;
|
|
4667
|
+
exports.personhood = personhood_exports;
|
|
4168
4668
|
exports.resilientFetch = resilientFetch;
|
|
4169
4669
|
exports.settlement = settlement_exports;
|
|
4170
4670
|
exports.subscription = subscription_exports;
|