@hsuite/smart-engines-sdk 3.0.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/CHANGELOG.md +20 -0
- package/README.md +373 -0
- package/dist/index.d.ts +2881 -0
- package/dist/index.js +4350 -0
- package/dist/index.js.map +1 -0
- package/dist/nestjs/index.d.ts +2341 -0
- package/dist/nestjs/index.js +3338 -0
- package/dist/nestjs/index.js.map +1 -0
- package/package.json +63 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,4350 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var zod = require('zod');
|
|
4
|
+
var common = require('@nestjs/common');
|
|
5
|
+
|
|
6
|
+
var __defProp = Object.defineProperty;
|
|
7
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
13
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
14
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
15
|
+
if (decorator = decorators[i])
|
|
16
|
+
result = (decorator(result)) || result;
|
|
17
|
+
return result;
|
|
18
|
+
};
|
|
19
|
+
var __decorateParam = (index, decorator) => (target, key) => decorator(target, key, index);
|
|
20
|
+
|
|
21
|
+
// src/_vendor/circuit-breaker.ts
|
|
22
|
+
var DEFAULT_CIRCUIT_BREAKER_CONFIG = {
|
|
23
|
+
failureThreshold: 5,
|
|
24
|
+
rollingWindowMs: 6e4,
|
|
25
|
+
cooldownMs: 3e4,
|
|
26
|
+
name: "circuit"
|
|
27
|
+
};
|
|
28
|
+
var CircuitBreakerOpenError = class extends Error {
|
|
29
|
+
constructor(name, nextProbeAt) {
|
|
30
|
+
super(`Circuit breaker '${name}' is open until ${new Date(nextProbeAt).toISOString()}`);
|
|
31
|
+
this.nextProbeAt = nextProbeAt;
|
|
32
|
+
this.name = "CircuitBreakerOpenError";
|
|
33
|
+
}
|
|
34
|
+
nextProbeAt;
|
|
35
|
+
code = "CIRCUIT_BREAKER_OPEN";
|
|
36
|
+
};
|
|
37
|
+
var CircuitBreaker = class {
|
|
38
|
+
state = "closed";
|
|
39
|
+
failureTimestamps = [];
|
|
40
|
+
successCount = 0;
|
|
41
|
+
openedAt;
|
|
42
|
+
config;
|
|
43
|
+
constructor(config = {}) {
|
|
44
|
+
const merged = { ...DEFAULT_CIRCUIT_BREAKER_CONFIG, ...config };
|
|
45
|
+
if (merged.failureThreshold < 1) {
|
|
46
|
+
throw new Error("failureThreshold must be >= 1");
|
|
47
|
+
}
|
|
48
|
+
if (merged.rollingWindowMs <= 0) {
|
|
49
|
+
throw new Error("rollingWindowMs must be > 0");
|
|
50
|
+
}
|
|
51
|
+
if (merged.cooldownMs <= 0) {
|
|
52
|
+
throw new Error("cooldownMs must be > 0");
|
|
53
|
+
}
|
|
54
|
+
this.config = merged;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Execute an operation through the breaker.
|
|
58
|
+
* @throws CircuitBreakerOpenError if the breaker is open
|
|
59
|
+
*/
|
|
60
|
+
async execute(operation) {
|
|
61
|
+
this.evaluateState();
|
|
62
|
+
if (this.state === "open") {
|
|
63
|
+
throw new CircuitBreakerOpenError(this.config.name ?? "circuit", this.nextProbeTime());
|
|
64
|
+
}
|
|
65
|
+
try {
|
|
66
|
+
const result = await operation();
|
|
67
|
+
this.recordSuccess();
|
|
68
|
+
return result;
|
|
69
|
+
} catch (err) {
|
|
70
|
+
this.recordFailure(err);
|
|
71
|
+
throw err;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/** Allow probing without executing (e.g. for health endpoints) */
|
|
75
|
+
canExecute() {
|
|
76
|
+
this.evaluateState();
|
|
77
|
+
return this.state !== "open";
|
|
78
|
+
}
|
|
79
|
+
getState() {
|
|
80
|
+
this.evaluateState();
|
|
81
|
+
return this.state;
|
|
82
|
+
}
|
|
83
|
+
snapshot() {
|
|
84
|
+
this.evaluateState();
|
|
85
|
+
return {
|
|
86
|
+
state: this.state,
|
|
87
|
+
failureCount: this.failureTimestamps.length,
|
|
88
|
+
successCount: this.successCount,
|
|
89
|
+
lastFailureAt: this.failureTimestamps[this.failureTimestamps.length - 1],
|
|
90
|
+
openedAt: this.openedAt,
|
|
91
|
+
nextProbeAt: this.state === "open" ? this.nextProbeTime() : void 0
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
/** Force the breaker back to CLOSED. Use sparingly (e.g. on operator override). */
|
|
95
|
+
reset() {
|
|
96
|
+
this.state = "closed";
|
|
97
|
+
this.failureTimestamps = [];
|
|
98
|
+
this.successCount = 0;
|
|
99
|
+
this.openedAt = void 0;
|
|
100
|
+
}
|
|
101
|
+
evaluateState() {
|
|
102
|
+
if (this.state === "open" && this.openedAt !== void 0) {
|
|
103
|
+
if (Date.now() - this.openedAt >= this.config.cooldownMs) {
|
|
104
|
+
this.state = "half_open";
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
this.pruneOldFailures();
|
|
108
|
+
}
|
|
109
|
+
nextProbeTime() {
|
|
110
|
+
return (this.openedAt ?? Date.now()) + this.config.cooldownMs;
|
|
111
|
+
}
|
|
112
|
+
pruneOldFailures() {
|
|
113
|
+
const cutoff = Date.now() - this.config.rollingWindowMs;
|
|
114
|
+
if (this.failureTimestamps.length === 0 || this.failureTimestamps[0] >= cutoff) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
this.failureTimestamps = this.failureTimestamps.filter((ts) => ts >= cutoff);
|
|
118
|
+
}
|
|
119
|
+
recordSuccess() {
|
|
120
|
+
this.successCount += 1;
|
|
121
|
+
if (this.state === "half_open") {
|
|
122
|
+
this.state = "closed";
|
|
123
|
+
this.failureTimestamps = [];
|
|
124
|
+
this.openedAt = void 0;
|
|
125
|
+
} else if (this.state === "closed") {
|
|
126
|
+
this.failureTimestamps.shift();
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
recordFailure(err) {
|
|
130
|
+
if (!this.shouldCountFailure(err)) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
const now = Date.now();
|
|
134
|
+
this.failureTimestamps.push(now);
|
|
135
|
+
this.pruneOldFailures();
|
|
136
|
+
if (this.state === "half_open") {
|
|
137
|
+
this.openedAt = now;
|
|
138
|
+
this.state = "open";
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
if (this.state === "closed" && this.failureTimestamps.length >= this.config.failureThreshold) {
|
|
142
|
+
this.openedAt = now;
|
|
143
|
+
this.state = "open";
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
shouldCountFailure(err) {
|
|
147
|
+
const filter = this.config.failureFilter;
|
|
148
|
+
if (!filter || filter.length === 0) return true;
|
|
149
|
+
const message = (err.message || "").toLowerCase();
|
|
150
|
+
const name = (err.name || "").toLowerCase();
|
|
151
|
+
return filter.some((pattern) => {
|
|
152
|
+
const p = pattern.toLowerCase();
|
|
153
|
+
return message.includes(p) || name.includes(p);
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
// src/_vendor/rate-limiter.ts
|
|
159
|
+
var DEFAULT_CONFIG = {
|
|
160
|
+
maxRequests: 60,
|
|
161
|
+
windowMs: 6e4
|
|
162
|
+
// 1 minute
|
|
163
|
+
};
|
|
164
|
+
var RateLimiter = class {
|
|
165
|
+
// Lightweight logger stub (no @nestjs/common dependency). Replace via constructor injection if needed.
|
|
166
|
+
logger = { debug: (..._args) => {
|
|
167
|
+
}, warn: (..._args) => {
|
|
168
|
+
} };
|
|
169
|
+
limits = /* @__PURE__ */ new Map();
|
|
170
|
+
config;
|
|
171
|
+
constructor(config) {
|
|
172
|
+
this.config = {
|
|
173
|
+
...DEFAULT_CONFIG,
|
|
174
|
+
...config
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Check if request is allowed under rate limit
|
|
179
|
+
*
|
|
180
|
+
* @param key - Unique identifier for rate limit bucket
|
|
181
|
+
* @returns True if request is allowed
|
|
182
|
+
*/
|
|
183
|
+
isAllowed(key) {
|
|
184
|
+
const now = Date.now();
|
|
185
|
+
const entry = this.limits.get(key);
|
|
186
|
+
if (!entry || now - entry.windowStart >= this.config.windowMs) {
|
|
187
|
+
this.limits.set(key, { count: 1, windowStart: now });
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
if (entry.count >= this.config.maxRequests) {
|
|
191
|
+
this.logger.debug(`Rate limit exceeded for key: ${key.substring(0, 20)}...`);
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
entry.count++;
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Reset rate limit for a specific key
|
|
199
|
+
*
|
|
200
|
+
* @param key - Key to reset
|
|
201
|
+
*/
|
|
202
|
+
reset(key) {
|
|
203
|
+
this.limits.delete(key);
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Clear all rate limits
|
|
207
|
+
*/
|
|
208
|
+
clear() {
|
|
209
|
+
this.limits.clear();
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Clean up expired entries
|
|
213
|
+
*/
|
|
214
|
+
cleanup() {
|
|
215
|
+
const now = Date.now();
|
|
216
|
+
const expiredThreshold = this.config.windowMs * 2;
|
|
217
|
+
for (const [key, entry] of this.limits.entries()) {
|
|
218
|
+
if (now - entry.windowStart > expiredThreshold) {
|
|
219
|
+
this.limits.delete(key);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Get current count for a key
|
|
225
|
+
*
|
|
226
|
+
* @param key - Key to check
|
|
227
|
+
* @returns Current request count or 0
|
|
228
|
+
*/
|
|
229
|
+
getCount(key) {
|
|
230
|
+
const entry = this.limits.get(key);
|
|
231
|
+
if (!entry) return 0;
|
|
232
|
+
if (Date.now() - entry.windowStart >= this.config.windowMs) {
|
|
233
|
+
return 0;
|
|
234
|
+
}
|
|
235
|
+
return entry.count;
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Get remaining requests for a key
|
|
239
|
+
*
|
|
240
|
+
* @param key - Key to check
|
|
241
|
+
* @returns Remaining requests in current window
|
|
242
|
+
*/
|
|
243
|
+
getRemaining(key) {
|
|
244
|
+
return Math.max(0, this.config.maxRequests - this.getCount(key));
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Get configuration
|
|
248
|
+
*/
|
|
249
|
+
getConfig() {
|
|
250
|
+
return Object.freeze({ ...this.config });
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
// src/_vendor/smart-engine.error.ts
|
|
255
|
+
var ErrorCode = {
|
|
256
|
+
// General errors
|
|
257
|
+
UNKNOWN_ERROR: "UNKNOWN_ERROR",
|
|
258
|
+
VALIDATION_ERROR: "VALIDATION_ERROR",
|
|
259
|
+
NOT_FOUND: "NOT_FOUND",
|
|
260
|
+
UNAUTHORIZED: "UNAUTHORIZED",
|
|
261
|
+
FORBIDDEN: "FORBIDDEN",
|
|
262
|
+
RATE_LIMIT_EXCEEDED: "RATE_LIMIT_EXCEEDED",
|
|
263
|
+
PAYLOAD_TOO_LARGE: "PAYLOAD_TOO_LARGE",
|
|
264
|
+
// Chain-specific errors
|
|
265
|
+
CHAIN_NOT_SUPPORTED: "CHAIN_NOT_SUPPORTED",
|
|
266
|
+
CHAIN_CONNECTION_ERROR: "CHAIN_CONNECTION_ERROR",
|
|
267
|
+
CHAIN_TIMEOUT: "CHAIN_TIMEOUT",
|
|
268
|
+
// Transaction errors
|
|
269
|
+
TRANSACTION_FAILED: "TRANSACTION_FAILED",
|
|
270
|
+
TRANSACTION_TIMEOUT: "TRANSACTION_TIMEOUT",
|
|
271
|
+
INSUFFICIENT_BALANCE: "INSUFFICIENT_BALANCE",
|
|
272
|
+
INVALID_TRANSACTION: "INVALID_TRANSACTION",
|
|
273
|
+
// Account errors
|
|
274
|
+
ACCOUNT_NOT_FOUND: "ACCOUNT_NOT_FOUND",
|
|
275
|
+
ACCOUNT_CREATION_FAILED: "ACCOUNT_CREATION_FAILED",
|
|
276
|
+
INVALID_ACCOUNT_ID: "INVALID_ACCOUNT_ID",
|
|
277
|
+
// Wallet errors
|
|
278
|
+
WALLET_NOT_FOUND: "WALLET_NOT_FOUND",
|
|
279
|
+
WALLET_ENCRYPTION_ERROR: "WALLET_ENCRYPTION_ERROR",
|
|
280
|
+
WALLET_DECRYPTION_ERROR: "WALLET_DECRYPTION_ERROR",
|
|
281
|
+
INVALID_PRIVATE_KEY: "INVALID_PRIVATE_KEY",
|
|
282
|
+
// Token errors
|
|
283
|
+
TOKEN_NOT_FOUND: "TOKEN_NOT_FOUND",
|
|
284
|
+
TOKEN_CREATION_FAILED: "TOKEN_CREATION_FAILED",
|
|
285
|
+
INSUFFICIENT_TOKEN_BALANCE: "INSUFFICIENT_TOKEN_BALANCE",
|
|
286
|
+
TRUST_LINE_REQUIRED: "TRUST_LINE_REQUIRED",
|
|
287
|
+
TOKEN_NOT_ASSOCIATED: "TOKEN_NOT_ASSOCIATED",
|
|
288
|
+
TOKEN_PAUSED: "TOKEN_PAUSED",
|
|
289
|
+
ACCOUNT_FROZEN: "ACCOUNT_FROZEN",
|
|
290
|
+
KYC_NOT_GRANTED: "KYC_NOT_GRANTED",
|
|
291
|
+
// Signature/Auth errors
|
|
292
|
+
INVALID_SIGNATURE: "INVALID_SIGNATURE",
|
|
293
|
+
NONCE_MISMATCH: "NONCE_MISMATCH",
|
|
294
|
+
// Contract errors
|
|
295
|
+
CONTRACT_REVERT: "CONTRACT_REVERT",
|
|
296
|
+
CONTRACT_NOT_FOUND: "CONTRACT_NOT_FOUND",
|
|
297
|
+
GAS_ERROR: "GAS_ERROR",
|
|
298
|
+
// Infrastructure errors
|
|
299
|
+
DATABASE_ERROR: "DATABASE_ERROR",
|
|
300
|
+
CACHE_ERROR: "CACHE_ERROR",
|
|
301
|
+
EVENT_BUS_ERROR: "EVENT_BUS_ERROR",
|
|
302
|
+
EXTERNAL_SERVICE_ERROR: "EXTERNAL_SERVICE_ERROR"
|
|
303
|
+
};
|
|
304
|
+
var SmartEngineError = class extends Error {
|
|
305
|
+
constructor(message, code, statusCode = 500, context, isRetryable = false) {
|
|
306
|
+
super(message);
|
|
307
|
+
this.code = code;
|
|
308
|
+
this.statusCode = statusCode;
|
|
309
|
+
this.context = context;
|
|
310
|
+
this.isRetryable = isRetryable;
|
|
311
|
+
this.name = "SmartEngineError";
|
|
312
|
+
Error.captureStackTrace(this, this.constructor);
|
|
313
|
+
}
|
|
314
|
+
code;
|
|
315
|
+
statusCode;
|
|
316
|
+
context;
|
|
317
|
+
isRetryable;
|
|
318
|
+
toJSON() {
|
|
319
|
+
return {
|
|
320
|
+
error: {
|
|
321
|
+
name: this.name,
|
|
322
|
+
message: this.message,
|
|
323
|
+
code: this.code,
|
|
324
|
+
statusCode: this.statusCode,
|
|
325
|
+
context: this.context,
|
|
326
|
+
isRetryable: this.isRetryable
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
// src/_vendor/capability.error.ts
|
|
333
|
+
var UnsupportedCapabilityError = class extends SmartEngineError {
|
|
334
|
+
constructor(chain, capability, alternatives) {
|
|
335
|
+
const message = `Capability '${capability}' is not supported on chain '${chain}'`;
|
|
336
|
+
const context = {
|
|
337
|
+
chain,
|
|
338
|
+
capability,
|
|
339
|
+
alternatives
|
|
340
|
+
};
|
|
341
|
+
super(message, ErrorCode.VALIDATION_ERROR, 400, context, false);
|
|
342
|
+
this.chain = chain;
|
|
343
|
+
this.capability = capability;
|
|
344
|
+
this.alternatives = alternatives;
|
|
345
|
+
this.name = "UnsupportedCapabilityError";
|
|
346
|
+
}
|
|
347
|
+
chain;
|
|
348
|
+
capability;
|
|
349
|
+
alternatives;
|
|
350
|
+
};
|
|
351
|
+
var CapabilityNotEnabledError = class extends SmartEngineError {
|
|
352
|
+
constructor(tokenId, capability) {
|
|
353
|
+
const message = `Capability '${capability}' was not enabled for token '${tokenId}'`;
|
|
354
|
+
const context = {
|
|
355
|
+
tokenId,
|
|
356
|
+
capability
|
|
357
|
+
};
|
|
358
|
+
super(message, ErrorCode.VALIDATION_ERROR, 400, context, false);
|
|
359
|
+
this.tokenId = tokenId;
|
|
360
|
+
this.capability = capability;
|
|
361
|
+
this.name = "CapabilityNotEnabledError";
|
|
362
|
+
}
|
|
363
|
+
tokenId;
|
|
364
|
+
capability;
|
|
365
|
+
};
|
|
366
|
+
var CapabilityValidationError = class extends SmartEngineError {
|
|
367
|
+
constructor(message, capabilities, chain) {
|
|
368
|
+
const context = {
|
|
369
|
+
capabilities,
|
|
370
|
+
chain
|
|
371
|
+
};
|
|
372
|
+
super(message, ErrorCode.VALIDATION_ERROR, 400, context, false);
|
|
373
|
+
this.capabilities = capabilities;
|
|
374
|
+
this.chain = chain;
|
|
375
|
+
this.name = "CapabilityValidationError";
|
|
376
|
+
}
|
|
377
|
+
capabilities;
|
|
378
|
+
chain;
|
|
379
|
+
};
|
|
380
|
+
var ChainTypeSchema = zod.z.enum([
|
|
381
|
+
"hedera",
|
|
382
|
+
"xrpl",
|
|
383
|
+
"polkadot",
|
|
384
|
+
"solana",
|
|
385
|
+
"stellar",
|
|
386
|
+
"ethereum",
|
|
387
|
+
"polygon",
|
|
388
|
+
"bitcoin",
|
|
389
|
+
"cardano"
|
|
390
|
+
]);
|
|
391
|
+
var NetworkTypeSchema = zod.z.enum(["mainnet", "testnet", "devnet", "local"]);
|
|
392
|
+
zod.z.object({
|
|
393
|
+
chain: ChainTypeSchema,
|
|
394
|
+
network: NetworkTypeSchema,
|
|
395
|
+
nativeCurrency: zod.z.object({
|
|
396
|
+
name: zod.z.string(),
|
|
397
|
+
symbol: zod.z.string(),
|
|
398
|
+
decimals: zod.z.number().int().min(0)
|
|
399
|
+
}),
|
|
400
|
+
blockTime: zod.z.number().optional(),
|
|
401
|
+
rpcEndpoint: zod.z.string().url().optional()
|
|
402
|
+
});
|
|
403
|
+
var NetworkMembershipTypeSchema = zod.z.enum(["validator", "host", "gateway"]);
|
|
404
|
+
var MembershipStatusSchema = zod.z.enum(["pending", "active", "exiting", "exited", "banned"]);
|
|
405
|
+
zod.z.object({
|
|
406
|
+
nodeId: zod.z.string().min(1),
|
|
407
|
+
networkType: NetworkMembershipTypeSchema,
|
|
408
|
+
chain: zod.z.enum(["hedera", "xrpl", "polkadot", "solana"]),
|
|
409
|
+
endpoint: zod.z.string().url(),
|
|
410
|
+
publicKey: zod.z.string().min(1),
|
|
411
|
+
joinedAt: zod.z.string().datetime(),
|
|
412
|
+
depositTxId: zod.z.string().min(1),
|
|
413
|
+
status: MembershipStatusSchema,
|
|
414
|
+
networkConfig: zod.z.record(zod.z.unknown()).optional()
|
|
415
|
+
});
|
|
416
|
+
var NetworkDepositRequirementsSchema = zod.z.object({
|
|
417
|
+
depositAmount: zod.z.string().min(1),
|
|
418
|
+
lockDurationDays: zod.z.number().int().positive(),
|
|
419
|
+
renewalWindowDays: zod.z.number().int().positive().optional()
|
|
420
|
+
});
|
|
421
|
+
zod.z.object({
|
|
422
|
+
validator: NetworkDepositRequirementsSchema,
|
|
423
|
+
host: NetworkDepositRequirementsSchema,
|
|
424
|
+
gateway: NetworkDepositRequirementsSchema
|
|
425
|
+
});
|
|
426
|
+
var TokenCapabilitiesSchema = zod.z.object({
|
|
427
|
+
/**
|
|
428
|
+
* Pause all token operations globally
|
|
429
|
+
* - Hedera: Adds pauseKey to token
|
|
430
|
+
* - XRPL: Enables GlobalFreeze flag on issuer account
|
|
431
|
+
*/
|
|
432
|
+
pausable: zod.z.boolean().default(false),
|
|
433
|
+
/**
|
|
434
|
+
* Freeze/restrict specific accounts from transacting
|
|
435
|
+
* - Hedera: Adds freezeKey to token
|
|
436
|
+
* - XRPL: Enables trust line freeze capability
|
|
437
|
+
*/
|
|
438
|
+
restrictable: zod.z.boolean().default(false),
|
|
439
|
+
/**
|
|
440
|
+
* KYC/compliance controls for accounts
|
|
441
|
+
* - Hedera: Adds kycKey to token
|
|
442
|
+
* - XRPL: Requires authorized trust lines (RequireAuth)
|
|
443
|
+
*/
|
|
444
|
+
compliant: zod.z.boolean().default(false),
|
|
445
|
+
/**
|
|
446
|
+
* Force remove tokens from accounts (compliance wipe)
|
|
447
|
+
* - Hedera: Adds wipeKey to token
|
|
448
|
+
* - XRPL: Enables clawback (lsfAllowTrustLineClawback)
|
|
449
|
+
*/
|
|
450
|
+
wipeable: zod.z.boolean().default(false),
|
|
451
|
+
/**
|
|
452
|
+
* Mint additional supply after creation
|
|
453
|
+
* - Hedera: Adds supplyKey to token
|
|
454
|
+
* - XRPL: Issuer can always issue more via Payment
|
|
455
|
+
*/
|
|
456
|
+
mintable: zod.z.boolean().default(true),
|
|
457
|
+
/**
|
|
458
|
+
* Burn tokens (reduce supply)
|
|
459
|
+
* - Hedera: Requires supplyKey
|
|
460
|
+
* - XRPL: Send back to issuer (reduces supply)
|
|
461
|
+
*/
|
|
462
|
+
burnable: zod.z.boolean().default(true),
|
|
463
|
+
/**
|
|
464
|
+
* Allow transfers between accounts
|
|
465
|
+
* - All chains: Generally always supported
|
|
466
|
+
*/
|
|
467
|
+
transferable: zod.z.boolean().default(true)
|
|
468
|
+
});
|
|
469
|
+
var AccountIdSchema = zod.z.string().min(1);
|
|
470
|
+
zod.z.object({
|
|
471
|
+
accountId: AccountIdSchema,
|
|
472
|
+
balance: zod.z.string(),
|
|
473
|
+
// String to handle large numbers and decimals
|
|
474
|
+
chain: ChainTypeSchema,
|
|
475
|
+
publicKey: zod.z.string().optional(),
|
|
476
|
+
metadata: zod.z.record(zod.z.any()).optional(),
|
|
477
|
+
createdAt: zod.z.date().optional(),
|
|
478
|
+
updatedAt: zod.z.date().optional()
|
|
479
|
+
});
|
|
480
|
+
zod.z.object({
|
|
481
|
+
accountId: AccountIdSchema,
|
|
482
|
+
chain: ChainTypeSchema,
|
|
483
|
+
nativeBalance: zod.z.string(),
|
|
484
|
+
tokens: zod.z.array(
|
|
485
|
+
zod.z.object({
|
|
486
|
+
tokenId: zod.z.string(),
|
|
487
|
+
balance: zod.z.string(),
|
|
488
|
+
decimals: zod.z.number().int().min(0),
|
|
489
|
+
symbol: zod.z.string().optional()
|
|
490
|
+
})
|
|
491
|
+
).optional(),
|
|
492
|
+
timestamp: zod.z.date()
|
|
493
|
+
});
|
|
494
|
+
var SecurityModeSchema = zod.z.enum(["none", "partial", "full"]);
|
|
495
|
+
var sovereigntyRefinePredicate = (v) => {
|
|
496
|
+
if (v.securityMode === "partial") {
|
|
497
|
+
return !!v.entityId && !!v.appOwnerPublicKey;
|
|
498
|
+
}
|
|
499
|
+
if (v.securityMode === "full") {
|
|
500
|
+
return !!v.entityId;
|
|
501
|
+
}
|
|
502
|
+
return true;
|
|
503
|
+
};
|
|
504
|
+
var SOVEREIGNTY_REFINE_MESSAGE = "securityMode='partial' requires entityId+appOwnerPublicKey; 'full' requires entityId";
|
|
505
|
+
var SovereigntyFieldsRawSchema = zod.z.object({
|
|
506
|
+
securityMode: SecurityModeSchema.default("none"),
|
|
507
|
+
entityId: zod.z.string().optional(),
|
|
508
|
+
appOwnerPublicKey: zod.z.string().optional()
|
|
509
|
+
});
|
|
510
|
+
SovereigntyFieldsRawSchema.refine(sovereigntyRefinePredicate, { message: SOVEREIGNTY_REFINE_MESSAGE });
|
|
511
|
+
var HederaKeyShapeSchema = zod.z.lazy(
|
|
512
|
+
() => zod.z.union([
|
|
513
|
+
zod.z.object({ type: zod.z.literal("ed25519"), key: zod.z.string() }),
|
|
514
|
+
zod.z.object({
|
|
515
|
+
type: zod.z.literal("keyList"),
|
|
516
|
+
threshold: zod.z.number().int().nonnegative(),
|
|
517
|
+
keys: zod.z.array(HederaKeyShapeSchema)
|
|
518
|
+
})
|
|
519
|
+
])
|
|
520
|
+
);
|
|
521
|
+
var HederaAuthorizationSetShapeSchema = zod.z.object({
|
|
522
|
+
chain: zod.z.literal("hedera"),
|
|
523
|
+
adminKey: HederaKeyShapeSchema,
|
|
524
|
+
supplyKey: HederaKeyShapeSchema.optional(),
|
|
525
|
+
freezeKey: HederaKeyShapeSchema.optional(),
|
|
526
|
+
wipeKey: HederaKeyShapeSchema.optional(),
|
|
527
|
+
pauseKey: HederaKeyShapeSchema.optional(),
|
|
528
|
+
kycKey: HederaKeyShapeSchema.optional()
|
|
529
|
+
});
|
|
530
|
+
var XrplAuthorizationSetShapeSchema = zod.z.object({
|
|
531
|
+
chain: zod.z.literal("xrpl"),
|
|
532
|
+
signerEntries: zod.z.array(
|
|
533
|
+
zod.z.object({
|
|
534
|
+
account: zod.z.string(),
|
|
535
|
+
weight: zod.z.number().int().nonnegative()
|
|
536
|
+
})
|
|
537
|
+
),
|
|
538
|
+
signerQuorum: zod.z.number().int().nonnegative(),
|
|
539
|
+
masterDisabled: zod.z.boolean()
|
|
540
|
+
});
|
|
541
|
+
var StellarAuthorizationSetShapeSchema = zod.z.object({
|
|
542
|
+
chain: zod.z.literal("stellar"),
|
|
543
|
+
signers: zod.z.array(
|
|
544
|
+
zod.z.object({
|
|
545
|
+
key: zod.z.string(),
|
|
546
|
+
weight: zod.z.number().int().nonnegative()
|
|
547
|
+
})
|
|
548
|
+
),
|
|
549
|
+
thresholds: zod.z.object({
|
|
550
|
+
low: zod.z.number().int().nonnegative(),
|
|
551
|
+
med: zod.z.number().int().nonnegative(),
|
|
552
|
+
high: zod.z.number().int().nonnegative()
|
|
553
|
+
}),
|
|
554
|
+
masterWeight: zod.z.number().int().nonnegative()
|
|
555
|
+
});
|
|
556
|
+
var PolkadotSignatoryShapeSchema = zod.z.lazy(
|
|
557
|
+
() => zod.z.union([
|
|
558
|
+
zod.z.object({ type: zod.z.literal("account"), address: zod.z.string() }),
|
|
559
|
+
zod.z.object({
|
|
560
|
+
type: zod.z.literal("multisig"),
|
|
561
|
+
address: zod.z.string(),
|
|
562
|
+
threshold: zod.z.number().int().positive(),
|
|
563
|
+
signatories: zod.z.array(PolkadotSignatoryShapeSchema)
|
|
564
|
+
})
|
|
565
|
+
])
|
|
566
|
+
);
|
|
567
|
+
var PolkadotAuthorizationSetShapeSchema = zod.z.object({
|
|
568
|
+
chain: zod.z.literal("polkadot"),
|
|
569
|
+
address: zod.z.string(),
|
|
570
|
+
signatory: PolkadotSignatoryShapeSchema,
|
|
571
|
+
ss58Format: zod.z.number().int().nonnegative()
|
|
572
|
+
});
|
|
573
|
+
var PreparedTransactionAuthorizationSetSchema = zod.z.discriminatedUnion("chain", [
|
|
574
|
+
HederaAuthorizationSetShapeSchema,
|
|
575
|
+
XrplAuthorizationSetShapeSchema,
|
|
576
|
+
StellarAuthorizationSetShapeSchema,
|
|
577
|
+
PolkadotAuthorizationSetShapeSchema
|
|
578
|
+
]);
|
|
579
|
+
var PreparedTransactionSovereigntySchema = zod.z.discriminatedUnion("mode", [
|
|
580
|
+
zod.z.object({ mode: zod.z.literal("none") }),
|
|
581
|
+
zod.z.object({
|
|
582
|
+
mode: zod.z.literal("partial"),
|
|
583
|
+
entityId: zod.z.string(),
|
|
584
|
+
validatorPublicKeys: zod.z.array(zod.z.string()),
|
|
585
|
+
authorizationSet: PreparedTransactionAuthorizationSetSchema.optional()
|
|
586
|
+
}),
|
|
587
|
+
zod.z.object({
|
|
588
|
+
mode: zod.z.literal("full"),
|
|
589
|
+
entityId: zod.z.string(),
|
|
590
|
+
validatorPublicKeys: zod.z.array(zod.z.string()),
|
|
591
|
+
authorizationSet: PreparedTransactionAuthorizationSetSchema.optional()
|
|
592
|
+
})
|
|
593
|
+
]);
|
|
594
|
+
var TransactionStatusSchema = zod.z.enum(["pending", "success", "failed", "expired"]);
|
|
595
|
+
var TransactionTypeSchema = zod.z.enum([
|
|
596
|
+
"transfer",
|
|
597
|
+
"token_transfer",
|
|
598
|
+
"account_create",
|
|
599
|
+
"token_create",
|
|
600
|
+
"token_mint",
|
|
601
|
+
"token_burn",
|
|
602
|
+
"contract_call",
|
|
603
|
+
"contract_create",
|
|
604
|
+
"topic_message",
|
|
605
|
+
"other"
|
|
606
|
+
]);
|
|
607
|
+
zod.z.object({
|
|
608
|
+
id: zod.z.string(),
|
|
609
|
+
chain: ChainTypeSchema,
|
|
610
|
+
type: TransactionTypeSchema,
|
|
611
|
+
status: TransactionStatusSchema,
|
|
612
|
+
timestamp: zod.z.date(),
|
|
613
|
+
from: AccountIdSchema,
|
|
614
|
+
to: AccountIdSchema.optional(),
|
|
615
|
+
amount: zod.z.string().optional(),
|
|
616
|
+
fee: zod.z.string(),
|
|
617
|
+
memo: zod.z.string().optional(),
|
|
618
|
+
metadata: zod.z.record(zod.z.any()).optional()
|
|
619
|
+
});
|
|
620
|
+
zod.z.object({
|
|
621
|
+
transactionId: zod.z.string(),
|
|
622
|
+
chain: ChainTypeSchema,
|
|
623
|
+
status: TransactionStatusSchema,
|
|
624
|
+
blockNumber: zod.z.number().optional(),
|
|
625
|
+
blockHash: zod.z.string().optional(),
|
|
626
|
+
timestamp: zod.z.date(),
|
|
627
|
+
gasUsed: zod.z.string().optional(),
|
|
628
|
+
effectiveFee: zod.z.string(),
|
|
629
|
+
logs: zod.z.array(zod.z.any()).optional(),
|
|
630
|
+
metadata: zod.z.record(zod.z.any()).optional()
|
|
631
|
+
});
|
|
632
|
+
var TokenTypeSchema = zod.z.enum(["fungible", "nft", "semi_fungible"]);
|
|
633
|
+
var TokenSchema = zod.z.object({
|
|
634
|
+
tokenId: zod.z.string(),
|
|
635
|
+
chain: ChainTypeSchema,
|
|
636
|
+
name: zod.z.string(),
|
|
637
|
+
symbol: zod.z.string(),
|
|
638
|
+
decimals: zod.z.number().int().min(0),
|
|
639
|
+
totalSupply: zod.z.string(),
|
|
640
|
+
type: TokenTypeSchema,
|
|
641
|
+
creator: AccountIdSchema.optional(),
|
|
642
|
+
metadata: zod.z.record(zod.z.any()).optional(),
|
|
643
|
+
createdAt: zod.z.date().optional()
|
|
644
|
+
});
|
|
645
|
+
zod.z.object({
|
|
646
|
+
name: zod.z.string(),
|
|
647
|
+
description: zod.z.string().optional(),
|
|
648
|
+
image: zod.z.string().url().optional(),
|
|
649
|
+
attributes: zod.z.array(
|
|
650
|
+
zod.z.object({
|
|
651
|
+
trait_type: zod.z.string(),
|
|
652
|
+
value: zod.z.union([zod.z.string(), zod.z.number(), zod.z.boolean()])
|
|
653
|
+
})
|
|
654
|
+
).optional(),
|
|
655
|
+
external_url: zod.z.string().url().optional()
|
|
656
|
+
});
|
|
657
|
+
TokenSchema.extend({
|
|
658
|
+
holders: zod.z.number().optional(),
|
|
659
|
+
transferCount: zod.z.number().optional(),
|
|
660
|
+
circulatingSupply: zod.z.string().optional()
|
|
661
|
+
});
|
|
662
|
+
var CreateAccountRequestSchema = zod.z.object({
|
|
663
|
+
chain: ChainTypeSchema,
|
|
664
|
+
initialBalance: zod.z.string(),
|
|
665
|
+
publicKey: zod.z.string().optional(),
|
|
666
|
+
memo: zod.z.string().optional(),
|
|
667
|
+
/**
|
|
668
|
+
* Who pays the chain fee for this transaction. Resolution happens in the
|
|
669
|
+
* controller layer (`resolvePayerAccountId`): explicit `payerAccountId`
|
|
670
|
+
* wins, else the JWT-authenticated smart-app's wallet address, else the
|
|
671
|
+
* request is rejected with 400. The validator **never** silently falls
|
|
672
|
+
* back to its own operator account.
|
|
673
|
+
*/
|
|
674
|
+
payerAccountId: zod.z.string().optional(),
|
|
675
|
+
/**
|
|
676
|
+
* HCS consensus timestamp of the validator rules (REQUIRED).
|
|
677
|
+
* Format: "1766490325.123456789"
|
|
678
|
+
*/
|
|
679
|
+
validatorTimestamp: zod.z.string().min(1, "validatorTimestamp is required"),
|
|
680
|
+
/**
|
|
681
|
+
* HCS topic ID where validator rules are stored (REQUIRED).
|
|
682
|
+
*/
|
|
683
|
+
validatorTopicId: zod.z.string().min(1, "validatorTopicId is required"),
|
|
684
|
+
/**
|
|
685
|
+
* Whether to remove admin key after creation (makes entity immutable).
|
|
686
|
+
* Default: true for production-grade immutability.
|
|
687
|
+
*/
|
|
688
|
+
immutable: zod.z.boolean().default(true),
|
|
689
|
+
/**
|
|
690
|
+
* Smart node security mode for the account key structure.
|
|
691
|
+
* - 'none': Owner-only key (no validator involvement)
|
|
692
|
+
* - 'partial': threshold(2, [appOwnerKey, tssKeyList]) — co-control
|
|
693
|
+
* - 'full': TSS KeyList only — full validator network control
|
|
694
|
+
* Default: 'none' for basic account creation via SDK.
|
|
695
|
+
*/
|
|
696
|
+
securityMode: zod.z.enum(["none", "partial", "full"]).default("none"),
|
|
697
|
+
/**
|
|
698
|
+
* App owner's public key (required for 'partial' security mode).
|
|
699
|
+
* The owner key + TSS network key form a threshold-2 multi-sig.
|
|
700
|
+
*/
|
|
701
|
+
appOwnerPublicKey: zod.z.string().optional(),
|
|
702
|
+
metadata: zod.z.record(zod.z.any()).optional()
|
|
703
|
+
});
|
|
704
|
+
zod.z.object({
|
|
705
|
+
accountId: AccountIdSchema,
|
|
706
|
+
publicKey: zod.z.string().optional(),
|
|
707
|
+
privateKey: zod.z.string().optional(),
|
|
708
|
+
// Only returned on creation, store securely!
|
|
709
|
+
transactionId: zod.z.string(),
|
|
710
|
+
chain: ChainTypeSchema,
|
|
711
|
+
timestamp: zod.z.date().optional()
|
|
712
|
+
});
|
|
713
|
+
var TransferRequestSchema = zod.z.object({
|
|
714
|
+
chain: ChainTypeSchema,
|
|
715
|
+
from: AccountIdSchema,
|
|
716
|
+
to: AccountIdSchema,
|
|
717
|
+
amount: zod.z.string(),
|
|
718
|
+
tokenId: zod.z.string().optional(),
|
|
719
|
+
// undefined = native token
|
|
720
|
+
memo: zod.z.string().optional(),
|
|
721
|
+
/** See CreateAccountRequestSchema.payerAccountId — same resolution rules. */
|
|
722
|
+
payerAccountId: zod.z.string().optional(),
|
|
723
|
+
metadata: zod.z.record(zod.z.any()).optional()
|
|
724
|
+
});
|
|
725
|
+
zod.z.object({
|
|
726
|
+
transactionId: zod.z.string(),
|
|
727
|
+
status: zod.z.enum(["pending", "success", "failed"]),
|
|
728
|
+
chain: ChainTypeSchema,
|
|
729
|
+
fee: zod.z.string().optional(),
|
|
730
|
+
timestamp: zod.z.date().optional()
|
|
731
|
+
});
|
|
732
|
+
zod.z.object({
|
|
733
|
+
chain: ChainTypeSchema,
|
|
734
|
+
accountId: AccountIdSchema
|
|
735
|
+
});
|
|
736
|
+
zod.z.object({
|
|
737
|
+
chain: ChainTypeSchema,
|
|
738
|
+
transactionId: zod.z.string()
|
|
739
|
+
});
|
|
740
|
+
var CreateTokenRequestSchema = zod.z.object({
|
|
741
|
+
chain: ChainTypeSchema,
|
|
742
|
+
name: zod.z.string().min(1).max(100),
|
|
743
|
+
symbol: zod.z.string().min(1).max(10),
|
|
744
|
+
decimals: zod.z.number().int().min(0).max(18),
|
|
745
|
+
initialSupply: zod.z.string(),
|
|
746
|
+
type: zod.z.enum(["fungible", "nft"]),
|
|
747
|
+
treasury: AccountIdSchema.optional(),
|
|
748
|
+
/**
|
|
749
|
+
* Token capabilities define what operations the token supports.
|
|
750
|
+
* These are validated against chain support and translated to native implementations.
|
|
751
|
+
*
|
|
752
|
+
* @example
|
|
753
|
+
* ```typescript
|
|
754
|
+
* capabilities: {
|
|
755
|
+
* pausable: true, // Hedera: pauseKey, XRPL: GlobalFreeze
|
|
756
|
+
* restrictable: true, // Hedera: freezeKey, XRPL: TrustLineFreeze
|
|
757
|
+
* compliant: true, // Hedera: kycKey, XRPL: RequireAuth
|
|
758
|
+
* wipeable: true, // Hedera: wipeKey, XRPL: Clawback
|
|
759
|
+
* mintable: true, // Allow additional minting
|
|
760
|
+
* burnable: true, // Allow burning
|
|
761
|
+
* }
|
|
762
|
+
* ```
|
|
763
|
+
*/
|
|
764
|
+
capabilities: TokenCapabilitiesSchema.optional().default({
|
|
765
|
+
pausable: false,
|
|
766
|
+
restrictable: false,
|
|
767
|
+
compliant: false,
|
|
768
|
+
wipeable: false,
|
|
769
|
+
mintable: true,
|
|
770
|
+
burnable: true,
|
|
771
|
+
transferable: true
|
|
772
|
+
}),
|
|
773
|
+
/**
|
|
774
|
+
* HCS consensus timestamp of the validator rules (REQUIRED).
|
|
775
|
+
* Format: "1766490325.123456789"
|
|
776
|
+
*/
|
|
777
|
+
validatorTimestamp: zod.z.string().min(1, "validatorTimestamp is required"),
|
|
778
|
+
/**
|
|
779
|
+
* HCS topic ID where validator rules are stored (REQUIRED).
|
|
780
|
+
*/
|
|
781
|
+
validatorTopicId: zod.z.string().min(1, "validatorTopicId is required"),
|
|
782
|
+
/**
|
|
783
|
+
* Whether to remove admin key after creation (makes entity immutable).
|
|
784
|
+
* Default: true for production-grade immutability.
|
|
785
|
+
*/
|
|
786
|
+
immutable: zod.z.boolean().default(true),
|
|
787
|
+
/** See CreateAccountRequestSchema.payerAccountId — same resolution rules. */
|
|
788
|
+
payerAccountId: zod.z.string().optional(),
|
|
789
|
+
metadata: zod.z.record(zod.z.any()).optional()
|
|
790
|
+
});
|
|
791
|
+
zod.z.object({
|
|
792
|
+
tokenId: zod.z.string(),
|
|
793
|
+
transactionId: zod.z.string(),
|
|
794
|
+
chain: ChainTypeSchema,
|
|
795
|
+
timestamp: zod.z.date().optional()
|
|
796
|
+
});
|
|
797
|
+
var MintTokenRequestSchema = zod.z.object({
|
|
798
|
+
chain: ChainTypeSchema,
|
|
799
|
+
tokenId: zod.z.string(),
|
|
800
|
+
/** Amount to mint (fungible tokens). Ignored for NFTs. */
|
|
801
|
+
amount: zod.z.string().optional(),
|
|
802
|
+
/** Recipient account (fungible: transfer after mint; NFT: Hedera mints to treasury). */
|
|
803
|
+
recipient: AccountIdSchema.optional(),
|
|
804
|
+
/**
|
|
805
|
+
* NFT metadata entries — one per NFT to mint.
|
|
806
|
+
* Each entry becomes on-chain metadata (e.g. IPFS CID, encoded memo).
|
|
807
|
+
* On Hedera: passed to TokenMintTransaction.setMetadata().
|
|
808
|
+
* On XRPL: used as URI in NFTokenMint.
|
|
809
|
+
*/
|
|
810
|
+
nftMetadata: zod.z.array(zod.z.string()).optional(),
|
|
811
|
+
/** Additional chain-specific options. */
|
|
812
|
+
metadata: zod.z.record(zod.z.any()).optional(),
|
|
813
|
+
/** See CreateAccountRequestSchema.payerAccountId — same resolution rules. */
|
|
814
|
+
payerAccountId: zod.z.string().optional()
|
|
815
|
+
});
|
|
816
|
+
var BurnTokenRequestSchema = zod.z.object({
|
|
817
|
+
chain: ChainTypeSchema,
|
|
818
|
+
tokenId: zod.z.string(),
|
|
819
|
+
amount: zod.z.string(),
|
|
820
|
+
/** See CreateAccountRequestSchema.payerAccountId — same resolution rules. */
|
|
821
|
+
payerAccountId: zod.z.string().optional(),
|
|
822
|
+
metadata: zod.z.record(zod.z.any()).optional()
|
|
823
|
+
});
|
|
824
|
+
var TokenActionRequestSchema = zod.z.object({
|
|
825
|
+
chain: ChainTypeSchema,
|
|
826
|
+
tokenId: zod.z.string(),
|
|
827
|
+
accountId: AccountIdSchema.optional(),
|
|
828
|
+
// Required for account-specific actions
|
|
829
|
+
amount: zod.z.string().optional(),
|
|
830
|
+
// Required for wipe action
|
|
831
|
+
/** See CreateAccountRequestSchema.payerAccountId — same resolution rules. */
|
|
832
|
+
payerAccountId: zod.z.string().optional(),
|
|
833
|
+
metadata: zod.z.record(zod.z.any()).optional()
|
|
834
|
+
});
|
|
835
|
+
var ActionResultSchema = zod.z.object({
|
|
836
|
+
success: zod.z.boolean(),
|
|
837
|
+
transactionId: zod.z.string(),
|
|
838
|
+
chain: ChainTypeSchema,
|
|
839
|
+
/** The native chain operation that was executed */
|
|
840
|
+
chainOperation: zod.z.string(),
|
|
841
|
+
/** Additional context or notes about the operation */
|
|
842
|
+
notes: zod.z.array(zod.z.string()).optional(),
|
|
843
|
+
/** Timestamp of the operation */
|
|
844
|
+
timestamp: zod.z.date().optional()
|
|
845
|
+
});
|
|
846
|
+
ActionResultSchema.extend({
|
|
847
|
+
tokenId: zod.z.string(),
|
|
848
|
+
/** The capabilities that were enabled for this token */
|
|
849
|
+
enabledCapabilities: zod.z.array(zod.z.string())
|
|
850
|
+
});
|
|
851
|
+
var ValidatorSignatureSchema = zod.z.object({
|
|
852
|
+
/** Validator's node ID */
|
|
853
|
+
validatorId: zod.z.string(),
|
|
854
|
+
/** Validator's public key (hex-encoded) */
|
|
855
|
+
publicKey: zod.z.string(),
|
|
856
|
+
/** Signature over the transaction bytes (hex-encoded) */
|
|
857
|
+
signature: zod.z.string(),
|
|
858
|
+
/** When this signature was created */
|
|
859
|
+
signedAt: zod.z.date(),
|
|
860
|
+
/** Signature algorithm used */
|
|
861
|
+
algorithm: zod.z.enum(["ed25519", "ecdsa-secp256k1", "bls12-381"]).optional()
|
|
862
|
+
});
|
|
863
|
+
var HederaPreparedMetadataSchema = zod.z.object({
|
|
864
|
+
/** Hedera node account IDs that will process this transaction */
|
|
865
|
+
nodeAccountIds: zod.z.array(zod.z.string()).optional(),
|
|
866
|
+
/** Maximum transaction fee in tinybars */
|
|
867
|
+
maxTransactionFee: zod.z.string().optional(),
|
|
868
|
+
/** Transaction memo */
|
|
869
|
+
memo: zod.z.string().optional(),
|
|
870
|
+
/** Schedule info if this is a scheduled transaction */
|
|
871
|
+
scheduleInfo: zod.z.object({
|
|
872
|
+
scheduleId: zod.z.string(),
|
|
873
|
+
adminKey: zod.z.string().optional()
|
|
874
|
+
}).optional()
|
|
875
|
+
});
|
|
876
|
+
var XRPLPreparedMetadataSchema = zod.z.object({
|
|
877
|
+
/** Sequence number for this transaction */
|
|
878
|
+
sequence: zod.z.number().optional(),
|
|
879
|
+
/** Last ledger sequence for expiration */
|
|
880
|
+
lastLedgerSequence: zod.z.number().optional(),
|
|
881
|
+
/** Signer list info for multi-sig */
|
|
882
|
+
signerList: zod.z.array(
|
|
883
|
+
zod.z.object({
|
|
884
|
+
account: zod.z.string(),
|
|
885
|
+
signerWeight: zod.z.number()
|
|
886
|
+
})
|
|
887
|
+
).optional(),
|
|
888
|
+
/** Quorum weight required */
|
|
889
|
+
signerQuorum: zod.z.number().optional()
|
|
890
|
+
});
|
|
891
|
+
var SolanaPreparedMetadataSchema = zod.z.object({
|
|
892
|
+
/** Recent blockhash for transaction validity */
|
|
893
|
+
recentBlockhash: zod.z.string().optional(),
|
|
894
|
+
/** Fee payer account */
|
|
895
|
+
feePayer: zod.z.string().optional(),
|
|
896
|
+
/** Program IDs involved */
|
|
897
|
+
programIds: zod.z.array(zod.z.string()).optional(),
|
|
898
|
+
/** Squads v4 multisig PDA address (partial/full modes) */
|
|
899
|
+
squadsMultisigPda: zod.z.string().optional(),
|
|
900
|
+
/** Squads v4 vault PDA address (partial/full modes) */
|
|
901
|
+
squadsVaultPda: zod.z.string().optional(),
|
|
902
|
+
/** Index of the vault transaction proposal */
|
|
903
|
+
squadsTransactionIndex: zod.z.number().optional(),
|
|
904
|
+
/** Proposal PDA address */
|
|
905
|
+
squadsProposalAddress: zod.z.string().optional(),
|
|
906
|
+
/** Whether the multisig was created in this call (vs already existed) */
|
|
907
|
+
multisigCreated: zod.z.boolean().optional(),
|
|
908
|
+
/** Security mode used to prepare this transaction */
|
|
909
|
+
securityMode: zod.z.enum(["none", "partial", "full"]).optional(),
|
|
910
|
+
/** SPL Token mint address (token-create and all SPL operations) */
|
|
911
|
+
splMint: zod.z.string().optional()
|
|
912
|
+
});
|
|
913
|
+
var PolkadotPreparedMetadataSchema = zod.z.object({
|
|
914
|
+
/** Era for transaction mortality */
|
|
915
|
+
era: zod.z.string().optional(),
|
|
916
|
+
/** Nonce for the sender */
|
|
917
|
+
nonce: zod.z.number().optional(),
|
|
918
|
+
/** Tip for priority */
|
|
919
|
+
tip: zod.z.string().optional(),
|
|
920
|
+
/** Spec version for runtime */
|
|
921
|
+
specVersion: zod.z.number().optional()
|
|
922
|
+
});
|
|
923
|
+
var ChainPreparedMetadataSchema = zod.z.union([
|
|
924
|
+
HederaPreparedMetadataSchema,
|
|
925
|
+
XRPLPreparedMetadataSchema,
|
|
926
|
+
SolanaPreparedMetadataSchema,
|
|
927
|
+
PolkadotPreparedMetadataSchema,
|
|
928
|
+
zod.z.record(zod.z.any())
|
|
929
|
+
// Allow extension for future chains
|
|
930
|
+
]);
|
|
931
|
+
zod.z.object({
|
|
932
|
+
/** The blockchain this transaction is for */
|
|
933
|
+
chain: ChainTypeSchema,
|
|
934
|
+
/** Type of transaction (e.g., 'TokenCreate', 'Payment', 'NFTMint') */
|
|
935
|
+
transactionType: zod.z.string(),
|
|
936
|
+
/** Pre-assigned transaction ID for tracking */
|
|
937
|
+
transactionId: zod.z.string(),
|
|
938
|
+
/** Base64-encoded frozen transaction bytes ready for submission */
|
|
939
|
+
transactionBytes: zod.z.string(),
|
|
940
|
+
/** When this prepared transaction expires */
|
|
941
|
+
expiresAt: zod.z.date(),
|
|
942
|
+
/** Validator signatures collected for this transaction */
|
|
943
|
+
validatorSignatures: zod.z.array(ValidatorSignatureSchema),
|
|
944
|
+
/** Required payer account ID (caller must fund this account) */
|
|
945
|
+
payerAccountId: zod.z.string().optional(),
|
|
946
|
+
/** Estimated network fee */
|
|
947
|
+
estimatedFee: zod.z.string().optional(),
|
|
948
|
+
/** Number of signatures required for submission */
|
|
949
|
+
requiredSignatures: zod.z.number().optional(),
|
|
950
|
+
/** Whether the transaction has enough signatures to submit */
|
|
951
|
+
readyToSubmit: zod.z.boolean().default(false),
|
|
952
|
+
/** Chain-specific metadata */
|
|
953
|
+
metadata: ChainPreparedMetadataSchema.optional(),
|
|
954
|
+
/**
|
|
955
|
+
* Sovereignty metadata: the mode and (for partial/full) the on-chain
|
|
956
|
+
* authorization set the validator is binding to. Lets smart-apps and tests
|
|
957
|
+
* inspect the authorization topology without decoding chain-native bytes.
|
|
958
|
+
*/
|
|
959
|
+
sovereignty: PreparedTransactionSovereigntySchema.optional()
|
|
960
|
+
});
|
|
961
|
+
zod.z.object({
|
|
962
|
+
/** The original transaction ID */
|
|
963
|
+
transactionId: zod.z.string(),
|
|
964
|
+
/** The blockchain */
|
|
965
|
+
chain: ChainTypeSchema,
|
|
966
|
+
/** Whether submission was successful */
|
|
967
|
+
success: zod.z.boolean(),
|
|
968
|
+
/** Consensus timestamp (Hedera) or ledger/block info */
|
|
969
|
+
consensusTimestamp: zod.z.string().optional(),
|
|
970
|
+
/** Block or ledger number */
|
|
971
|
+
blockNumber: zod.z.number().optional(),
|
|
972
|
+
/** Actual fee paid */
|
|
973
|
+
actualFee: zod.z.string().optional(),
|
|
974
|
+
/** Receipt or confirmation data */
|
|
975
|
+
receipt: zod.z.record(zod.z.any()).optional(),
|
|
976
|
+
/** Error message if submission failed */
|
|
977
|
+
error: zod.z.string().optional()
|
|
978
|
+
});
|
|
979
|
+
zod.z.object({
|
|
980
|
+
chain: zod.z.literal("hedera"),
|
|
981
|
+
payerAccountId: zod.z.string().optional(),
|
|
982
|
+
name: zod.z.string().min(1).max(100),
|
|
983
|
+
symbol: zod.z.string().min(1).max(10),
|
|
984
|
+
decimals: zod.z.number().int().min(0).max(18),
|
|
985
|
+
initialSupply: zod.z.string(),
|
|
986
|
+
treasuryAccountId: zod.z.string().min(1, "treasuryAccountId is required"),
|
|
987
|
+
memo: zod.z.string().optional(),
|
|
988
|
+
tokenType: zod.z.enum(["FUNGIBLE_COMMON", "NON_FUNGIBLE_UNIQUE"]).default("FUNGIBLE_COMMON"),
|
|
989
|
+
supplyKey: zod.z.string().optional(),
|
|
990
|
+
adminKey: zod.z.string().optional(),
|
|
991
|
+
pauseKey: zod.z.string().optional(),
|
|
992
|
+
freezeKey: zod.z.string().optional(),
|
|
993
|
+
kycKey: zod.z.string().optional(),
|
|
994
|
+
wipeKey: zod.z.string().optional(),
|
|
995
|
+
validatorTimestamp: zod.z.string().min(1),
|
|
996
|
+
validatorTopicId: zod.z.string().min(1)
|
|
997
|
+
}).merge(SovereigntyFieldsRawSchema).refine(sovereigntyRefinePredicate, { message: SOVEREIGNTY_REFINE_MESSAGE });
|
|
998
|
+
zod.z.object({
|
|
999
|
+
chain: zod.z.literal("xrpl"),
|
|
1000
|
+
payerAccountId: zod.z.string().optional(),
|
|
1001
|
+
// User's XRPL account whose authorization we're configuring.
|
|
1002
|
+
accountAddress: zod.z.string().min(25).max(34),
|
|
1003
|
+
validatorTimestamp: zod.z.string().min(1),
|
|
1004
|
+
validatorTopicId: zod.z.string().min(1)
|
|
1005
|
+
}).merge(SovereigntyFieldsRawSchema).refine(sovereigntyRefinePredicate, { message: SOVEREIGNTY_REFINE_MESSAGE });
|
|
1006
|
+
zod.z.object({
|
|
1007
|
+
chain: zod.z.literal("stellar"),
|
|
1008
|
+
payerAccountId: zod.z.string().optional(),
|
|
1009
|
+
// The master public key of the new Stellar account.
|
|
1010
|
+
publicKey: zod.z.string().min(56).max(56),
|
|
1011
|
+
startingBalance: zod.z.string().regex(/^\d+(\.\d+)?$/),
|
|
1012
|
+
validatorTimestamp: zod.z.string().min(1),
|
|
1013
|
+
validatorTopicId: zod.z.string().min(1)
|
|
1014
|
+
}).merge(SovereigntyFieldsRawSchema).refine(
|
|
1015
|
+
(v) => {
|
|
1016
|
+
if (v.securityMode === "partial" || v.securityMode === "full") {
|
|
1017
|
+
return !!v.entityId;
|
|
1018
|
+
}
|
|
1019
|
+
return true;
|
|
1020
|
+
},
|
|
1021
|
+
{ message: "securityMode='partial'/'full' requires entityId" }
|
|
1022
|
+
);
|
|
1023
|
+
|
|
1024
|
+
// src/discovery/index.ts
|
|
1025
|
+
var discovery_exports = {};
|
|
1026
|
+
__export(discovery_exports, {
|
|
1027
|
+
MIRROR_NODE_URLS: () => MIRROR_NODE_URLS,
|
|
1028
|
+
MirrorNodeClient: () => MirrorNodeClient,
|
|
1029
|
+
MirrorNodeError: () => MirrorNodeError,
|
|
1030
|
+
ValidatorDiscoveryClient: () => ValidatorDiscoveryClient
|
|
1031
|
+
});
|
|
1032
|
+
|
|
1033
|
+
// src/discovery/mirror-node.ts
|
|
1034
|
+
function validateUrl(url, allowInsecure = false) {
|
|
1035
|
+
try {
|
|
1036
|
+
const parsed = new URL(url);
|
|
1037
|
+
if (!["http:", "https:"].includes(parsed.protocol)) {
|
|
1038
|
+
throw new Error(`Invalid protocol: ${parsed.protocol}`);
|
|
1039
|
+
}
|
|
1040
|
+
if (!allowInsecure && parsed.protocol !== "https:") {
|
|
1041
|
+
throw new Error(
|
|
1042
|
+
"HTTPS is required for secure connections. Set allowInsecure=true to override."
|
|
1043
|
+
);
|
|
1044
|
+
}
|
|
1045
|
+
const hostname = parsed.hostname.toLowerCase();
|
|
1046
|
+
const blockedPatterns = [
|
|
1047
|
+
/^localhost$/i,
|
|
1048
|
+
/^127\./,
|
|
1049
|
+
/^10\./,
|
|
1050
|
+
/^172\.(1[6-9]|2[0-9]|3[01])\./,
|
|
1051
|
+
/^192\.168\./,
|
|
1052
|
+
/^169\.254\./,
|
|
1053
|
+
/^::1$/,
|
|
1054
|
+
/^fe80:/i,
|
|
1055
|
+
/^fc00:/i,
|
|
1056
|
+
/^fd00:/i
|
|
1057
|
+
];
|
|
1058
|
+
const isPrivate = blockedPatterns.some((pattern) => pattern.test(hostname));
|
|
1059
|
+
if (isPrivate && !allowInsecure) {
|
|
1060
|
+
throw new Error(
|
|
1061
|
+
"Private/internal URLs are blocked. Set allowInsecure=true for local development."
|
|
1062
|
+
);
|
|
1063
|
+
}
|
|
1064
|
+
return parsed;
|
|
1065
|
+
} catch (error) {
|
|
1066
|
+
if (error instanceof Error && error.message.includes("Invalid URL")) {
|
|
1067
|
+
throw new MirrorNodeError(`Invalid URL format: ${url}`, 400);
|
|
1068
|
+
}
|
|
1069
|
+
throw error;
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
function validateTopicId(topicId) {
|
|
1073
|
+
if (!/^\d+\.\d+\.\d+$/.test(topicId)) {
|
|
1074
|
+
throw new MirrorNodeError(
|
|
1075
|
+
`Invalid topic ID format: ${topicId}. Expected format: 0.0.123456`,
|
|
1076
|
+
400
|
|
1077
|
+
);
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
var MIRROR_NODE_URLS = {
|
|
1081
|
+
mainnet: "https://mainnet-public.mirrornode.hedera.com",
|
|
1082
|
+
testnet: "https://testnet.mirrornode.hedera.com",
|
|
1083
|
+
previewnet: "https://previewnet.mirrornode.hedera.com"
|
|
1084
|
+
};
|
|
1085
|
+
var MirrorNodeClient = class _MirrorNodeClient {
|
|
1086
|
+
baseUrl;
|
|
1087
|
+
timeout;
|
|
1088
|
+
allowInsecure;
|
|
1089
|
+
constructor(config) {
|
|
1090
|
+
this.allowInsecure = config.allowInsecure ?? false;
|
|
1091
|
+
const validatedUrl = validateUrl(config.baseUrl, this.allowInsecure);
|
|
1092
|
+
this.baseUrl = validatedUrl.origin;
|
|
1093
|
+
this.timeout = config.timeout || 3e4;
|
|
1094
|
+
}
|
|
1095
|
+
/**
|
|
1096
|
+
* Create client for a specific network
|
|
1097
|
+
*/
|
|
1098
|
+
static forNetwork(network) {
|
|
1099
|
+
const baseUrl = MIRROR_NODE_URLS[network];
|
|
1100
|
+
if (!baseUrl) {
|
|
1101
|
+
throw new Error(`Unknown network: ${network}`);
|
|
1102
|
+
}
|
|
1103
|
+
return new _MirrorNodeClient({ baseUrl });
|
|
1104
|
+
}
|
|
1105
|
+
/**
|
|
1106
|
+
* Get topic messages from mirror node
|
|
1107
|
+
*
|
|
1108
|
+
* @param topicId - HCS topic ID (e.g., '0.0.123456')
|
|
1109
|
+
* @param options - Query options
|
|
1110
|
+
* @returns Topic messages
|
|
1111
|
+
*/
|
|
1112
|
+
async getTopicMessages(topicId, options) {
|
|
1113
|
+
validateTopicId(topicId);
|
|
1114
|
+
const params = new URLSearchParams();
|
|
1115
|
+
if (options?.limit) {
|
|
1116
|
+
params.set("limit", options.limit.toString());
|
|
1117
|
+
}
|
|
1118
|
+
if (options?.order) {
|
|
1119
|
+
params.set("order", options.order);
|
|
1120
|
+
}
|
|
1121
|
+
if (options?.timestampStart) {
|
|
1122
|
+
params.set("timestamp", `gte:${options.timestampStart}`);
|
|
1123
|
+
}
|
|
1124
|
+
if (options?.timestampEnd) {
|
|
1125
|
+
params.set("timestamp", `lte:${options.timestampEnd}`);
|
|
1126
|
+
}
|
|
1127
|
+
if (options?.sequenceNumberStart) {
|
|
1128
|
+
params.set("sequencenumber", `gte:${options.sequenceNumberStart}`);
|
|
1129
|
+
}
|
|
1130
|
+
const url = `${this.baseUrl}/api/v1/topics/${topicId}/messages?${params.toString()}`;
|
|
1131
|
+
const controller = new AbortController();
|
|
1132
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
1133
|
+
try {
|
|
1134
|
+
const response = await fetch(url, {
|
|
1135
|
+
method: "GET",
|
|
1136
|
+
headers: {
|
|
1137
|
+
Accept: "application/json"
|
|
1138
|
+
},
|
|
1139
|
+
signal: controller.signal
|
|
1140
|
+
});
|
|
1141
|
+
clearTimeout(timeoutId);
|
|
1142
|
+
if (!response.ok) {
|
|
1143
|
+
throw new MirrorNodeError(
|
|
1144
|
+
`Mirror node error: ${response.status} ${response.statusText}`,
|
|
1145
|
+
response.status
|
|
1146
|
+
);
|
|
1147
|
+
}
|
|
1148
|
+
const data = await response.json();
|
|
1149
|
+
return data.messages;
|
|
1150
|
+
} catch (error) {
|
|
1151
|
+
clearTimeout(timeoutId);
|
|
1152
|
+
if (error instanceof MirrorNodeError) {
|
|
1153
|
+
throw error;
|
|
1154
|
+
}
|
|
1155
|
+
const err = error;
|
|
1156
|
+
if (err.name === "AbortError") {
|
|
1157
|
+
throw new MirrorNodeError("Mirror node request timeout", 408);
|
|
1158
|
+
}
|
|
1159
|
+
throw new MirrorNodeError(`Mirror node network error: ${err.message}`, 0);
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
/**
|
|
1163
|
+
* Get all topic messages with pagination
|
|
1164
|
+
*
|
|
1165
|
+
* @param topicId - HCS topic ID
|
|
1166
|
+
* @param maxMessages - Maximum messages to fetch (default: 1000)
|
|
1167
|
+
* @returns All topic messages
|
|
1168
|
+
*/
|
|
1169
|
+
async getAllTopicMessages(topicId, maxMessages = 1e3) {
|
|
1170
|
+
validateTopicId(topicId);
|
|
1171
|
+
const safeMaxMessages = Math.min(maxMessages, 1e4);
|
|
1172
|
+
const allMessages = [];
|
|
1173
|
+
let nextPath = `/api/v1/topics/${topicId}/messages?limit=100&order=desc`;
|
|
1174
|
+
while (nextPath && allMessages.length < safeMaxMessages) {
|
|
1175
|
+
const controller = new AbortController();
|
|
1176
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
1177
|
+
try {
|
|
1178
|
+
const fullUrl = `${this.baseUrl}${nextPath}`;
|
|
1179
|
+
const response = await fetch(fullUrl, {
|
|
1180
|
+
method: "GET",
|
|
1181
|
+
headers: { Accept: "application/json" },
|
|
1182
|
+
signal: controller.signal
|
|
1183
|
+
});
|
|
1184
|
+
clearTimeout(timeoutId);
|
|
1185
|
+
if (!response.ok) {
|
|
1186
|
+
throw new MirrorNodeError(
|
|
1187
|
+
`Mirror node error: ${response.status} ${response.statusText}`,
|
|
1188
|
+
response.status
|
|
1189
|
+
);
|
|
1190
|
+
}
|
|
1191
|
+
const data = await response.json();
|
|
1192
|
+
allMessages.push(...data.messages);
|
|
1193
|
+
if (data.links.next) {
|
|
1194
|
+
const nextLink = data.links.next;
|
|
1195
|
+
if (nextLink.startsWith("/api/v1/topics/")) {
|
|
1196
|
+
nextPath = nextLink;
|
|
1197
|
+
} else {
|
|
1198
|
+
nextPath = void 0;
|
|
1199
|
+
}
|
|
1200
|
+
} else {
|
|
1201
|
+
nextPath = void 0;
|
|
1202
|
+
}
|
|
1203
|
+
} catch (error) {
|
|
1204
|
+
clearTimeout(timeoutId);
|
|
1205
|
+
if (error instanceof MirrorNodeError) {
|
|
1206
|
+
throw error;
|
|
1207
|
+
}
|
|
1208
|
+
const err = error;
|
|
1209
|
+
throw new MirrorNodeError(`Mirror node error: ${err.message}`, 0);
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
return allMessages.slice(0, safeMaxMessages);
|
|
1213
|
+
}
|
|
1214
|
+
/**
|
|
1215
|
+
* Decode base64 message content
|
|
1216
|
+
*/
|
|
1217
|
+
static decodeMessage(base64Message) {
|
|
1218
|
+
if (typeof Buffer !== "undefined") {
|
|
1219
|
+
return Buffer.from(base64Message, "base64").toString("utf-8");
|
|
1220
|
+
}
|
|
1221
|
+
return atob(base64Message);
|
|
1222
|
+
}
|
|
1223
|
+
};
|
|
1224
|
+
var MirrorNodeError = class extends Error {
|
|
1225
|
+
constructor(message, statusCode) {
|
|
1226
|
+
super(message);
|
|
1227
|
+
this.statusCode = statusCode;
|
|
1228
|
+
this.name = "MirrorNodeError";
|
|
1229
|
+
}
|
|
1230
|
+
statusCode;
|
|
1231
|
+
};
|
|
1232
|
+
|
|
1233
|
+
// src/discovery/validator-discovery.ts
|
|
1234
|
+
var ValidatorDiscoveryClient = class {
|
|
1235
|
+
mirrorNode;
|
|
1236
|
+
registryTopicId;
|
|
1237
|
+
cacheTtlMs;
|
|
1238
|
+
cache = null;
|
|
1239
|
+
constructor(config) {
|
|
1240
|
+
const mirrorNodeUrl = config.mirrorNodeUrl || MIRROR_NODE_URLS[config.network];
|
|
1241
|
+
if (!mirrorNodeUrl) {
|
|
1242
|
+
throw new Error(`Unknown network: ${config.network}`);
|
|
1243
|
+
}
|
|
1244
|
+
this.mirrorNode = new MirrorNodeClient({
|
|
1245
|
+
baseUrl: mirrorNodeUrl,
|
|
1246
|
+
allowInsecure: config.allowInsecure
|
|
1247
|
+
});
|
|
1248
|
+
this.registryTopicId = config.registryTopicId;
|
|
1249
|
+
this.cacheTtlMs = config.cacheTtlMs ?? 6e4;
|
|
1250
|
+
}
|
|
1251
|
+
/**
|
|
1252
|
+
* Get all active validators from the registry
|
|
1253
|
+
*
|
|
1254
|
+
* Results are cached for efficiency. Use `forceRefresh` to bypass cache.
|
|
1255
|
+
*
|
|
1256
|
+
* @param forceRefresh - Force refresh from mirror node
|
|
1257
|
+
* @returns List of active validators
|
|
1258
|
+
*/
|
|
1259
|
+
async getValidators(forceRefresh = false) {
|
|
1260
|
+
if (!forceRefresh && this.cache && this.isCacheValid()) {
|
|
1261
|
+
return this.cache.validators;
|
|
1262
|
+
}
|
|
1263
|
+
const messages = await this.mirrorNode.getAllTopicMessages(
|
|
1264
|
+
this.registryTopicId,
|
|
1265
|
+
500
|
|
1266
|
+
// Max messages to fetch
|
|
1267
|
+
);
|
|
1268
|
+
const validatorMap = /* @__PURE__ */ new Map();
|
|
1269
|
+
const leftValidators = /* @__PURE__ */ new Set();
|
|
1270
|
+
for (const msg of messages) {
|
|
1271
|
+
try {
|
|
1272
|
+
const content = MirrorNodeClient.decodeMessage(msg.message);
|
|
1273
|
+
const raw = JSON.parse(content);
|
|
1274
|
+
const entry = normalizeRegistryEntry(raw);
|
|
1275
|
+
if (!entry) continue;
|
|
1276
|
+
if (entry.messageType === "validator.leave") {
|
|
1277
|
+
leftValidators.add(entry.nodeId);
|
|
1278
|
+
continue;
|
|
1279
|
+
}
|
|
1280
|
+
if (leftValidators.has(entry.nodeId)) {
|
|
1281
|
+
continue;
|
|
1282
|
+
}
|
|
1283
|
+
if (!validatorMap.has(entry.nodeId)) {
|
|
1284
|
+
validatorMap.set(entry.nodeId, entry);
|
|
1285
|
+
}
|
|
1286
|
+
} catch {
|
|
1287
|
+
continue;
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
const validators = Array.from(validatorMap.values());
|
|
1291
|
+
this.cache = {
|
|
1292
|
+
validators,
|
|
1293
|
+
lastUpdated: Date.now()
|
|
1294
|
+
};
|
|
1295
|
+
return validators;
|
|
1296
|
+
}
|
|
1297
|
+
/**
|
|
1298
|
+
* Get validators with API endpoints available
|
|
1299
|
+
*
|
|
1300
|
+
* @param forceRefresh - Force refresh from mirror node
|
|
1301
|
+
* @returns Validators with apiEndpoint configured
|
|
1302
|
+
*/
|
|
1303
|
+
async getValidatorsWithEndpoints(forceRefresh = false) {
|
|
1304
|
+
const validators = await this.getValidators(forceRefresh);
|
|
1305
|
+
return validators.filter((v) => v.networkEndpoints?.apiEndpoint);
|
|
1306
|
+
}
|
|
1307
|
+
/**
|
|
1308
|
+
* Get a random validator from the registry
|
|
1309
|
+
*
|
|
1310
|
+
* @param forceRefresh - Force refresh from mirror node
|
|
1311
|
+
* @returns Random validator info or null if none available
|
|
1312
|
+
*/
|
|
1313
|
+
async getRandomValidator(forceRefresh = false) {
|
|
1314
|
+
const validators = await this.getValidatorsWithEndpoints(forceRefresh);
|
|
1315
|
+
if (validators.length === 0) {
|
|
1316
|
+
return null;
|
|
1317
|
+
}
|
|
1318
|
+
const randomBytes = new Uint32Array(1);
|
|
1319
|
+
crypto.getRandomValues(randomBytes);
|
|
1320
|
+
const randomIndex = randomBytes[0] % validators.length;
|
|
1321
|
+
return validators[randomIndex];
|
|
1322
|
+
}
|
|
1323
|
+
/**
|
|
1324
|
+
* Get a random validator API endpoint URL
|
|
1325
|
+
*
|
|
1326
|
+
* @param forceRefresh - Force refresh from mirror node
|
|
1327
|
+
* @returns Random validator API URL or null if none available
|
|
1328
|
+
*/
|
|
1329
|
+
async getRandomValidatorUrl(forceRefresh = false) {
|
|
1330
|
+
const validator = await this.getRandomValidator(forceRefresh);
|
|
1331
|
+
return validator?.networkEndpoints?.apiEndpoint ?? null;
|
|
1332
|
+
}
|
|
1333
|
+
/**
|
|
1334
|
+
* Get validator by node ID
|
|
1335
|
+
*
|
|
1336
|
+
* @param nodeId - Validator node ID (e.g., 'validator-1')
|
|
1337
|
+
* @param forceRefresh - Force refresh from mirror node
|
|
1338
|
+
* @returns Validator info or null if not found
|
|
1339
|
+
*/
|
|
1340
|
+
async getValidatorByNodeId(nodeId, forceRefresh = false) {
|
|
1341
|
+
const validators = await this.getValidators(forceRefresh);
|
|
1342
|
+
return validators.find((v) => v.nodeId === nodeId) ?? null;
|
|
1343
|
+
}
|
|
1344
|
+
/**
|
|
1345
|
+
* Get validators by capability
|
|
1346
|
+
*
|
|
1347
|
+
* @param capability - Required capability (e.g., 'hedera', 'xrpl', 'dkg')
|
|
1348
|
+
* @param forceRefresh - Force refresh from mirror node
|
|
1349
|
+
* @returns Validators with the specified capability
|
|
1350
|
+
*/
|
|
1351
|
+
async getValidatorsByCapability(capability, forceRefresh = false) {
|
|
1352
|
+
const validators = await this.getValidatorsWithEndpoints(forceRefresh);
|
|
1353
|
+
return validators.filter((v) => v.capabilities?.includes(capability));
|
|
1354
|
+
}
|
|
1355
|
+
/**
|
|
1356
|
+
* Clear the validator cache
|
|
1357
|
+
*/
|
|
1358
|
+
clearCache() {
|
|
1359
|
+
this.cache = null;
|
|
1360
|
+
}
|
|
1361
|
+
/**
|
|
1362
|
+
* Check if cache is still valid
|
|
1363
|
+
*/
|
|
1364
|
+
isCacheValid() {
|
|
1365
|
+
if (!this.cache) return false;
|
|
1366
|
+
return Date.now() - this.cache.lastUpdated < this.cacheTtlMs;
|
|
1367
|
+
}
|
|
1368
|
+
};
|
|
1369
|
+
function normalizeRegistryEntry(raw) {
|
|
1370
|
+
if (raw.messageType && typeof raw.nodeId === "string") {
|
|
1371
|
+
return raw;
|
|
1372
|
+
}
|
|
1373
|
+
if (raw.type === "validator.register") {
|
|
1374
|
+
if (raw.validatorType && raw.validatorType !== void 0 && raw.validatorType !== null) {
|
|
1375
|
+
if (typeof raw.validatorType === "string" && raw.validatorType !== "node") {
|
|
1376
|
+
return null;
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
const nodeId = typeof raw.validatorId === "string" && raw.validatorId || typeof raw.nodeId === "string" && raw.nodeId || null;
|
|
1380
|
+
if (!nodeId) return null;
|
|
1381
|
+
const endpoint = typeof raw.endpoint === "string" ? raw.endpoint : void 0;
|
|
1382
|
+
if (!endpoint) return null;
|
|
1383
|
+
const capabilities = Array.isArray(raw.capabilities) ? raw.capabilities.filter((c) => typeof c === "string") : void 0;
|
|
1384
|
+
return {
|
|
1385
|
+
validatorTimestamp: typeof raw.timestamp === "string" ? raw.timestamp : "",
|
|
1386
|
+
nodeId,
|
|
1387
|
+
type: "consensus",
|
|
1388
|
+
networkEndpoints: { apiEndpoint: endpoint },
|
|
1389
|
+
publicKey: typeof raw.publicKey === "string" ? raw.publicKey : void 0,
|
|
1390
|
+
capabilities,
|
|
1391
|
+
metadata: {},
|
|
1392
|
+
registeredAt: typeof raw.timestamp === "string" ? raw.timestamp : "",
|
|
1393
|
+
messageType: "validator.join"
|
|
1394
|
+
};
|
|
1395
|
+
}
|
|
1396
|
+
return null;
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
// src/auth/index.ts
|
|
1400
|
+
var auth_exports = {};
|
|
1401
|
+
__export(auth_exports, {
|
|
1402
|
+
ValidatorAuthClient: () => ValidatorAuthClient,
|
|
1403
|
+
ValidatorAuthError: () => ValidatorAuthError
|
|
1404
|
+
});
|
|
1405
|
+
|
|
1406
|
+
// src/auth/validator-auth.ts
|
|
1407
|
+
var SUPPORTED_AUTH_CHAINS = [
|
|
1408
|
+
"hedera",
|
|
1409
|
+
"xrpl",
|
|
1410
|
+
"polkadot",
|
|
1411
|
+
"stellar",
|
|
1412
|
+
"solana"
|
|
1413
|
+
];
|
|
1414
|
+
function validateValidatorUrl(url, allowInsecure = false) {
|
|
1415
|
+
try {
|
|
1416
|
+
const parsed = new URL(url);
|
|
1417
|
+
if (!["http:", "https:"].includes(parsed.protocol)) {
|
|
1418
|
+
throw new ValidatorAuthError(`Invalid protocol: ${parsed.protocol}`, 400);
|
|
1419
|
+
}
|
|
1420
|
+
if (!allowInsecure && parsed.protocol !== "https:") {
|
|
1421
|
+
throw new ValidatorAuthError(
|
|
1422
|
+
"HTTPS is required for validator connections. Set allowInsecure=true for local development.",
|
|
1423
|
+
400
|
|
1424
|
+
);
|
|
1425
|
+
}
|
|
1426
|
+
const hostname = parsed.hostname.toLowerCase();
|
|
1427
|
+
const isLocalhost = hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1";
|
|
1428
|
+
if (isLocalhost && !allowInsecure) {
|
|
1429
|
+
throw new ValidatorAuthError(
|
|
1430
|
+
"Localhost connections blocked in secure mode. Set allowInsecure=true for local development.",
|
|
1431
|
+
400
|
|
1432
|
+
);
|
|
1433
|
+
}
|
|
1434
|
+
return parsed.origin;
|
|
1435
|
+
} catch (error) {
|
|
1436
|
+
if (error instanceof ValidatorAuthError) {
|
|
1437
|
+
throw error;
|
|
1438
|
+
}
|
|
1439
|
+
throw new ValidatorAuthError(`Invalid validator URL: ${url}`, 400);
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
function sanitizeInput(input, fieldName, maxLength = 256) {
|
|
1443
|
+
if (typeof input !== "string") {
|
|
1444
|
+
throw new ValidatorAuthError(`${fieldName} must be a string`, 400);
|
|
1445
|
+
}
|
|
1446
|
+
if (input.length > maxLength) {
|
|
1447
|
+
throw new ValidatorAuthError(`${fieldName} exceeds maximum length of ${maxLength}`, 400);
|
|
1448
|
+
}
|
|
1449
|
+
return input.replace(/[\x00-\x1F\x7F]/g, "");
|
|
1450
|
+
}
|
|
1451
|
+
var ValidatorAuthClient = class {
|
|
1452
|
+
timeout;
|
|
1453
|
+
allowInsecure;
|
|
1454
|
+
constructor(config) {
|
|
1455
|
+
this.timeout = config?.timeout ?? 3e4;
|
|
1456
|
+
this.allowInsecure = config?.security?.allowInsecure ?? false;
|
|
1457
|
+
}
|
|
1458
|
+
/**
|
|
1459
|
+
* Request authentication challenge from validator
|
|
1460
|
+
*
|
|
1461
|
+
* @param validatorUrl - Validator API base URL
|
|
1462
|
+
* @param chain - Blockchain type
|
|
1463
|
+
* @param address - Wallet address
|
|
1464
|
+
* @returns Challenge to sign
|
|
1465
|
+
*/
|
|
1466
|
+
async requestChallenge(validatorUrl, chain, address) {
|
|
1467
|
+
const safeUrl = validateValidatorUrl(validatorUrl, this.allowInsecure);
|
|
1468
|
+
const safeAddress = sanitizeInput(address, "address", 128);
|
|
1469
|
+
if (!SUPPORTED_AUTH_CHAINS.includes(chain)) {
|
|
1470
|
+
throw new ValidatorAuthError(`Invalid chain type: ${chain}`, 400);
|
|
1471
|
+
}
|
|
1472
|
+
const url = `${safeUrl}/auth/validator/challenge`;
|
|
1473
|
+
const controller = new AbortController();
|
|
1474
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
1475
|
+
try {
|
|
1476
|
+
const response = await fetch(url, {
|
|
1477
|
+
method: "POST",
|
|
1478
|
+
headers: {
|
|
1479
|
+
"Content-Type": "application/json"
|
|
1480
|
+
},
|
|
1481
|
+
body: JSON.stringify({ chain, address: safeAddress }),
|
|
1482
|
+
signal: controller.signal
|
|
1483
|
+
});
|
|
1484
|
+
clearTimeout(timeoutId);
|
|
1485
|
+
if (!response.ok) {
|
|
1486
|
+
const error = await response.json().catch(() => ({}));
|
|
1487
|
+
throw new ValidatorAuthError(
|
|
1488
|
+
`Challenge request failed: ${response.status} ${response.statusText}`,
|
|
1489
|
+
response.status,
|
|
1490
|
+
error
|
|
1491
|
+
);
|
|
1492
|
+
}
|
|
1493
|
+
return await response.json();
|
|
1494
|
+
} catch (error) {
|
|
1495
|
+
clearTimeout(timeoutId);
|
|
1496
|
+
if (error instanceof ValidatorAuthError) {
|
|
1497
|
+
throw error;
|
|
1498
|
+
}
|
|
1499
|
+
const err = error;
|
|
1500
|
+
if (err.name === "AbortError") {
|
|
1501
|
+
throw new ValidatorAuthError("Challenge request timeout", 408);
|
|
1502
|
+
}
|
|
1503
|
+
throw new ValidatorAuthError(`Challenge request failed: ${err.message}`, 0);
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
/**
|
|
1507
|
+
* Authenticate with signed challenge
|
|
1508
|
+
*
|
|
1509
|
+
* @param validatorUrl - Validator API base URL
|
|
1510
|
+
* @param request - Authentication request with signature
|
|
1511
|
+
* @returns Session token and info
|
|
1512
|
+
*/
|
|
1513
|
+
async authenticate(validatorUrl, request) {
|
|
1514
|
+
const safeUrl = validateValidatorUrl(validatorUrl, this.allowInsecure);
|
|
1515
|
+
const sanitizedRequest = {
|
|
1516
|
+
chain: request.chain,
|
|
1517
|
+
address: sanitizeInput(request.address, "address", 128),
|
|
1518
|
+
publicKey: sanitizeInput(request.publicKey, "publicKey", 512),
|
|
1519
|
+
signature: sanitizeInput(request.signature, "signature", 1024),
|
|
1520
|
+
challenge: sanitizeInput(request.challenge, "challenge", 512),
|
|
1521
|
+
metadata: request.metadata ? {
|
|
1522
|
+
nodeId: request.metadata.nodeId ? sanitizeInput(request.metadata.nodeId, "nodeId", 64) : void 0,
|
|
1523
|
+
endpoint: request.metadata.endpoint ? sanitizeInput(request.metadata.endpoint, "endpoint", 256) : void 0,
|
|
1524
|
+
capabilities: request.metadata.capabilities,
|
|
1525
|
+
appId: request.metadata.appId ? sanitizeInput(request.metadata.appId, "appId", 64) : void 0,
|
|
1526
|
+
appName: request.metadata.appName ? sanitizeInput(request.metadata.appName, "appName", 128) : void 0
|
|
1527
|
+
} : void 0
|
|
1528
|
+
};
|
|
1529
|
+
if (!SUPPORTED_AUTH_CHAINS.includes(sanitizedRequest.chain)) {
|
|
1530
|
+
throw new ValidatorAuthError(`Invalid chain type: ${sanitizedRequest.chain}`, 400);
|
|
1531
|
+
}
|
|
1532
|
+
if (!/^[0-9a-fA-F]+$/.test(sanitizedRequest.signature.replace(/_.*$/, ""))) {
|
|
1533
|
+
throw new ValidatorAuthError("Invalid signature format: must be hex encoded", 400);
|
|
1534
|
+
}
|
|
1535
|
+
const url = `${safeUrl}/auth/validator/authenticate`;
|
|
1536
|
+
const controller = new AbortController();
|
|
1537
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
1538
|
+
try {
|
|
1539
|
+
const response = await fetch(url, {
|
|
1540
|
+
method: "POST",
|
|
1541
|
+
headers: {
|
|
1542
|
+
"Content-Type": "application/json"
|
|
1543
|
+
},
|
|
1544
|
+
body: JSON.stringify(sanitizedRequest),
|
|
1545
|
+
signal: controller.signal
|
|
1546
|
+
});
|
|
1547
|
+
clearTimeout(timeoutId);
|
|
1548
|
+
if (!response.ok) {
|
|
1549
|
+
const error = await response.json().catch(() => ({}));
|
|
1550
|
+
throw new ValidatorAuthError(
|
|
1551
|
+
`Authentication failed: ${response.status} ${response.statusText}`,
|
|
1552
|
+
response.status,
|
|
1553
|
+
error
|
|
1554
|
+
);
|
|
1555
|
+
}
|
|
1556
|
+
return await response.json();
|
|
1557
|
+
} catch (error) {
|
|
1558
|
+
clearTimeout(timeoutId);
|
|
1559
|
+
if (error instanceof ValidatorAuthError) {
|
|
1560
|
+
throw error;
|
|
1561
|
+
}
|
|
1562
|
+
const err = error;
|
|
1563
|
+
if (err.name === "AbortError") {
|
|
1564
|
+
throw new ValidatorAuthError("Authentication request timeout", 408);
|
|
1565
|
+
}
|
|
1566
|
+
throw new ValidatorAuthError(`Authentication failed: ${err.message}`, 0);
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
/**
|
|
1570
|
+
* Get current session info
|
|
1571
|
+
*
|
|
1572
|
+
* @param validatorUrl - Validator API base URL
|
|
1573
|
+
* @param token - Session token
|
|
1574
|
+
* @returns Session information
|
|
1575
|
+
*/
|
|
1576
|
+
async getSession(validatorUrl, token) {
|
|
1577
|
+
const safeUrl = validateValidatorUrl(validatorUrl, this.allowInsecure);
|
|
1578
|
+
const safeToken = sanitizeInput(token, "token", 2048);
|
|
1579
|
+
const url = `${safeUrl}/auth/validator/session`;
|
|
1580
|
+
const controller = new AbortController();
|
|
1581
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
1582
|
+
try {
|
|
1583
|
+
const response = await fetch(url, {
|
|
1584
|
+
method: "GET",
|
|
1585
|
+
headers: {
|
|
1586
|
+
Authorization: `Bearer ${safeToken}`
|
|
1587
|
+
},
|
|
1588
|
+
signal: controller.signal
|
|
1589
|
+
});
|
|
1590
|
+
clearTimeout(timeoutId);
|
|
1591
|
+
if (!response.ok) {
|
|
1592
|
+
const error = await response.json().catch(() => ({}));
|
|
1593
|
+
throw new ValidatorAuthError(
|
|
1594
|
+
`Session request failed: ${response.status} ${response.statusText}`,
|
|
1595
|
+
response.status,
|
|
1596
|
+
error
|
|
1597
|
+
);
|
|
1598
|
+
}
|
|
1599
|
+
return await response.json();
|
|
1600
|
+
} catch (error) {
|
|
1601
|
+
clearTimeout(timeoutId);
|
|
1602
|
+
if (error instanceof ValidatorAuthError) {
|
|
1603
|
+
throw error;
|
|
1604
|
+
}
|
|
1605
|
+
const err = error;
|
|
1606
|
+
if (err.name === "AbortError") {
|
|
1607
|
+
throw new ValidatorAuthError("Session request timeout", 408);
|
|
1608
|
+
}
|
|
1609
|
+
throw new ValidatorAuthError(`Session request failed: ${err.message}`, 0);
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
/**
|
|
1613
|
+
* Sign challenge with Hedera private key
|
|
1614
|
+
*
|
|
1615
|
+
* @param challenge - Challenge string from validator
|
|
1616
|
+
* @param privateKey - Hedera PrivateKey instance from @hashgraph/sdk
|
|
1617
|
+
* @returns Hex-encoded signature
|
|
1618
|
+
*/
|
|
1619
|
+
signChallengeHedera(challenge, privateKey) {
|
|
1620
|
+
const messageBytes = Buffer.from(challenge, "utf-8");
|
|
1621
|
+
const signature = privateKey.sign(messageBytes);
|
|
1622
|
+
return Buffer.from(signature).toString("hex");
|
|
1623
|
+
}
|
|
1624
|
+
/**
|
|
1625
|
+
* Sign challenge with XRPL wallet
|
|
1626
|
+
*
|
|
1627
|
+
* @param challenge - Challenge string from validator
|
|
1628
|
+
* @param wallet - XRPL Wallet instance from xrpl library
|
|
1629
|
+
* @returns Hex-encoded signature
|
|
1630
|
+
*/
|
|
1631
|
+
signChallengeXRPL(challenge, wallet) {
|
|
1632
|
+
const signature = wallet.sign(challenge);
|
|
1633
|
+
if (typeof signature === "string" && /^[0-9A-Fa-f]+$/.test(signature)) {
|
|
1634
|
+
return signature;
|
|
1635
|
+
}
|
|
1636
|
+
return Buffer.from(signature).toString("hex");
|
|
1637
|
+
}
|
|
1638
|
+
/**
|
|
1639
|
+
* Complete authentication flow in one call
|
|
1640
|
+
*
|
|
1641
|
+
* @param validatorUrl - Validator API base URL
|
|
1642
|
+
* @param chain - Blockchain type
|
|
1643
|
+
* @param address - Wallet address
|
|
1644
|
+
* @param publicKey - Public key (hex)
|
|
1645
|
+
* @param signFn - Function to sign the challenge
|
|
1646
|
+
* @param metadata - Optional metadata
|
|
1647
|
+
* @returns Session token and info
|
|
1648
|
+
*/
|
|
1649
|
+
async authenticateWithSigner(validatorUrl, chain, address, publicKey, signFn, metadata) {
|
|
1650
|
+
const challengeResponse = await this.requestChallenge(validatorUrl, chain, address);
|
|
1651
|
+
const signature = await signFn(challengeResponse.challenge);
|
|
1652
|
+
return this.authenticate(validatorUrl, {
|
|
1653
|
+
chain,
|
|
1654
|
+
address,
|
|
1655
|
+
publicKey,
|
|
1656
|
+
signature,
|
|
1657
|
+
challenge: challengeResponse.challenge,
|
|
1658
|
+
metadata
|
|
1659
|
+
});
|
|
1660
|
+
}
|
|
1661
|
+
};
|
|
1662
|
+
var ValidatorAuthError = class extends Error {
|
|
1663
|
+
constructor(message, statusCode, details) {
|
|
1664
|
+
super(message);
|
|
1665
|
+
this.statusCode = statusCode;
|
|
1666
|
+
this.details = details;
|
|
1667
|
+
this.name = "ValidatorAuthError";
|
|
1668
|
+
}
|
|
1669
|
+
statusCode;
|
|
1670
|
+
details;
|
|
1671
|
+
};
|
|
1672
|
+
|
|
1673
|
+
// src/http/index.ts
|
|
1674
|
+
var SdkHttpError = class extends Error {
|
|
1675
|
+
constructor(message, statusCode, details) {
|
|
1676
|
+
super(message);
|
|
1677
|
+
this.statusCode = statusCode;
|
|
1678
|
+
this.details = details;
|
|
1679
|
+
this.name = "SdkHttpError";
|
|
1680
|
+
}
|
|
1681
|
+
statusCode;
|
|
1682
|
+
details;
|
|
1683
|
+
};
|
|
1684
|
+
function createHttpClient(config) {
|
|
1685
|
+
const timeout = config.timeout ?? 3e4;
|
|
1686
|
+
function getHeaders(contentType) {
|
|
1687
|
+
const headers = {};
|
|
1688
|
+
{
|
|
1689
|
+
headers["Content-Type"] = contentType;
|
|
1690
|
+
}
|
|
1691
|
+
if (config.authToken) {
|
|
1692
|
+
headers["Authorization"] = `Bearer ${config.authToken}`;
|
|
1693
|
+
}
|
|
1694
|
+
if (config.apiKey) {
|
|
1695
|
+
headers["X-API-Key"] = config.apiKey;
|
|
1696
|
+
}
|
|
1697
|
+
return headers;
|
|
1698
|
+
}
|
|
1699
|
+
function setAuthToken(token) {
|
|
1700
|
+
config.authToken = token;
|
|
1701
|
+
}
|
|
1702
|
+
async function request(method, path, body) {
|
|
1703
|
+
const url = `${config.baseUrl}${path}`;
|
|
1704
|
+
const controller = new AbortController();
|
|
1705
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
1706
|
+
try {
|
|
1707
|
+
const init = {
|
|
1708
|
+
method,
|
|
1709
|
+
headers: getHeaders("application/json"),
|
|
1710
|
+
signal: controller.signal
|
|
1711
|
+
};
|
|
1712
|
+
if (body !== void 0) {
|
|
1713
|
+
init.body = JSON.stringify(body);
|
|
1714
|
+
}
|
|
1715
|
+
const response = await fetch(url, init);
|
|
1716
|
+
clearTimeout(timeoutId);
|
|
1717
|
+
if (!response.ok) {
|
|
1718
|
+
const errorData = await response.json().catch(() => ({}));
|
|
1719
|
+
throw new SdkHttpError(
|
|
1720
|
+
errorData.message || `API error: ${response.status} ${response.statusText}`,
|
|
1721
|
+
response.status,
|
|
1722
|
+
errorData
|
|
1723
|
+
);
|
|
1724
|
+
}
|
|
1725
|
+
const text = await response.text();
|
|
1726
|
+
if (!text) return void 0;
|
|
1727
|
+
return JSON.parse(text);
|
|
1728
|
+
} catch (error) {
|
|
1729
|
+
clearTimeout(timeoutId);
|
|
1730
|
+
if (error instanceof SdkHttpError) throw error;
|
|
1731
|
+
const err = error;
|
|
1732
|
+
if (err.name === "AbortError") {
|
|
1733
|
+
throw new SdkHttpError("Request timeout", 408);
|
|
1734
|
+
}
|
|
1735
|
+
throw new SdkHttpError(`Network error: ${err.message}`, 0, error);
|
|
1736
|
+
}
|
|
1737
|
+
}
|
|
1738
|
+
async function upload(path, file, filename, metadata) {
|
|
1739
|
+
const url = `${config.baseUrl}${path}`;
|
|
1740
|
+
const controller = new AbortController();
|
|
1741
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout * 2);
|
|
1742
|
+
try {
|
|
1743
|
+
const formData = new FormData();
|
|
1744
|
+
const blob = file instanceof Blob ? file : new Blob([new Uint8Array(file)]);
|
|
1745
|
+
formData.append("file", blob, filename);
|
|
1746
|
+
if (metadata) {
|
|
1747
|
+
for (const [key, value] of Object.entries(metadata)) {
|
|
1748
|
+
formData.append(key, value);
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
const headers = {};
|
|
1752
|
+
if (config.authToken) {
|
|
1753
|
+
headers["Authorization"] = `Bearer ${config.authToken}`;
|
|
1754
|
+
}
|
|
1755
|
+
if (config.apiKey) {
|
|
1756
|
+
headers["X-API-Key"] = config.apiKey;
|
|
1757
|
+
}
|
|
1758
|
+
const response = await fetch(url, {
|
|
1759
|
+
method: "POST",
|
|
1760
|
+
headers,
|
|
1761
|
+
body: formData,
|
|
1762
|
+
signal: controller.signal
|
|
1763
|
+
});
|
|
1764
|
+
clearTimeout(timeoutId);
|
|
1765
|
+
if (!response.ok) {
|
|
1766
|
+
const errorData = await response.json().catch(() => ({}));
|
|
1767
|
+
throw new SdkHttpError(
|
|
1768
|
+
errorData.message || `Upload error: ${response.status} ${response.statusText}`,
|
|
1769
|
+
response.status,
|
|
1770
|
+
errorData
|
|
1771
|
+
);
|
|
1772
|
+
}
|
|
1773
|
+
return response.json();
|
|
1774
|
+
} catch (error) {
|
|
1775
|
+
clearTimeout(timeoutId);
|
|
1776
|
+
if (error instanceof SdkHttpError) throw error;
|
|
1777
|
+
const err = error;
|
|
1778
|
+
if (err.name === "AbortError") {
|
|
1779
|
+
throw new SdkHttpError("Upload timeout", 408);
|
|
1780
|
+
}
|
|
1781
|
+
throw new SdkHttpError(`Upload error: ${err.message}`, 0, error);
|
|
1782
|
+
}
|
|
1783
|
+
}
|
|
1784
|
+
const client = {
|
|
1785
|
+
post: (path, body) => request("POST", path, body),
|
|
1786
|
+
get: (path) => request("GET", path),
|
|
1787
|
+
put: (path, body) => request("PUT", path, body),
|
|
1788
|
+
delete: (path) => request("DELETE", path),
|
|
1789
|
+
upload,
|
|
1790
|
+
setAuthToken
|
|
1791
|
+
};
|
|
1792
|
+
return client;
|
|
1793
|
+
}
|
|
1794
|
+
function encodePathParam(param) {
|
|
1795
|
+
return encodeURIComponent(param).replace(/%2F/gi, "");
|
|
1796
|
+
}
|
|
1797
|
+
|
|
1798
|
+
// src/subscription/index.ts
|
|
1799
|
+
var subscription_exports = {};
|
|
1800
|
+
__export(subscription_exports, {
|
|
1801
|
+
SubscriptionClient: () => SubscriptionClient
|
|
1802
|
+
});
|
|
1803
|
+
var SubscriptionClient = class {
|
|
1804
|
+
constructor(http) {
|
|
1805
|
+
this.http = http;
|
|
1806
|
+
}
|
|
1807
|
+
http;
|
|
1808
|
+
/**
|
|
1809
|
+
* Request a new subscription.
|
|
1810
|
+
* Creates a deposit wallet and returns deposit instructions.
|
|
1811
|
+
*/
|
|
1812
|
+
async request(request) {
|
|
1813
|
+
return this.http.post("/subscription/request", request);
|
|
1814
|
+
}
|
|
1815
|
+
/**
|
|
1816
|
+
* Get subscription status by app ID
|
|
1817
|
+
*/
|
|
1818
|
+
async getStatus(appId) {
|
|
1819
|
+
return this.http.get(`/subscription/status/${encodeURIComponent(appId)}`);
|
|
1820
|
+
}
|
|
1821
|
+
/**
|
|
1822
|
+
* Mint subscription NFT after deposit is confirmed
|
|
1823
|
+
*/
|
|
1824
|
+
async mintNft(appId) {
|
|
1825
|
+
return this.http.post(`/subscription/mint/${encodeURIComponent(appId)}`, {});
|
|
1826
|
+
}
|
|
1827
|
+
/**
|
|
1828
|
+
* Renew subscription by extending period
|
|
1829
|
+
*/
|
|
1830
|
+
async renew(request) {
|
|
1831
|
+
return this.http.post("/subscription/renew", request);
|
|
1832
|
+
}
|
|
1833
|
+
/**
|
|
1834
|
+
* Fetch available subscription tiers from the network.
|
|
1835
|
+
*
|
|
1836
|
+
* Use this instead of hard-coding tier names -- the validator returns the
|
|
1837
|
+
* canonical list of tiers together with pricing, limits, and features.
|
|
1838
|
+
*
|
|
1839
|
+
* @returns Array of {@link SubscriptionTierInfo} objects.
|
|
1840
|
+
*
|
|
1841
|
+
* @example
|
|
1842
|
+
* ```typescript
|
|
1843
|
+
* const tiers = await subscription.getTiers();
|
|
1844
|
+
* console.log(tiers.map(t => `${t.name}: $${t.priceUsd}/mo`));
|
|
1845
|
+
* ```
|
|
1846
|
+
*/
|
|
1847
|
+
async getTiers() {
|
|
1848
|
+
try {
|
|
1849
|
+
return await this.http.get("/subscription/tiers");
|
|
1850
|
+
} catch (err) {
|
|
1851
|
+
const isNotFound = err != null && typeof err === "object" && "statusCode" in err && err.statusCode === 404;
|
|
1852
|
+
if (!isNotFound) throw err;
|
|
1853
|
+
const cfg = await this.http.get("/subscription/config");
|
|
1854
|
+
return (cfg.availableTiers || []).map((raw) => {
|
|
1855
|
+
const tier = raw.tier ?? raw.name;
|
|
1856
|
+
const displayName = raw.name ?? raw.displayName ?? String(tier);
|
|
1857
|
+
const monthlyPriceHsuite = Number(
|
|
1858
|
+
raw.monthlyPriceHsuite ?? raw.depositAmount ?? 0
|
|
1859
|
+
);
|
|
1860
|
+
const apiCallsPerDay = Number(raw.apiCallsPerDay ?? 0);
|
|
1861
|
+
const rawNetworks = raw.allowedNetworks ?? raw.supportedNetworks ?? [];
|
|
1862
|
+
const supportedNetworks = rawNetworks.some(
|
|
1863
|
+
(n) => n === "mainnet"
|
|
1864
|
+
) ? ["hedera", "xrpl"] : ["hedera"];
|
|
1865
|
+
return {
|
|
1866
|
+
name: tier,
|
|
1867
|
+
displayName,
|
|
1868
|
+
description: raw.description ?? `${displayName} tier`,
|
|
1869
|
+
priceUsd: Number(raw.priceUsd ?? 0),
|
|
1870
|
+
depositAmount: String(monthlyPriceHsuite),
|
|
1871
|
+
apiCallsPerDay: Number.isFinite(apiCallsPerDay) ? apiCallsPerDay : Number.MAX_SAFE_INTEGER,
|
|
1872
|
+
supportedNetworks,
|
|
1873
|
+
features: raw.features ?? []
|
|
1874
|
+
};
|
|
1875
|
+
});
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1878
|
+
/**
|
|
1879
|
+
* Get subscription configuration
|
|
1880
|
+
*/
|
|
1881
|
+
async getConfig() {
|
|
1882
|
+
return this.http.get("/subscription/config");
|
|
1883
|
+
}
|
|
1884
|
+
/**
|
|
1885
|
+
* List all subscriptions
|
|
1886
|
+
*/
|
|
1887
|
+
async list() {
|
|
1888
|
+
return this.http.get("/subscription/list");
|
|
1889
|
+
}
|
|
1890
|
+
/**
|
|
1891
|
+
* List subscriptions by status
|
|
1892
|
+
*/
|
|
1893
|
+
async listByStatus(status) {
|
|
1894
|
+
return this.http.get(`/subscription/list/status/${encodeURIComponent(status)}`);
|
|
1895
|
+
}
|
|
1896
|
+
/**
|
|
1897
|
+
* Get subscription balance
|
|
1898
|
+
*/
|
|
1899
|
+
async getBalance(appId) {
|
|
1900
|
+
return this.http.get(`/subscription/balance/${encodeURIComponent(appId)}`);
|
|
1901
|
+
}
|
|
1902
|
+
};
|
|
1903
|
+
|
|
1904
|
+
// src/tss/index.ts
|
|
1905
|
+
var TSSClient = class {
|
|
1906
|
+
constructor(http) {
|
|
1907
|
+
this.http = http;
|
|
1908
|
+
}
|
|
1909
|
+
http;
|
|
1910
|
+
/**
|
|
1911
|
+
* Create a multi-sig entity with TSS
|
|
1912
|
+
*/
|
|
1913
|
+
async createEntity(options) {
|
|
1914
|
+
return this.http.post("/tss/entity/create", options);
|
|
1915
|
+
}
|
|
1916
|
+
/**
|
|
1917
|
+
* Reshare keys when cluster membership changes.
|
|
1918
|
+
* Redistributes secret shares WITHOUT changing public keys.
|
|
1919
|
+
*/
|
|
1920
|
+
async reshareCluster(request) {
|
|
1921
|
+
return this.http.post("/tss/cluster/reshare", request);
|
|
1922
|
+
}
|
|
1923
|
+
/**
|
|
1924
|
+
* Get entity details by ID
|
|
1925
|
+
*/
|
|
1926
|
+
async getEntity(entityId) {
|
|
1927
|
+
return this.http.get(`/tss/entity/${encodeURIComponent(entityId)}`);
|
|
1928
|
+
}
|
|
1929
|
+
/**
|
|
1930
|
+
* Sign a transaction using MPC — chain-agnostic.
|
|
1931
|
+
* Routes to the correct chain backend based on the `chain` parameter.
|
|
1932
|
+
*
|
|
1933
|
+
* @param request - MPC signing request with chain, entityId, and transaction bytes
|
|
1934
|
+
*/
|
|
1935
|
+
async signMPC(request) {
|
|
1936
|
+
const chain = request.chain || "hedera";
|
|
1937
|
+
return this.http.post(`/tss/${encodeURIComponent(chain)}/sign-mpc`, request);
|
|
1938
|
+
}
|
|
1939
|
+
/**
|
|
1940
|
+
* Get known validators and their public keys
|
|
1941
|
+
*/
|
|
1942
|
+
async getValidators() {
|
|
1943
|
+
return this.http.get("/tss/validators");
|
|
1944
|
+
}
|
|
1945
|
+
/**
|
|
1946
|
+
* Force announcement of this node's public key
|
|
1947
|
+
*/
|
|
1948
|
+
async announceKey() {
|
|
1949
|
+
return this.http.post("/tss/announce", {});
|
|
1950
|
+
}
|
|
1951
|
+
/**
|
|
1952
|
+
* Get TSS statistics
|
|
1953
|
+
*/
|
|
1954
|
+
async getStats() {
|
|
1955
|
+
return this.http.get("/tss/stats");
|
|
1956
|
+
}
|
|
1957
|
+
/**
|
|
1958
|
+
* List all TSS entities
|
|
1959
|
+
*/
|
|
1960
|
+
async listEntities() {
|
|
1961
|
+
return this.http.get("/tss/entities");
|
|
1962
|
+
}
|
|
1963
|
+
/**
|
|
1964
|
+
* TSS health check
|
|
1965
|
+
*/
|
|
1966
|
+
async getHealth() {
|
|
1967
|
+
return this.http.get("/tss/health");
|
|
1968
|
+
}
|
|
1969
|
+
/**
|
|
1970
|
+
* List DKG ceremonies and their statistics
|
|
1971
|
+
*/
|
|
1972
|
+
async listCeremonies() {
|
|
1973
|
+
return this.http.get("/tss/multisig/ceremonies");
|
|
1974
|
+
}
|
|
1975
|
+
/**
|
|
1976
|
+
* Get multi-sig transaction status by transaction ID
|
|
1977
|
+
*/
|
|
1978
|
+
async getMultiSigStatus(txId) {
|
|
1979
|
+
return this.http.get(`/tss/multisig/transactions/${encodeURIComponent(txId)}`);
|
|
1980
|
+
}
|
|
1981
|
+
};
|
|
1982
|
+
|
|
1983
|
+
// src/ipfs/index.ts
|
|
1984
|
+
var IPFSClient = class {
|
|
1985
|
+
constructor(http) {
|
|
1986
|
+
this.http = http;
|
|
1987
|
+
}
|
|
1988
|
+
http;
|
|
1989
|
+
/**
|
|
1990
|
+
* Upload a file to IPFS
|
|
1991
|
+
*
|
|
1992
|
+
* @param file - File data as Blob or Buffer
|
|
1993
|
+
* @param filename - Name of the file
|
|
1994
|
+
* @param metadata - Optional metadata key-value pairs
|
|
1995
|
+
* @returns Upload result with CID
|
|
1996
|
+
*/
|
|
1997
|
+
async upload(file, filename, metadata) {
|
|
1998
|
+
return this.http.upload("/ipfs/upload", file, filename, metadata);
|
|
1999
|
+
}
|
|
2000
|
+
/**
|
|
2001
|
+
* Pin content by CID to ensure it persists
|
|
2002
|
+
*/
|
|
2003
|
+
async pin(cid) {
|
|
2004
|
+
return this.http.post(`/ipfs/pin/${encodeURIComponent(cid)}`, {});
|
|
2005
|
+
}
|
|
2006
|
+
/**
|
|
2007
|
+
* Unpin content by CID
|
|
2008
|
+
*/
|
|
2009
|
+
async unpin(cid) {
|
|
2010
|
+
return this.http.delete(`/ipfs/unpin/${encodeURIComponent(cid)}`);
|
|
2011
|
+
}
|
|
2012
|
+
/**
|
|
2013
|
+
* Get a file by CID
|
|
2014
|
+
*/
|
|
2015
|
+
async getFile(cid) {
|
|
2016
|
+
return this.http.get(`/ipfs/file/${encodeURIComponent(cid)}`);
|
|
2017
|
+
}
|
|
2018
|
+
/**
|
|
2019
|
+
* Get raw content by CID
|
|
2020
|
+
*/
|
|
2021
|
+
async getContent(cid) {
|
|
2022
|
+
return this.http.get(`/ipfs/${encodeURIComponent(cid)}`);
|
|
2023
|
+
}
|
|
2024
|
+
/**
|
|
2025
|
+
* Get file metadata by CID
|
|
2026
|
+
*/
|
|
2027
|
+
async getMetadata(cid) {
|
|
2028
|
+
return this.http.get(`/ipfs/metadata/${encodeURIComponent(cid)}`);
|
|
2029
|
+
}
|
|
2030
|
+
/**
|
|
2031
|
+
* List all pinned content
|
|
2032
|
+
*/
|
|
2033
|
+
async listPins() {
|
|
2034
|
+
return this.http.get("/ipfs/pins");
|
|
2035
|
+
}
|
|
2036
|
+
/**
|
|
2037
|
+
* Get IPFS node status
|
|
2038
|
+
*/
|
|
2039
|
+
async getStatus() {
|
|
2040
|
+
return this.http.get("/ipfs/status");
|
|
2041
|
+
}
|
|
2042
|
+
/**
|
|
2043
|
+
* Get storage usage information
|
|
2044
|
+
*/
|
|
2045
|
+
async getStorageUsage() {
|
|
2046
|
+
return this.http.get("/ipfs/storage");
|
|
2047
|
+
}
|
|
2048
|
+
};
|
|
2049
|
+
|
|
2050
|
+
// src/transactions/index.ts
|
|
2051
|
+
var TransactionsClient = class {
|
|
2052
|
+
constructor(http) {
|
|
2053
|
+
this.http = http;
|
|
2054
|
+
}
|
|
2055
|
+
http;
|
|
2056
|
+
/**
|
|
2057
|
+
* Get transaction preparation service info
|
|
2058
|
+
*/
|
|
2059
|
+
async getInfo() {
|
|
2060
|
+
return this.http.get("/info");
|
|
2061
|
+
}
|
|
2062
|
+
/**
|
|
2063
|
+
* Prepare a transfer transaction for local signing
|
|
2064
|
+
*/
|
|
2065
|
+
async prepareTransfer(request) {
|
|
2066
|
+
return this.http.post("/transfer/prepare", request);
|
|
2067
|
+
}
|
|
2068
|
+
/**
|
|
2069
|
+
* Prepare an NFT mint transaction
|
|
2070
|
+
*/
|
|
2071
|
+
async prepareNftMint(request) {
|
|
2072
|
+
return this.http.post("/nft/mint/prepare", request);
|
|
2073
|
+
}
|
|
2074
|
+
/**
|
|
2075
|
+
* Prepare an NFT burn transaction
|
|
2076
|
+
*/
|
|
2077
|
+
async prepareNftBurn(request) {
|
|
2078
|
+
return this.http.post("/nft/burn/prepare", request);
|
|
2079
|
+
}
|
|
2080
|
+
/**
|
|
2081
|
+
* Prepare an NFT transfer transaction
|
|
2082
|
+
*/
|
|
2083
|
+
async prepareNftTransfer(request) {
|
|
2084
|
+
return this.http.post("/nft/transfer/prepare", request);
|
|
2085
|
+
}
|
|
2086
|
+
/**
|
|
2087
|
+
* Prepare a token creation transaction
|
|
2088
|
+
*/
|
|
2089
|
+
async prepareTokenCreate(request) {
|
|
2090
|
+
return this.http.post("/token/create/prepare", request);
|
|
2091
|
+
}
|
|
2092
|
+
/**
|
|
2093
|
+
* Prepare a token mint transaction
|
|
2094
|
+
*/
|
|
2095
|
+
async prepareTokenMint(request) {
|
|
2096
|
+
return this.http.post("/token/mint/prepare", request);
|
|
2097
|
+
}
|
|
2098
|
+
/**
|
|
2099
|
+
* Prepare a fungible token burn transaction (Hedera).
|
|
2100
|
+
*
|
|
2101
|
+
* For NFT burn use `prepareNftBurn` with a serialNumber.
|
|
2102
|
+
*/
|
|
2103
|
+
async prepareTokenBurn(request) {
|
|
2104
|
+
return this.http.post("/token/burn/prepare", request);
|
|
2105
|
+
}
|
|
2106
|
+
/**
|
|
2107
|
+
* Prepare a token association transaction
|
|
2108
|
+
*/
|
|
2109
|
+
async prepareTokenAssociation(request) {
|
|
2110
|
+
return this.http.post("/token/associate/prepare", request);
|
|
2111
|
+
}
|
|
2112
|
+
// ── Capability actions (Hedera) ──────────────────────────────────────
|
|
2113
|
+
/** Prepare a token pause transaction (capability: pausable). */
|
|
2114
|
+
async prepareTokenPause(request) {
|
|
2115
|
+
return this.http.post("/token/pause/prepare", request);
|
|
2116
|
+
}
|
|
2117
|
+
/** Prepare a token unpause transaction (capability: pausable). */
|
|
2118
|
+
async prepareTokenUnpause(request) {
|
|
2119
|
+
return this.http.post("/token/unpause/prepare", request);
|
|
2120
|
+
}
|
|
2121
|
+
/**
|
|
2122
|
+
* Prepare a token restrict (freeze account) transaction (capability:
|
|
2123
|
+
* restrictable). Freezes `accountId` from transacting `tokenId`.
|
|
2124
|
+
*/
|
|
2125
|
+
async prepareTokenRestrict(request) {
|
|
2126
|
+
return this.http.post("/token/restrict/prepare", request);
|
|
2127
|
+
}
|
|
2128
|
+
/** Prepare a token unrestrict (unfreeze account) transaction. */
|
|
2129
|
+
async prepareTokenUnrestrict(request) {
|
|
2130
|
+
return this.http.post("/token/unrestrict/prepare", request);
|
|
2131
|
+
}
|
|
2132
|
+
/**
|
|
2133
|
+
* Prepare a token compliance-enable (grant KYC) transaction (capability:
|
|
2134
|
+
* compliant).
|
|
2135
|
+
*/
|
|
2136
|
+
async prepareTokenComplianceEnable(request) {
|
|
2137
|
+
return this.http.post("/token/compliance/enable/prepare", request);
|
|
2138
|
+
}
|
|
2139
|
+
/** Prepare a token compliance-disable (revoke KYC) transaction. */
|
|
2140
|
+
async prepareTokenComplianceDisable(request) {
|
|
2141
|
+
return this.http.post("/token/compliance/disable/prepare", request);
|
|
2142
|
+
}
|
|
2143
|
+
/**
|
|
2144
|
+
* Prepare a token wipe transaction (capability: wipeable). Force-removes
|
|
2145
|
+
* `amount` of `tokenId` from `accountId`.
|
|
2146
|
+
*/
|
|
2147
|
+
async prepareTokenWipe(request) {
|
|
2148
|
+
return this.http.post("/token/wipe/prepare", request);
|
|
2149
|
+
}
|
|
2150
|
+
/**
|
|
2151
|
+
* Prepare a topic creation transaction
|
|
2152
|
+
*/
|
|
2153
|
+
async prepareTopicCreate(request) {
|
|
2154
|
+
return this.http.post("/topic/create/prepare", request);
|
|
2155
|
+
}
|
|
2156
|
+
/**
|
|
2157
|
+
* Prepare a topic message submission transaction
|
|
2158
|
+
*/
|
|
2159
|
+
async prepareTopicMessage(request) {
|
|
2160
|
+
return this.http.post("/topic/message/prepare", request);
|
|
2161
|
+
}
|
|
2162
|
+
/**
|
|
2163
|
+
* Prepare a trust line transaction (e.g. XRPL trust lines)
|
|
2164
|
+
*/
|
|
2165
|
+
async prepareTrustLine(request) {
|
|
2166
|
+
return this.http.post("/trustline/prepare", request);
|
|
2167
|
+
}
|
|
2168
|
+
};
|
|
2169
|
+
|
|
2170
|
+
// src/snapshots/index.ts
|
|
2171
|
+
var SnapshotsClient = class {
|
|
2172
|
+
constructor(http) {
|
|
2173
|
+
this.http = http;
|
|
2174
|
+
}
|
|
2175
|
+
http;
|
|
2176
|
+
/**
|
|
2177
|
+
* Generate a new snapshot for a token
|
|
2178
|
+
*
|
|
2179
|
+
* @param tokenId - Token identifier
|
|
2180
|
+
* @param options - Generation options (format, filters)
|
|
2181
|
+
*/
|
|
2182
|
+
async generate(tokenId, options) {
|
|
2183
|
+
return this.http.post(
|
|
2184
|
+
`/snapshots/generate/${encodeURIComponent(tokenId)}`,
|
|
2185
|
+
options || {}
|
|
2186
|
+
);
|
|
2187
|
+
}
|
|
2188
|
+
/**
|
|
2189
|
+
* Get snapshot details by ID
|
|
2190
|
+
*/
|
|
2191
|
+
async get(snapshotId) {
|
|
2192
|
+
return this.http.get(`/snapshots/${encodeURIComponent(snapshotId)}`);
|
|
2193
|
+
}
|
|
2194
|
+
/**
|
|
2195
|
+
* List snapshots for a token
|
|
2196
|
+
*/
|
|
2197
|
+
async listByToken(tokenId, pagination) {
|
|
2198
|
+
const params = new URLSearchParams();
|
|
2199
|
+
if (pagination?.page !== void 0) params.set("page", String(pagination.page));
|
|
2200
|
+
if (pagination?.limit !== void 0) params.set("limit", String(pagination.limit));
|
|
2201
|
+
const qs = params.toString();
|
|
2202
|
+
return this.http.get(
|
|
2203
|
+
`/snapshots/token/${encodeURIComponent(tokenId)}${qs ? `?${qs}` : ""}`
|
|
2204
|
+
);
|
|
2205
|
+
}
|
|
2206
|
+
/**
|
|
2207
|
+
* Download snapshot data
|
|
2208
|
+
*
|
|
2209
|
+
* @param snapshotId - Snapshot ID
|
|
2210
|
+
* @param format - Output format (json or csv)
|
|
2211
|
+
*/
|
|
2212
|
+
async download(snapshotId, format) {
|
|
2213
|
+
const params = format ? `?format=${encodeURIComponent(format)}` : "";
|
|
2214
|
+
return this.http.get(
|
|
2215
|
+
`/snapshots/${encodeURIComponent(snapshotId)}/download${params}`
|
|
2216
|
+
);
|
|
2217
|
+
}
|
|
2218
|
+
};
|
|
2219
|
+
|
|
2220
|
+
// src/settlement/index.ts
|
|
2221
|
+
var settlement_exports = {};
|
|
2222
|
+
__export(settlement_exports, {
|
|
2223
|
+
SettlementClient: () => SettlementClient
|
|
2224
|
+
});
|
|
2225
|
+
var SettlementClient = class {
|
|
2226
|
+
constructor(http) {
|
|
2227
|
+
this.http = http;
|
|
2228
|
+
}
|
|
2229
|
+
http;
|
|
2230
|
+
/**
|
|
2231
|
+
* Initiate a new settlement operation.
|
|
2232
|
+
* Submits source-chain payment details and begins the conversion pipeline.
|
|
2233
|
+
*/
|
|
2234
|
+
async initiate(request) {
|
|
2235
|
+
return this.http.post("/settlement/initiate", request);
|
|
2236
|
+
}
|
|
2237
|
+
/**
|
|
2238
|
+
* Get the current status of a settlement by ID.
|
|
2239
|
+
*/
|
|
2240
|
+
async getStatus(settlementId) {
|
|
2241
|
+
return this.http.get(`/settlement/${encodeURIComponent(settlementId)}/status`);
|
|
2242
|
+
}
|
|
2243
|
+
/**
|
|
2244
|
+
* Confirm that XRP has landed on the destination address.
|
|
2245
|
+
* Advances the settlement to the next processing step.
|
|
2246
|
+
*/
|
|
2247
|
+
async confirmXrpLanded(settlementId) {
|
|
2248
|
+
return this.http.post(`/settlement/${encodeURIComponent(settlementId)}/confirm-xrp`, {});
|
|
2249
|
+
}
|
|
2250
|
+
/**
|
|
2251
|
+
* Get settlement history for a given entity.
|
|
2252
|
+
*/
|
|
2253
|
+
async getHistory(entityId) {
|
|
2254
|
+
return this.http.get(`/settlement/history/${encodeURIComponent(entityId)}`);
|
|
2255
|
+
}
|
|
2256
|
+
};
|
|
2257
|
+
|
|
2258
|
+
// src/client.ts
|
|
2259
|
+
var SmartEngineClient = class _SmartEngineClient {
|
|
2260
|
+
baseUrl;
|
|
2261
|
+
allowInsecure;
|
|
2262
|
+
http;
|
|
2263
|
+
/** Separate HTTP client for /api/transactions (non-v3 base path) */
|
|
2264
|
+
txHttp;
|
|
2265
|
+
/** Last HTTP error (for getHttpHealth) */
|
|
2266
|
+
lastHttpError;
|
|
2267
|
+
// ========== Sub-Clients ==========
|
|
2268
|
+
/** Application subscription management */
|
|
2269
|
+
subscription;
|
|
2270
|
+
/** Threshold Signature Scheme — chain-agnostic MPC operations */
|
|
2271
|
+
tss;
|
|
2272
|
+
/** IPFS decentralized file storage */
|
|
2273
|
+
ipfs;
|
|
2274
|
+
/** Transaction preparation for local signing (sovereignty model) */
|
|
2275
|
+
transactions;
|
|
2276
|
+
/** Token holder snapshot generation and retrieval */
|
|
2277
|
+
snapshots;
|
|
2278
|
+
/** Cross-chain settlement operations */
|
|
2279
|
+
settlement;
|
|
2280
|
+
constructor(config) {
|
|
2281
|
+
this.allowInsecure = config.allowInsecure ?? false;
|
|
2282
|
+
this.baseUrl = validateClientUrl(config.baseUrl, this.allowInsecure);
|
|
2283
|
+
this.http = createHttpClient({
|
|
2284
|
+
baseUrl: `${this.baseUrl}/api/v3`,
|
|
2285
|
+
apiKey: config.apiKey,
|
|
2286
|
+
authToken: config.authToken,
|
|
2287
|
+
timeout: config.timeout
|
|
2288
|
+
});
|
|
2289
|
+
this.txHttp = createHttpClient({
|
|
2290
|
+
baseUrl: `${this.baseUrl}/api/transactions`,
|
|
2291
|
+
apiKey: config.apiKey,
|
|
2292
|
+
authToken: config.authToken,
|
|
2293
|
+
timeout: config.timeout
|
|
2294
|
+
});
|
|
2295
|
+
this.subscription = new SubscriptionClient(this.http);
|
|
2296
|
+
this.tss = new TSSClient(this.http);
|
|
2297
|
+
this.ipfs = new IPFSClient(this.http);
|
|
2298
|
+
this.transactions = new TransactionsClient(this.txHttp);
|
|
2299
|
+
this.snapshots = new SnapshotsClient(this.http);
|
|
2300
|
+
this.settlement = new SettlementClient(this.http);
|
|
2301
|
+
}
|
|
2302
|
+
/**
|
|
2303
|
+
* Connect to the smart-engines network with auto-discovery and authentication
|
|
2304
|
+
*
|
|
2305
|
+
* This method:
|
|
2306
|
+
* 1. Discovers validators via HCS registry topic
|
|
2307
|
+
* 2. Selects a random validator with API endpoint
|
|
2308
|
+
* 3. Authenticates with Web3-style challenge-response
|
|
2309
|
+
* 4. Returns a configured client ready to use
|
|
2310
|
+
*/
|
|
2311
|
+
static async connectToNetwork(config) {
|
|
2312
|
+
const allowInsecure = config.allowInsecure ?? false;
|
|
2313
|
+
const discovery = new ValidatorDiscoveryClient({
|
|
2314
|
+
network: config.network,
|
|
2315
|
+
registryTopicId: config.registryTopicId,
|
|
2316
|
+
mirrorNodeUrl: config.mirrorNodeUrl
|
|
2317
|
+
});
|
|
2318
|
+
const validator = await discovery.getRandomValidator();
|
|
2319
|
+
if (!validator || !validator.networkEndpoints?.apiEndpoint) {
|
|
2320
|
+
throw new SmartEngineError2(
|
|
2321
|
+
"No validators available. Check registry topic and network configuration.",
|
|
2322
|
+
503
|
|
2323
|
+
);
|
|
2324
|
+
}
|
|
2325
|
+
const validatorUrl = validator.networkEndpoints.apiEndpoint;
|
|
2326
|
+
validateClientUrl(validatorUrl, allowInsecure);
|
|
2327
|
+
const auth = new ValidatorAuthClient({
|
|
2328
|
+
security: { allowInsecure }
|
|
2329
|
+
});
|
|
2330
|
+
const session = await auth.authenticateWithSigner(
|
|
2331
|
+
validatorUrl,
|
|
2332
|
+
config.chain,
|
|
2333
|
+
config.address,
|
|
2334
|
+
config.publicKey,
|
|
2335
|
+
config.signFn,
|
|
2336
|
+
config.metadata
|
|
2337
|
+
);
|
|
2338
|
+
const client = new _SmartEngineClient({
|
|
2339
|
+
baseUrl: validatorUrl,
|
|
2340
|
+
authToken: session.token,
|
|
2341
|
+
allowInsecure
|
|
2342
|
+
});
|
|
2343
|
+
return { client, validator, session };
|
|
2344
|
+
}
|
|
2345
|
+
/** Get the current validator URL */
|
|
2346
|
+
getBaseUrl() {
|
|
2347
|
+
return this.baseUrl;
|
|
2348
|
+
}
|
|
2349
|
+
/** Check if client has an auth token */
|
|
2350
|
+
isAuthenticated() {
|
|
2351
|
+
return !!this.http.authToken;
|
|
2352
|
+
}
|
|
2353
|
+
/**
|
|
2354
|
+
* Get HTTP resilience health information
|
|
2355
|
+
* @returns Object with circuit breaker state and last error (if any)
|
|
2356
|
+
*/
|
|
2357
|
+
getHttpHealth() {
|
|
2358
|
+
return {
|
|
2359
|
+
breaker: null,
|
|
2360
|
+
lastError: this.lastHttpError
|
|
2361
|
+
};
|
|
2362
|
+
}
|
|
2363
|
+
// ========== Health & Info ==========
|
|
2364
|
+
/** Get health status of the validator */
|
|
2365
|
+
async getHealth() {
|
|
2366
|
+
return this.http.get("/health");
|
|
2367
|
+
}
|
|
2368
|
+
/** Get list of supported chains */
|
|
2369
|
+
async getSupportedChains() {
|
|
2370
|
+
return this.http.get("/chains");
|
|
2371
|
+
}
|
|
2372
|
+
// ========== Account Operations ==========
|
|
2373
|
+
/** Create a new account on the specified chain */
|
|
2374
|
+
async createAccount(request) {
|
|
2375
|
+
const validated = CreateAccountRequestSchema.parse(request);
|
|
2376
|
+
return this.http.post("/accounts", validated);
|
|
2377
|
+
}
|
|
2378
|
+
/** Get account information */
|
|
2379
|
+
async getAccountInfo(chain, accountId) {
|
|
2380
|
+
return this.http.get(`/accounts/${encodePathParam(chain)}/${encodePathParam(accountId)}`);
|
|
2381
|
+
}
|
|
2382
|
+
/** Get account balance */
|
|
2383
|
+
async getBalance(chain, accountId) {
|
|
2384
|
+
return this.http.get(`/accounts/${encodePathParam(chain)}/${encodePathParam(accountId)}/balance`);
|
|
2385
|
+
}
|
|
2386
|
+
// ========== Transaction Operations ==========
|
|
2387
|
+
/** Execute a transfer transaction */
|
|
2388
|
+
async transfer(request) {
|
|
2389
|
+
const validated = TransferRequestSchema.parse(request);
|
|
2390
|
+
return this.http.post("/transfer", validated);
|
|
2391
|
+
}
|
|
2392
|
+
/** Get transaction details */
|
|
2393
|
+
async getTransaction(chain, txId) {
|
|
2394
|
+
return this.http.get(`/transactions/${encodePathParam(chain)}/${encodePathParam(txId)}`);
|
|
2395
|
+
}
|
|
2396
|
+
/** Get transaction receipt */
|
|
2397
|
+
async getTransactionReceipt(chain, txId) {
|
|
2398
|
+
return this.http.get(`/transactions/${encodePathParam(chain)}/${encodePathParam(txId)}/receipt`);
|
|
2399
|
+
}
|
|
2400
|
+
// ========== Token Operations ==========
|
|
2401
|
+
/** Create a new token */
|
|
2402
|
+
async createToken(request) {
|
|
2403
|
+
const validated = CreateTokenRequestSchema.parse(request);
|
|
2404
|
+
return this.http.post("/tokens", validated);
|
|
2405
|
+
}
|
|
2406
|
+
/** Mint tokens */
|
|
2407
|
+
async mintToken(request) {
|
|
2408
|
+
const validated = MintTokenRequestSchema.parse(request);
|
|
2409
|
+
return this.http.post("/tokens/mint", validated);
|
|
2410
|
+
}
|
|
2411
|
+
/** Get token information */
|
|
2412
|
+
async getTokenInfo(chain, tokenId) {
|
|
2413
|
+
return this.http.get(`/tokens/${encodePathParam(chain)}/${encodePathParam(tokenId)}`);
|
|
2414
|
+
}
|
|
2415
|
+
/** Burn tokens to reduce supply */
|
|
2416
|
+
async burnToken(request) {
|
|
2417
|
+
const validated = BurnTokenRequestSchema.parse(request);
|
|
2418
|
+
return this.http.post("/tokens/burn", validated);
|
|
2419
|
+
}
|
|
2420
|
+
/** Pause all token operations globally */
|
|
2421
|
+
async pauseToken(request) {
|
|
2422
|
+
const validated = TokenActionRequestSchema.parse(request);
|
|
2423
|
+
return this.http.post("/tokens/pause", validated);
|
|
2424
|
+
}
|
|
2425
|
+
/** Unpause token operations */
|
|
2426
|
+
async unpauseToken(request) {
|
|
2427
|
+
const validated = TokenActionRequestSchema.parse(request);
|
|
2428
|
+
return this.http.post("/tokens/unpause", validated);
|
|
2429
|
+
}
|
|
2430
|
+
/** Freeze/restrict an account from transacting the token */
|
|
2431
|
+
async restrictAccount(request) {
|
|
2432
|
+
const validated = TokenActionRequestSchema.parse(request);
|
|
2433
|
+
if (!validated.accountId) {
|
|
2434
|
+
throw new SmartEngineError2("accountId is required for restrictAccount", 400);
|
|
2435
|
+
}
|
|
2436
|
+
return this.http.post("/tokens/restrict", validated);
|
|
2437
|
+
}
|
|
2438
|
+
/** Unfreeze an account */
|
|
2439
|
+
async unrestrictAccount(request) {
|
|
2440
|
+
const validated = TokenActionRequestSchema.parse(request);
|
|
2441
|
+
if (!validated.accountId) {
|
|
2442
|
+
throw new SmartEngineError2("accountId is required for unrestrictAccount", 400);
|
|
2443
|
+
}
|
|
2444
|
+
return this.http.post("/tokens/unrestrict", validated);
|
|
2445
|
+
}
|
|
2446
|
+
/** Grant KYC/compliance approval to an account */
|
|
2447
|
+
async enableCompliance(request) {
|
|
2448
|
+
const validated = TokenActionRequestSchema.parse(request);
|
|
2449
|
+
if (!validated.accountId) {
|
|
2450
|
+
throw new SmartEngineError2("accountId is required for enableCompliance", 400);
|
|
2451
|
+
}
|
|
2452
|
+
return this.http.post("/tokens/compliance/enable", validated);
|
|
2453
|
+
}
|
|
2454
|
+
/** Revoke KYC/compliance approval from an account */
|
|
2455
|
+
async disableCompliance(request) {
|
|
2456
|
+
const validated = TokenActionRequestSchema.parse(request);
|
|
2457
|
+
if (!validated.accountId) {
|
|
2458
|
+
throw new SmartEngineError2("accountId is required for disableCompliance", 400);
|
|
2459
|
+
}
|
|
2460
|
+
return this.http.post("/tokens/compliance/disable", validated);
|
|
2461
|
+
}
|
|
2462
|
+
/** Force remove tokens from an account (compliance action) */
|
|
2463
|
+
async wipeFromAccount(request) {
|
|
2464
|
+
const validated = TokenActionRequestSchema.parse(request);
|
|
2465
|
+
if (!validated.accountId) {
|
|
2466
|
+
throw new SmartEngineError2("accountId is required for wipeFromAccount", 400);
|
|
2467
|
+
}
|
|
2468
|
+
if (!validated.amount) {
|
|
2469
|
+
throw new SmartEngineError2("amount is required for wipeFromAccount", 400);
|
|
2470
|
+
}
|
|
2471
|
+
return this.http.post("/tokens/wipe", validated);
|
|
2472
|
+
}
|
|
2473
|
+
// ========== Capabilities Discovery ==========
|
|
2474
|
+
/** Get capability support matrix for all chains */
|
|
2475
|
+
async getAllCapabilities() {
|
|
2476
|
+
return this.http.get("/capabilities");
|
|
2477
|
+
}
|
|
2478
|
+
/** Get capability support for a specific chain */
|
|
2479
|
+
async getChainCapabilities(chain) {
|
|
2480
|
+
return this.http.get(`/capabilities/${encodePathParam(chain)}`);
|
|
2481
|
+
}
|
|
2482
|
+
/** Get comprehensive system status */
|
|
2483
|
+
async getSystemStatus() {
|
|
2484
|
+
return this.http.get("/status");
|
|
2485
|
+
}
|
|
2486
|
+
// ========== Messaging Operations ==========
|
|
2487
|
+
/** Submit a message to consensus */
|
|
2488
|
+
async submitMessage(chain, topicId, message) {
|
|
2489
|
+
if (message.length > 1024 * 1024) {
|
|
2490
|
+
throw new SmartEngineError2("Message too large (max 1MB)", 400);
|
|
2491
|
+
}
|
|
2492
|
+
return this.http.post(`/messages/${encodePathParam(chain)}/${encodePathParam(topicId)}`, {
|
|
2493
|
+
message
|
|
2494
|
+
});
|
|
2495
|
+
}
|
|
2496
|
+
// ========== Cluster Operations ==========
|
|
2497
|
+
/** Get cluster health status */
|
|
2498
|
+
async getClusterHealth() {
|
|
2499
|
+
return this.http.get("/cluster/health");
|
|
2500
|
+
}
|
|
2501
|
+
/** Get cluster status including node details */
|
|
2502
|
+
async getClusterStatus() {
|
|
2503
|
+
return this.http.get("/cluster/status");
|
|
2504
|
+
}
|
|
2505
|
+
// ========== Metrics & Monitoring ==========
|
|
2506
|
+
/** Get Prometheus-format metrics */
|
|
2507
|
+
async getMetrics() {
|
|
2508
|
+
return this.http.get("/metrics");
|
|
2509
|
+
}
|
|
2510
|
+
/** Get queue statistics for monitoring */
|
|
2511
|
+
async getQueueStats() {
|
|
2512
|
+
return this.http.get("/monitoring/queue");
|
|
2513
|
+
}
|
|
2514
|
+
/** Get circuit breaker status for all services */
|
|
2515
|
+
async getCircuitBreakerStatus() {
|
|
2516
|
+
return this.http.get("/monitoring/circuit-breakers");
|
|
2517
|
+
}
|
|
2518
|
+
// ========== Signature Verification ==========
|
|
2519
|
+
/** Verify an arbitrary signature */
|
|
2520
|
+
async verifySignature(request) {
|
|
2521
|
+
return this.http.post("/auth/verify-signature", request);
|
|
2522
|
+
}
|
|
2523
|
+
};
|
|
2524
|
+
var SmartEngineError2 = class extends Error {
|
|
2525
|
+
constructor(message, statusCode, details) {
|
|
2526
|
+
super(message);
|
|
2527
|
+
this.statusCode = statusCode;
|
|
2528
|
+
this.details = details;
|
|
2529
|
+
this.name = "SmartEngineError";
|
|
2530
|
+
}
|
|
2531
|
+
statusCode;
|
|
2532
|
+
details;
|
|
2533
|
+
};
|
|
2534
|
+
function validateClientUrl(url, allowInsecure = false) {
|
|
2535
|
+
try {
|
|
2536
|
+
const parsed = new URL(url);
|
|
2537
|
+
if (!["http:", "https:"].includes(parsed.protocol)) {
|
|
2538
|
+
throw new SmartEngineError2(`Invalid protocol: ${parsed.protocol}`, 400);
|
|
2539
|
+
}
|
|
2540
|
+
if (!allowInsecure && parsed.protocol !== "https:") {
|
|
2541
|
+
throw new SmartEngineError2(
|
|
2542
|
+
"HTTPS is required for secure connections. Set allowInsecure=true for local development.",
|
|
2543
|
+
400
|
|
2544
|
+
);
|
|
2545
|
+
}
|
|
2546
|
+
return parsed.origin;
|
|
2547
|
+
} catch (error) {
|
|
2548
|
+
if (error instanceof SmartEngineError2) throw error;
|
|
2549
|
+
throw new SmartEngineError2(`Invalid URL: ${url}`, 400);
|
|
2550
|
+
}
|
|
2551
|
+
}
|
|
2552
|
+
|
|
2553
|
+
// src/gateway/routing/index.ts
|
|
2554
|
+
var RoutingClient = class {
|
|
2555
|
+
constructor(http) {
|
|
2556
|
+
this.http = http;
|
|
2557
|
+
}
|
|
2558
|
+
http;
|
|
2559
|
+
/** Register a new host */
|
|
2560
|
+
async registerHost(request) {
|
|
2561
|
+
return this.http.post("/routing/hosts", request);
|
|
2562
|
+
}
|
|
2563
|
+
/** Unregister a host */
|
|
2564
|
+
async unregisterHost(hostId) {
|
|
2565
|
+
return this.http.delete(`/routing/hosts/${encodeURIComponent(hostId)}`);
|
|
2566
|
+
}
|
|
2567
|
+
/** Get all registered hosts */
|
|
2568
|
+
async getAllHosts() {
|
|
2569
|
+
return this.http.get("/routing/hosts");
|
|
2570
|
+
}
|
|
2571
|
+
/** Get only verified hosts */
|
|
2572
|
+
async getVerifiedHosts() {
|
|
2573
|
+
return this.http.get("/routing/hosts/verified");
|
|
2574
|
+
}
|
|
2575
|
+
/** Get a specific host by ID */
|
|
2576
|
+
async getHost(hostId) {
|
|
2577
|
+
return this.http.get(`/routing/hosts/${encodeURIComponent(hostId)}`);
|
|
2578
|
+
}
|
|
2579
|
+
/** Verify a host */
|
|
2580
|
+
async verifyHost(hostId) {
|
|
2581
|
+
return this.http.post(`/routing/hosts/${encodeURIComponent(hostId)}/verify`, {});
|
|
2582
|
+
}
|
|
2583
|
+
/** Set routing configuration for an app */
|
|
2584
|
+
async setRoutingConfig(appId, config) {
|
|
2585
|
+
return this.http.put(`/routing/config/${encodeURIComponent(appId)}`, config);
|
|
2586
|
+
}
|
|
2587
|
+
/** Get routing configuration for an app */
|
|
2588
|
+
async getRoutingConfig(appId) {
|
|
2589
|
+
return this.http.get(`/routing/config/${encodeURIComponent(appId)}`);
|
|
2590
|
+
}
|
|
2591
|
+
/** Proxy a request through the gateway */
|
|
2592
|
+
async proxyRequest(request) {
|
|
2593
|
+
return this.http.post("/routing/proxy", request);
|
|
2594
|
+
}
|
|
2595
|
+
/** Get routing statistics */
|
|
2596
|
+
async getStats() {
|
|
2597
|
+
return this.http.get("/routing/stats");
|
|
2598
|
+
}
|
|
2599
|
+
/** Map a domain to an application */
|
|
2600
|
+
async mapDomainToApp(domain, appId) {
|
|
2601
|
+
return this.http.post(`/routing/domains/${encodeURIComponent(domain)}/map`, { appId });
|
|
2602
|
+
}
|
|
2603
|
+
};
|
|
2604
|
+
|
|
2605
|
+
// src/gateway/domains/index.ts
|
|
2606
|
+
var DomainsClient = class {
|
|
2607
|
+
constructor(http) {
|
|
2608
|
+
this.http = http;
|
|
2609
|
+
}
|
|
2610
|
+
http;
|
|
2611
|
+
/** Register a new domain */
|
|
2612
|
+
async register(request) {
|
|
2613
|
+
return this.http.post("/domains", request);
|
|
2614
|
+
}
|
|
2615
|
+
/** Check domain availability */
|
|
2616
|
+
async checkAvailability(domain) {
|
|
2617
|
+
return this.http.get(`/domains/check/${encodeURIComponent(domain)}`);
|
|
2618
|
+
}
|
|
2619
|
+
/** Get domain information */
|
|
2620
|
+
async getInfo(domain) {
|
|
2621
|
+
return this.http.get(`/domains/${encodeURIComponent(domain)}`);
|
|
2622
|
+
}
|
|
2623
|
+
/** List domains, optionally filtered by owner */
|
|
2624
|
+
async list(owner) {
|
|
2625
|
+
const params = owner ? `?owner=${encodeURIComponent(owner)}` : "";
|
|
2626
|
+
return this.http.get(`/domains${params}`);
|
|
2627
|
+
}
|
|
2628
|
+
/** Generate a verification token */
|
|
2629
|
+
async generateVerificationToken(domain, method) {
|
|
2630
|
+
return this.http.post(`/domains/${encodeURIComponent(domain)}/verification`, { method });
|
|
2631
|
+
}
|
|
2632
|
+
/** Verify domain ownership */
|
|
2633
|
+
async verifyOwnership(domain, token) {
|
|
2634
|
+
return this.http.post(`/domains/${encodeURIComponent(domain)}/verify`, { token });
|
|
2635
|
+
}
|
|
2636
|
+
/** Configure DNS records for a domain */
|
|
2637
|
+
async configureDns(domain, records) {
|
|
2638
|
+
return this.http.post(`/domains/${encodeURIComponent(domain)}/dns`, { records });
|
|
2639
|
+
}
|
|
2640
|
+
/** Enable DNSSEC for a domain */
|
|
2641
|
+
async enableDnssec(domain) {
|
|
2642
|
+
return this.http.post(`/domains/${encodeURIComponent(domain)}/dnssec/enable`, {});
|
|
2643
|
+
}
|
|
2644
|
+
/** Disable DNSSEC for a domain */
|
|
2645
|
+
async disableDnssec(domain) {
|
|
2646
|
+
return this.http.post(`/domains/${encodeURIComponent(domain)}/dnssec/disable`, {});
|
|
2647
|
+
}
|
|
2648
|
+
/** Renew a domain */
|
|
2649
|
+
async renew(domain, years) {
|
|
2650
|
+
return this.http.post(`/domains/${encodeURIComponent(domain)}/renew`, { years: years ?? 1 });
|
|
2651
|
+
}
|
|
2652
|
+
/** Initiate a domain transfer */
|
|
2653
|
+
async transfer(domain, request) {
|
|
2654
|
+
return this.http.post(`/domains/${encodeURIComponent(domain)}/transfer`, request);
|
|
2655
|
+
}
|
|
2656
|
+
/** Approve a pending domain transfer */
|
|
2657
|
+
async approveTransfer(domain) {
|
|
2658
|
+
return this.http.post(`/domains/${encodeURIComponent(domain)}/transfer/approve`, {});
|
|
2659
|
+
}
|
|
2660
|
+
/** Reject a pending domain transfer */
|
|
2661
|
+
async rejectTransfer(domain) {
|
|
2662
|
+
return this.http.post(`/domains/${encodeURIComponent(domain)}/transfer/reject`, {});
|
|
2663
|
+
}
|
|
2664
|
+
/** Suspend a domain */
|
|
2665
|
+
async suspend(domain, reason) {
|
|
2666
|
+
return this.http.post(`/domains/${encodeURIComponent(domain)}/suspend`, { reason });
|
|
2667
|
+
}
|
|
2668
|
+
/** Unsuspend a domain */
|
|
2669
|
+
async unsuspend(domain) {
|
|
2670
|
+
return this.http.post(`/domains/${encodeURIComponent(domain)}/unsuspend`, {});
|
|
2671
|
+
}
|
|
2672
|
+
};
|
|
2673
|
+
|
|
2674
|
+
// src/gateway/dns/index.ts
|
|
2675
|
+
var DnsClient = class {
|
|
2676
|
+
constructor(http) {
|
|
2677
|
+
this.http = http;
|
|
2678
|
+
}
|
|
2679
|
+
http;
|
|
2680
|
+
/** Resolve a DNS name */
|
|
2681
|
+
async resolve(name, type, dnssec) {
|
|
2682
|
+
const params = new URLSearchParams({ name });
|
|
2683
|
+
if (type) params.set("type", type);
|
|
2684
|
+
if (dnssec !== void 0) params.set("dnssec", String(dnssec));
|
|
2685
|
+
return this.http.get(`/dns/resolve?${params.toString()}`);
|
|
2686
|
+
}
|
|
2687
|
+
/** Batch resolve multiple DNS queries */
|
|
2688
|
+
async resolveBatch(queries) {
|
|
2689
|
+
return this.http.post("/dns/resolve/batch", { queries });
|
|
2690
|
+
}
|
|
2691
|
+
/** List all DNS zones */
|
|
2692
|
+
async listZones() {
|
|
2693
|
+
return this.http.get("/dns/zones");
|
|
2694
|
+
}
|
|
2695
|
+
/** Get a specific zone */
|
|
2696
|
+
async getZone(zoneName) {
|
|
2697
|
+
return this.http.get(`/dns/zones/${encodeURIComponent(zoneName)}`);
|
|
2698
|
+
}
|
|
2699
|
+
/** Create a new DNS zone */
|
|
2700
|
+
async createZone(request) {
|
|
2701
|
+
return this.http.post("/dns/zones", request);
|
|
2702
|
+
}
|
|
2703
|
+
/** Delete a DNS zone */
|
|
2704
|
+
async deleteZone(zoneName) {
|
|
2705
|
+
return this.http.delete(`/dns/zones/${encodeURIComponent(zoneName)}`);
|
|
2706
|
+
}
|
|
2707
|
+
/** Add a record to a zone */
|
|
2708
|
+
async addRecord(zoneName, record) {
|
|
2709
|
+
return this.http.post(`/dns/zones/${encodeURIComponent(zoneName)}/records`, record);
|
|
2710
|
+
}
|
|
2711
|
+
/** Update a record in a zone */
|
|
2712
|
+
async updateRecord(zoneName, recordId, updates) {
|
|
2713
|
+
return this.http.put(
|
|
2714
|
+
`/dns/zones/${encodeURIComponent(zoneName)}/records/${encodeURIComponent(recordId)}`,
|
|
2715
|
+
updates
|
|
2716
|
+
);
|
|
2717
|
+
}
|
|
2718
|
+
/** Delete a record from a zone */
|
|
2719
|
+
async deleteRecord(zoneName, recordId) {
|
|
2720
|
+
return this.http.delete(
|
|
2721
|
+
`/dns/zones/${encodeURIComponent(zoneName)}/records/${encodeURIComponent(recordId)}`
|
|
2722
|
+
);
|
|
2723
|
+
}
|
|
2724
|
+
/** Generate DNSSEC keys for a zone */
|
|
2725
|
+
async generateDnssecKeys(zoneName, algorithm) {
|
|
2726
|
+
return this.http.post(`/dns/zones/${encodeURIComponent(zoneName)}/dnssec/keys`, {
|
|
2727
|
+
algorithm
|
|
2728
|
+
});
|
|
2729
|
+
}
|
|
2730
|
+
/** Get DNSSEC keys for a zone */
|
|
2731
|
+
async getDnssecKeys(zoneName) {
|
|
2732
|
+
return this.http.get(`/dns/zones/${encodeURIComponent(zoneName)}/dnssec/keys`);
|
|
2733
|
+
}
|
|
2734
|
+
/** Get DS record for a zone (for registrar configuration) */
|
|
2735
|
+
async getDsRecord(zoneName) {
|
|
2736
|
+
return this.http.get(`/dns/zones/${encodeURIComponent(zoneName)}/dnssec/ds`);
|
|
2737
|
+
}
|
|
2738
|
+
/** Clear DNS cache */
|
|
2739
|
+
async clearCache() {
|
|
2740
|
+
return this.http.post("/dns/cache/clear", {});
|
|
2741
|
+
}
|
|
2742
|
+
};
|
|
2743
|
+
|
|
2744
|
+
// src/gateway/client.ts
|
|
2745
|
+
var SmartGatewayClient = class {
|
|
2746
|
+
http;
|
|
2747
|
+
/** Host routing and proxy management */
|
|
2748
|
+
routing;
|
|
2749
|
+
/** Domain registration and management */
|
|
2750
|
+
domains;
|
|
2751
|
+
/** DNS resolution and zone management */
|
|
2752
|
+
dns;
|
|
2753
|
+
constructor(config) {
|
|
2754
|
+
const baseUrl = config.baseUrl.replace(/\/+$/, "");
|
|
2755
|
+
this.http = createHttpClient({
|
|
2756
|
+
baseUrl: `${baseUrl}/api/v3`,
|
|
2757
|
+
apiKey: config.apiKey,
|
|
2758
|
+
authToken: config.authToken,
|
|
2759
|
+
timeout: config.timeout
|
|
2760
|
+
});
|
|
2761
|
+
this.routing = new RoutingClient(this.http);
|
|
2762
|
+
this.domains = new DomainsClient(this.http);
|
|
2763
|
+
this.dns = new DnsClient(this.http);
|
|
2764
|
+
}
|
|
2765
|
+
// ========== Health & Metrics ==========
|
|
2766
|
+
/** Get gateway health status */
|
|
2767
|
+
async getHealth() {
|
|
2768
|
+
return this.http.get("/health");
|
|
2769
|
+
}
|
|
2770
|
+
/** Get gateway status with host and domain counts */
|
|
2771
|
+
async getStatus() {
|
|
2772
|
+
return this.http.get("/status");
|
|
2773
|
+
}
|
|
2774
|
+
/** Check gateway readiness */
|
|
2775
|
+
async getReadiness() {
|
|
2776
|
+
return this.http.get("/ready");
|
|
2777
|
+
}
|
|
2778
|
+
/** Check gateway liveness */
|
|
2779
|
+
async getLiveness() {
|
|
2780
|
+
return this.http.get("/live");
|
|
2781
|
+
}
|
|
2782
|
+
/** Get detailed gateway metrics */
|
|
2783
|
+
async getMetrics(refresh) {
|
|
2784
|
+
const params = refresh ? "?refresh=true" : "";
|
|
2785
|
+
return this.http.get(`/metrics${params}`);
|
|
2786
|
+
}
|
|
2787
|
+
/** Get metrics summary */
|
|
2788
|
+
async getMetricsSummary() {
|
|
2789
|
+
return this.http.get("/metrics/summary");
|
|
2790
|
+
}
|
|
2791
|
+
};
|
|
2792
|
+
|
|
2793
|
+
// src/http/resilient-http.ts
|
|
2794
|
+
var DEFAULT_RETRY_CONFIG = {
|
|
2795
|
+
maxRetries: 3,
|
|
2796
|
+
initialDelayMs: 1e3,
|
|
2797
|
+
maxDelayMs: 3e4,
|
|
2798
|
+
backoffMultiplier: 2,
|
|
2799
|
+
jitterFactor: 0.1
|
|
2800
|
+
};
|
|
2801
|
+
function calculateBackoffDelay(attempt, config) {
|
|
2802
|
+
const exponentialDelay = config.initialDelayMs * Math.pow(config.backoffMultiplier, attempt);
|
|
2803
|
+
const cappedDelay = Math.min(exponentialDelay, config.maxDelayMs);
|
|
2804
|
+
if (config.jitterFactor && config.jitterFactor > 0) {
|
|
2805
|
+
const jitter = cappedDelay * config.jitterFactor * Math.random();
|
|
2806
|
+
return Math.floor(cappedDelay + jitter);
|
|
2807
|
+
}
|
|
2808
|
+
return Math.floor(cappedDelay);
|
|
2809
|
+
}
|
|
2810
|
+
function parseRetryAfter(retryAfter) {
|
|
2811
|
+
if (!retryAfter) return 0;
|
|
2812
|
+
const seconds = parseInt(retryAfter, 10);
|
|
2813
|
+
if (!isNaN(seconds)) {
|
|
2814
|
+
return seconds * 1e3;
|
|
2815
|
+
}
|
|
2816
|
+
const date = new Date(retryAfter);
|
|
2817
|
+
if (!isNaN(date.getTime())) {
|
|
2818
|
+
return Math.max(0, date.getTime() - Date.now());
|
|
2819
|
+
}
|
|
2820
|
+
return 0;
|
|
2821
|
+
}
|
|
2822
|
+
function sleep(ms) {
|
|
2823
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
2824
|
+
}
|
|
2825
|
+
function isIdempotent(method) {
|
|
2826
|
+
const m = (method || "GET").toUpperCase();
|
|
2827
|
+
return m === "GET" || m === "HEAD" || m === "OPTIONS";
|
|
2828
|
+
}
|
|
2829
|
+
async function resilientFetch(url, init, config) {
|
|
2830
|
+
const timeoutMs = config?.timeoutMs ?? 3e4;
|
|
2831
|
+
const retryConfig = { ...DEFAULT_RETRY_CONFIG, ...config?.retry };
|
|
2832
|
+
const method = (init?.method || "GET").toUpperCase();
|
|
2833
|
+
const shouldUseCircuitBreaker = config?.circuitBreaker !== false;
|
|
2834
|
+
const breaker = shouldUseCircuitBreaker ? new CircuitBreaker({
|
|
2835
|
+
failureThreshold: 5,
|
|
2836
|
+
rollingWindowMs: 6e4,
|
|
2837
|
+
cooldownMs: 3e4,
|
|
2838
|
+
name: `http-${method}`,
|
|
2839
|
+
...config?.circuitBreaker || {}
|
|
2840
|
+
}) : null;
|
|
2841
|
+
let lastError;
|
|
2842
|
+
let lastStatus;
|
|
2843
|
+
for (let attempt = 0; attempt <= retryConfig.maxRetries; attempt++) {
|
|
2844
|
+
try {
|
|
2845
|
+
if (breaker) {
|
|
2846
|
+
if (!breaker.canExecute()) {
|
|
2847
|
+
const state = breaker.getState();
|
|
2848
|
+
if (config?.onCircuitBreakerStateChange) {
|
|
2849
|
+
config.onCircuitBreakerStateChange(state);
|
|
2850
|
+
}
|
|
2851
|
+
const snapshot = breaker.snapshot();
|
|
2852
|
+
throw new Error(
|
|
2853
|
+
`Circuit breaker is ${state}. Next probe at ${new Date(snapshot.nextProbeAt || 0).toISOString()}`
|
|
2854
|
+
);
|
|
2855
|
+
}
|
|
2856
|
+
}
|
|
2857
|
+
const controller = new AbortController();
|
|
2858
|
+
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
2859
|
+
let response;
|
|
2860
|
+
try {
|
|
2861
|
+
response = await fetch(url, { ...init, signal: controller.signal });
|
|
2862
|
+
clearTimeout(timeoutId);
|
|
2863
|
+
} catch (fetchError) {
|
|
2864
|
+
clearTimeout(timeoutId);
|
|
2865
|
+
throw fetchError;
|
|
2866
|
+
}
|
|
2867
|
+
if ((response.status === 429 || response.status === 503) && isIdempotent(method)) {
|
|
2868
|
+
if (attempt < retryConfig.maxRetries) {
|
|
2869
|
+
const retryAfterMs = parseRetryAfter(response.headers.get("Retry-After"));
|
|
2870
|
+
const delay = retryAfterMs > 0 ? retryAfterMs : calculateBackoffDelay(attempt, retryConfig);
|
|
2871
|
+
lastStatus = response.status;
|
|
2872
|
+
if (config?.onRetry) {
|
|
2873
|
+
config.onRetry(attempt + 1, delay, response.status);
|
|
2874
|
+
}
|
|
2875
|
+
const statusError = new Error(`HTTP ${response.status}`);
|
|
2876
|
+
if (breaker) {
|
|
2877
|
+
try {
|
|
2878
|
+
await breaker.execute(async () => {
|
|
2879
|
+
await sleep(delay);
|
|
2880
|
+
throw statusError;
|
|
2881
|
+
});
|
|
2882
|
+
} catch {
|
|
2883
|
+
}
|
|
2884
|
+
} else {
|
|
2885
|
+
await sleep(delay);
|
|
2886
|
+
}
|
|
2887
|
+
continue;
|
|
2888
|
+
}
|
|
2889
|
+
}
|
|
2890
|
+
return response;
|
|
2891
|
+
} catch (error) {
|
|
2892
|
+
lastError = error;
|
|
2893
|
+
if (attempt >= retryConfig.maxRetries) {
|
|
2894
|
+
break;
|
|
2895
|
+
}
|
|
2896
|
+
if (!isIdempotent(method)) {
|
|
2897
|
+
break;
|
|
2898
|
+
}
|
|
2899
|
+
const delay = calculateBackoffDelay(attempt, retryConfig);
|
|
2900
|
+
if (config?.onRetry) {
|
|
2901
|
+
config.onRetry(attempt + 1, delay, lastStatus);
|
|
2902
|
+
}
|
|
2903
|
+
await sleep(delay);
|
|
2904
|
+
}
|
|
2905
|
+
}
|
|
2906
|
+
if (lastError) {
|
|
2907
|
+
throw lastError;
|
|
2908
|
+
}
|
|
2909
|
+
throw new Error(`Failed to fetch ${url} after ${retryConfig.maxRetries} retries`);
|
|
2910
|
+
}
|
|
2911
|
+
function createResilientFetchWithBreaker(config) {
|
|
2912
|
+
const shouldUseCircuitBreaker = config?.circuitBreaker !== false;
|
|
2913
|
+
const breaker = shouldUseCircuitBreaker ? new CircuitBreaker({
|
|
2914
|
+
failureThreshold: 5,
|
|
2915
|
+
rollingWindowMs: 6e4,
|
|
2916
|
+
cooldownMs: 3e4,
|
|
2917
|
+
name: "http-breaker",
|
|
2918
|
+
...config?.circuitBreaker || {}
|
|
2919
|
+
}) : null;
|
|
2920
|
+
const boundFetch = async (url, init) => {
|
|
2921
|
+
const timeoutMs = config?.timeoutMs ?? 3e4;
|
|
2922
|
+
const retryConfig = { ...DEFAULT_RETRY_CONFIG, ...config?.retry };
|
|
2923
|
+
const method = (init?.method || "GET").toUpperCase();
|
|
2924
|
+
let lastError;
|
|
2925
|
+
let lastStatus;
|
|
2926
|
+
for (let attempt = 0; attempt <= retryConfig.maxRetries; attempt++) {
|
|
2927
|
+
try {
|
|
2928
|
+
if (breaker) {
|
|
2929
|
+
const response = await breaker.execute(async () => {
|
|
2930
|
+
const controller = new AbortController();
|
|
2931
|
+
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
2932
|
+
try {
|
|
2933
|
+
const resp = await fetch(url, { ...init, signal: controller.signal });
|
|
2934
|
+
clearTimeout(timeoutId);
|
|
2935
|
+
return resp;
|
|
2936
|
+
} catch (fetchError) {
|
|
2937
|
+
clearTimeout(timeoutId);
|
|
2938
|
+
throw fetchError;
|
|
2939
|
+
}
|
|
2940
|
+
});
|
|
2941
|
+
if ((response.status === 429 || response.status === 503) && isIdempotent(method)) {
|
|
2942
|
+
if (attempt < retryConfig.maxRetries) {
|
|
2943
|
+
const retryAfterMs = parseRetryAfter(response.headers.get("Retry-After"));
|
|
2944
|
+
const delay = retryAfterMs > 0 ? retryAfterMs : calculateBackoffDelay(attempt, retryConfig);
|
|
2945
|
+
lastStatus = response.status;
|
|
2946
|
+
if (config?.onRetry) {
|
|
2947
|
+
config.onRetry(attempt + 1, delay, response.status);
|
|
2948
|
+
}
|
|
2949
|
+
await sleep(delay);
|
|
2950
|
+
continue;
|
|
2951
|
+
}
|
|
2952
|
+
}
|
|
2953
|
+
return response;
|
|
2954
|
+
} else {
|
|
2955
|
+
const controller = new AbortController();
|
|
2956
|
+
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
2957
|
+
try {
|
|
2958
|
+
const response = await fetch(url, { ...init, signal: controller.signal });
|
|
2959
|
+
clearTimeout(timeoutId);
|
|
2960
|
+
if ((response.status === 429 || response.status === 503) && isIdempotent(method)) {
|
|
2961
|
+
if (attempt < retryConfig.maxRetries) {
|
|
2962
|
+
const retryAfterMs = parseRetryAfter(response.headers.get("Retry-After"));
|
|
2963
|
+
const delay = retryAfterMs > 0 ? retryAfterMs : calculateBackoffDelay(attempt, retryConfig);
|
|
2964
|
+
lastStatus = response.status;
|
|
2965
|
+
if (config?.onRetry) {
|
|
2966
|
+
config.onRetry(attempt + 1, delay, response.status);
|
|
2967
|
+
}
|
|
2968
|
+
await sleep(delay);
|
|
2969
|
+
continue;
|
|
2970
|
+
}
|
|
2971
|
+
}
|
|
2972
|
+
return response;
|
|
2973
|
+
} catch (fetchError) {
|
|
2974
|
+
clearTimeout(timeoutId);
|
|
2975
|
+
throw fetchError;
|
|
2976
|
+
}
|
|
2977
|
+
}
|
|
2978
|
+
} catch (error) {
|
|
2979
|
+
lastError = error;
|
|
2980
|
+
if (attempt >= retryConfig.maxRetries) {
|
|
2981
|
+
break;
|
|
2982
|
+
}
|
|
2983
|
+
if (!isIdempotent(method)) {
|
|
2984
|
+
break;
|
|
2985
|
+
}
|
|
2986
|
+
const delay = calculateBackoffDelay(attempt, retryConfig);
|
|
2987
|
+
if (config?.onRetry) {
|
|
2988
|
+
config.onRetry(attempt + 1, delay, lastStatus);
|
|
2989
|
+
}
|
|
2990
|
+
await sleep(delay);
|
|
2991
|
+
}
|
|
2992
|
+
}
|
|
2993
|
+
if (lastError) {
|
|
2994
|
+
throw lastError;
|
|
2995
|
+
}
|
|
2996
|
+
throw new Error(`Failed to fetch ${url} after ${retryConfig.maxRetries} retries`);
|
|
2997
|
+
};
|
|
2998
|
+
return {
|
|
2999
|
+
fetch: boundFetch,
|
|
3000
|
+
getCircuitBreakerSnapshot: () => breaker ? breaker.snapshot() : null
|
|
3001
|
+
};
|
|
3002
|
+
}
|
|
3003
|
+
|
|
3004
|
+
// src/chains/index.ts
|
|
3005
|
+
var chains_exports = {};
|
|
3006
|
+
__export(chains_exports, {
|
|
3007
|
+
bitcoin: () => bitcoin_exports,
|
|
3008
|
+
hedera: () => hedera_exports,
|
|
3009
|
+
polkadot: () => polkadot_exports,
|
|
3010
|
+
solana: () => solana_exports,
|
|
3011
|
+
stellar: () => stellar_exports,
|
|
3012
|
+
xrpl: () => xrpl_exports
|
|
3013
|
+
});
|
|
3014
|
+
|
|
3015
|
+
// src/chains/hedera.ts
|
|
3016
|
+
var hedera_exports = {};
|
|
3017
|
+
__export(hedera_exports, {
|
|
3018
|
+
formatHederaAccountId: () => formatHederaAccountId,
|
|
3019
|
+
formatHederaTokenId: () => formatHederaTokenId,
|
|
3020
|
+
formatHederaTopicId: () => formatHederaTopicId,
|
|
3021
|
+
hbarToTinybars: () => hbarToTinybars,
|
|
3022
|
+
parseHbar: () => parseHbar,
|
|
3023
|
+
tinybarsToHbar: () => tinybarsToHbar
|
|
3024
|
+
});
|
|
3025
|
+
function formatHederaAccountId(id) {
|
|
3026
|
+
if (!/^\d+\.\d+\.\d+$/.test(id)) {
|
|
3027
|
+
throw new Error(`Invalid Hedera account ID format: ${id}`);
|
|
3028
|
+
}
|
|
3029
|
+
return id;
|
|
3030
|
+
}
|
|
3031
|
+
function parseHbar(amount) {
|
|
3032
|
+
const num = parseFloat(amount);
|
|
3033
|
+
if (isNaN(num) || num < 0) {
|
|
3034
|
+
throw new Error(`Invalid HBAR amount: ${amount}`);
|
|
3035
|
+
}
|
|
3036
|
+
return num;
|
|
3037
|
+
}
|
|
3038
|
+
function hbarToTinybars(hbar) {
|
|
3039
|
+
const amount = typeof hbar === "string" ? parseFloat(hbar) : hbar;
|
|
3040
|
+
return (amount * 1e8).toFixed(0);
|
|
3041
|
+
}
|
|
3042
|
+
function tinybarsToHbar(tinybars) {
|
|
3043
|
+
const amount = typeof tinybars === "string" ? parseInt(tinybars, 10) : tinybars;
|
|
3044
|
+
return (amount / 1e8).toString();
|
|
3045
|
+
}
|
|
3046
|
+
function formatHederaTokenId(id) {
|
|
3047
|
+
if (!/^\d+\.\d+\.\d+$/.test(id)) {
|
|
3048
|
+
throw new Error(`Invalid Hedera token ID format: ${id}`);
|
|
3049
|
+
}
|
|
3050
|
+
return id;
|
|
3051
|
+
}
|
|
3052
|
+
function formatHederaTopicId(id) {
|
|
3053
|
+
if (!/^\d+\.\d+\.\d+$/.test(id)) {
|
|
3054
|
+
throw new Error(`Invalid Hedera topic ID format: ${id}`);
|
|
3055
|
+
}
|
|
3056
|
+
return id;
|
|
3057
|
+
}
|
|
3058
|
+
|
|
3059
|
+
// src/chains/xrpl.ts
|
|
3060
|
+
var xrpl_exports = {};
|
|
3061
|
+
__export(xrpl_exports, {
|
|
3062
|
+
dropsToXrp: () => dropsToXrp,
|
|
3063
|
+
formatXRPLAddress: () => formatXRPLAddress,
|
|
3064
|
+
parseXRP: () => parseXRP,
|
|
3065
|
+
validateCurrencyCode: () => validateCurrencyCode,
|
|
3066
|
+
validateXRPLAddress: () => validateXRPLAddress,
|
|
3067
|
+
xrpToDrops: () => xrpToDrops
|
|
3068
|
+
});
|
|
3069
|
+
function validateXRPLAddress(address) {
|
|
3070
|
+
return /^r[1-9A-HJ-NP-Za-km-z]{25,34}$/.test(address);
|
|
3071
|
+
}
|
|
3072
|
+
function formatXRPLAddress(address) {
|
|
3073
|
+
if (!validateXRPLAddress(address)) {
|
|
3074
|
+
throw new Error(`Invalid XRPL address format: ${address}`);
|
|
3075
|
+
}
|
|
3076
|
+
return address;
|
|
3077
|
+
}
|
|
3078
|
+
function xrpToDrops(xrp) {
|
|
3079
|
+
const amount = typeof xrp === "string" ? parseFloat(xrp) : xrp;
|
|
3080
|
+
if (isNaN(amount) || amount < 0) {
|
|
3081
|
+
throw new Error(`Invalid XRP amount: ${xrp}`);
|
|
3082
|
+
}
|
|
3083
|
+
return (amount * 1e6).toFixed(0);
|
|
3084
|
+
}
|
|
3085
|
+
function dropsToXrp(drops) {
|
|
3086
|
+
const amount = typeof drops === "string" ? parseInt(drops, 10) : drops;
|
|
3087
|
+
if (isNaN(amount) || amount < 0) {
|
|
3088
|
+
throw new Error(`Invalid drops amount: ${drops}`);
|
|
3089
|
+
}
|
|
3090
|
+
return (amount / 1e6).toString();
|
|
3091
|
+
}
|
|
3092
|
+
function parseXRP(amount) {
|
|
3093
|
+
const num = parseFloat(amount);
|
|
3094
|
+
if (isNaN(num) || num < 0) {
|
|
3095
|
+
throw new Error(`Invalid XRP amount: ${amount}`);
|
|
3096
|
+
}
|
|
3097
|
+
return num;
|
|
3098
|
+
}
|
|
3099
|
+
function validateCurrencyCode(code) {
|
|
3100
|
+
return /^[A-Z]{3}$/.test(code) || /^[0-9A-F]{40}$/i.test(code);
|
|
3101
|
+
}
|
|
3102
|
+
|
|
3103
|
+
// src/chains/polkadot.ts
|
|
3104
|
+
var polkadot_exports = {};
|
|
3105
|
+
__export(polkadot_exports, {
|
|
3106
|
+
dotToPlanck: () => dotToPlanck,
|
|
3107
|
+
formatDot: () => formatDot,
|
|
3108
|
+
formatPolkadotAddress: () => formatPolkadotAddress,
|
|
3109
|
+
parseDotString: () => parseDotString,
|
|
3110
|
+
planckToDot: () => planckToDot,
|
|
3111
|
+
validatePolkadotAddress: () => validatePolkadotAddress
|
|
3112
|
+
});
|
|
3113
|
+
var PLANCK_PER_DOT = 10000000000n;
|
|
3114
|
+
function validatePolkadotAddress(address) {
|
|
3115
|
+
if (!address || typeof address !== "string") {
|
|
3116
|
+
return false;
|
|
3117
|
+
}
|
|
3118
|
+
const ss58Regex = /^[1-9A-HJ-NP-Za-km-z]{47,48}$/;
|
|
3119
|
+
return ss58Regex.test(address);
|
|
3120
|
+
}
|
|
3121
|
+
function formatPolkadotAddress(address, prefixLength = 6, suffixLength = 5) {
|
|
3122
|
+
if (!address || address.length <= prefixLength + suffixLength) {
|
|
3123
|
+
return address;
|
|
3124
|
+
}
|
|
3125
|
+
return `${address.slice(0, prefixLength)}...${address.slice(-suffixLength)}`;
|
|
3126
|
+
}
|
|
3127
|
+
function dotToPlanck(dot) {
|
|
3128
|
+
if (dot < 0) {
|
|
3129
|
+
throw new Error("DOT amount cannot be negative");
|
|
3130
|
+
}
|
|
3131
|
+
const [whole, decimal = ""] = dot.toString().split(".");
|
|
3132
|
+
const paddedDecimal = decimal.padEnd(10, "0").slice(0, 10);
|
|
3133
|
+
return BigInt(whole) * PLANCK_PER_DOT + BigInt(paddedDecimal);
|
|
3134
|
+
}
|
|
3135
|
+
function planckToDot(planck) {
|
|
3136
|
+
if (planck < 0n) {
|
|
3137
|
+
throw new Error("Planck amount cannot be negative");
|
|
3138
|
+
}
|
|
3139
|
+
const whole = planck / PLANCK_PER_DOT;
|
|
3140
|
+
const remainder = planck % PLANCK_PER_DOT;
|
|
3141
|
+
return Number(whole) + Number(remainder) / Number(PLANCK_PER_DOT);
|
|
3142
|
+
}
|
|
3143
|
+
function formatDot(planck, decimals = 4) {
|
|
3144
|
+
const dot = planckToDot(planck);
|
|
3145
|
+
return `${dot.toFixed(decimals)} DOT`;
|
|
3146
|
+
}
|
|
3147
|
+
function parseDotString(dotString) {
|
|
3148
|
+
const cleaned = dotString.replace(/\s*DOT\s*$/i, "").trim();
|
|
3149
|
+
const dot = parseFloat(cleaned);
|
|
3150
|
+
if (isNaN(dot)) {
|
|
3151
|
+
throw new Error(`Invalid DOT amount: ${dotString}`);
|
|
3152
|
+
}
|
|
3153
|
+
return dotToPlanck(dot);
|
|
3154
|
+
}
|
|
3155
|
+
|
|
3156
|
+
// src/chains/solana.ts
|
|
3157
|
+
var solana_exports = {};
|
|
3158
|
+
__export(solana_exports, {
|
|
3159
|
+
formatSol: () => formatSol,
|
|
3160
|
+
formatSolanaAddress: () => formatSolanaAddress,
|
|
3161
|
+
isTransactionSignature: () => isTransactionSignature,
|
|
3162
|
+
lamportsToSol: () => lamportsToSol,
|
|
3163
|
+
parseSolString: () => parseSolString,
|
|
3164
|
+
solToLamports: () => solToLamports,
|
|
3165
|
+
validateSolanaPublicKey: () => validateSolanaPublicKey
|
|
3166
|
+
});
|
|
3167
|
+
var LAMPORTS_PER_SOL = 1000000000n;
|
|
3168
|
+
var BASE58_CHARS = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
3169
|
+
function validateSolanaPublicKey(publicKey) {
|
|
3170
|
+
if (!publicKey || typeof publicKey !== "string") {
|
|
3171
|
+
return false;
|
|
3172
|
+
}
|
|
3173
|
+
if (publicKey.length < 32 || publicKey.length > 44) {
|
|
3174
|
+
return false;
|
|
3175
|
+
}
|
|
3176
|
+
for (const char of publicKey) {
|
|
3177
|
+
if (!BASE58_CHARS.includes(char)) {
|
|
3178
|
+
return false;
|
|
3179
|
+
}
|
|
3180
|
+
}
|
|
3181
|
+
return true;
|
|
3182
|
+
}
|
|
3183
|
+
function formatSolanaAddress(address, prefixLength = 4, suffixLength = 4) {
|
|
3184
|
+
if (!address || address.length <= prefixLength + suffixLength) {
|
|
3185
|
+
return address;
|
|
3186
|
+
}
|
|
3187
|
+
return `${address.slice(0, prefixLength)}...${address.slice(-suffixLength)}`;
|
|
3188
|
+
}
|
|
3189
|
+
function solToLamports(sol) {
|
|
3190
|
+
if (sol < 0) {
|
|
3191
|
+
throw new Error("SOL amount cannot be negative");
|
|
3192
|
+
}
|
|
3193
|
+
const [whole, decimal = ""] = sol.toString().split(".");
|
|
3194
|
+
const paddedDecimal = decimal.padEnd(9, "0").slice(0, 9);
|
|
3195
|
+
return BigInt(whole) * LAMPORTS_PER_SOL + BigInt(paddedDecimal);
|
|
3196
|
+
}
|
|
3197
|
+
function lamportsToSol(lamports) {
|
|
3198
|
+
if (lamports < 0n) {
|
|
3199
|
+
throw new Error("Lamports amount cannot be negative");
|
|
3200
|
+
}
|
|
3201
|
+
const whole = lamports / LAMPORTS_PER_SOL;
|
|
3202
|
+
const remainder = lamports % LAMPORTS_PER_SOL;
|
|
3203
|
+
return Number(whole) + Number(remainder) / Number(LAMPORTS_PER_SOL);
|
|
3204
|
+
}
|
|
3205
|
+
function formatSol(lamports, decimals = 4) {
|
|
3206
|
+
const sol = lamportsToSol(lamports);
|
|
3207
|
+
return `${sol.toFixed(decimals)} SOL`;
|
|
3208
|
+
}
|
|
3209
|
+
function parseSolString(solString) {
|
|
3210
|
+
const cleaned = solString.replace(/\s*SOL\s*$/i, "").trim();
|
|
3211
|
+
const sol = parseFloat(cleaned);
|
|
3212
|
+
if (isNaN(sol)) {
|
|
3213
|
+
throw new Error(`Invalid SOL amount: ${solString}`);
|
|
3214
|
+
}
|
|
3215
|
+
return solToLamports(sol);
|
|
3216
|
+
}
|
|
3217
|
+
function isTransactionSignature(signature) {
|
|
3218
|
+
if (!signature || typeof signature !== "string") {
|
|
3219
|
+
return false;
|
|
3220
|
+
}
|
|
3221
|
+
if (signature.length < 87 || signature.length > 88) {
|
|
3222
|
+
return false;
|
|
3223
|
+
}
|
|
3224
|
+
for (const char of signature) {
|
|
3225
|
+
if (!BASE58_CHARS.includes(char)) {
|
|
3226
|
+
return false;
|
|
3227
|
+
}
|
|
3228
|
+
}
|
|
3229
|
+
return true;
|
|
3230
|
+
}
|
|
3231
|
+
|
|
3232
|
+
// src/chains/stellar.ts
|
|
3233
|
+
var stellar_exports = {};
|
|
3234
|
+
__export(stellar_exports, {
|
|
3235
|
+
stroopsToXlm: () => stroopsToXlm,
|
|
3236
|
+
validateStellarAddress: () => validateStellarAddress,
|
|
3237
|
+
xlmToStroops: () => xlmToStroops
|
|
3238
|
+
});
|
|
3239
|
+
function validateStellarAddress(address) {
|
|
3240
|
+
if (!address || address.length !== 56 || !address.startsWith("G")) {
|
|
3241
|
+
return false;
|
|
3242
|
+
}
|
|
3243
|
+
return /^G[A-Z2-7]{55}$/.test(address);
|
|
3244
|
+
}
|
|
3245
|
+
function stroopsToXlm(stroops) {
|
|
3246
|
+
const amount = typeof stroops === "string" ? parseInt(stroops, 10) : stroops;
|
|
3247
|
+
if (isNaN(amount) || amount < 0) {
|
|
3248
|
+
throw new Error(`Invalid stroops amount: ${stroops}`);
|
|
3249
|
+
}
|
|
3250
|
+
return (amount / 1e7).toString();
|
|
3251
|
+
}
|
|
3252
|
+
function xlmToStroops(xlm) {
|
|
3253
|
+
const amount = typeof xlm === "string" ? parseFloat(xlm) : xlm;
|
|
3254
|
+
if (isNaN(amount) || amount < 0) {
|
|
3255
|
+
throw new Error(`Invalid XLM amount: ${xlm}`);
|
|
3256
|
+
}
|
|
3257
|
+
return (amount * 1e7).toFixed(0);
|
|
3258
|
+
}
|
|
3259
|
+
|
|
3260
|
+
// src/chains/bitcoin.ts
|
|
3261
|
+
var bitcoin_exports = {};
|
|
3262
|
+
__export(bitcoin_exports, {
|
|
3263
|
+
btcToSatoshis: () => btcToSatoshis,
|
|
3264
|
+
satoshisToBtc: () => satoshisToBtc,
|
|
3265
|
+
validateBitcoinAddress: () => validateBitcoinAddress
|
|
3266
|
+
});
|
|
3267
|
+
function validateBitcoinAddress(address) {
|
|
3268
|
+
if (!address) return false;
|
|
3269
|
+
const p2pkh = /^1[1-9A-HJ-NP-Za-km-z]{24,33}$/;
|
|
3270
|
+
const p2sh = /^3[1-9A-HJ-NP-Za-km-z]{24,33}$/;
|
|
3271
|
+
const bech32 = /^bc1[a-z0-9]{38,58}$/;
|
|
3272
|
+
return p2pkh.test(address) || p2sh.test(address) || bech32.test(address);
|
|
3273
|
+
}
|
|
3274
|
+
function satoshisToBtc(satoshis) {
|
|
3275
|
+
const amount = typeof satoshis === "string" ? parseInt(satoshis, 10) : satoshis;
|
|
3276
|
+
if (isNaN(amount) || amount < 0) {
|
|
3277
|
+
throw new Error(`Invalid satoshis amount: ${satoshis}`);
|
|
3278
|
+
}
|
|
3279
|
+
return (amount / 1e8).toString();
|
|
3280
|
+
}
|
|
3281
|
+
function btcToSatoshis(btc) {
|
|
3282
|
+
const amount = typeof btc === "string" ? parseFloat(btc) : btc;
|
|
3283
|
+
if (isNaN(amount) || amount < 0) {
|
|
3284
|
+
throw new Error(`Invalid BTC amount: ${btc}`);
|
|
3285
|
+
}
|
|
3286
|
+
return (amount * 1e8).toFixed(0);
|
|
3287
|
+
}
|
|
3288
|
+
|
|
3289
|
+
// src/baas/index.ts
|
|
3290
|
+
var baas_exports = {};
|
|
3291
|
+
__export(baas_exports, {
|
|
3292
|
+
AgentsClient: () => AgentsClient,
|
|
3293
|
+
BaasClient: () => BaasClient,
|
|
3294
|
+
BaasError: () => BaasError,
|
|
3295
|
+
DatabaseClient: () => DatabaseClient,
|
|
3296
|
+
DeploymentClient: () => DeploymentClient,
|
|
3297
|
+
FunctionsClient: () => FunctionsClient,
|
|
3298
|
+
MessagingClient: () => MessagingClient,
|
|
3299
|
+
StorageClient: () => StorageClient,
|
|
3300
|
+
validateAgentRules: () => validateAgentRules
|
|
3301
|
+
});
|
|
3302
|
+
|
|
3303
|
+
// src/baas/database/index.ts
|
|
3304
|
+
var DatabaseClient = class {
|
|
3305
|
+
constructor(http, getAppId) {
|
|
3306
|
+
this.http = http;
|
|
3307
|
+
this.getAppId = getAppId;
|
|
3308
|
+
}
|
|
3309
|
+
http;
|
|
3310
|
+
getAppId;
|
|
3311
|
+
/**
|
|
3312
|
+
* Insert a document into a collection
|
|
3313
|
+
*/
|
|
3314
|
+
async insert(collection, document) {
|
|
3315
|
+
const appId = this.getAppId();
|
|
3316
|
+
return this.http.post(
|
|
3317
|
+
`/api/db/${encodeURIComponent(appId)}/${encodeURIComponent(collection)}`,
|
|
3318
|
+
document
|
|
3319
|
+
);
|
|
3320
|
+
}
|
|
3321
|
+
/**
|
|
3322
|
+
* Find documents in a collection
|
|
3323
|
+
*/
|
|
3324
|
+
async find(collection, query, options) {
|
|
3325
|
+
const appId = this.getAppId();
|
|
3326
|
+
const params = new URLSearchParams();
|
|
3327
|
+
if (query && Object.keys(query).length > 0) {
|
|
3328
|
+
params.set("query", JSON.stringify(query));
|
|
3329
|
+
}
|
|
3330
|
+
if (options?.limit !== void 0) params.set("limit", String(options.limit));
|
|
3331
|
+
if (options?.skip !== void 0) params.set("skip", String(options.skip));
|
|
3332
|
+
if (options?.sort) params.set("sort", options.sort);
|
|
3333
|
+
const qs = params.toString();
|
|
3334
|
+
return this.http.get(
|
|
3335
|
+
`/api/db/${encodeURIComponent(appId)}/${encodeURIComponent(collection)}${qs ? `?${qs}` : ""}`
|
|
3336
|
+
);
|
|
3337
|
+
}
|
|
3338
|
+
/**
|
|
3339
|
+
* Update a document in a collection
|
|
3340
|
+
*/
|
|
3341
|
+
async update(collection, documentId, updates) {
|
|
3342
|
+
const appId = this.getAppId();
|
|
3343
|
+
return this.http.put(
|
|
3344
|
+
`/api/db/${encodeURIComponent(appId)}/${encodeURIComponent(collection)}/${encodeURIComponent(documentId)}`,
|
|
3345
|
+
updates
|
|
3346
|
+
);
|
|
3347
|
+
}
|
|
3348
|
+
/**
|
|
3349
|
+
* Delete a document from a collection
|
|
3350
|
+
*/
|
|
3351
|
+
async delete(collection, documentId) {
|
|
3352
|
+
const appId = this.getAppId();
|
|
3353
|
+
return this.http.delete(
|
|
3354
|
+
`/api/db/${encodeURIComponent(appId)}/${encodeURIComponent(collection)}/${encodeURIComponent(documentId)}`
|
|
3355
|
+
);
|
|
3356
|
+
}
|
|
3357
|
+
/**
|
|
3358
|
+
* List collections for the app
|
|
3359
|
+
*/
|
|
3360
|
+
async listCollections() {
|
|
3361
|
+
const appId = this.getAppId();
|
|
3362
|
+
return this.http.get(`/api/db/${encodeURIComponent(appId)}`);
|
|
3363
|
+
}
|
|
3364
|
+
// ========== State Proofs ==========
|
|
3365
|
+
/**
|
|
3366
|
+
* Get the current state root for the app
|
|
3367
|
+
*/
|
|
3368
|
+
async getStateRoot() {
|
|
3369
|
+
const appId = this.getAppId();
|
|
3370
|
+
return this.http.get(`/api/db/${encodeURIComponent(appId)}/state/root`);
|
|
3371
|
+
}
|
|
3372
|
+
/**
|
|
3373
|
+
* Get a Merkle proof for a specific document
|
|
3374
|
+
*/
|
|
3375
|
+
async getDocumentProof(documentId) {
|
|
3376
|
+
const appId = this.getAppId();
|
|
3377
|
+
return this.http.get(
|
|
3378
|
+
`/api/db/${encodeURIComponent(appId)}/${encodeURIComponent(documentId)}/proof`
|
|
3379
|
+
);
|
|
3380
|
+
}
|
|
3381
|
+
/**
|
|
3382
|
+
* Get state transitions (audit log)
|
|
3383
|
+
*/
|
|
3384
|
+
async getStateTransitions(options) {
|
|
3385
|
+
const appId = this.getAppId();
|
|
3386
|
+
const params = new URLSearchParams();
|
|
3387
|
+
if (options?.fromBlock !== void 0) params.set("fromBlock", String(options.fromBlock));
|
|
3388
|
+
if (options?.toBlock !== void 0) params.set("toBlock", String(options.toBlock));
|
|
3389
|
+
if (options?.limit !== void 0) params.set("limit", String(options.limit));
|
|
3390
|
+
const qs = params.toString();
|
|
3391
|
+
return this.http.get(
|
|
3392
|
+
`/api/db/${encodeURIComponent(appId)}/state/transitions${qs ? `?${qs}` : ""}`
|
|
3393
|
+
);
|
|
3394
|
+
}
|
|
3395
|
+
/**
|
|
3396
|
+
* Get database statistics
|
|
3397
|
+
*/
|
|
3398
|
+
async getDbStats() {
|
|
3399
|
+
const appId = this.getAppId();
|
|
3400
|
+
return this.http.get(`/api/db/${encodeURIComponent(appId)}/stats`);
|
|
3401
|
+
}
|
|
3402
|
+
};
|
|
3403
|
+
|
|
3404
|
+
// src/baas/storage/index.ts
|
|
3405
|
+
var StorageClient = class {
|
|
3406
|
+
constructor(http, getAppId) {
|
|
3407
|
+
this.http = http;
|
|
3408
|
+
this.getAppId = getAppId;
|
|
3409
|
+
}
|
|
3410
|
+
http;
|
|
3411
|
+
getAppId;
|
|
3412
|
+
/**
|
|
3413
|
+
* Upload a file to storage
|
|
3414
|
+
*/
|
|
3415
|
+
async upload(file, filename, metadata) {
|
|
3416
|
+
const appId = this.getAppId();
|
|
3417
|
+
return this.http.upload(`/api/storage/${encodeURIComponent(appId)}/upload`, file, filename, metadata);
|
|
3418
|
+
}
|
|
3419
|
+
/**
|
|
3420
|
+
* Download a file by CID
|
|
3421
|
+
*/
|
|
3422
|
+
async download(cid) {
|
|
3423
|
+
return this.http.get(`/api/storage/download/${encodeURIComponent(cid)}`);
|
|
3424
|
+
}
|
|
3425
|
+
/**
|
|
3426
|
+
* Get file metadata
|
|
3427
|
+
*/
|
|
3428
|
+
async getMetadata(cid) {
|
|
3429
|
+
return this.http.get(`/api/storage/metadata/${encodeURIComponent(cid)}`);
|
|
3430
|
+
}
|
|
3431
|
+
/**
|
|
3432
|
+
* Delete a file
|
|
3433
|
+
*/
|
|
3434
|
+
async delete(cid) {
|
|
3435
|
+
const appId = this.getAppId();
|
|
3436
|
+
return this.http.delete(`/api/storage/${encodeURIComponent(appId)}/${encodeURIComponent(cid)}`);
|
|
3437
|
+
}
|
|
3438
|
+
/**
|
|
3439
|
+
* Get file info
|
|
3440
|
+
*/
|
|
3441
|
+
async getFile(cid) {
|
|
3442
|
+
const appId = this.getAppId();
|
|
3443
|
+
return this.http.get(`/api/storage/${encodeURIComponent(appId)}/${encodeURIComponent(cid)}`);
|
|
3444
|
+
}
|
|
3445
|
+
/**
|
|
3446
|
+
* List all files for the app
|
|
3447
|
+
*/
|
|
3448
|
+
async listFiles(pagination) {
|
|
3449
|
+
const appId = this.getAppId();
|
|
3450
|
+
const params = new URLSearchParams();
|
|
3451
|
+
if (pagination?.limit !== void 0) params.set("limit", String(pagination.limit));
|
|
3452
|
+
if (pagination?.skip !== void 0) params.set("skip", String(pagination.skip));
|
|
3453
|
+
const qs = params.toString();
|
|
3454
|
+
return this.http.get(`/api/storage/${encodeURIComponent(appId)}/files${qs ? `?${qs}` : ""}`);
|
|
3455
|
+
}
|
|
3456
|
+
/**
|
|
3457
|
+
* Get storage usage for the current app
|
|
3458
|
+
*/
|
|
3459
|
+
async getUsage() {
|
|
3460
|
+
return this.http.get("/api/storage/usage");
|
|
3461
|
+
}
|
|
3462
|
+
/**
|
|
3463
|
+
* Check if a file exists
|
|
3464
|
+
*/
|
|
3465
|
+
async exists(cid) {
|
|
3466
|
+
return this.http.get(`/api/storage/exists/${encodeURIComponent(cid)}`);
|
|
3467
|
+
}
|
|
3468
|
+
};
|
|
3469
|
+
|
|
3470
|
+
// src/baas/functions/index.ts
|
|
3471
|
+
var FunctionsClient = class {
|
|
3472
|
+
constructor(http, getAppId) {
|
|
3473
|
+
this.http = http;
|
|
3474
|
+
this.getAppId = getAppId;
|
|
3475
|
+
}
|
|
3476
|
+
http;
|
|
3477
|
+
getAppId;
|
|
3478
|
+
/**
|
|
3479
|
+
* Deploy a new function
|
|
3480
|
+
*/
|
|
3481
|
+
async deploy(request) {
|
|
3482
|
+
const appId = this.getAppId();
|
|
3483
|
+
return this.http.post(`/api/functions/${encodeURIComponent(appId)}`, request);
|
|
3484
|
+
}
|
|
3485
|
+
/**
|
|
3486
|
+
* Invoke a function
|
|
3487
|
+
*/
|
|
3488
|
+
async invoke(functionId, payload) {
|
|
3489
|
+
return this.http.post(`/api/functions/${encodeURIComponent(functionId)}/invoke`, payload ?? {});
|
|
3490
|
+
}
|
|
3491
|
+
/**
|
|
3492
|
+
* List all functions
|
|
3493
|
+
*/
|
|
3494
|
+
async list() {
|
|
3495
|
+
return this.http.get("/api/functions");
|
|
3496
|
+
}
|
|
3497
|
+
/**
|
|
3498
|
+
* Get function details
|
|
3499
|
+
*/
|
|
3500
|
+
async get(functionId) {
|
|
3501
|
+
return this.http.get(`/api/functions/${encodeURIComponent(functionId)}`);
|
|
3502
|
+
}
|
|
3503
|
+
/**
|
|
3504
|
+
* Update a function
|
|
3505
|
+
*/
|
|
3506
|
+
async update(functionId, updates) {
|
|
3507
|
+
return this.http.put(`/api/functions/${encodeURIComponent(functionId)}`, updates);
|
|
3508
|
+
}
|
|
3509
|
+
/**
|
|
3510
|
+
* Delete a function
|
|
3511
|
+
*/
|
|
3512
|
+
async delete(functionId) {
|
|
3513
|
+
return this.http.delete(`/api/functions/${encodeURIComponent(functionId)}`);
|
|
3514
|
+
}
|
|
3515
|
+
/**
|
|
3516
|
+
* Get function execution logs
|
|
3517
|
+
*/
|
|
3518
|
+
async getLogs(functionId, options) {
|
|
3519
|
+
const params = new URLSearchParams();
|
|
3520
|
+
if (options?.limit !== void 0) params.set("limit", String(options.limit));
|
|
3521
|
+
if (options?.startTime) params.set("startTime", options.startTime);
|
|
3522
|
+
if (options?.level) params.set("level", options.level);
|
|
3523
|
+
const qs = params.toString();
|
|
3524
|
+
return this.http.get(`/api/functions/${encodeURIComponent(functionId)}/logs${qs ? `?${qs}` : ""}`);
|
|
3525
|
+
}
|
|
3526
|
+
/**
|
|
3527
|
+
* Get function statistics for an app
|
|
3528
|
+
*/
|
|
3529
|
+
async getStats() {
|
|
3530
|
+
const appId = this.getAppId();
|
|
3531
|
+
return this.http.get(`/api/functions/${encodeURIComponent(appId)}/stats`);
|
|
3532
|
+
}
|
|
3533
|
+
};
|
|
3534
|
+
|
|
3535
|
+
// src/baas/messaging/index.ts
|
|
3536
|
+
var MessagingClient = class {
|
|
3537
|
+
constructor(http, getAppId) {
|
|
3538
|
+
this.http = http;
|
|
3539
|
+
this.getAppId = getAppId;
|
|
3540
|
+
}
|
|
3541
|
+
http;
|
|
3542
|
+
getAppId;
|
|
3543
|
+
/**
|
|
3544
|
+
* Create a new channel
|
|
3545
|
+
*/
|
|
3546
|
+
async createChannel(config) {
|
|
3547
|
+
const appId = this.getAppId();
|
|
3548
|
+
return this.http.post(`/api/messaging/${encodeURIComponent(appId)}/channels`, config);
|
|
3549
|
+
}
|
|
3550
|
+
/**
|
|
3551
|
+
* Delete a channel
|
|
3552
|
+
*/
|
|
3553
|
+
async deleteChannel(channelId) {
|
|
3554
|
+
const appId = this.getAppId();
|
|
3555
|
+
return this.http.delete(`/api/messaging/${encodeURIComponent(appId)}/channels/${encodeURIComponent(channelId)}`);
|
|
3556
|
+
}
|
|
3557
|
+
/**
|
|
3558
|
+
* Get a channel by ID
|
|
3559
|
+
*/
|
|
3560
|
+
async getChannel(channelId) {
|
|
3561
|
+
const appId = this.getAppId();
|
|
3562
|
+
return this.http.get(`/api/messaging/${encodeURIComponent(appId)}/channels/${encodeURIComponent(channelId)}`);
|
|
3563
|
+
}
|
|
3564
|
+
/**
|
|
3565
|
+
* List all channels for the app
|
|
3566
|
+
*/
|
|
3567
|
+
async listChannels() {
|
|
3568
|
+
const appId = this.getAppId();
|
|
3569
|
+
return this.http.get(`/api/messaging/${encodeURIComponent(appId)}/channels`);
|
|
3570
|
+
}
|
|
3571
|
+
/**
|
|
3572
|
+
* Publish a message to a channel
|
|
3573
|
+
*/
|
|
3574
|
+
async publish(channel, message, metadata) {
|
|
3575
|
+
const appId = this.getAppId();
|
|
3576
|
+
return this.http.post(
|
|
3577
|
+
`/api/messaging/${encodeURIComponent(appId)}/channels/${encodeURIComponent(channel)}/publish`,
|
|
3578
|
+
{ data: message, metadata }
|
|
3579
|
+
);
|
|
3580
|
+
}
|
|
3581
|
+
/**
|
|
3582
|
+
* Get message history for a channel
|
|
3583
|
+
*/
|
|
3584
|
+
async getHistory(channel, options) {
|
|
3585
|
+
const appId = this.getAppId();
|
|
3586
|
+
const params = new URLSearchParams();
|
|
3587
|
+
if (options?.limit !== void 0) params.set("limit", String(options.limit));
|
|
3588
|
+
if (options?.before) params.set("before", options.before);
|
|
3589
|
+
if (options?.after) params.set("after", options.after);
|
|
3590
|
+
const qs = params.toString();
|
|
3591
|
+
return this.http.get(
|
|
3592
|
+
`/api/messaging/${encodeURIComponent(appId)}/channels/${encodeURIComponent(channel)}/history${qs ? `?${qs}` : ""}`
|
|
3593
|
+
);
|
|
3594
|
+
}
|
|
3595
|
+
/**
|
|
3596
|
+
* Set presence for a member
|
|
3597
|
+
*/
|
|
3598
|
+
async setPresence(member) {
|
|
3599
|
+
const appId = this.getAppId();
|
|
3600
|
+
return this.http.post(`/api/messaging/${encodeURIComponent(appId)}/presence`, member);
|
|
3601
|
+
}
|
|
3602
|
+
/**
|
|
3603
|
+
* Remove presence for a member
|
|
3604
|
+
*/
|
|
3605
|
+
async removePresence(memberId) {
|
|
3606
|
+
const appId = this.getAppId();
|
|
3607
|
+
return this.http.delete(`/api/messaging/${encodeURIComponent(appId)}/presence/${encodeURIComponent(memberId)}`);
|
|
3608
|
+
}
|
|
3609
|
+
/**
|
|
3610
|
+
* Get presence info for a channel
|
|
3611
|
+
*/
|
|
3612
|
+
async getPresence(channel) {
|
|
3613
|
+
const appId = this.getAppId();
|
|
3614
|
+
return this.http.get(`/api/messaging/${encodeURIComponent(appId)}/presence/${encodeURIComponent(channel)}`);
|
|
3615
|
+
}
|
|
3616
|
+
/**
|
|
3617
|
+
* Get messaging statistics
|
|
3618
|
+
*/
|
|
3619
|
+
async getStats() {
|
|
3620
|
+
const appId = this.getAppId();
|
|
3621
|
+
return this.http.get(`/api/messaging/${encodeURIComponent(appId)}/stats`);
|
|
3622
|
+
}
|
|
3623
|
+
};
|
|
3624
|
+
|
|
3625
|
+
// src/baas/deployment/index.ts
|
|
3626
|
+
var DeploymentClient = class {
|
|
3627
|
+
constructor(http) {
|
|
3628
|
+
this.http = http;
|
|
3629
|
+
}
|
|
3630
|
+
http;
|
|
3631
|
+
/**
|
|
3632
|
+
* Create (deploy) a new app
|
|
3633
|
+
*/
|
|
3634
|
+
async create(request) {
|
|
3635
|
+
return this.http.post("/api/deployment/apps", request);
|
|
3636
|
+
}
|
|
3637
|
+
/**
|
|
3638
|
+
* List all deployed apps
|
|
3639
|
+
*/
|
|
3640
|
+
async list() {
|
|
3641
|
+
return this.http.get("/api/deployment/apps");
|
|
3642
|
+
}
|
|
3643
|
+
/**
|
|
3644
|
+
* Get app details
|
|
3645
|
+
*/
|
|
3646
|
+
async get(appId) {
|
|
3647
|
+
return this.http.get(`/api/deployment/apps/${encodeURIComponent(appId)}`);
|
|
3648
|
+
}
|
|
3649
|
+
/**
|
|
3650
|
+
* Update app configuration
|
|
3651
|
+
*/
|
|
3652
|
+
async update(appId, updates) {
|
|
3653
|
+
return this.http.put(`/api/deployment/apps/${encodeURIComponent(appId)}`, updates);
|
|
3654
|
+
}
|
|
3655
|
+
/**
|
|
3656
|
+
* Delete an app
|
|
3657
|
+
*/
|
|
3658
|
+
async delete(appId) {
|
|
3659
|
+
return this.http.delete(`/api/deployment/apps/${encodeURIComponent(appId)}`);
|
|
3660
|
+
}
|
|
3661
|
+
/**
|
|
3662
|
+
* Suspend an app
|
|
3663
|
+
*/
|
|
3664
|
+
async suspend(appId) {
|
|
3665
|
+
return this.http.post(`/api/deployment/apps/${encodeURIComponent(appId)}/suspend`, {});
|
|
3666
|
+
}
|
|
3667
|
+
/**
|
|
3668
|
+
* Resume a suspended app
|
|
3669
|
+
*/
|
|
3670
|
+
async resume(appId) {
|
|
3671
|
+
return this.http.post(`/api/deployment/apps/${encodeURIComponent(appId)}/resume`, {});
|
|
3672
|
+
}
|
|
3673
|
+
/**
|
|
3674
|
+
* Get deployment statistics
|
|
3675
|
+
*/
|
|
3676
|
+
async getStats() {
|
|
3677
|
+
return this.http.get("/api/deployment/stats");
|
|
3678
|
+
}
|
|
3679
|
+
};
|
|
3680
|
+
|
|
3681
|
+
// src/baas/agents/types.ts
|
|
3682
|
+
var VALID_CHAIN_NAMES = /* @__PURE__ */ new Set([
|
|
3683
|
+
"hedera",
|
|
3684
|
+
"xrpl",
|
|
3685
|
+
"polkadot",
|
|
3686
|
+
"solana",
|
|
3687
|
+
"stellar",
|
|
3688
|
+
"ethereum",
|
|
3689
|
+
"polygon",
|
|
3690
|
+
"bitcoin"
|
|
3691
|
+
]);
|
|
3692
|
+
function validateAgentRules(rules) {
|
|
3693
|
+
const errors = [];
|
|
3694
|
+
if (rules.maxTradeAmount !== void 0) {
|
|
3695
|
+
if (!isPositiveDecimalString(rules.maxTradeAmount)) {
|
|
3696
|
+
errors.push("maxTradeAmount must be a positive decimal string");
|
|
3697
|
+
}
|
|
3698
|
+
}
|
|
3699
|
+
if (rules.allowedPairs !== void 0) {
|
|
3700
|
+
if (!Array.isArray(rules.allowedPairs)) {
|
|
3701
|
+
errors.push("allowedPairs must be an array of strings");
|
|
3702
|
+
} else {
|
|
3703
|
+
for (const pair of rules.allowedPairs) {
|
|
3704
|
+
if (typeof pair !== "string" || pair.trim().length === 0) {
|
|
3705
|
+
errors.push("allowedPairs contains an empty or non-string entry");
|
|
3706
|
+
break;
|
|
3707
|
+
}
|
|
3708
|
+
}
|
|
3709
|
+
}
|
|
3710
|
+
}
|
|
3711
|
+
if (rules.allowedChains !== void 0) {
|
|
3712
|
+
if (!Array.isArray(rules.allowedChains)) {
|
|
3713
|
+
errors.push("allowedChains must be an array of strings");
|
|
3714
|
+
} else {
|
|
3715
|
+
for (const chain of rules.allowedChains) {
|
|
3716
|
+
if (!VALID_CHAIN_NAMES.has(chain)) {
|
|
3717
|
+
errors.push(`allowedChains contains invalid chain name: ${chain}`);
|
|
3718
|
+
}
|
|
3719
|
+
}
|
|
3720
|
+
}
|
|
3721
|
+
}
|
|
3722
|
+
if (rules.dailyLimit !== void 0) {
|
|
3723
|
+
if (!isPositiveDecimalString(rules.dailyLimit)) {
|
|
3724
|
+
errors.push("dailyLimit must be a positive decimal string");
|
|
3725
|
+
}
|
|
3726
|
+
}
|
|
3727
|
+
if (rules.requireApprovalAbove !== void 0) {
|
|
3728
|
+
if (!isPositiveDecimalString(rules.requireApprovalAbove)) {
|
|
3729
|
+
errors.push("requireApprovalAbove must be a positive decimal string");
|
|
3730
|
+
}
|
|
3731
|
+
}
|
|
3732
|
+
return { valid: errors.length === 0, errors };
|
|
3733
|
+
}
|
|
3734
|
+
function isPositiveDecimalString(value) {
|
|
3735
|
+
if (typeof value !== "string") return false;
|
|
3736
|
+
const num = Number(value);
|
|
3737
|
+
return !Number.isNaN(num) && num > 0;
|
|
3738
|
+
}
|
|
3739
|
+
|
|
3740
|
+
// src/baas/agents/index.ts
|
|
3741
|
+
var AgentsClient = class {
|
|
3742
|
+
constructor(http) {
|
|
3743
|
+
this.http = http;
|
|
3744
|
+
}
|
|
3745
|
+
http;
|
|
3746
|
+
/** Register a new agent */
|
|
3747
|
+
async register(request) {
|
|
3748
|
+
return this.http.post("/api/agents/register", request);
|
|
3749
|
+
}
|
|
3750
|
+
/** Get agent details */
|
|
3751
|
+
async get(agentId) {
|
|
3752
|
+
return this.http.get(`/api/agents/${encodeURIComponent(agentId)}`);
|
|
3753
|
+
}
|
|
3754
|
+
/** List all agents */
|
|
3755
|
+
async list() {
|
|
3756
|
+
return this.http.get("/api/agents");
|
|
3757
|
+
}
|
|
3758
|
+
/** Fund an agent */
|
|
3759
|
+
async fund(agentId, request) {
|
|
3760
|
+
return this.http.post(`/api/agents/${encodeURIComponent(agentId)}/fund`, request);
|
|
3761
|
+
}
|
|
3762
|
+
/** Execute a trade */
|
|
3763
|
+
async trade(agentId, request) {
|
|
3764
|
+
return this.http.post(`/api/agents/${encodeURIComponent(agentId)}/trade`, request);
|
|
3765
|
+
}
|
|
3766
|
+
/** Withdraw funds from agent */
|
|
3767
|
+
async withdraw(agentId, request) {
|
|
3768
|
+
return this.http.post(`/api/agents/${encodeURIComponent(agentId)}/withdraw`, request);
|
|
3769
|
+
}
|
|
3770
|
+
/** Pause an agent */
|
|
3771
|
+
async pause(agentId) {
|
|
3772
|
+
return this.http.post(`/api/agents/${encodeURIComponent(agentId)}/pause`, {});
|
|
3773
|
+
}
|
|
3774
|
+
/** Resume a paused agent */
|
|
3775
|
+
async resume(agentId) {
|
|
3776
|
+
return this.http.post(`/api/agents/${encodeURIComponent(agentId)}/resume`, {});
|
|
3777
|
+
}
|
|
3778
|
+
/** Revoke an agent (permanent) */
|
|
3779
|
+
async revoke(agentId) {
|
|
3780
|
+
return this.http.post(`/api/agents/${encodeURIComponent(agentId)}/revoke`, {});
|
|
3781
|
+
}
|
|
3782
|
+
/** Update agent rules */
|
|
3783
|
+
async updateRules(agentId, rules) {
|
|
3784
|
+
return this.http.put(`/api/agents/${encodeURIComponent(agentId)}/rules`, rules);
|
|
3785
|
+
}
|
|
3786
|
+
/** Get agent events */
|
|
3787
|
+
async getEvents(agentId) {
|
|
3788
|
+
return this.http.get(`/api/agents/${encodeURIComponent(agentId)}/events`);
|
|
3789
|
+
}
|
|
3790
|
+
/** Get agent balances across chains */
|
|
3791
|
+
async getBalances(agentId) {
|
|
3792
|
+
return this.http.get(`/api/agents/${encodeURIComponent(agentId)}/balances`);
|
|
3793
|
+
}
|
|
3794
|
+
/** Approve a pending agent operation */
|
|
3795
|
+
async approve(agentId, operationId) {
|
|
3796
|
+
return this.http.post(`/api/agents/${encodeURIComponent(agentId)}/approve/${encodeURIComponent(operationId)}`, {});
|
|
3797
|
+
}
|
|
3798
|
+
/** Reject a pending agent operation */
|
|
3799
|
+
async reject(agentId, operationId) {
|
|
3800
|
+
return this.http.post(`/api/agents/${encodeURIComponent(agentId)}/reject/${encodeURIComponent(operationId)}`, {});
|
|
3801
|
+
}
|
|
3802
|
+
};
|
|
3803
|
+
|
|
3804
|
+
// src/baas/client.ts
|
|
3805
|
+
var BaasClient = class {
|
|
3806
|
+
hostUrl;
|
|
3807
|
+
pathPrefix;
|
|
3808
|
+
appId;
|
|
3809
|
+
timeout;
|
|
3810
|
+
allowInsecure;
|
|
3811
|
+
authToken = null;
|
|
3812
|
+
http;
|
|
3813
|
+
/** Last HTTP error (for getHttpHealth) */
|
|
3814
|
+
lastHttpError;
|
|
3815
|
+
// ========== Sub-Clients ==========
|
|
3816
|
+
/** Trustless database with state proofs and Merkle verification */
|
|
3817
|
+
db;
|
|
3818
|
+
/** Decentralized file storage */
|
|
3819
|
+
storage;
|
|
3820
|
+
/** Serverless function deployment and invocation */
|
|
3821
|
+
functions;
|
|
3822
|
+
/** Real-time pub/sub messaging with channels, history, and presence */
|
|
3823
|
+
messaging;
|
|
3824
|
+
/** App deployment lifecycle management */
|
|
3825
|
+
deployment;
|
|
3826
|
+
/** Autonomous smart agent management */
|
|
3827
|
+
agents;
|
|
3828
|
+
constructor(config) {
|
|
3829
|
+
this.allowInsecure = config.allowInsecure ?? false;
|
|
3830
|
+
this.hostUrl = validateUrl2(config.hostUrl, this.allowInsecure);
|
|
3831
|
+
this.appId = config.appId;
|
|
3832
|
+
this.timeout = config.timeout ?? 3e4;
|
|
3833
|
+
const prefix = (config.pathPrefix ?? "").replace(/\/$/, "");
|
|
3834
|
+
this.pathPrefix = prefix ? prefix.startsWith("/") ? prefix : `/${prefix}` : "";
|
|
3835
|
+
const baseUrlWithPrefix = this.pathPrefix ? this.hostUrl.replace(/\/$/, "") + this.pathPrefix : this.hostUrl;
|
|
3836
|
+
this.http = createHttpClient({
|
|
3837
|
+
baseUrl: baseUrlWithPrefix,
|
|
3838
|
+
timeout: this.timeout
|
|
3839
|
+
});
|
|
3840
|
+
const getAppId = () => this.requireAppId();
|
|
3841
|
+
this.db = new DatabaseClient(this.http, getAppId);
|
|
3842
|
+
this.storage = new StorageClient(this.http, getAppId);
|
|
3843
|
+
this.functions = new FunctionsClient(this.http, getAppId);
|
|
3844
|
+
this.messaging = new MessagingClient(this.http, getAppId);
|
|
3845
|
+
this.deployment = new DeploymentClient(this.http);
|
|
3846
|
+
this.agents = new AgentsClient(this.http);
|
|
3847
|
+
}
|
|
3848
|
+
/** Set the app ID (for newly registered apps) */
|
|
3849
|
+
setAppId(appId) {
|
|
3850
|
+
this.appId = appId;
|
|
3851
|
+
}
|
|
3852
|
+
/** Check if the client is authenticated */
|
|
3853
|
+
isAuthenticated() {
|
|
3854
|
+
return this.authToken !== null;
|
|
3855
|
+
}
|
|
3856
|
+
/** Get the current app ID */
|
|
3857
|
+
getAppId() {
|
|
3858
|
+
return this.appId;
|
|
3859
|
+
}
|
|
3860
|
+
/**
|
|
3861
|
+
* Get HTTP resilience health information
|
|
3862
|
+
* @returns Object with circuit breaker state and last error (if any)
|
|
3863
|
+
*/
|
|
3864
|
+
getHttpHealth() {
|
|
3865
|
+
return {
|
|
3866
|
+
breaker: null,
|
|
3867
|
+
lastError: this.lastHttpError
|
|
3868
|
+
};
|
|
3869
|
+
}
|
|
3870
|
+
requireAppId() {
|
|
3871
|
+
if (!this.appId) {
|
|
3872
|
+
throw new BaasError(
|
|
3873
|
+
"App ID required. Either provide appId in config or call register() first.",
|
|
3874
|
+
400
|
|
3875
|
+
);
|
|
3876
|
+
}
|
|
3877
|
+
return this.appId;
|
|
3878
|
+
}
|
|
3879
|
+
// ========== Authentication ==========
|
|
3880
|
+
/**
|
|
3881
|
+
* Authenticate with the BaaS host using wallet challenge-response
|
|
3882
|
+
*
|
|
3883
|
+
* 1. Requests a challenge message from the host
|
|
3884
|
+
* 2. Signs the challenge with the provided signing function
|
|
3885
|
+
* 3. Submits the signature for verification
|
|
3886
|
+
* 4. Stores the JWT token for subsequent requests
|
|
3887
|
+
*/
|
|
3888
|
+
async authenticate(options) {
|
|
3889
|
+
const { chain, walletAddress, publicKey, signFn } = options;
|
|
3890
|
+
const challenge = await this.post("/api/auth/challenge", {
|
|
3891
|
+
chain,
|
|
3892
|
+
walletAddress,
|
|
3893
|
+
appId: this.appId
|
|
3894
|
+
});
|
|
3895
|
+
const signature = await signFn(challenge.message);
|
|
3896
|
+
const result = await this.post("/api/auth/verify", {
|
|
3897
|
+
challengeId: challenge.challengeId,
|
|
3898
|
+
signature,
|
|
3899
|
+
publicKey
|
|
3900
|
+
});
|
|
3901
|
+
this.authToken = result.token;
|
|
3902
|
+
this.http.setAuthToken?.(result.token);
|
|
3903
|
+
return result;
|
|
3904
|
+
}
|
|
3905
|
+
/** Validate the current session */
|
|
3906
|
+
async validateSession() {
|
|
3907
|
+
this.requireAuth();
|
|
3908
|
+
return this.get("/api/auth/session");
|
|
3909
|
+
}
|
|
3910
|
+
/** Destroy the current session on server and clear local token */
|
|
3911
|
+
async logout() {
|
|
3912
|
+
if (this.authToken) {
|
|
3913
|
+
try {
|
|
3914
|
+
await this.post("/api/auth/logout", {});
|
|
3915
|
+
} catch {
|
|
3916
|
+
}
|
|
3917
|
+
}
|
|
3918
|
+
this.authToken = null;
|
|
3919
|
+
}
|
|
3920
|
+
// ========== App Registration ==========
|
|
3921
|
+
/** Register a new app on the BaaS host */
|
|
3922
|
+
async register(request) {
|
|
3923
|
+
this.requireAuth();
|
|
3924
|
+
return this.post("/api/deployment/apps", request);
|
|
3925
|
+
}
|
|
3926
|
+
// ========== HTTP Helpers ==========
|
|
3927
|
+
requireAuth() {
|
|
3928
|
+
if (!this.authToken) {
|
|
3929
|
+
throw new BaasError("Authentication required. Call authenticate() first.", 401);
|
|
3930
|
+
}
|
|
3931
|
+
}
|
|
3932
|
+
getHeaders() {
|
|
3933
|
+
const headers = {
|
|
3934
|
+
"Content-Type": "application/json"
|
|
3935
|
+
};
|
|
3936
|
+
if (this.authToken) {
|
|
3937
|
+
headers["Authorization"] = `Bearer ${this.authToken}`;
|
|
3938
|
+
}
|
|
3939
|
+
return headers;
|
|
3940
|
+
}
|
|
3941
|
+
async post(path, body) {
|
|
3942
|
+
return this.request("POST", path, body);
|
|
3943
|
+
}
|
|
3944
|
+
async get(path) {
|
|
3945
|
+
return this.request("GET", path);
|
|
3946
|
+
}
|
|
3947
|
+
async request(method, path, body) {
|
|
3948
|
+
const url = `${this.hostUrl}${this.pathPrefix}${path}`;
|
|
3949
|
+
const controller = new AbortController();
|
|
3950
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
3951
|
+
try {
|
|
3952
|
+
const options = {
|
|
3953
|
+
method,
|
|
3954
|
+
headers: this.getHeaders(),
|
|
3955
|
+
signal: controller.signal
|
|
3956
|
+
};
|
|
3957
|
+
if (body !== void 0) {
|
|
3958
|
+
options.body = JSON.stringify(body);
|
|
3959
|
+
}
|
|
3960
|
+
const response = await fetch(url, options);
|
|
3961
|
+
clearTimeout(timeoutId);
|
|
3962
|
+
if (!response.ok) {
|
|
3963
|
+
const errorData = await response.json().catch(() => ({}));
|
|
3964
|
+
throw new BaasError(
|
|
3965
|
+
errorData.message || `API error: ${response.status} ${response.statusText}`,
|
|
3966
|
+
response.status,
|
|
3967
|
+
errorData
|
|
3968
|
+
);
|
|
3969
|
+
}
|
|
3970
|
+
const text = await response.text();
|
|
3971
|
+
if (!text) return void 0;
|
|
3972
|
+
return JSON.parse(text);
|
|
3973
|
+
} catch (error) {
|
|
3974
|
+
clearTimeout(timeoutId);
|
|
3975
|
+
if (error instanceof BaasError) throw error;
|
|
3976
|
+
const err = error;
|
|
3977
|
+
if (err.name === "AbortError") {
|
|
3978
|
+
throw new BaasError("Request timeout", 408);
|
|
3979
|
+
}
|
|
3980
|
+
throw new BaasError(`Network error: ${err.message}`, 0, { originalError: err.message });
|
|
3981
|
+
}
|
|
3982
|
+
}
|
|
3983
|
+
};
|
|
3984
|
+
var BaasError = class extends Error {
|
|
3985
|
+
constructor(message, statusCode, details) {
|
|
3986
|
+
super(message);
|
|
3987
|
+
this.statusCode = statusCode;
|
|
3988
|
+
this.details = details;
|
|
3989
|
+
this.name = "BaasError";
|
|
3990
|
+
}
|
|
3991
|
+
statusCode;
|
|
3992
|
+
details;
|
|
3993
|
+
};
|
|
3994
|
+
function validateUrl2(url, allowInsecure = false) {
|
|
3995
|
+
try {
|
|
3996
|
+
const parsed = new URL(url);
|
|
3997
|
+
if (!["http:", "https:"].includes(parsed.protocol)) {
|
|
3998
|
+
throw new BaasError(`Invalid protocol: ${parsed.protocol}`, 400);
|
|
3999
|
+
}
|
|
4000
|
+
if (!allowInsecure && parsed.protocol !== "https:") {
|
|
4001
|
+
throw new BaasError(
|
|
4002
|
+
"HTTPS is required for secure connections. Set allowInsecure=true for local development.",
|
|
4003
|
+
400
|
|
4004
|
+
);
|
|
4005
|
+
}
|
|
4006
|
+
return parsed.origin;
|
|
4007
|
+
} catch (error) {
|
|
4008
|
+
if (error instanceof BaasError) throw error;
|
|
4009
|
+
throw new BaasError(`Invalid URL: ${url}`, 400);
|
|
4010
|
+
}
|
|
4011
|
+
}
|
|
4012
|
+
|
|
4013
|
+
// src/nestjs/index.ts
|
|
4014
|
+
var nestjs_exports = {};
|
|
4015
|
+
__export(nestjs_exports, {
|
|
4016
|
+
SMART_ENGINE_CONFIG: () => SMART_ENGINE_CONFIG,
|
|
4017
|
+
SmartEngineModule: () => exports.SmartEngineModule,
|
|
4018
|
+
SmartEngineService: () => exports.SmartEngineService
|
|
4019
|
+
});
|
|
4020
|
+
var SMART_ENGINE_CONFIG = "SMART_ENGINE_CONFIG";
|
|
4021
|
+
exports.SmartEngineService = class SmartEngineService {
|
|
4022
|
+
constructor(config) {
|
|
4023
|
+
this.config = config;
|
|
4024
|
+
}
|
|
4025
|
+
config;
|
|
4026
|
+
logger = new common.Logger(exports.SmartEngineService.name);
|
|
4027
|
+
client = null;
|
|
4028
|
+
baasClient = null;
|
|
4029
|
+
connected = false;
|
|
4030
|
+
reconnectTimer = null;
|
|
4031
|
+
reconnectAttempts = 0;
|
|
4032
|
+
/**
|
|
4033
|
+
* Initialize the service when the module starts
|
|
4034
|
+
*/
|
|
4035
|
+
async onModuleInit() {
|
|
4036
|
+
if (!this.config) {
|
|
4037
|
+
this.logger.warn(
|
|
4038
|
+
"SmartEngineService initialized without configuration. Call configure() before using."
|
|
4039
|
+
);
|
|
4040
|
+
return;
|
|
4041
|
+
}
|
|
4042
|
+
await this.initialize(this.config);
|
|
4043
|
+
}
|
|
4044
|
+
/**
|
|
4045
|
+
* Clean up resources when the module is destroyed
|
|
4046
|
+
*/
|
|
4047
|
+
async onModuleDestroy() {
|
|
4048
|
+
await this.shutdown();
|
|
4049
|
+
}
|
|
4050
|
+
/**
|
|
4051
|
+
* Initialize the service with configuration
|
|
4052
|
+
*
|
|
4053
|
+
* Can be called manually if not using DI configuration
|
|
4054
|
+
*/
|
|
4055
|
+
async initialize(config) {
|
|
4056
|
+
this.logger.log(`Initializing SmartEngineService for ${config.baseUrl}`);
|
|
4057
|
+
try {
|
|
4058
|
+
this.client = new SmartEngineClient({
|
|
4059
|
+
baseUrl: config.baseUrl,
|
|
4060
|
+
apiKey: config.apiKey,
|
|
4061
|
+
authToken: config.authToken,
|
|
4062
|
+
timeout: config.timeout,
|
|
4063
|
+
allowInsecure: config.allowInsecure
|
|
4064
|
+
});
|
|
4065
|
+
this.baasClient = this.createBaasClient(this.client);
|
|
4066
|
+
if (config.testConnection !== false) {
|
|
4067
|
+
await this.testConnection();
|
|
4068
|
+
}
|
|
4069
|
+
this.connected = true;
|
|
4070
|
+
this.reconnectAttempts = 0;
|
|
4071
|
+
this.logger.log("SmartEngineService initialized successfully");
|
|
4072
|
+
} catch (error) {
|
|
4073
|
+
const err = error;
|
|
4074
|
+
this.logger.error(`Failed to initialize SmartEngineService: ${err.message}`);
|
|
4075
|
+
if (config.autoReconnect) {
|
|
4076
|
+
this.scheduleReconnect(config);
|
|
4077
|
+
} else {
|
|
4078
|
+
throw error;
|
|
4079
|
+
}
|
|
4080
|
+
}
|
|
4081
|
+
}
|
|
4082
|
+
/**
|
|
4083
|
+
* Gracefully shutdown the service
|
|
4084
|
+
*/
|
|
4085
|
+
async shutdown() {
|
|
4086
|
+
this.logger.log("Shutting down SmartEngineService");
|
|
4087
|
+
if (this.reconnectTimer) {
|
|
4088
|
+
clearTimeout(this.reconnectTimer);
|
|
4089
|
+
this.reconnectTimer = null;
|
|
4090
|
+
}
|
|
4091
|
+
this.client = null;
|
|
4092
|
+
this.baasClient = null;
|
|
4093
|
+
this.connected = false;
|
|
4094
|
+
this.logger.log("SmartEngineService shutdown complete");
|
|
4095
|
+
}
|
|
4096
|
+
/**
|
|
4097
|
+
* Get the SmartEngineClient instance
|
|
4098
|
+
*
|
|
4099
|
+
* @throws SmartEngineError if client is not initialized
|
|
4100
|
+
*/
|
|
4101
|
+
getClient() {
|
|
4102
|
+
if (!this.client) {
|
|
4103
|
+
throw new SmartEngineError2(
|
|
4104
|
+
"SmartEngineClient not initialized. Ensure SmartEngineService is configured properly.",
|
|
4105
|
+
500
|
|
4106
|
+
);
|
|
4107
|
+
}
|
|
4108
|
+
return this.client;
|
|
4109
|
+
}
|
|
4110
|
+
/**
|
|
4111
|
+
* Get the BaaS client for simplified blockchain access
|
|
4112
|
+
*
|
|
4113
|
+
* The BaaS client provides a simplified interface for applications
|
|
4114
|
+
* that need managed blockchain infrastructure access.
|
|
4115
|
+
*
|
|
4116
|
+
* @throws SmartEngineError if client is not initialized
|
|
4117
|
+
*/
|
|
4118
|
+
getBaasClient() {
|
|
4119
|
+
if (!this.baasClient) {
|
|
4120
|
+
throw new SmartEngineError2(
|
|
4121
|
+
"BaasClient not initialized. Ensure SmartEngineService is configured properly.",
|
|
4122
|
+
500
|
|
4123
|
+
);
|
|
4124
|
+
}
|
|
4125
|
+
return this.baasClient;
|
|
4126
|
+
}
|
|
4127
|
+
/**
|
|
4128
|
+
* Check if the service is connected and healthy
|
|
4129
|
+
*/
|
|
4130
|
+
isConnected() {
|
|
4131
|
+
return this.connected;
|
|
4132
|
+
}
|
|
4133
|
+
/**
|
|
4134
|
+
* Test the connection to the validator
|
|
4135
|
+
*/
|
|
4136
|
+
async testConnection() {
|
|
4137
|
+
if (!this.client) {
|
|
4138
|
+
return false;
|
|
4139
|
+
}
|
|
4140
|
+
try {
|
|
4141
|
+
const health = await this.client.getHealth();
|
|
4142
|
+
this.connected = health.status === "healthy" || health.status === "ok";
|
|
4143
|
+
return this.connected;
|
|
4144
|
+
} catch (error) {
|
|
4145
|
+
const err = error;
|
|
4146
|
+
this.logger.warn(`Connection test failed: ${err.message}`);
|
|
4147
|
+
this.connected = false;
|
|
4148
|
+
return false;
|
|
4149
|
+
}
|
|
4150
|
+
}
|
|
4151
|
+
/**
|
|
4152
|
+
* Get the current connection status
|
|
4153
|
+
*/
|
|
4154
|
+
getStatus() {
|
|
4155
|
+
return {
|
|
4156
|
+
connected: this.connected,
|
|
4157
|
+
baseUrl: this.client?.getBaseUrl() ?? null,
|
|
4158
|
+
authenticated: this.client?.isAuthenticated() ?? false,
|
|
4159
|
+
reconnectAttempts: this.reconnectAttempts
|
|
4160
|
+
};
|
|
4161
|
+
}
|
|
4162
|
+
/**
|
|
4163
|
+
* Create a SmartGatewayClient for gateway operations
|
|
4164
|
+
*/
|
|
4165
|
+
createGatewayClient(config) {
|
|
4166
|
+
return new SmartGatewayClient(config);
|
|
4167
|
+
}
|
|
4168
|
+
/**
|
|
4169
|
+
* Create a BaaS client for host operations
|
|
4170
|
+
*/
|
|
4171
|
+
createHostClient(config) {
|
|
4172
|
+
return new BaasClient(config);
|
|
4173
|
+
}
|
|
4174
|
+
/**
|
|
4175
|
+
* Create a BaaS client wrapper around the SmartEngineClient
|
|
4176
|
+
*/
|
|
4177
|
+
createBaasClient(client) {
|
|
4178
|
+
return {
|
|
4179
|
+
client,
|
|
4180
|
+
isHealthy: async () => {
|
|
4181
|
+
try {
|
|
4182
|
+
const health = await client.getHealth();
|
|
4183
|
+
return health.status === "healthy" || health.status === "ok";
|
|
4184
|
+
} catch {
|
|
4185
|
+
return false;
|
|
4186
|
+
}
|
|
4187
|
+
},
|
|
4188
|
+
getBaseUrl: () => client.getBaseUrl()
|
|
4189
|
+
};
|
|
4190
|
+
}
|
|
4191
|
+
/**
|
|
4192
|
+
* Schedule a reconnection attempt
|
|
4193
|
+
*/
|
|
4194
|
+
scheduleReconnect(config) {
|
|
4195
|
+
const maxAttempts = config.maxReconnectAttempts ?? 0;
|
|
4196
|
+
const interval = config.reconnectInterval ?? 5e3;
|
|
4197
|
+
if (maxAttempts > 0 && this.reconnectAttempts >= maxAttempts) {
|
|
4198
|
+
this.logger.error(`Max reconnection attempts (${maxAttempts}) reached. Giving up.`);
|
|
4199
|
+
return;
|
|
4200
|
+
}
|
|
4201
|
+
this.reconnectAttempts++;
|
|
4202
|
+
this.logger.log(`Scheduling reconnection attempt ${this.reconnectAttempts} in ${interval}ms`);
|
|
4203
|
+
this.reconnectTimer = setTimeout(async () => {
|
|
4204
|
+
try {
|
|
4205
|
+
await this.initialize(config);
|
|
4206
|
+
} catch {
|
|
4207
|
+
}
|
|
4208
|
+
}, interval);
|
|
4209
|
+
}
|
|
4210
|
+
};
|
|
4211
|
+
exports.SmartEngineService = __decorateClass([
|
|
4212
|
+
common.Injectable(),
|
|
4213
|
+
__decorateParam(0, common.Optional()),
|
|
4214
|
+
__decorateParam(0, common.Inject(SMART_ENGINE_CONFIG))
|
|
4215
|
+
], exports.SmartEngineService);
|
|
4216
|
+
|
|
4217
|
+
// src/nestjs/smart-engine.module.ts
|
|
4218
|
+
exports.SmartEngineModule = class SmartEngineModule {
|
|
4219
|
+
/**
|
|
4220
|
+
* Configure the module with static configuration
|
|
4221
|
+
*
|
|
4222
|
+
* @param config - SmartEngine service configuration
|
|
4223
|
+
* @param isGlobal - Whether to make the module global (default: true)
|
|
4224
|
+
*/
|
|
4225
|
+
static forRoot(config, isGlobal = true) {
|
|
4226
|
+
return {
|
|
4227
|
+
module: exports.SmartEngineModule,
|
|
4228
|
+
global: isGlobal,
|
|
4229
|
+
providers: [
|
|
4230
|
+
{
|
|
4231
|
+
provide: SMART_ENGINE_CONFIG,
|
|
4232
|
+
useValue: config
|
|
4233
|
+
},
|
|
4234
|
+
exports.SmartEngineService
|
|
4235
|
+
],
|
|
4236
|
+
exports: [exports.SmartEngineService]
|
|
4237
|
+
};
|
|
4238
|
+
}
|
|
4239
|
+
/**
|
|
4240
|
+
* Configure the module with async configuration
|
|
4241
|
+
*
|
|
4242
|
+
* Supports factory functions, configuration classes, and existing providers.
|
|
4243
|
+
*
|
|
4244
|
+
* @param options - Async configuration options
|
|
4245
|
+
*/
|
|
4246
|
+
static forRootAsync(options) {
|
|
4247
|
+
const asyncProviders = this.createAsyncProviders(options);
|
|
4248
|
+
return {
|
|
4249
|
+
module: exports.SmartEngineModule,
|
|
4250
|
+
global: options.isGlobal ?? true,
|
|
4251
|
+
imports: options.imports || [],
|
|
4252
|
+
providers: [...asyncProviders, exports.SmartEngineService],
|
|
4253
|
+
exports: [exports.SmartEngineService]
|
|
4254
|
+
};
|
|
4255
|
+
}
|
|
4256
|
+
/**
|
|
4257
|
+
* Create async providers based on configuration options
|
|
4258
|
+
*/
|
|
4259
|
+
static createAsyncProviders(options) {
|
|
4260
|
+
if (options.useFactory) {
|
|
4261
|
+
return [
|
|
4262
|
+
{
|
|
4263
|
+
provide: SMART_ENGINE_CONFIG,
|
|
4264
|
+
useFactory: options.useFactory,
|
|
4265
|
+
inject: options.inject || []
|
|
4266
|
+
}
|
|
4267
|
+
];
|
|
4268
|
+
}
|
|
4269
|
+
if (options.useClass) {
|
|
4270
|
+
return [
|
|
4271
|
+
{
|
|
4272
|
+
provide: options.useClass,
|
|
4273
|
+
useClass: options.useClass
|
|
4274
|
+
},
|
|
4275
|
+
{
|
|
4276
|
+
provide: SMART_ENGINE_CONFIG,
|
|
4277
|
+
useFactory: async (optionsFactory) => optionsFactory.createSmartEngineOptions(),
|
|
4278
|
+
inject: [options.useClass]
|
|
4279
|
+
}
|
|
4280
|
+
];
|
|
4281
|
+
}
|
|
4282
|
+
if (options.useExisting) {
|
|
4283
|
+
return [
|
|
4284
|
+
{
|
|
4285
|
+
provide: SMART_ENGINE_CONFIG,
|
|
4286
|
+
useFactory: async (optionsFactory) => optionsFactory.createSmartEngineOptions(),
|
|
4287
|
+
inject: [options.useExisting]
|
|
4288
|
+
}
|
|
4289
|
+
];
|
|
4290
|
+
}
|
|
4291
|
+
throw new Error(
|
|
4292
|
+
"Invalid SmartEngineModuleAsyncOptions: must provide useFactory, useClass, or useExisting"
|
|
4293
|
+
);
|
|
4294
|
+
}
|
|
4295
|
+
};
|
|
4296
|
+
exports.SmartEngineModule = __decorateClass([
|
|
4297
|
+
common.Module({})
|
|
4298
|
+
], exports.SmartEngineModule);
|
|
4299
|
+
|
|
4300
|
+
exports.AgentsClient = AgentsClient;
|
|
4301
|
+
exports.BaasClient = BaasClient;
|
|
4302
|
+
exports.BaasError = BaasError;
|
|
4303
|
+
exports.CapabilityNotEnabledError = CapabilityNotEnabledError;
|
|
4304
|
+
exports.CapabilityValidationError = CapabilityValidationError;
|
|
4305
|
+
exports.CircuitBreaker = CircuitBreaker;
|
|
4306
|
+
exports.CircuitBreakerOpenError = CircuitBreakerOpenError;
|
|
4307
|
+
exports.DEFAULT_CIRCUIT_BREAKER_CONFIG = DEFAULT_CIRCUIT_BREAKER_CONFIG;
|
|
4308
|
+
exports.DEFAULT_RETRY_CONFIG = DEFAULT_RETRY_CONFIG;
|
|
4309
|
+
exports.DatabaseClient = DatabaseClient;
|
|
4310
|
+
exports.DeploymentClient = DeploymentClient;
|
|
4311
|
+
exports.DnsClient = DnsClient;
|
|
4312
|
+
exports.DomainsClient = DomainsClient;
|
|
4313
|
+
exports.ErrorCode = ErrorCode;
|
|
4314
|
+
exports.FunctionsClient = FunctionsClient;
|
|
4315
|
+
exports.IPFSClient = IPFSClient;
|
|
4316
|
+
exports.MIRROR_NODE_URLS = MIRROR_NODE_URLS;
|
|
4317
|
+
exports.MessagingClient = MessagingClient;
|
|
4318
|
+
exports.MirrorNodeClient = MirrorNodeClient;
|
|
4319
|
+
exports.MirrorNodeError = MirrorNodeError;
|
|
4320
|
+
exports.RateLimiter = RateLimiter;
|
|
4321
|
+
exports.RoutingClient = RoutingClient;
|
|
4322
|
+
exports.SMART_ENGINE_CONFIG = SMART_ENGINE_CONFIG;
|
|
4323
|
+
exports.SdkHttpError = SdkHttpError;
|
|
4324
|
+
exports.SettlementClient = SettlementClient;
|
|
4325
|
+
exports.SmartEngineClient = SmartEngineClient;
|
|
4326
|
+
exports.SmartEngineError = SmartEngineError;
|
|
4327
|
+
exports.SmartGatewayClient = SmartGatewayClient;
|
|
4328
|
+
exports.SnapshotsClient = SnapshotsClient;
|
|
4329
|
+
exports.StorageClient = StorageClient;
|
|
4330
|
+
exports.SubscriptionClient = SubscriptionClient;
|
|
4331
|
+
exports.TSSClient = TSSClient;
|
|
4332
|
+
exports.TransactionsClient = TransactionsClient;
|
|
4333
|
+
exports.UnsupportedCapabilityError = UnsupportedCapabilityError;
|
|
4334
|
+
exports.ValidatorAuthClient = ValidatorAuthClient;
|
|
4335
|
+
exports.ValidatorAuthError = ValidatorAuthError;
|
|
4336
|
+
exports.ValidatorDiscoveryClient = ValidatorDiscoveryClient;
|
|
4337
|
+
exports.auth = auth_exports;
|
|
4338
|
+
exports.baas = baas_exports;
|
|
4339
|
+
exports.chains = chains_exports;
|
|
4340
|
+
exports.createHttpClient = createHttpClient;
|
|
4341
|
+
exports.createResilientFetchWithBreaker = createResilientFetchWithBreaker;
|
|
4342
|
+
exports.discovery = discovery_exports;
|
|
4343
|
+
exports.encodePathParam = encodePathParam;
|
|
4344
|
+
exports.nestjs = nestjs_exports;
|
|
4345
|
+
exports.resilientFetch = resilientFetch;
|
|
4346
|
+
exports.settlement = settlement_exports;
|
|
4347
|
+
exports.subscription = subscription_exports;
|
|
4348
|
+
exports.validateAgentRules = validateAgentRules;
|
|
4349
|
+
//# sourceMappingURL=index.js.map
|
|
4350
|
+
//# sourceMappingURL=index.js.map
|