@hsuite/smart-engines-sdk 3.0.3 → 3.2.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 +437 -67
- package/dist/index.js +602 -68
- package/dist/index.js.map +1 -1
- package/dist/nestjs/index.d.ts +382 -66
- package/dist/nestjs/index.js +555 -68
- package/dist/nestjs/index.js.map +1 -1
- package/package.json +1 -1
package/dist/nestjs/index.js
CHANGED
|
@@ -1022,6 +1022,185 @@ function normalizeRegistryEntry(raw) {
|
|
|
1022
1022
|
return null;
|
|
1023
1023
|
}
|
|
1024
1024
|
|
|
1025
|
+
// src/discovery/cluster-discovery.ts
|
|
1026
|
+
var ClusterDiscoveryClient = class {
|
|
1027
|
+
bootstrap;
|
|
1028
|
+
cacheTtlMs;
|
|
1029
|
+
fetchTimeoutMs;
|
|
1030
|
+
allowInsecure;
|
|
1031
|
+
trustAnchorClient;
|
|
1032
|
+
cache = null;
|
|
1033
|
+
constructor(config) {
|
|
1034
|
+
if (!config.bootstrap || config.bootstrap.length === 0) {
|
|
1035
|
+
throw new Error("ClusterDiscoveryClient: bootstrap must list at least one seed URL");
|
|
1036
|
+
}
|
|
1037
|
+
if (!config.allowInsecure) {
|
|
1038
|
+
for (const seed of config.bootstrap) {
|
|
1039
|
+
if (!seed.startsWith("https://")) {
|
|
1040
|
+
throw new Error(
|
|
1041
|
+
`ClusterDiscoveryClient: bootstrap seed "${seed}" is not HTTPS. Set allowInsecure=true for local development only.`
|
|
1042
|
+
);
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
this.bootstrap = config.bootstrap;
|
|
1047
|
+
this.cacheTtlMs = config.cacheTtlMs ?? 6e4;
|
|
1048
|
+
this.fetchTimeoutMs = config.fetchTimeoutMs ?? 5e3;
|
|
1049
|
+
this.allowInsecure = config.allowInsecure ?? false;
|
|
1050
|
+
this.trustAnchorClient = config.trustAnchor ? new ValidatorDiscoveryClient(config.trustAnchor) : null;
|
|
1051
|
+
}
|
|
1052
|
+
/**
|
|
1053
|
+
* Fetch the active-cluster set, with caching.
|
|
1054
|
+
*
|
|
1055
|
+
* Tries each bootstrap seed in order until one returns HTTP 200 with a
|
|
1056
|
+
* parseable cluster list. If all seeds fail, throws — callers can
|
|
1057
|
+
* handle by falling back to a previously-cached value if they want,
|
|
1058
|
+
* or by initializing with multiple seeds.
|
|
1059
|
+
*/
|
|
1060
|
+
async getClusters(forceRefresh = false) {
|
|
1061
|
+
if (!forceRefresh && this.cache && this.isCacheValid()) {
|
|
1062
|
+
return this.cache.clusters;
|
|
1063
|
+
}
|
|
1064
|
+
const fetched = await this.fetchFromFirstAvailableSeed();
|
|
1065
|
+
const verified = this.trustAnchorClient ? await this.verifyAgainstTrustAnchor(fetched) : fetched;
|
|
1066
|
+
this.cache = { clusters: verified, lastUpdated: Date.now() };
|
|
1067
|
+
return verified;
|
|
1068
|
+
}
|
|
1069
|
+
/**
|
|
1070
|
+
* Random pick over the active-cluster set. Returns `null` when no
|
|
1071
|
+
* cluster passes verification (empty registry / all-rejected anchor).
|
|
1072
|
+
*
|
|
1073
|
+
* Uses `crypto.getRandomValues` when available (browsers, Deno, modern
|
|
1074
|
+
* Node) for cryptographic-strength randomness; `Math.random` is a
|
|
1075
|
+
* fallback for older runtimes where `globalThis.crypto` is undefined.
|
|
1076
|
+
* Discovery isn't cryptographically sensitive — load distribution is
|
|
1077
|
+
* the goal here.
|
|
1078
|
+
*/
|
|
1079
|
+
async getRandomCluster(forceRefresh = false) {
|
|
1080
|
+
const clusters = await this.getClusters(forceRefresh);
|
|
1081
|
+
if (clusters.length === 0) return null;
|
|
1082
|
+
return clusters[this.pickRandomIndex(clusters.length)];
|
|
1083
|
+
}
|
|
1084
|
+
/**
|
|
1085
|
+
* Convenience wrapper returning just the gateway URL of a random
|
|
1086
|
+
* active cluster. The single most-used SDK entry point — most
|
|
1087
|
+
* downstream callers want `new SmartEngineClient({ baseUrl: ... })`
|
|
1088
|
+
* and don't care about the rest of the metadata.
|
|
1089
|
+
*/
|
|
1090
|
+
async getRandomGatewayUrl(forceRefresh = false) {
|
|
1091
|
+
const cluster = await this.getRandomCluster(forceRefresh);
|
|
1092
|
+
return cluster?.endpoints.gatewayUrl ?? null;
|
|
1093
|
+
}
|
|
1094
|
+
/** Per-clusterId lookup, useful for "stick the SDK to cluster X" flows. */
|
|
1095
|
+
async getClusterById(clusterId, forceRefresh = false) {
|
|
1096
|
+
const clusters = await this.getClusters(forceRefresh);
|
|
1097
|
+
return clusters.find((c) => c.clusterId === clusterId) ?? null;
|
|
1098
|
+
}
|
|
1099
|
+
clearCache() {
|
|
1100
|
+
this.cache = null;
|
|
1101
|
+
}
|
|
1102
|
+
isCacheValid() {
|
|
1103
|
+
if (!this.cache) return false;
|
|
1104
|
+
return Date.now() - this.cache.lastUpdated < this.cacheTtlMs;
|
|
1105
|
+
}
|
|
1106
|
+
async fetchFromFirstAvailableSeed() {
|
|
1107
|
+
const errors = [];
|
|
1108
|
+
for (const seed of this.bootstrap) {
|
|
1109
|
+
try {
|
|
1110
|
+
return await this.fetchClustersFromSeed(seed);
|
|
1111
|
+
} catch (err) {
|
|
1112
|
+
errors.push(`${seed}: ${err.message}`);
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
throw new Error(
|
|
1116
|
+
`ClusterDiscoveryClient: no bootstrap seed reachable. Attempts:
|
|
1117
|
+
${errors.join("\n ")}`
|
|
1118
|
+
);
|
|
1119
|
+
}
|
|
1120
|
+
async fetchClustersFromSeed(seed) {
|
|
1121
|
+
const url = `${seed.replace(/\/$/, "")}/api/v3/discovery/clusters`;
|
|
1122
|
+
const ctrl = new AbortController();
|
|
1123
|
+
const timer = setTimeout(() => ctrl.abort(), this.fetchTimeoutMs);
|
|
1124
|
+
try {
|
|
1125
|
+
const res = await fetch(url, { signal: ctrl.signal });
|
|
1126
|
+
if (!res.ok) {
|
|
1127
|
+
throw new Error(`HTTP ${res.status}`);
|
|
1128
|
+
}
|
|
1129
|
+
const body = await res.json();
|
|
1130
|
+
if (!Array.isArray(body.clusters)) {
|
|
1131
|
+
throw new Error("response missing clusters[]");
|
|
1132
|
+
}
|
|
1133
|
+
return body.clusters.map((c) => this.normalizeClusterEntry(c)).filter((c) => c !== null);
|
|
1134
|
+
} finally {
|
|
1135
|
+
clearTimeout(timer);
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
normalizeClusterEntry(raw) {
|
|
1139
|
+
if (typeof raw.clusterId !== "string" || !raw.clusterId) return null;
|
|
1140
|
+
if (!raw.endpoints || typeof raw.endpoints !== "object") return null;
|
|
1141
|
+
const ep = raw.endpoints;
|
|
1142
|
+
if (typeof ep.gatewayUrl !== "string" || !ep.gatewayUrl) return null;
|
|
1143
|
+
if (!this.allowInsecure && !ep.gatewayUrl.startsWith("https://")) {
|
|
1144
|
+
return null;
|
|
1145
|
+
}
|
|
1146
|
+
const nodeIds = Array.isArray(raw.nodeIds) ? raw.nodeIds.filter((n) => typeof n === "string") : [];
|
|
1147
|
+
return {
|
|
1148
|
+
clusterId: raw.clusterId,
|
|
1149
|
+
endpoints: {
|
|
1150
|
+
clusterId: raw.clusterId,
|
|
1151
|
+
gatewayUrl: ep.gatewayUrl,
|
|
1152
|
+
harborUrl: typeof ep.harborUrl === "string" ? ep.harborUrl : void 0,
|
|
1153
|
+
natsUrl: typeof ep.natsUrl === "string" ? ep.natsUrl : void 0,
|
|
1154
|
+
publicIp: typeof ep.publicIp === "string" ? ep.publicIp : void 0,
|
|
1155
|
+
region: typeof ep.region === "string" ? ep.region : void 0
|
|
1156
|
+
},
|
|
1157
|
+
nodeIds
|
|
1158
|
+
};
|
|
1159
|
+
}
|
|
1160
|
+
/**
|
|
1161
|
+
* Cross-check the HTTP-fetched cluster set against the HCS validator
|
|
1162
|
+
* registry. Clusters whose nodeIds are not on-chain are dropped.
|
|
1163
|
+
*
|
|
1164
|
+
* Two failure modes are deliberately silent here:
|
|
1165
|
+
* - the HCS read itself throws → original list is returned
|
|
1166
|
+
* un-verified. Trust-anchor is advisory; a network partition
|
|
1167
|
+
* between SDK and Hedera mirror shouldn't strand the SDK.
|
|
1168
|
+
* - the HCS read returns empty (mirror node lag, wrong topicId)
|
|
1169
|
+
* → original list is returned. Better to keep working off
|
|
1170
|
+
* possibly-stale-but-real data than reject every cluster.
|
|
1171
|
+
*
|
|
1172
|
+
* Both behaviors are why this is called "trust *anchor*", not
|
|
1173
|
+
* "trust gate". Crypto-strong gating is a follow-up.
|
|
1174
|
+
*/
|
|
1175
|
+
async verifyAgainstTrustAnchor(clusters) {
|
|
1176
|
+
if (!this.trustAnchorClient || clusters.length === 0) return clusters;
|
|
1177
|
+
let onChainNodeIds;
|
|
1178
|
+
try {
|
|
1179
|
+
const validators = await this.trustAnchorClient.getValidators();
|
|
1180
|
+
if (validators.length === 0) return clusters;
|
|
1181
|
+
onChainNodeIds = new Set(validators.map((v) => v.nodeId));
|
|
1182
|
+
} catch {
|
|
1183
|
+
return clusters;
|
|
1184
|
+
}
|
|
1185
|
+
return clusters.filter((c) => {
|
|
1186
|
+
if (c.nodeIds.length === 0) {
|
|
1187
|
+
return false;
|
|
1188
|
+
}
|
|
1189
|
+
return c.nodeIds.some((id) => onChainNodeIds.has(id));
|
|
1190
|
+
});
|
|
1191
|
+
}
|
|
1192
|
+
pickRandomIndex(n) {
|
|
1193
|
+
if (n <= 1) return 0;
|
|
1194
|
+
const g = globalThis.crypto;
|
|
1195
|
+
if (g?.getRandomValues) {
|
|
1196
|
+
const buf = new Uint32Array(1);
|
|
1197
|
+
g.getRandomValues(buf);
|
|
1198
|
+
return buf[0] % n;
|
|
1199
|
+
}
|
|
1200
|
+
return Math.floor(Math.random() * n);
|
|
1201
|
+
}
|
|
1202
|
+
};
|
|
1203
|
+
|
|
1025
1204
|
// src/auth/validator-auth.ts
|
|
1026
1205
|
var SUPPORTED_AUTH_CHAINS = [
|
|
1027
1206
|
"hedera",
|
|
@@ -1662,80 +1841,134 @@ var IPFSClient = class {
|
|
|
1662
1841
|
}
|
|
1663
1842
|
};
|
|
1664
1843
|
|
|
1844
|
+
// src/transactions/chains/hedera.ts
|
|
1845
|
+
var HederaTransactionsClient = class {
|
|
1846
|
+
constructor(http) {
|
|
1847
|
+
this.http = http;
|
|
1848
|
+
}
|
|
1849
|
+
http;
|
|
1850
|
+
/** Prepare an HCS topic creation transaction. */
|
|
1851
|
+
async prepareTopicCreate(request) {
|
|
1852
|
+
return this.http.post("/topic/create/prepare", { ...request, chain: "hedera" });
|
|
1853
|
+
}
|
|
1854
|
+
/** Prepare an HCS topic message submission. */
|
|
1855
|
+
async prepareTopicMessage(request) {
|
|
1856
|
+
return this.http.post("/topic/message/prepare", { ...request, chain: "hedera" });
|
|
1857
|
+
}
|
|
1858
|
+
/** Prepare a token compliance-enable (KYC grant) transaction. */
|
|
1859
|
+
async prepareTokenComplianceEnable(request) {
|
|
1860
|
+
return this.http.post("/token/compliance/enable/prepare", { ...request, chain: "hedera" });
|
|
1861
|
+
}
|
|
1862
|
+
/** Prepare a token compliance-disable (KYC revoke) transaction. */
|
|
1863
|
+
async prepareTokenComplianceDisable(request) {
|
|
1864
|
+
return this.http.post("/token/compliance/disable/prepare", { ...request, chain: "hedera" });
|
|
1865
|
+
}
|
|
1866
|
+
/** Prepare a token wipe transaction (force-remove tokens from an account). */
|
|
1867
|
+
async prepareTokenWipe(request) {
|
|
1868
|
+
return this.http.post("/token/wipe/prepare", { ...request, chain: "hedera" });
|
|
1869
|
+
}
|
|
1870
|
+
};
|
|
1871
|
+
|
|
1872
|
+
// src/transactions/chains/xrpl.ts
|
|
1873
|
+
var XrplTransactionsClient = class {
|
|
1874
|
+
constructor(http) {
|
|
1875
|
+
this.http = http;
|
|
1876
|
+
}
|
|
1877
|
+
http;
|
|
1878
|
+
/** Prepare an XRPL TrustSet (trust line) transaction. */
|
|
1879
|
+
async prepareTrustLine(request) {
|
|
1880
|
+
return this.http.post("/trustline/prepare", { ...request, chain: "xrpl" });
|
|
1881
|
+
}
|
|
1882
|
+
};
|
|
1883
|
+
|
|
1884
|
+
// src/transactions/chains/solana.ts
|
|
1885
|
+
var SolanaTransactionsClient = class {
|
|
1886
|
+
constructor(http) {
|
|
1887
|
+
this.http = http;
|
|
1888
|
+
}
|
|
1889
|
+
http;
|
|
1890
|
+
/** Prepare an SPL Token close-account transaction. */
|
|
1891
|
+
async prepareTokenCloseAccount(request) {
|
|
1892
|
+
return this.http.post("/token/close-account/prepare", { ...request, chain: "solana" });
|
|
1893
|
+
}
|
|
1894
|
+
};
|
|
1895
|
+
|
|
1896
|
+
// src/transactions/chains/polkadot.ts
|
|
1897
|
+
var PolkadotTransactionsClient = class {
|
|
1898
|
+
constructor(http) {
|
|
1899
|
+
this.http = http;
|
|
1900
|
+
}
|
|
1901
|
+
http;
|
|
1902
|
+
/** Prepare a pallet-assets.setTeam transaction. */
|
|
1903
|
+
async prepareAssetSetTeam(request) {
|
|
1904
|
+
return this.http.post("/token/set-team/prepare", { ...request, chain: "polkadot" });
|
|
1905
|
+
}
|
|
1906
|
+
/** Prepare a pallet-assets.setMetadata transaction. */
|
|
1907
|
+
async prepareAssetSetMetadata(request) {
|
|
1908
|
+
return this.http.post("/token/set-metadata/prepare", { ...request, chain: "polkadot" });
|
|
1909
|
+
}
|
|
1910
|
+
};
|
|
1911
|
+
|
|
1665
1912
|
// src/transactions/index.ts
|
|
1666
1913
|
var TransactionsClient = class {
|
|
1667
1914
|
constructor(http) {
|
|
1668
1915
|
this.http = http;
|
|
1669
1916
|
}
|
|
1670
1917
|
http;
|
|
1671
|
-
/**
|
|
1672
|
-
* Get transaction preparation service info
|
|
1673
|
-
*/
|
|
1918
|
+
/** Get transaction preparation service info. */
|
|
1674
1919
|
async getInfo() {
|
|
1675
1920
|
return this.http.get("/info");
|
|
1676
1921
|
}
|
|
1677
|
-
/**
|
|
1678
|
-
* Prepare a transfer transaction for local signing
|
|
1679
|
-
*/
|
|
1922
|
+
/** Prepare a transfer transaction (any supported chain). */
|
|
1680
1923
|
async prepareTransfer(request) {
|
|
1681
1924
|
return this.http.post("/transfer/prepare", request);
|
|
1682
1925
|
}
|
|
1683
|
-
/**
|
|
1684
|
-
* Prepare an NFT mint transaction
|
|
1685
|
-
*/
|
|
1926
|
+
/** Prepare an NFT mint transaction (any supported chain). */
|
|
1686
1927
|
async prepareNftMint(request) {
|
|
1687
1928
|
return this.http.post("/nft/mint/prepare", request);
|
|
1688
1929
|
}
|
|
1689
|
-
/**
|
|
1690
|
-
* Prepare an NFT burn transaction
|
|
1691
|
-
*/
|
|
1930
|
+
/** Prepare an NFT burn transaction (any supported chain). */
|
|
1692
1931
|
async prepareNftBurn(request) {
|
|
1693
1932
|
return this.http.post("/nft/burn/prepare", request);
|
|
1694
1933
|
}
|
|
1695
|
-
/**
|
|
1696
|
-
* Prepare an NFT transfer transaction
|
|
1697
|
-
*/
|
|
1934
|
+
/** Prepare an NFT transfer transaction (any supported chain). */
|
|
1698
1935
|
async prepareNftTransfer(request) {
|
|
1699
1936
|
return this.http.post("/nft/transfer/prepare", request);
|
|
1700
1937
|
}
|
|
1701
|
-
/**
|
|
1702
|
-
* Prepare a token creation transaction
|
|
1703
|
-
*/
|
|
1938
|
+
/** Prepare a token creation transaction (Hedera, Solana, Polkadot, Cardano). */
|
|
1704
1939
|
async prepareTokenCreate(request) {
|
|
1705
1940
|
return this.http.post("/token/create/prepare", request);
|
|
1706
1941
|
}
|
|
1707
|
-
/**
|
|
1708
|
-
* Prepare a token mint transaction
|
|
1709
|
-
*/
|
|
1942
|
+
/** Prepare a token mint transaction (any supported chain). */
|
|
1710
1943
|
async prepareTokenMint(request) {
|
|
1711
1944
|
return this.http.post("/token/mint/prepare", request);
|
|
1712
1945
|
}
|
|
1713
1946
|
/**
|
|
1714
|
-
* Prepare a fungible token burn transaction (Hedera
|
|
1715
|
-
*
|
|
1716
|
-
* For NFT burn use `prepareNftBurn` with a serialNumber.
|
|
1947
|
+
* Prepare a fungible token burn transaction (Hedera, Solana, Polkadot,
|
|
1948
|
+
* Cardano). For NFT burn use `prepareNftBurn` with a `serialNumber`.
|
|
1717
1949
|
*/
|
|
1718
1950
|
async prepareTokenBurn(request) {
|
|
1719
1951
|
return this.http.post("/token/burn/prepare", request);
|
|
1720
1952
|
}
|
|
1721
1953
|
/**
|
|
1722
|
-
* Prepare a token association transaction
|
|
1954
|
+
* Prepare a token association transaction (Hedera HTS associate / Solana
|
|
1955
|
+
* ATA create). On chains that don't need explicit association (XRPL uses
|
|
1956
|
+
* trustlines — see `client.xrpl.prepareTrustLine`), the server returns 400.
|
|
1723
1957
|
*/
|
|
1724
|
-
async
|
|
1958
|
+
async prepareTokenAssociate(request) {
|
|
1725
1959
|
return this.http.post("/token/associate/prepare", request);
|
|
1726
1960
|
}
|
|
1727
|
-
|
|
1728
|
-
/** Prepare a token pause transaction (capability: pausable). */
|
|
1961
|
+
/** Prepare a token pause transaction (Hedera, Polkadot, Solana). */
|
|
1729
1962
|
async prepareTokenPause(request) {
|
|
1730
1963
|
return this.http.post("/token/pause/prepare", request);
|
|
1731
1964
|
}
|
|
1732
|
-
/** Prepare a token unpause transaction (
|
|
1965
|
+
/** Prepare a token unpause transaction (Hedera, Polkadot, Solana). */
|
|
1733
1966
|
async prepareTokenUnpause(request) {
|
|
1734
1967
|
return this.http.post("/token/unpause/prepare", request);
|
|
1735
1968
|
}
|
|
1736
1969
|
/**
|
|
1737
|
-
* Prepare a token restrict (freeze account) transaction (
|
|
1738
|
-
*
|
|
1970
|
+
* Prepare a token restrict (freeze account) transaction (Hedera, Polkadot,
|
|
1971
|
+
* Solana). Freezes `accountId`/`who`/`account` from transacting the token.
|
|
1739
1972
|
*/
|
|
1740
1973
|
async prepareTokenRestrict(request) {
|
|
1741
1974
|
return this.http.post("/token/restrict/prepare", request);
|
|
@@ -1744,42 +1977,6 @@ var TransactionsClient = class {
|
|
|
1744
1977
|
async prepareTokenUnrestrict(request) {
|
|
1745
1978
|
return this.http.post("/token/unrestrict/prepare", request);
|
|
1746
1979
|
}
|
|
1747
|
-
/**
|
|
1748
|
-
* Prepare a token compliance-enable (grant KYC) transaction (capability:
|
|
1749
|
-
* compliant).
|
|
1750
|
-
*/
|
|
1751
|
-
async prepareTokenComplianceEnable(request) {
|
|
1752
|
-
return this.http.post("/token/compliance/enable/prepare", request);
|
|
1753
|
-
}
|
|
1754
|
-
/** Prepare a token compliance-disable (revoke KYC) transaction. */
|
|
1755
|
-
async prepareTokenComplianceDisable(request) {
|
|
1756
|
-
return this.http.post("/token/compliance/disable/prepare", request);
|
|
1757
|
-
}
|
|
1758
|
-
/**
|
|
1759
|
-
* Prepare a token wipe transaction (capability: wipeable). Force-removes
|
|
1760
|
-
* `amount` of `tokenId` from `accountId`.
|
|
1761
|
-
*/
|
|
1762
|
-
async prepareTokenWipe(request) {
|
|
1763
|
-
return this.http.post("/token/wipe/prepare", request);
|
|
1764
|
-
}
|
|
1765
|
-
/**
|
|
1766
|
-
* Prepare a topic creation transaction
|
|
1767
|
-
*/
|
|
1768
|
-
async prepareTopicCreate(request) {
|
|
1769
|
-
return this.http.post("/topic/create/prepare", request);
|
|
1770
|
-
}
|
|
1771
|
-
/**
|
|
1772
|
-
* Prepare a topic message submission transaction
|
|
1773
|
-
*/
|
|
1774
|
-
async prepareTopicMessage(request) {
|
|
1775
|
-
return this.http.post("/topic/message/prepare", request);
|
|
1776
|
-
}
|
|
1777
|
-
/**
|
|
1778
|
-
* Prepare a trust line transaction (e.g. XRPL trust lines)
|
|
1779
|
-
*/
|
|
1780
|
-
async prepareTrustLine(request) {
|
|
1781
|
-
return this.http.post("/trustline/prepare", request);
|
|
1782
|
-
}
|
|
1783
1980
|
};
|
|
1784
1981
|
|
|
1785
1982
|
// src/snapshots/index.ts
|
|
@@ -1832,6 +2029,167 @@ var SnapshotsClient = class {
|
|
|
1832
2029
|
}
|
|
1833
2030
|
};
|
|
1834
2031
|
|
|
2032
|
+
// src/historical-balance/historical-balance-client.ts
|
|
2033
|
+
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
2034
|
+
var ENDPOINT_PATH = "/api/v3/historical-balance/query";
|
|
2035
|
+
var HistoricalBalanceClientError = class extends Error {
|
|
2036
|
+
constructor(message, statusCode, details) {
|
|
2037
|
+
super(message);
|
|
2038
|
+
this.statusCode = statusCode;
|
|
2039
|
+
this.details = details;
|
|
2040
|
+
this.name = "HistoricalBalanceClientError";
|
|
2041
|
+
}
|
|
2042
|
+
statusCode;
|
|
2043
|
+
details;
|
|
2044
|
+
};
|
|
2045
|
+
var HistoricalBalanceClient = class _HistoricalBalanceClient {
|
|
2046
|
+
// Standalone-mode fields (set when the client builds its own fetch).
|
|
2047
|
+
baseUrl;
|
|
2048
|
+
authToken;
|
|
2049
|
+
apiKey;
|
|
2050
|
+
timeoutMs;
|
|
2051
|
+
fetchImpl;
|
|
2052
|
+
// Sub-client-mode field (set when wired via SmartEngineClient).
|
|
2053
|
+
http;
|
|
2054
|
+
constructor(config) {
|
|
2055
|
+
if ("http" in config) {
|
|
2056
|
+
this.http = config.http;
|
|
2057
|
+
this.timeoutMs = DEFAULT_TIMEOUT_MS;
|
|
2058
|
+
return;
|
|
2059
|
+
}
|
|
2060
|
+
if (!config.baseUrl) {
|
|
2061
|
+
throw new HistoricalBalanceClientError(
|
|
2062
|
+
"HistoricalBalanceClient: baseUrl is required for standalone construction",
|
|
2063
|
+
400
|
|
2064
|
+
);
|
|
2065
|
+
}
|
|
2066
|
+
this.baseUrl = config.baseUrl.replace(/\/+$/, "");
|
|
2067
|
+
this.authToken = config.authToken;
|
|
2068
|
+
this.apiKey = config.apiKey;
|
|
2069
|
+
this.timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
2070
|
+
this.fetchImpl = config.fetchImpl;
|
|
2071
|
+
}
|
|
2072
|
+
/**
|
|
2073
|
+
* Factory used by `SmartEngineClient` to wire this sub-client onto the
|
|
2074
|
+
* parent's shared `HttpClient` (which already carries auth + baseUrl + a
|
|
2075
|
+
* `/api/v3` prefix). Routes through `/historical-balance/query` since the
|
|
2076
|
+
* parent's HttpClient is rooted at `/api/v3`.
|
|
2077
|
+
*/
|
|
2078
|
+
static fromHttp(http) {
|
|
2079
|
+
return new _HistoricalBalanceClient({ http });
|
|
2080
|
+
}
|
|
2081
|
+
/**
|
|
2082
|
+
* Query a point-in-time balance.
|
|
2083
|
+
*
|
|
2084
|
+
* The validator returns the on-chain bigint as a base-10 decimal string.
|
|
2085
|
+
* Callers needing arithmetic precision should wrap the result:
|
|
2086
|
+
*
|
|
2087
|
+
* ```ts
|
|
2088
|
+
* const { balance } = await client.historicalBalance.getBalance({ ... });
|
|
2089
|
+
* const value = BigInt(balance);
|
|
2090
|
+
* ```
|
|
2091
|
+
*/
|
|
2092
|
+
async getBalance(params) {
|
|
2093
|
+
this.validateParams(params);
|
|
2094
|
+
if (this.http) {
|
|
2095
|
+
return this.http.post("/historical-balance/query", params);
|
|
2096
|
+
}
|
|
2097
|
+
return this.standaloneFetch(params);
|
|
2098
|
+
}
|
|
2099
|
+
validateParams(params) {
|
|
2100
|
+
if (!params || typeof params !== "object") {
|
|
2101
|
+
throw new HistoricalBalanceClientError(
|
|
2102
|
+
"HistoricalBalanceClient.getBalance: params object is required",
|
|
2103
|
+
400
|
|
2104
|
+
);
|
|
2105
|
+
}
|
|
2106
|
+
const { chain, entityId, account, atTimestamp } = params;
|
|
2107
|
+
if (chain !== "hedera" && chain !== "xrpl" && chain !== "polkadot") {
|
|
2108
|
+
throw new HistoricalBalanceClientError(
|
|
2109
|
+
`HistoricalBalanceClient.getBalance: unsupported chain "${String(chain)}" (expected hedera | xrpl | polkadot)`,
|
|
2110
|
+
400
|
|
2111
|
+
);
|
|
2112
|
+
}
|
|
2113
|
+
if (typeof entityId !== "string" || entityId.length === 0) {
|
|
2114
|
+
throw new HistoricalBalanceClientError(
|
|
2115
|
+
"HistoricalBalanceClient.getBalance: entityId must be a non-empty string",
|
|
2116
|
+
400
|
|
2117
|
+
);
|
|
2118
|
+
}
|
|
2119
|
+
if (typeof account !== "string" || account.length === 0) {
|
|
2120
|
+
throw new HistoricalBalanceClientError(
|
|
2121
|
+
"HistoricalBalanceClient.getBalance: account must be a non-empty string",
|
|
2122
|
+
400
|
|
2123
|
+
);
|
|
2124
|
+
}
|
|
2125
|
+
if (typeof atTimestamp !== "number" || !Number.isInteger(atTimestamp) || atTimestamp <= 0) {
|
|
2126
|
+
throw new HistoricalBalanceClientError(
|
|
2127
|
+
"HistoricalBalanceClient.getBalance: atTimestamp must be a positive integer (unix seconds)",
|
|
2128
|
+
400
|
|
2129
|
+
);
|
|
2130
|
+
}
|
|
2131
|
+
}
|
|
2132
|
+
async standaloneFetch(params) {
|
|
2133
|
+
const url = `${this.baseUrl}${ENDPOINT_PATH}`;
|
|
2134
|
+
const headers = {
|
|
2135
|
+
"Content-Type": "application/json",
|
|
2136
|
+
Accept: "application/json"
|
|
2137
|
+
};
|
|
2138
|
+
if (this.authToken) headers.Authorization = `Bearer ${this.authToken}`;
|
|
2139
|
+
if (this.apiKey) headers["X-API-Key"] = this.apiKey;
|
|
2140
|
+
const fetchFn = this.fetchImpl ?? (typeof fetch !== "undefined" ? fetch : void 0);
|
|
2141
|
+
if (!fetchFn) {
|
|
2142
|
+
throw new HistoricalBalanceClientError(
|
|
2143
|
+
"HistoricalBalanceClient: no fetch implementation available (provide fetchImpl)",
|
|
2144
|
+
500
|
|
2145
|
+
);
|
|
2146
|
+
}
|
|
2147
|
+
const controller = new AbortController();
|
|
2148
|
+
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
2149
|
+
let response;
|
|
2150
|
+
try {
|
|
2151
|
+
response = await fetchFn(url, {
|
|
2152
|
+
method: "POST",
|
|
2153
|
+
headers,
|
|
2154
|
+
body: JSON.stringify(params),
|
|
2155
|
+
signal: controller.signal
|
|
2156
|
+
});
|
|
2157
|
+
} catch (err) {
|
|
2158
|
+
clearTimeout(timer);
|
|
2159
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2160
|
+
throw new HistoricalBalanceClientError(
|
|
2161
|
+
`HistoricalBalanceClient: transport error: ${message}`,
|
|
2162
|
+
0,
|
|
2163
|
+
err
|
|
2164
|
+
);
|
|
2165
|
+
}
|
|
2166
|
+
clearTimeout(timer);
|
|
2167
|
+
let body;
|
|
2168
|
+
const text = await response.text();
|
|
2169
|
+
if (text.length > 0) {
|
|
2170
|
+
try {
|
|
2171
|
+
body = JSON.parse(text);
|
|
2172
|
+
} catch (err) {
|
|
2173
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2174
|
+
throw new HistoricalBalanceClientError(
|
|
2175
|
+
`HistoricalBalanceClient: non-JSON response (status=${response.status}): ${message}`,
|
|
2176
|
+
response.status,
|
|
2177
|
+
{ raw: text }
|
|
2178
|
+
);
|
|
2179
|
+
}
|
|
2180
|
+
}
|
|
2181
|
+
if (!response.ok) {
|
|
2182
|
+
const detail = body && typeof body === "object" && "message" in body ? String(body.message) : `HTTP ${response.status}`;
|
|
2183
|
+
throw new HistoricalBalanceClientError(
|
|
2184
|
+
`HistoricalBalanceClient: ${detail}`,
|
|
2185
|
+
response.status,
|
|
2186
|
+
body
|
|
2187
|
+
);
|
|
2188
|
+
}
|
|
2189
|
+
return body;
|
|
2190
|
+
}
|
|
2191
|
+
};
|
|
2192
|
+
|
|
1835
2193
|
// src/settlement/index.ts
|
|
1836
2194
|
var SettlementClient = class {
|
|
1837
2195
|
constructor(http) {
|
|
@@ -1866,6 +2224,53 @@ var SettlementClient = class {
|
|
|
1866
2224
|
}
|
|
1867
2225
|
};
|
|
1868
2226
|
|
|
2227
|
+
// src/governance/governance-client.ts
|
|
2228
|
+
var GovernanceClient = class {
|
|
2229
|
+
constructor(http) {
|
|
2230
|
+
this.http = http;
|
|
2231
|
+
}
|
|
2232
|
+
http;
|
|
2233
|
+
/**
|
|
2234
|
+
* Dry-run a governance proposal/vote/etc. against `GovernanceMolecule.validate(...)`.
|
|
2235
|
+
*
|
|
2236
|
+
* Returns whatever `ValidationResult` the molecule produced. A `false`
|
|
2237
|
+
* `isValid` is **not** an error — it is a successful "this proposal is
|
|
2238
|
+
* invalid" outcome and is surfaced via the normal return value.
|
|
2239
|
+
*
|
|
2240
|
+
* HTTP errors (400 malformed body, 500 unexpected) bubble up as
|
|
2241
|
+
* `SdkHttpError` via the shared `HttpClient` layer.
|
|
2242
|
+
*/
|
|
2243
|
+
async simulate(params) {
|
|
2244
|
+
return this.http.post("/governance/simulate", params);
|
|
2245
|
+
}
|
|
2246
|
+
};
|
|
2247
|
+
|
|
2248
|
+
// src/personhood/personhood-client.ts
|
|
2249
|
+
var PersonhoodClient = class {
|
|
2250
|
+
constructor(http) {
|
|
2251
|
+
this.http = http;
|
|
2252
|
+
}
|
|
2253
|
+
http;
|
|
2254
|
+
/**
|
|
2255
|
+
* Verify a personhood proof for `candidate`.
|
|
2256
|
+
*
|
|
2257
|
+
* Returns the issued cert on accept, `null` on clean rejection. All
|
|
2258
|
+
* other failure modes (validation, transport, 5xx) propagate as
|
|
2259
|
+
* `SdkHttpError`.
|
|
2260
|
+
*/
|
|
2261
|
+
async verify(params) {
|
|
2262
|
+
const result = await this.http.post(
|
|
2263
|
+
"/personhood/verify",
|
|
2264
|
+
{
|
|
2265
|
+
candidate: params.candidate,
|
|
2266
|
+
proof: params.proof
|
|
2267
|
+
}
|
|
2268
|
+
);
|
|
2269
|
+
if (result === void 0) return null;
|
|
2270
|
+
return result;
|
|
2271
|
+
}
|
|
2272
|
+
};
|
|
2273
|
+
|
|
1869
2274
|
// src/client.ts
|
|
1870
2275
|
var SmartEngineClient = class _SmartEngineClient {
|
|
1871
2276
|
baseUrl;
|
|
@@ -1882,12 +2287,26 @@ var SmartEngineClient = class _SmartEngineClient {
|
|
|
1882
2287
|
tss;
|
|
1883
2288
|
/** IPFS decentralized file storage */
|
|
1884
2289
|
ipfs;
|
|
1885
|
-
/** Transaction preparation for local signing (sovereignty model) */
|
|
2290
|
+
/** Transaction preparation for local signing (sovereignty model) — chain-agnostic */
|
|
1886
2291
|
transactions;
|
|
2292
|
+
/** Hedera-specific transaction preparation (HCS topics, KYC, wipe) */
|
|
2293
|
+
hedera;
|
|
2294
|
+
/** XRPL-specific transaction preparation (trust lines) */
|
|
2295
|
+
xrpl;
|
|
2296
|
+
/** Solana-specific transaction preparation (close-account) */
|
|
2297
|
+
solana;
|
|
2298
|
+
/** Polkadot-specific transaction preparation (asset setTeam/setMetadata) */
|
|
2299
|
+
polkadot;
|
|
1887
2300
|
/** Token holder snapshot generation and retrieval */
|
|
1888
2301
|
snapshots;
|
|
2302
|
+
/** Historical balance archive reads (chain-native bigint, returned as decimal string) */
|
|
2303
|
+
historicalBalance;
|
|
1889
2304
|
/** Cross-chain settlement operations */
|
|
1890
2305
|
settlement;
|
|
2306
|
+
/** Governance proposal dry-run (simulate-only) */
|
|
2307
|
+
governance;
|
|
2308
|
+
/** Personhood verification (HPP one-human-one-member) */
|
|
2309
|
+
personhood;
|
|
1891
2310
|
constructor(config) {
|
|
1892
2311
|
this.allowInsecure = config.allowInsecure ?? false;
|
|
1893
2312
|
this.baseUrl = validateClientUrl(config.baseUrl, this.allowInsecure);
|
|
@@ -1907,8 +2326,15 @@ var SmartEngineClient = class _SmartEngineClient {
|
|
|
1907
2326
|
this.tss = new TSSClient(this.http);
|
|
1908
2327
|
this.ipfs = new IPFSClient(this.http);
|
|
1909
2328
|
this.transactions = new TransactionsClient(this.txHttp);
|
|
2329
|
+
this.hedera = new HederaTransactionsClient(this.txHttp);
|
|
2330
|
+
this.xrpl = new XrplTransactionsClient(this.txHttp);
|
|
2331
|
+
this.solana = new SolanaTransactionsClient(this.txHttp);
|
|
2332
|
+
this.polkadot = new PolkadotTransactionsClient(this.txHttp);
|
|
1910
2333
|
this.snapshots = new SnapshotsClient(this.http);
|
|
2334
|
+
this.historicalBalance = HistoricalBalanceClient.fromHttp(this.http);
|
|
1911
2335
|
this.settlement = new SettlementClient(this.http);
|
|
2336
|
+
this.governance = new GovernanceClient(this.http);
|
|
2337
|
+
this.personhood = new PersonhoodClient(this.http);
|
|
1912
2338
|
}
|
|
1913
2339
|
/**
|
|
1914
2340
|
* Connect to the smart-engines network with auto-discovery and authentication
|
|
@@ -1953,6 +2379,67 @@ var SmartEngineClient = class _SmartEngineClient {
|
|
|
1953
2379
|
});
|
|
1954
2380
|
return { client, validator, session };
|
|
1955
2381
|
}
|
|
2382
|
+
/**
|
|
2383
|
+
* Connect to the smart-engines network via the **service-registry**
|
|
2384
|
+
* (PR-1 of the cluster-discovery arc). Preferred over
|
|
2385
|
+
* {@link connectToNetwork} once the validator pods in the target network
|
|
2386
|
+
* have published their cluster endpoints — the SDK auto-balances across
|
|
2387
|
+
* the active cluster set and rides permissionless cluster join/leave
|
|
2388
|
+
* without code edits.
|
|
2389
|
+
*
|
|
2390
|
+
* Fallback ladder (per `docs/ops/HANDOFF-service-registry-distribution-layer.md` §6):
|
|
2391
|
+
* 1. HTTP fetch `/api/v3/discovery/clusters` from each bootstrap seed.
|
|
2392
|
+
* 2. (Optional) HCS trust-anchor membership cross-check.
|
|
2393
|
+
* 3. Random-pick over the verified set.
|
|
2394
|
+
*
|
|
2395
|
+
* @example
|
|
2396
|
+
* ```ts
|
|
2397
|
+
* const { client, cluster, session } = await SmartEngineClient.connectToCluster({
|
|
2398
|
+
* bootstrap: ['https://sn1.testnet.hsuite.network', 'https://sn2.testnet.hsuite.network'],
|
|
2399
|
+
* chain: 'xrpl',
|
|
2400
|
+
* address: '...',
|
|
2401
|
+
* publicKey: '...',
|
|
2402
|
+
* signFn: async (challenge) => sign(challenge),
|
|
2403
|
+
* });
|
|
2404
|
+
* ```
|
|
2405
|
+
*/
|
|
2406
|
+
static async connectToCluster(config) {
|
|
2407
|
+
const allowInsecure = config.allowInsecure ?? false;
|
|
2408
|
+
const discovery = new ClusterDiscoveryClient({
|
|
2409
|
+
bootstrap: config.bootstrap,
|
|
2410
|
+
allowInsecure,
|
|
2411
|
+
trustAnchor: config.trustAnchor ? {
|
|
2412
|
+
network: config.trustAnchor.network,
|
|
2413
|
+
registryTopicId: config.trustAnchor.registryTopicId,
|
|
2414
|
+
mirrorNodeUrl: config.trustAnchor.mirrorNodeUrl,
|
|
2415
|
+
allowInsecure
|
|
2416
|
+
} : void 0
|
|
2417
|
+
});
|
|
2418
|
+
const cluster = await discovery.getRandomCluster();
|
|
2419
|
+
if (!cluster) {
|
|
2420
|
+
throw new SmartEngineError2(
|
|
2421
|
+
"No active clusters available via bootstrap seeds. Check bootstrap URLs and network reachability.",
|
|
2422
|
+
503
|
|
2423
|
+
);
|
|
2424
|
+
}
|
|
2425
|
+
const gatewayUrl = cluster.endpoints.gatewayUrl;
|
|
2426
|
+
validateClientUrl(gatewayUrl, allowInsecure);
|
|
2427
|
+
const auth = new ValidatorAuthClient({ security: { allowInsecure } });
|
|
2428
|
+
const session = await auth.authenticateWithSigner(
|
|
2429
|
+
gatewayUrl,
|
|
2430
|
+
config.chain,
|
|
2431
|
+
config.address,
|
|
2432
|
+
config.publicKey,
|
|
2433
|
+
config.signFn,
|
|
2434
|
+
config.metadata
|
|
2435
|
+
);
|
|
2436
|
+
const client = new _SmartEngineClient({
|
|
2437
|
+
baseUrl: gatewayUrl,
|
|
2438
|
+
authToken: session.token,
|
|
2439
|
+
allowInsecure
|
|
2440
|
+
});
|
|
2441
|
+
return { client, cluster, session };
|
|
2442
|
+
}
|
|
1956
2443
|
/** Get the current validator URL */
|
|
1957
2444
|
getBaseUrl() {
|
|
1958
2445
|
return this.baseUrl;
|