openttt 0.2.8 → 0.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +0 -2
- package/dist/index.js +2 -2
- package/dist/integrity_client.d.ts +2 -0
- package/dist/integrity_client.js +10 -2
- package/package.json +1 -1
- package/dist/ttt_client.d.ts +0 -131
- package/dist/ttt_client.js +0 -441
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
export * from "./evm_connector";
|
|
2
2
|
export * from "./x402_enforcer";
|
|
3
|
-
export * from "./ttt_builder";
|
|
4
3
|
export * from "./protocol_fee";
|
|
5
4
|
export * from "./pool_registry";
|
|
6
5
|
export * from "./v4_hook";
|
|
7
6
|
export * from "./logger";
|
|
8
7
|
export * from "./types";
|
|
9
|
-
export * from "./ttt_client";
|
|
10
8
|
export * from "./http_client";
|
|
11
9
|
export * from "./time_synthesis";
|
|
12
10
|
export * from "./dynamic_fee";
|
package/dist/index.js
CHANGED
|
@@ -21,13 +21,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
21
21
|
// 위 모듈은 서버 코드에서 직접 경로로 import할 것.
|
|
22
22
|
__exportStar(require("./evm_connector"), exports);
|
|
23
23
|
__exportStar(require("./x402_enforcer"), exports);
|
|
24
|
-
|
|
24
|
+
// ttt_builder omitted — requires adaptive_switch (server-internal, not in public SDK)
|
|
25
25
|
__exportStar(require("./protocol_fee"), exports);
|
|
26
26
|
__exportStar(require("./pool_registry"), exports);
|
|
27
27
|
__exportStar(require("./v4_hook"), exports);
|
|
28
28
|
__exportStar(require("./logger"), exports);
|
|
29
29
|
__exportStar(require("./types"), exports);
|
|
30
|
-
|
|
30
|
+
// ttt_client omitted — requires auto_mint (server-internal, not in public SDK)
|
|
31
31
|
__exportStar(require("./http_client"), exports);
|
|
32
32
|
__exportStar(require("./time_synthesis"), exports);
|
|
33
33
|
__exportStar(require("./dynamic_fee"), exports);
|
|
@@ -14,8 +14,10 @@ export interface IntegrityVerifyResult {
|
|
|
14
14
|
export declare class IntegrityClient {
|
|
15
15
|
private baseUrl;
|
|
16
16
|
private timeoutMs;
|
|
17
|
+
private apiKey;
|
|
17
18
|
constructor(baseUrl?: string, options?: {
|
|
18
19
|
timeoutMs?: number;
|
|
20
|
+
apiKey?: string;
|
|
19
21
|
});
|
|
20
22
|
/**
|
|
21
23
|
* Forward pass: encode data through integrity pipeline (server-side).
|
package/dist/integrity_client.js
CHANGED
|
@@ -12,9 +12,11 @@ exports.setDefaultIntegrityClient = setDefaultIntegrityClient;
|
|
|
12
12
|
class IntegrityClient {
|
|
13
13
|
baseUrl;
|
|
14
14
|
timeoutMs;
|
|
15
|
+
apiKey;
|
|
15
16
|
constructor(baseUrl = "https://integrity.helmprotocol.com/api/v1", options) {
|
|
16
17
|
this.baseUrl = baseUrl.replace(/\/$/, "");
|
|
17
18
|
this.timeoutMs = options?.timeoutMs ?? 5000;
|
|
19
|
+
this.apiKey = options?.apiKey ?? (typeof process !== "undefined" ? process.env["INTEGRITY_API_KEY"] : undefined);
|
|
18
20
|
}
|
|
19
21
|
/**
|
|
20
22
|
* Forward pass: encode data through integrity pipeline (server-side).
|
|
@@ -27,7 +29,10 @@ class IntegrityClient {
|
|
|
27
29
|
try {
|
|
28
30
|
const resp = await fetch(`${this.baseUrl}/encode`, {
|
|
29
31
|
method: "POST",
|
|
30
|
-
headers: {
|
|
32
|
+
headers: {
|
|
33
|
+
"Content-Type": "application/json",
|
|
34
|
+
...(this.apiKey ? { "X-Integrity-Key": this.apiKey } : {}),
|
|
35
|
+
},
|
|
31
36
|
body: JSON.stringify({
|
|
32
37
|
data: Buffer.from(data).toString("hex"),
|
|
33
38
|
chainId: chainId,
|
|
@@ -56,7 +61,10 @@ class IntegrityClient {
|
|
|
56
61
|
try {
|
|
57
62
|
const resp = await fetch(`${this.baseUrl}/verify`, {
|
|
58
63
|
method: "POST",
|
|
59
|
-
headers: {
|
|
64
|
+
headers: {
|
|
65
|
+
"Content-Type": "application/json",
|
|
66
|
+
...(this.apiKey ? { "X-Integrity-Key": this.apiKey } : {}),
|
|
67
|
+
},
|
|
60
68
|
body: JSON.stringify({
|
|
61
69
|
data: Buffer.from(data).toString("hex"),
|
|
62
70
|
shards: originalShards.map((s) => Buffer.from(s).toString("hex")),
|
package/package.json
CHANGED
package/dist/ttt_client.d.ts
DELETED
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
import { EventEmitter } from "events";
|
|
2
|
-
import { AutoMintConfig, TTTClientConfig, MintResult, HealthStatus } from "./types";
|
|
3
|
-
import { HttpOnlyClient, HttpOnlyClientOptions } from "./http_client";
|
|
4
|
-
export { HealthStatus } from "./types";
|
|
5
|
-
/**
|
|
6
|
-
* Typed event map for TTTClient EventEmitter.
|
|
7
|
-
*/
|
|
8
|
-
export interface TTTClientEvents {
|
|
9
|
-
mint: [result: MintResult];
|
|
10
|
-
error: [error: Error];
|
|
11
|
-
alert: [alert: string];
|
|
12
|
-
latency: [ms: number];
|
|
13
|
-
modeSwitch: [mode: string];
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* TTTClient - SDK entry point for DEX operators.
|
|
17
|
-
* Initializes all internal modules and manages the auto-minting process.
|
|
18
|
-
*/
|
|
19
|
-
export declare class TTTClient extends EventEmitter {
|
|
20
|
-
private config;
|
|
21
|
-
private autoMintEngine;
|
|
22
|
-
private poolRegistry;
|
|
23
|
-
private isInitialized;
|
|
24
|
-
private mintCount;
|
|
25
|
-
private mintFailures;
|
|
26
|
-
private totalFeesPaid;
|
|
27
|
-
private signer;
|
|
28
|
-
private lastTokenId;
|
|
29
|
-
private mintLatencies;
|
|
30
|
-
private maxLatencyHistory;
|
|
31
|
-
private lastMintAt;
|
|
32
|
-
private startedAt;
|
|
33
|
-
private minBalanceWei;
|
|
34
|
-
constructor(config: AutoMintConfig);
|
|
35
|
-
/**
|
|
36
|
-
* Static factory for Base Mainnet
|
|
37
|
-
*/
|
|
38
|
-
static forBase(config: Omit<TTTClientConfig, 'network'>): Promise<TTTClient>;
|
|
39
|
-
/**
|
|
40
|
-
* Static factory for Base Sepolia
|
|
41
|
-
*/
|
|
42
|
-
static forSepolia(config: Omit<TTTClientConfig, 'network'>): Promise<TTTClient>;
|
|
43
|
-
/**
|
|
44
|
-
* Zero-friction sandbox mode — no ETH, no signer, no on-chain interaction.
|
|
45
|
-
*
|
|
46
|
-
* Returns an HttpOnlyClient that fetches verified time from NIST, Apple,
|
|
47
|
-
* Google, and Cloudflare HTTPS endpoints and produces HMAC-signed PoTs.
|
|
48
|
-
*
|
|
49
|
-
* @example
|
|
50
|
-
* // Zero-friction: no wallet, no RPC, no gas
|
|
51
|
-
* const client = TTTClient.httpOnly();
|
|
52
|
-
* const pot = await client.generatePoT();
|
|
53
|
-
* console.log(pot.timestamp, pot.confidence);
|
|
54
|
-
*
|
|
55
|
-
* // Verify locally (no on-chain check needed)
|
|
56
|
-
* const result = client.verifyPoT(pot);
|
|
57
|
-
* console.log(result.valid); // true
|
|
58
|
-
*/
|
|
59
|
-
static httpOnly(options?: HttpOnlyClientOptions): HttpOnlyClient;
|
|
60
|
-
/**
|
|
61
|
-
* Universal factory to create and initialize a client
|
|
62
|
-
*/
|
|
63
|
-
static create(config: TTTClientConfig): Promise<TTTClient>;
|
|
64
|
-
/**
|
|
65
|
-
* Gracefully shuts down the SDK, stopping all background processes and listeners.
|
|
66
|
-
*/
|
|
67
|
-
destroy(): Promise<void>;
|
|
68
|
-
/**
|
|
69
|
-
* Initialize the SDK: RPC connection, time sources, fee engine wiring.
|
|
70
|
-
*/
|
|
71
|
-
initialize(): Promise<void>;
|
|
72
|
-
/**
|
|
73
|
-
* Start the auto-minting process.
|
|
74
|
-
*/
|
|
75
|
-
startAutoMint(): void;
|
|
76
|
-
/**
|
|
77
|
-
* Stop the auto-minting process.
|
|
78
|
-
*/
|
|
79
|
-
stopAutoMint(): void;
|
|
80
|
-
/**
|
|
81
|
-
* Resume auto-minting after a circuit breaker trip.
|
|
82
|
-
* Resets consecutive failure count and restarts the engine.
|
|
83
|
-
*/
|
|
84
|
-
resume(): void;
|
|
85
|
-
/**
|
|
86
|
-
* List registered pools.
|
|
87
|
-
*/
|
|
88
|
-
listPools(): string[];
|
|
89
|
-
/**
|
|
90
|
-
* Get stats for a specific pool.
|
|
91
|
-
*/
|
|
92
|
-
getPoolStats(poolAddress: string): {
|
|
93
|
-
minted: bigint;
|
|
94
|
-
burned: bigint;
|
|
95
|
-
} | null;
|
|
96
|
-
/**
|
|
97
|
-
* Set minimum ETH balance threshold for health alerts.
|
|
98
|
-
*/
|
|
99
|
-
setMinBalance(weiAmount: bigint): void;
|
|
100
|
-
/**
|
|
101
|
-
* Register alert callback for real-time notifications.
|
|
102
|
-
* Backward compatible: delegates to EventEmitter 'alert' event.
|
|
103
|
-
*/
|
|
104
|
-
onAlert(callback: (alert: string) => void): void;
|
|
105
|
-
private emitAlert;
|
|
106
|
-
/**
|
|
107
|
-
* Record a mint failure (called internally or externally).
|
|
108
|
-
*/
|
|
109
|
-
recordMintFailure(): void;
|
|
110
|
-
/**
|
|
111
|
-
* Record mint latency in ms (called from auto-mint wrapper).
|
|
112
|
-
*/
|
|
113
|
-
recordMintLatency(ms: number): void;
|
|
114
|
-
/**
|
|
115
|
-
* Production health check — liveness + readiness + metrics.
|
|
116
|
-
* No exceptions: always returns a HealthStatus object.
|
|
117
|
-
*/
|
|
118
|
-
getHealth(): Promise<HealthStatus>;
|
|
119
|
-
/**
|
|
120
|
-
* Return current SDK status and statistics (balance, mint count, fees, etc.)
|
|
121
|
-
*/
|
|
122
|
-
getStatus(): Promise<{
|
|
123
|
-
isInitialized: boolean;
|
|
124
|
-
tier: string;
|
|
125
|
-
mintCount: number;
|
|
126
|
-
totalFeesPaid: string;
|
|
127
|
-
balance: string;
|
|
128
|
-
tttBalance: string;
|
|
129
|
-
lastTokenId: string | null;
|
|
130
|
-
}>;
|
|
131
|
-
}
|
package/dist/ttt_client.js
DELETED
|
@@ -1,441 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.TTTClient = void 0;
|
|
4
|
-
const ethers_1 = require("ethers");
|
|
5
|
-
const events_1 = require("events");
|
|
6
|
-
const auto_mint_1 = require("./auto_mint");
|
|
7
|
-
const types_1 = require("./types");
|
|
8
|
-
const logger_1 = require("./logger");
|
|
9
|
-
const pool_registry_1 = require("./pool_registry");
|
|
10
|
-
const networks_1 = require("./networks");
|
|
11
|
-
const signer_1 = require("./signer");
|
|
12
|
-
const http_client_1 = require("./http_client");
|
|
13
|
-
/**
|
|
14
|
-
* TTTClient - SDK entry point for DEX operators.
|
|
15
|
-
* Initializes all internal modules and manages the auto-minting process.
|
|
16
|
-
*/
|
|
17
|
-
class TTTClient extends events_1.EventEmitter {
|
|
18
|
-
config;
|
|
19
|
-
autoMintEngine;
|
|
20
|
-
poolRegistry;
|
|
21
|
-
isInitialized = false;
|
|
22
|
-
mintCount = 0;
|
|
23
|
-
mintFailures = 0;
|
|
24
|
-
totalFeesPaid = 0n;
|
|
25
|
-
signer = null;
|
|
26
|
-
lastTokenId = null;
|
|
27
|
-
mintLatencies = [];
|
|
28
|
-
maxLatencyHistory;
|
|
29
|
-
lastMintAt = null;
|
|
30
|
-
startedAt = new Date();
|
|
31
|
-
minBalanceWei = ethers_1.ethers.parseEther("0.01"); // 0.01 ETH alert threshold
|
|
32
|
-
constructor(config) {
|
|
33
|
-
super();
|
|
34
|
-
this.config = config;
|
|
35
|
-
this.maxLatencyHistory = config.maxLatencyHistory ?? 100;
|
|
36
|
-
this.autoMintEngine = new auto_mint_1.AutoMintEngine(config);
|
|
37
|
-
this.poolRegistry = new pool_registry_1.PoolRegistry();
|
|
38
|
-
// Wire callbacks to update stats + emit events
|
|
39
|
-
this.autoMintEngine.setOnMint((result) => {
|
|
40
|
-
this.mintCount++;
|
|
41
|
-
this.totalFeesPaid += result.protocolFeePaid;
|
|
42
|
-
this.lastTokenId = result.tokenId;
|
|
43
|
-
this.lastMintAt = new Date();
|
|
44
|
-
// Record in registry
|
|
45
|
-
this.poolRegistry.recordMint(this.config.poolAddress, 1n);
|
|
46
|
-
// Emit typed 'mint' event
|
|
47
|
-
this.emit('mint', result);
|
|
48
|
-
});
|
|
49
|
-
// Wire failure/latency metrics from AutoMint loop to TTTClient
|
|
50
|
-
this.autoMintEngine.setOnFailure((error) => {
|
|
51
|
-
this.mintFailures++;
|
|
52
|
-
this.emit('error', error);
|
|
53
|
-
});
|
|
54
|
-
this.autoMintEngine.setOnLatency((ms) => {
|
|
55
|
-
this.mintLatencies.push(ms);
|
|
56
|
-
if (this.mintLatencies.length > this.maxLatencyHistory) {
|
|
57
|
-
this.mintLatencies.shift();
|
|
58
|
-
}
|
|
59
|
-
this.emit('latency', ms);
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
/**
|
|
63
|
-
* Static factory for Base Mainnet
|
|
64
|
-
*/
|
|
65
|
-
static async forBase(config) {
|
|
66
|
-
return this.create({ ...config, network: 'base' });
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* Static factory for Base Sepolia
|
|
70
|
-
*/
|
|
71
|
-
static async forSepolia(config) {
|
|
72
|
-
return this.create({ ...config, network: 'sepolia' });
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* Zero-friction sandbox mode — no ETH, no signer, no on-chain interaction.
|
|
76
|
-
*
|
|
77
|
-
* Returns an HttpOnlyClient that fetches verified time from NIST, Apple,
|
|
78
|
-
* Google, and Cloudflare HTTPS endpoints and produces HMAC-signed PoTs.
|
|
79
|
-
*
|
|
80
|
-
* @example
|
|
81
|
-
* // Zero-friction: no wallet, no RPC, no gas
|
|
82
|
-
* const client = TTTClient.httpOnly();
|
|
83
|
-
* const pot = await client.generatePoT();
|
|
84
|
-
* console.log(pot.timestamp, pot.confidence);
|
|
85
|
-
*
|
|
86
|
-
* // Verify locally (no on-chain check needed)
|
|
87
|
-
* const result = client.verifyPoT(pot);
|
|
88
|
-
* console.log(result.valid); // true
|
|
89
|
-
*/
|
|
90
|
-
static httpOnly(options) {
|
|
91
|
-
return new http_client_1.HttpOnlyClient(options);
|
|
92
|
-
}
|
|
93
|
-
/**
|
|
94
|
-
* Universal factory to create and initialize a client
|
|
95
|
-
*/
|
|
96
|
-
static async create(config) {
|
|
97
|
-
// 0. Shorthand: privateKey string -> SignerConfig
|
|
98
|
-
if (config.privateKey && !config.signer) {
|
|
99
|
-
const key = config.privateKey.startsWith('0x') ? config.privateKey : '0x' + config.privateKey;
|
|
100
|
-
config = { ...config, signer: { type: 'privateKey', key } };
|
|
101
|
-
}
|
|
102
|
-
// 0.5. Validation: require either signer or privateKey
|
|
103
|
-
if (!config.signer) {
|
|
104
|
-
throw new Error('TTTClient requires either `signer` or `privateKey`. Simplest: TTTClient.forBase({ privateKey: process.env.OPERATOR_PK! })');
|
|
105
|
-
}
|
|
106
|
-
// 0.7. Validate tier — CRITICAL: invalid tier causes setInterval(fn, undefined) = 1ms loop = wallet drain
|
|
107
|
-
const tier = config.tier || "T1_block";
|
|
108
|
-
if (!types_1.TierIntervals[tier]) {
|
|
109
|
-
const validTiers = Object.keys(types_1.TierIntervals).join(', ');
|
|
110
|
-
throw new Error(`[TTTClient] Invalid tier "${tier}". Valid tiers: ${validTiers}`);
|
|
111
|
-
}
|
|
112
|
-
// 0.8. Validate fee rate bounds
|
|
113
|
-
if (config.protocolFeeRate !== undefined) {
|
|
114
|
-
if (config.protocolFeeRate < 0 || config.protocolFeeRate > 1) {
|
|
115
|
-
throw new Error(`[TTTClient] protocolFeeRate must be 0-1 (got ${config.protocolFeeRate}). Example: 0.05 = 5%`);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
// 0.9. Validate network string
|
|
119
|
-
if (typeof config.network === 'string' && !networks_1.NETWORKS[config.network]) {
|
|
120
|
-
const validNets = Object.keys(networks_1.NETWORKS).join(', ');
|
|
121
|
-
throw new Error(`[TTTClient] Unknown network "${config.network}". Valid: ${validNets}. Or pass a custom NetworkConfig object.`);
|
|
122
|
-
}
|
|
123
|
-
// 1. Resolve network defaults
|
|
124
|
-
let net;
|
|
125
|
-
if (typeof config.network === 'string') {
|
|
126
|
-
net = networks_1.NETWORKS[config.network];
|
|
127
|
-
}
|
|
128
|
-
else if (config.network) {
|
|
129
|
-
net = config.network;
|
|
130
|
-
}
|
|
131
|
-
else {
|
|
132
|
-
net = networks_1.NETWORKS.base;
|
|
133
|
-
}
|
|
134
|
-
// 2. Create signer via abstraction
|
|
135
|
-
const abstractSigner = await (0, signer_1.createSigner)(config.signer);
|
|
136
|
-
const signer = abstractSigner.inner;
|
|
137
|
-
// 2.5. Validate addresses — prevent accidental mainnet zero-address usage
|
|
138
|
-
const contractAddr = config.contractAddress || net.tttAddress;
|
|
139
|
-
if (contractAddr === "0x0000000000000000000000000000000000000000") {
|
|
140
|
-
throw new Error("[TTTClient] TTT contract address is zero address. On Base Mainnet, you must provide contractAddress in config (contracts not yet deployed).");
|
|
141
|
-
}
|
|
142
|
-
// 3. Build AutoMintConfig from TTTClientConfig + defaults
|
|
143
|
-
const autoMintConfig = {
|
|
144
|
-
chainId: net.chainId,
|
|
145
|
-
rpcUrl: config.rpcUrl || net.rpcUrl,
|
|
146
|
-
signer: signer,
|
|
147
|
-
contractAddress: config.contractAddress || net.tttAddress,
|
|
148
|
-
feeCollectorAddress: net.protocolFeeAddress,
|
|
149
|
-
poolAddress: (() => {
|
|
150
|
-
const addr = config.poolAddress;
|
|
151
|
-
if (!addr || addr === "0x0000000000000000000000000000000000000000") {
|
|
152
|
-
throw new Error("[TTTClient] poolAddress is required. Provide a valid DEX pool address.");
|
|
153
|
-
}
|
|
154
|
-
return addr;
|
|
155
|
-
})(),
|
|
156
|
-
tier: config.tier || "T1_block",
|
|
157
|
-
timeSources: config.timeSources || ["nist", "google", "cloudflare", "apple"],
|
|
158
|
-
protocolFeeRate: config.protocolFeeRate || 0.05,
|
|
159
|
-
protocolFeeRecipient: (() => {
|
|
160
|
-
const addr = config.protocolFeeRecipient;
|
|
161
|
-
if (!addr || addr === "0x0000000000000000000000000000000000000000") {
|
|
162
|
-
throw new Error("[TTTClient] protocolFeeRecipient is required. Provide a valid fee recipient address.");
|
|
163
|
-
}
|
|
164
|
-
return addr;
|
|
165
|
-
})(),
|
|
166
|
-
fallbackPriceUsd: config.fallbackPriceUsd || 10000n,
|
|
167
|
-
maxLatencyHistory: config.maxLatencyHistory,
|
|
168
|
-
};
|
|
169
|
-
// 4. Instantiate and initialize
|
|
170
|
-
const client = new TTTClient(autoMintConfig);
|
|
171
|
-
await client.initialize();
|
|
172
|
-
if (config.enableGracefulShutdown) {
|
|
173
|
-
process.on('SIGINT', async () => {
|
|
174
|
-
logger_1.logger.info("[TTTClient] SIGINT received, shutting down gracefully...");
|
|
175
|
-
await client.destroy();
|
|
176
|
-
process.exit(0);
|
|
177
|
-
});
|
|
178
|
-
}
|
|
179
|
-
return client;
|
|
180
|
-
}
|
|
181
|
-
/**
|
|
182
|
-
* Gracefully shuts down the SDK, stopping all background processes and listeners.
|
|
183
|
-
*/
|
|
184
|
-
async destroy() {
|
|
185
|
-
if (!this.isInitialized)
|
|
186
|
-
return;
|
|
187
|
-
logger_1.logger.info("[TTTClient] Destroying client...");
|
|
188
|
-
// 1. Stop auto-mint engine
|
|
189
|
-
this.autoMintEngine.stop();
|
|
190
|
-
// 2. Unsubscribe all event listeners
|
|
191
|
-
this.autoMintEngine.getEvmConnector().unsubscribeAll();
|
|
192
|
-
// 3. Clear local state
|
|
193
|
-
this.isInitialized = false;
|
|
194
|
-
this.signer = null;
|
|
195
|
-
logger_1.logger.info("[TTTClient] Client destroyed successfully.");
|
|
196
|
-
}
|
|
197
|
-
/**
|
|
198
|
-
* Initialize the SDK: RPC connection, time sources, fee engine wiring.
|
|
199
|
-
*/
|
|
200
|
-
async initialize() {
|
|
201
|
-
if (this.isInitialized)
|
|
202
|
-
return;
|
|
203
|
-
try {
|
|
204
|
-
logger_1.logger.info(`[TTTClient] Initializing for chain ${this.config.chainId}...`);
|
|
205
|
-
await this.autoMintEngine.initialize();
|
|
206
|
-
const connector = this.autoMintEngine.getEvmConnector();
|
|
207
|
-
this.signer = connector.getSigner();
|
|
208
|
-
// Register initial pool
|
|
209
|
-
await this.poolRegistry.registerPool(this.config.chainId, this.config.poolAddress);
|
|
210
|
-
// Validate feeCollectorAddress early before use
|
|
211
|
-
if (this.config.feeCollectorAddress && !ethers_1.ethers.isAddress(this.config.feeCollectorAddress)) {
|
|
212
|
-
throw new Error(`[TTTClient] Invalid feeCollectorAddress: ${this.config.feeCollectorAddress}`);
|
|
213
|
-
}
|
|
214
|
-
// Attach ProtocolFee contract if address is provided
|
|
215
|
-
if (this.config.feeCollectorAddress) {
|
|
216
|
-
const protocolFeeAbi = [
|
|
217
|
-
"event FeeCollected(address indexed payer, uint256 amount, uint256 nonce)"
|
|
218
|
-
];
|
|
219
|
-
connector.attachProtocolFeeContract(this.config.feeCollectorAddress, protocolFeeAbi);
|
|
220
|
-
}
|
|
221
|
-
// Subscribe to events
|
|
222
|
-
await connector.subscribeToEvents({
|
|
223
|
-
onMinted: async (to, tokenId, amount) => {
|
|
224
|
-
logger_1.logger.info(`[TTTClient] Event: TTTMinted to ${to}, tokenId: ${tokenId}, amount: ${amount}`);
|
|
225
|
-
const myAddr = await this.signer?.getAddress();
|
|
226
|
-
if (to.toLowerCase() === myAddr?.toLowerCase()) {
|
|
227
|
-
// Logic could go here to update local state
|
|
228
|
-
}
|
|
229
|
-
},
|
|
230
|
-
onBurned: (from, tokenId, amount, tier) => {
|
|
231
|
-
logger_1.logger.info(`[TTTClient] Event: TTTBurned from ${from}, tokenId: ${tokenId}, amount: ${amount}, tier: ${tier}`);
|
|
232
|
-
this.poolRegistry.recordBurn(this.config.poolAddress, amount);
|
|
233
|
-
},
|
|
234
|
-
onFeeCollected: (payer, amount, nonce) => {
|
|
235
|
-
logger_1.logger.info(`[TTTClient] Event: FeeCollected from ${payer}, amount: ${amount}, nonce: ${nonce}`);
|
|
236
|
-
}
|
|
237
|
-
});
|
|
238
|
-
this.isInitialized = true;
|
|
239
|
-
logger_1.logger.info(`[TTTClient] SDK initialized successfully`);
|
|
240
|
-
}
|
|
241
|
-
catch (error) {
|
|
242
|
-
this.isInitialized = false;
|
|
243
|
-
this.signer = null;
|
|
244
|
-
logger_1.logger.error(`[TTTClient] Initialization failed, state rolled back: ${error}`);
|
|
245
|
-
throw error;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
/**
|
|
249
|
-
* Start the auto-minting process.
|
|
250
|
-
*/
|
|
251
|
-
startAutoMint() {
|
|
252
|
-
if (!this.isInitialized) {
|
|
253
|
-
throw new Error("SDK must be initialized before starting auto-mint");
|
|
254
|
-
}
|
|
255
|
-
this.autoMintEngine.start();
|
|
256
|
-
logger_1.logger.info(`[TTTClient] Auto-minting started for tier ${this.config.tier}`);
|
|
257
|
-
}
|
|
258
|
-
/**
|
|
259
|
-
* Stop the auto-minting process.
|
|
260
|
-
*/
|
|
261
|
-
stopAutoMint() {
|
|
262
|
-
this.autoMintEngine.stop();
|
|
263
|
-
}
|
|
264
|
-
/**
|
|
265
|
-
* Resume auto-minting after a circuit breaker trip.
|
|
266
|
-
* Resets consecutive failure count and restarts the engine.
|
|
267
|
-
*/
|
|
268
|
-
resume() {
|
|
269
|
-
this.autoMintEngine.resume();
|
|
270
|
-
this.emit('modeSwitch', 'resumed');
|
|
271
|
-
logger_1.logger.info(`[TTTClient] Auto-minting resumed`);
|
|
272
|
-
}
|
|
273
|
-
/**
|
|
274
|
-
* List registered pools.
|
|
275
|
-
*/
|
|
276
|
-
listPools() {
|
|
277
|
-
return this.poolRegistry.listPools();
|
|
278
|
-
}
|
|
279
|
-
/**
|
|
280
|
-
* Get stats for a specific pool.
|
|
281
|
-
*/
|
|
282
|
-
getPoolStats(poolAddress) {
|
|
283
|
-
return this.poolRegistry.getPoolStats(poolAddress);
|
|
284
|
-
}
|
|
285
|
-
/**
|
|
286
|
-
* Set minimum ETH balance threshold for health alerts.
|
|
287
|
-
*/
|
|
288
|
-
setMinBalance(weiAmount) {
|
|
289
|
-
this.minBalanceWei = weiAmount;
|
|
290
|
-
}
|
|
291
|
-
/**
|
|
292
|
-
* Register alert callback for real-time notifications.
|
|
293
|
-
* Backward compatible: delegates to EventEmitter 'alert' event.
|
|
294
|
-
*/
|
|
295
|
-
onAlert(callback) {
|
|
296
|
-
this.on('alert', callback);
|
|
297
|
-
}
|
|
298
|
-
emitAlert(alert) {
|
|
299
|
-
logger_1.logger.warn(`[TTTClient] ALERT: ${alert}`);
|
|
300
|
-
this.emit('alert', alert);
|
|
301
|
-
}
|
|
302
|
-
/**
|
|
303
|
-
* Record a mint failure (called internally or externally).
|
|
304
|
-
*/
|
|
305
|
-
recordMintFailure() {
|
|
306
|
-
this.mintFailures++;
|
|
307
|
-
}
|
|
308
|
-
/**
|
|
309
|
-
* Record mint latency in ms (called from auto-mint wrapper).
|
|
310
|
-
*/
|
|
311
|
-
recordMintLatency(ms) {
|
|
312
|
-
this.mintLatencies.push(ms);
|
|
313
|
-
if (this.mintLatencies.length > this.maxLatencyHistory) {
|
|
314
|
-
this.mintLatencies.shift();
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
/**
|
|
318
|
-
* Production health check — liveness + readiness + metrics.
|
|
319
|
-
* No exceptions: always returns a HealthStatus object.
|
|
320
|
-
*/
|
|
321
|
-
async getHealth() {
|
|
322
|
-
const alerts = [];
|
|
323
|
-
let rpcConnected = false;
|
|
324
|
-
let balanceSufficient = false;
|
|
325
|
-
let ntpSourcesOk = false;
|
|
326
|
-
// 1. RPC connectivity check
|
|
327
|
-
if (this.isInitialized && this.signer?.provider) {
|
|
328
|
-
try {
|
|
329
|
-
const blockNumber = await this.signer.provider.getBlockNumber();
|
|
330
|
-
rpcConnected = blockNumber > 0;
|
|
331
|
-
}
|
|
332
|
-
catch {
|
|
333
|
-
rpcConnected = false;
|
|
334
|
-
alerts.push("RPC connection failed");
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
// 2. Balance check
|
|
338
|
-
if (this.isInitialized && this.signer?.provider) {
|
|
339
|
-
try {
|
|
340
|
-
const address = await this.signer.getAddress();
|
|
341
|
-
const balance = await this.signer.provider.getBalance(address);
|
|
342
|
-
balanceSufficient = balance >= this.minBalanceWei;
|
|
343
|
-
if (!balanceSufficient) {
|
|
344
|
-
alerts.push(`ETH balance low: ${ethers_1.ethers.formatEther(balance)} ETH (min: ${ethers_1.ethers.formatEther(this.minBalanceWei)})`);
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
catch {
|
|
348
|
-
alerts.push("Balance check failed");
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
// 3. NTP/time source health check via quick synthesis with timeout
|
|
352
|
-
try {
|
|
353
|
-
const synthResult = await Promise.race([
|
|
354
|
-
this.autoMintEngine.getTimeSynthesis().synthesize(),
|
|
355
|
-
new Promise((_, reject) => setTimeout(() => reject(new Error("Time synthesis health check timed out")), 3000)),
|
|
356
|
-
]);
|
|
357
|
-
if (synthResult && synthResult.sources >= 2 && synthResult.confidence >= 0.5) {
|
|
358
|
-
ntpSourcesOk = true;
|
|
359
|
-
}
|
|
360
|
-
else {
|
|
361
|
-
ntpSourcesOk = false;
|
|
362
|
-
alerts.push(`Time sources degraded: ${synthResult?.sources ?? 0} source(s), confidence ${synthResult?.confidence?.toFixed(2) ?? "N/A"}`);
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
catch {
|
|
366
|
-
ntpSourcesOk = false;
|
|
367
|
-
alerts.push("Time source health check failed");
|
|
368
|
-
}
|
|
369
|
-
// 4. Consecutive failure check
|
|
370
|
-
const total = this.mintCount + this.mintFailures;
|
|
371
|
-
const successRate = total > 0 ? this.mintCount / total : 1;
|
|
372
|
-
if (this.mintFailures > 5 && successRate < 0.8) {
|
|
373
|
-
alerts.push(`High mint failure rate: ${this.mintFailures} failures, ${(successRate * 100).toFixed(1)}% success`);
|
|
374
|
-
}
|
|
375
|
-
// 4. Avg latency
|
|
376
|
-
const avgLatency = this.mintLatencies.length > 0
|
|
377
|
-
? this.mintLatencies.reduce((a, b) => a + b, 0) / this.mintLatencies.length
|
|
378
|
-
: 0;
|
|
379
|
-
// 5. Uptime
|
|
380
|
-
const uptimeMs = Date.now() - this.startedAt.getTime();
|
|
381
|
-
// Emit alerts
|
|
382
|
-
for (const alert of alerts) {
|
|
383
|
-
this.emitAlert(alert);
|
|
384
|
-
}
|
|
385
|
-
const healthy = this.isInitialized && rpcConnected && balanceSufficient && alerts.length === 0;
|
|
386
|
-
return {
|
|
387
|
-
healthy,
|
|
388
|
-
checks: {
|
|
389
|
-
initialized: this.isInitialized,
|
|
390
|
-
rpcConnected,
|
|
391
|
-
signerAvailable: this.signer !== null,
|
|
392
|
-
balanceSufficient,
|
|
393
|
-
ntpSourcesOk,
|
|
394
|
-
},
|
|
395
|
-
metrics: {
|
|
396
|
-
mintCount: this.mintCount,
|
|
397
|
-
mintFailures: this.mintFailures,
|
|
398
|
-
successRate: Math.round(successRate * 1000) / 1000,
|
|
399
|
-
totalFeesPaid: this.totalFeesPaid.toString(),
|
|
400
|
-
avgMintLatencyMs: Math.round(avgLatency),
|
|
401
|
-
lastMintAt: this.lastMintAt?.toISOString() ?? null,
|
|
402
|
-
uptimeMs,
|
|
403
|
-
},
|
|
404
|
-
alerts,
|
|
405
|
-
};
|
|
406
|
-
}
|
|
407
|
-
/**
|
|
408
|
-
* Return current SDK status and statistics (balance, mint count, fees, etc.)
|
|
409
|
-
*/
|
|
410
|
-
async getStatus() {
|
|
411
|
-
if (!this.isInitialized || !this.signer) {
|
|
412
|
-
throw new Error("SDK must be initialized before getting status");
|
|
413
|
-
}
|
|
414
|
-
const provider = this.signer.provider;
|
|
415
|
-
if (!provider) {
|
|
416
|
-
throw new Error("Signer provider is not available");
|
|
417
|
-
}
|
|
418
|
-
const address = await this.signer.getAddress();
|
|
419
|
-
const balance = await provider.getBalance(address);
|
|
420
|
-
const formattedBalance = ethers_1.ethers.formatEther(balance);
|
|
421
|
-
let tttBalance = 0n;
|
|
422
|
-
if (this.lastTokenId) {
|
|
423
|
-
try {
|
|
424
|
-
tttBalance = await this.autoMintEngine.getEvmConnector().getTTTBalance(address, BigInt(this.lastTokenId));
|
|
425
|
-
}
|
|
426
|
-
catch (e) {
|
|
427
|
-
logger_1.logger.error(`[TTTClient] Failed to fetch TTT balance: ${e}`);
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
return {
|
|
431
|
-
isInitialized: this.isInitialized,
|
|
432
|
-
tier: this.config.tier,
|
|
433
|
-
mintCount: this.mintCount,
|
|
434
|
-
totalFeesPaid: this.totalFeesPaid.toString(),
|
|
435
|
-
balance: formattedBalance,
|
|
436
|
-
tttBalance: tttBalance.toString(),
|
|
437
|
-
lastTokenId: this.lastTokenId
|
|
438
|
-
};
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
exports.TTTClient = TTTClient;
|