openttt 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/README.md +52 -30
  2. package/dist/adaptive_switch.d.ts +22 -7
  3. package/dist/adaptive_switch.js +52 -15
  4. package/dist/auto_mint.d.ts +22 -7
  5. package/dist/auto_mint.js +107 -30
  6. package/dist/ct_log.d.ts +47 -0
  7. package/dist/ct_log.js +107 -0
  8. package/dist/dynamic_fee.d.ts +13 -2
  9. package/dist/dynamic_fee.js +62 -11
  10. package/dist/errors.d.ts +44 -25
  11. package/dist/errors.js +58 -42
  12. package/dist/evm_connector.d.ts +28 -1
  13. package/dist/evm_connector.js +124 -32
  14. package/dist/grg_api_client.d.ts +41 -0
  15. package/dist/grg_api_client.js +116 -0
  16. package/dist/http_client.d.ts +98 -0
  17. package/dist/http_client.js +252 -0
  18. package/dist/index.d.ts +5 -5
  19. package/dist/index.js +5 -5
  20. package/dist/logger.d.ts +36 -4
  21. package/dist/logger.js +70 -11
  22. package/dist/networks.d.ts +21 -0
  23. package/dist/networks.js +30 -4
  24. package/dist/pool_registry.d.ts +9 -0
  25. package/dist/pool_registry.js +37 -0
  26. package/dist/pot_signer.d.ts +15 -0
  27. package/dist/pot_signer.js +28 -0
  28. package/dist/protocol_fee.d.ts +42 -26
  29. package/dist/protocol_fee.js +77 -54
  30. package/dist/revenue_tiers.d.ts +36 -0
  31. package/dist/revenue_tiers.js +83 -0
  32. package/dist/signer.d.ts +1 -2
  33. package/dist/signer.js +72 -14
  34. package/dist/time_synthesis.d.ts +38 -0
  35. package/dist/time_synthesis.js +131 -21
  36. package/dist/trust_store.d.ts +49 -0
  37. package/dist/trust_store.js +89 -0
  38. package/dist/ttt_builder.d.ts +1 -1
  39. package/dist/ttt_builder.js +2 -2
  40. package/dist/ttt_client.d.ts +42 -29
  41. package/dist/ttt_client.js +117 -28
  42. package/dist/types.d.ts +46 -3
  43. package/dist/v4_hook.d.ts +10 -2
  44. package/dist/v4_hook.js +10 -2
  45. package/dist/x402_enforcer.d.ts +17 -2
  46. package/dist/x402_enforcer.js +27 -2
  47. package/package.json +6 -2
  48. package/dist/golay.d.ts +0 -6
  49. package/dist/golay.js +0 -166
  50. package/dist/grg_forward.d.ts +0 -11
  51. package/dist/grg_forward.js +0 -74
  52. package/dist/grg_inverse.d.ts +0 -7
  53. package/dist/grg_inverse.js +0 -100
  54. package/dist/grg_pipeline.d.ts +0 -13
  55. package/dist/grg_pipeline.js +0 -64
  56. package/dist/reed_solomon.d.ts +0 -12
  57. package/dist/reed_solomon.js +0 -179
  58. package/vendor/helm-crypto/golay.d.ts +0 -6
  59. package/vendor/helm-crypto/golay.js +0 -167
  60. package/vendor/helm-crypto/golay.js.map +0 -1
  61. package/vendor/helm-crypto/grg_forward.d.ts +0 -22
  62. package/vendor/helm-crypto/grg_forward.js +0 -89
  63. package/vendor/helm-crypto/grg_forward.js.map +0 -1
  64. package/vendor/helm-crypto/grg_inverse.d.ts +0 -16
  65. package/vendor/helm-crypto/grg_inverse.js +0 -118
  66. package/vendor/helm-crypto/grg_inverse.js.map +0 -1
  67. package/vendor/helm-crypto/grg_pipeline.d.ts +0 -13
  68. package/vendor/helm-crypto/grg_pipeline.js +0 -66
  69. package/vendor/helm-crypto/grg_pipeline.js.map +0 -1
  70. package/vendor/helm-crypto/index.d.ts +0 -5
  71. package/vendor/helm-crypto/index.js +0 -17
  72. package/vendor/helm-crypto/index.js.map +0 -1
  73. package/vendor/helm-crypto/logger.d.ts +0 -6
  74. package/vendor/helm-crypto/logger.js +0 -11
  75. package/vendor/helm-crypto/logger.js.map +0 -1
  76. package/vendor/helm-crypto/reed_solomon.d.ts +0 -37
  77. package/vendor/helm-crypto/reed_solomon.js +0 -210
  78. package/vendor/helm-crypto/reed_solomon.js.map +0 -1
@@ -0,0 +1,98 @@
1
+ /**
2
+ * TTTClient.httpOnly() — Zero-friction Proof of Time
3
+ * No ETH, no signer, no on-chain. Just verified time.
4
+ *
5
+ * Usage:
6
+ * const client = TTTClient.httpOnly();
7
+ * const pot = await client.generatePoT();
8
+ * console.log(pot.timestamp, pot.confidence);
9
+ *
10
+ * Time sources: NIST, Apple, Google, Cloudflare HTTPS Date headers.
11
+ * HMAC-SHA256 signature — no ethers.js dependency required.
12
+ */
13
+ import { ProofOfTime } from "./types";
14
+ export interface HttpPoT {
15
+ /** Synthesized timestamp in Unix nanoseconds (bigint). */
16
+ timestamp: bigint;
17
+ /** Fraction of configured sources that responded (0.0–1.0). */
18
+ confidence: number;
19
+ /** Lowest NTP stratum observed across sources (2 for HTTPS Date headers). */
20
+ stratum: number;
21
+ /** Number of sources that successfully responded. */
22
+ sources: number;
23
+ /** Per-source readings used to compute the median. */
24
+ sourceReadings: {
25
+ source: string;
26
+ timestamp: bigint;
27
+ uncertainty: number;
28
+ stratum?: number;
29
+ }[];
30
+ /** Replay-protection nonce (hex). */
31
+ nonce: string;
32
+ /** Expiry timestamp in Unix milliseconds (bigint). */
33
+ expiresAt: bigint;
34
+ /** HMAC-SHA256 over canonical fields, hex-encoded. */
35
+ hmac: string;
36
+ }
37
+ export interface HttpPoTVerifyResult {
38
+ valid: boolean;
39
+ reason?: string;
40
+ }
41
+ export interface HttpOnlyClientOptions {
42
+ /**
43
+ * Override HMAC secret for non-sandbox usage.
44
+ * Defaults to a fixed sandbox key — sufficient for local verification only.
45
+ */
46
+ hmacSecret?: string;
47
+ /**
48
+ * Per-source request timeout in ms. Default: 3000.
49
+ */
50
+ timeoutMs?: number;
51
+ /**
52
+ * PoT validity window in seconds. Default: 60.
53
+ */
54
+ expirySeconds?: number;
55
+ /**
56
+ * Maximum divergence allowed between sources in nanoseconds.
57
+ * Default: 2_000_000_000n (2 seconds — lenient for HTTPS Date 1s resolution).
58
+ */
59
+ toleranceNs?: bigint;
60
+ }
61
+ /**
62
+ * HttpOnlyClient — zero-dependency Proof of Time over HTTPS.
63
+ *
64
+ * Fetches time from NIST, Apple, Google, and Cloudflare HTTPS endpoints,
65
+ * computes the median, and returns a PoT with HMAC integrity protection.
66
+ *
67
+ * No ETH, no signer, no on-chain interaction required.
68
+ */
69
+ export declare class HttpOnlyClient {
70
+ private readonly hmacSecret;
71
+ private readonly timeoutMs;
72
+ private readonly expirySeconds;
73
+ private readonly toleranceNs;
74
+ private readonly usedNonces;
75
+ private readonly NONCE_TTL_MS;
76
+ private readonly MAX_NONCE_CACHE;
77
+ constructor(options?: HttpOnlyClientOptions);
78
+ /**
79
+ * Fetch time from all four HTTPS sources, compute median, return PoT.
80
+ * Requires at least 1 source to succeed.
81
+ */
82
+ generatePoT(): Promise<HttpPoT>;
83
+ /**
84
+ * Verify an HttpPoT:
85
+ * - HMAC integrity check
86
+ * - Expiry check
87
+ * - Nonce replay protection
88
+ * - Source divergence tolerance check
89
+ *
90
+ * No on-chain interaction. Pure local verification.
91
+ */
92
+ verifyPoT(pot: HttpPoT): HttpPoTVerifyResult;
93
+ /**
94
+ * Convert HttpPoT to the standard ProofOfTime shape used by the rest of the SDK.
95
+ * The issuerSignature field is omitted (no on-chain signer in httpOnly mode).
96
+ */
97
+ static toProofOfTime(pot: HttpPoT): ProofOfTime;
98
+ }
@@ -0,0 +1,252 @@
1
+ "use strict";
2
+ /**
3
+ * TTTClient.httpOnly() — Zero-friction Proof of Time
4
+ * No ETH, no signer, no on-chain. Just verified time.
5
+ *
6
+ * Usage:
7
+ * const client = TTTClient.httpOnly();
8
+ * const pot = await client.generatePoT();
9
+ * console.log(pot.timestamp, pot.confidence);
10
+ *
11
+ * Time sources: NIST, Apple, Google, Cloudflare HTTPS Date headers.
12
+ * HMAC-SHA256 signature — no ethers.js dependency required.
13
+ */
14
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ var desc = Object.getOwnPropertyDescriptor(m, k);
17
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
18
+ desc = { enumerable: true, get: function() { return m[k]; } };
19
+ }
20
+ Object.defineProperty(o, k2, desc);
21
+ }) : (function(o, m, k, k2) {
22
+ if (k2 === undefined) k2 = k;
23
+ o[k2] = m[k];
24
+ }));
25
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
26
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
27
+ }) : function(o, v) {
28
+ o["default"] = v;
29
+ });
30
+ var __importStar = (this && this.__importStar) || (function () {
31
+ var ownKeys = function(o) {
32
+ ownKeys = Object.getOwnPropertyNames || function (o) {
33
+ var ar = [];
34
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
35
+ return ar;
36
+ };
37
+ return ownKeys(o);
38
+ };
39
+ return function (mod) {
40
+ if (mod && mod.__esModule) return mod;
41
+ var result = {};
42
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
43
+ __setModuleDefault(result, mod);
44
+ return result;
45
+ };
46
+ })();
47
+ Object.defineProperty(exports, "__esModule", { value: true });
48
+ exports.HttpOnlyClient = void 0;
49
+ const crypto = __importStar(require("crypto"));
50
+ const https = __importStar(require("https"));
51
+ const SOURCES = [
52
+ { name: "nist", url: "https://time.nist.gov/" },
53
+ { name: "apple", url: "https://time.apple.com/" },
54
+ { name: "google", url: "https://time.google.com/" },
55
+ { name: "cloudflare", url: "https://time.cloudflare.com/" },
56
+ ];
57
+ function fetchHttpsDate(name, url, timeoutMs = 3000) {
58
+ return new Promise((resolve, reject) => {
59
+ const t1 = BigInt(Date.now()) * 1000000n;
60
+ const timer = setTimeout(() => {
61
+ req.destroy();
62
+ reject(new Error(`[httpOnly] Timeout: ${name} (${timeoutMs}ms)`));
63
+ }, timeoutMs);
64
+ const req = https.request(url, { method: "HEAD" }, (res) => {
65
+ clearTimeout(timer);
66
+ res.resume(); // drain so socket is freed
67
+ const t4 = BigInt(Date.now()) * 1000000n;
68
+ const dateHeader = res.headers["date"];
69
+ if (!dateHeader) {
70
+ reject(new Error(`[httpOnly] No Date header from ${name}`));
71
+ return;
72
+ }
73
+ const parsed = new Date(dateHeader).getTime();
74
+ if (isNaN(parsed)) {
75
+ reject(new Error(`[httpOnly] Invalid Date header from ${name}: ${dateHeader}`));
76
+ return;
77
+ }
78
+ const serverNs = BigInt(parsed) * 1000000n;
79
+ const rttNs = t4 - t1;
80
+ // Offset-corrected: server time + half RTT
81
+ const corrected = serverNs + rttNs / 2n;
82
+ const rttMs = Number(rttNs) / 1_000_000;
83
+ resolve({
84
+ source: name,
85
+ timestamp: corrected,
86
+ uncertainty: rttMs / 2 + 500, // 500ms base — HTTP Date has 1s resolution
87
+ stratum: 2,
88
+ });
89
+ });
90
+ req.on("error", (err) => {
91
+ clearTimeout(timer);
92
+ reject(new Error(`[httpOnly] Request error for ${name}: ${err.message}`));
93
+ });
94
+ req.end();
95
+ });
96
+ }
97
+ // ---------------------------------------------------------------------------
98
+ // HMAC helpers — default sandbox key derived from fixed chain+address strings
99
+ // ---------------------------------------------------------------------------
100
+ const SANDBOX_HMAC_SECRET = "openttt-sandbox:chainId=0:address=0x0000000000000000000000000000000000000000";
101
+ function computeHmac(timestamp, nonce, expiresAt, sources, secret = SANDBOX_HMAC_SECRET) {
102
+ // Canonical message: deterministic field concatenation
103
+ const msg = `${timestamp.toString()}:${nonce}:${expiresAt.toString()}:${sources}`;
104
+ return crypto.createHmac("sha256", secret).update(msg).digest("hex");
105
+ }
106
+ /**
107
+ * HttpOnlyClient — zero-dependency Proof of Time over HTTPS.
108
+ *
109
+ * Fetches time from NIST, Apple, Google, and Cloudflare HTTPS endpoints,
110
+ * computes the median, and returns a PoT with HMAC integrity protection.
111
+ *
112
+ * No ETH, no signer, no on-chain interaction required.
113
+ */
114
+ class HttpOnlyClient {
115
+ hmacSecret;
116
+ timeoutMs;
117
+ expirySeconds;
118
+ toleranceNs;
119
+ usedNonces = new Map();
120
+ NONCE_TTL_MS = 300_000; // 5 min
121
+ MAX_NONCE_CACHE = 10_000;
122
+ constructor(options = {}) {
123
+ this.hmacSecret = options.hmacSecret ?? SANDBOX_HMAC_SECRET;
124
+ this.timeoutMs = options.timeoutMs ?? 3000;
125
+ this.expirySeconds = options.expirySeconds ?? 60;
126
+ this.toleranceNs = options.toleranceNs ?? 2000000000n; // 2 s
127
+ }
128
+ /**
129
+ * Fetch time from all four HTTPS sources, compute median, return PoT.
130
+ * Requires at least 1 source to succeed.
131
+ */
132
+ async generatePoT() {
133
+ const results = await Promise.allSettled(SOURCES.map((s) => fetchHttpsDate(s.name, s.url, this.timeoutMs)));
134
+ const readings = [];
135
+ for (const r of results) {
136
+ if (r.status === "fulfilled")
137
+ readings.push(r.value);
138
+ }
139
+ if (readings.length === 0) {
140
+ throw new Error("[httpOnly] All HTTPS time sources failed. Check network connectivity.");
141
+ }
142
+ // Sort by timestamp for median selection
143
+ readings.sort((a, b) => (a.timestamp < b.timestamp ? -1 : a.timestamp > b.timestamp ? 1 : 0));
144
+ let finalTs;
145
+ let finalUnc;
146
+ let finalStratum;
147
+ if (readings.length === 1) {
148
+ finalTs = readings[0].timestamp;
149
+ finalUnc = readings[0].uncertainty;
150
+ finalStratum = readings[0].stratum;
151
+ }
152
+ else if (readings.length === 2) {
153
+ finalTs = (readings[0].timestamp + readings[1].timestamp) / 2n;
154
+ finalUnc = (readings[0].uncertainty + readings[1].uncertainty) / 2;
155
+ finalStratum = Math.min(readings[0].stratum, readings[1].stratum);
156
+ }
157
+ else {
158
+ const mid = Math.floor(readings.length / 2);
159
+ finalTs = readings[mid].timestamp;
160
+ finalUnc = readings[mid].uncertainty;
161
+ finalStratum = readings[mid].stratum;
162
+ }
163
+ const nonce = crypto.randomBytes(16).toString("hex");
164
+ const expiresAt = BigInt(Date.now()) + BigInt(this.expirySeconds * 1000);
165
+ const sourceReadings = readings.map((r) => ({
166
+ source: r.source,
167
+ timestamp: r.timestamp,
168
+ uncertainty: r.uncertainty,
169
+ stratum: r.stratum,
170
+ }));
171
+ const hmac = computeHmac(finalTs, nonce, expiresAt, readings.length, this.hmacSecret);
172
+ return {
173
+ timestamp: finalTs,
174
+ confidence: readings.length / SOURCES.length,
175
+ stratum: finalStratum,
176
+ sources: readings.length,
177
+ sourceReadings,
178
+ nonce,
179
+ expiresAt,
180
+ hmac,
181
+ };
182
+ }
183
+ /**
184
+ * Verify an HttpPoT:
185
+ * - HMAC integrity check
186
+ * - Expiry check
187
+ * - Nonce replay protection
188
+ * - Source divergence tolerance check
189
+ *
190
+ * No on-chain interaction. Pure local verification.
191
+ */
192
+ verifyPoT(pot) {
193
+ // 1. Expiry
194
+ if (BigInt(Date.now()) > pot.expiresAt) {
195
+ return { valid: false, reason: "PoT expired" };
196
+ }
197
+ // 2. HMAC integrity
198
+ const expected = computeHmac(pot.timestamp, pot.nonce, pot.expiresAt, pot.sources, this.hmacSecret);
199
+ if (!crypto.timingSafeEqual(Buffer.from(expected, "hex"), Buffer.from(pot.hmac, "hex"))) {
200
+ return { valid: false, reason: "HMAC mismatch — PoT may have been tampered" };
201
+ }
202
+ // 3. Nonce replay (bounded cache + TTL)
203
+ const now = Date.now();
204
+ if (this.usedNonces.size > this.MAX_NONCE_CACHE / 2) {
205
+ for (const [k, ts] of this.usedNonces) {
206
+ if (now - ts > this.NONCE_TTL_MS)
207
+ this.usedNonces.delete(k);
208
+ }
209
+ }
210
+ if (this.usedNonces.has(pot.nonce)) {
211
+ return { valid: false, reason: "Duplicate nonce — replay detected" };
212
+ }
213
+ if (this.usedNonces.size >= this.MAX_NONCE_CACHE) {
214
+ const oldest = this.usedNonces.keys().next().value;
215
+ if (oldest !== undefined)
216
+ this.usedNonces.delete(oldest);
217
+ }
218
+ this.usedNonces.set(pot.nonce, now);
219
+ // 4. Source divergence
220
+ for (const reading of pot.sourceReadings) {
221
+ const diff = reading.timestamp > pot.timestamp
222
+ ? reading.timestamp - pot.timestamp
223
+ : pot.timestamp - reading.timestamp;
224
+ if (diff > this.toleranceNs) {
225
+ return {
226
+ valid: false,
227
+ reason: `Source ${reading.source} diverges by ${diff}ns (tolerance: ${this.toleranceNs}ns)`,
228
+ };
229
+ }
230
+ }
231
+ return { valid: true };
232
+ }
233
+ /**
234
+ * Convert HttpPoT to the standard ProofOfTime shape used by the rest of the SDK.
235
+ * The issuerSignature field is omitted (no on-chain signer in httpOnly mode).
236
+ */
237
+ static toProofOfTime(pot) {
238
+ return {
239
+ timestamp: pot.timestamp,
240
+ uncertainty: pot.sourceReadings.length > 0
241
+ ? pot.sourceReadings.reduce((sum, r) => sum + r.uncertainty, 0) / pot.sourceReadings.length
242
+ : 500,
243
+ sources: pot.sources,
244
+ stratum: pot.stratum,
245
+ confidence: pot.confidence,
246
+ sourceReadings: pot.sourceReadings,
247
+ nonce: pot.nonce,
248
+ expiresAt: pot.expiresAt,
249
+ };
250
+ }
251
+ }
252
+ exports.HttpOnlyClient = HttpOnlyClient;
package/dist/index.d.ts CHANGED
@@ -1,5 +1,4 @@
1
- export * from "./grg_forward";
2
- export * from "./grg_inverse";
1
+ export * from "../vendor/helm-crypto";
3
2
  export * from "./adaptive_switch";
4
3
  export * from "./evm_connector";
5
4
  export * from "./x402_enforcer";
@@ -10,13 +9,14 @@ export * from "./v4_hook";
10
9
  export * from "./logger";
11
10
  export * from "./types";
12
11
  export * from "./ttt_client";
12
+ export * from "./http_client";
13
13
  export * from "./auto_mint";
14
14
  export * from "./time_synthesis";
15
15
  export * from "./dynamic_fee";
16
- export * from "./golay";
17
- export * from "./grg_pipeline";
18
16
  export * from "./signer";
19
17
  export * from "./networks";
20
- export * from "./reed_solomon";
21
18
  export * from "./errors";
22
19
  export * from "./pot_signer";
20
+ export * from "./ct_log";
21
+ export * from "./trust_store";
22
+ export * from "./revenue_tiers";
package/dist/index.js CHANGED
@@ -15,8 +15,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  // sdk/src/index.ts
18
- __exportStar(require("./grg_forward"), exports);
19
- __exportStar(require("./grg_inverse"), exports);
18
+ __exportStar(require("../vendor/helm-crypto"), exports);
20
19
  __exportStar(require("./adaptive_switch"), exports);
21
20
  __exportStar(require("./evm_connector"), exports);
22
21
  __exportStar(require("./x402_enforcer"), exports);
@@ -27,13 +26,14 @@ __exportStar(require("./v4_hook"), exports);
27
26
  __exportStar(require("./logger"), exports);
28
27
  __exportStar(require("./types"), exports);
29
28
  __exportStar(require("./ttt_client"), exports);
29
+ __exportStar(require("./http_client"), exports);
30
30
  __exportStar(require("./auto_mint"), exports);
31
31
  __exportStar(require("./time_synthesis"), exports);
32
32
  __exportStar(require("./dynamic_fee"), exports);
33
- __exportStar(require("./golay"), exports);
34
- __exportStar(require("./grg_pipeline"), exports);
35
33
  __exportStar(require("./signer"), exports);
36
34
  __exportStar(require("./networks"), exports);
37
- __exportStar(require("./reed_solomon"), exports);
38
35
  __exportStar(require("./errors"), exports);
39
36
  __exportStar(require("./pot_signer"), exports);
37
+ __exportStar(require("./ct_log"), exports);
38
+ __exportStar(require("./trust_store"), exports);
39
+ __exportStar(require("./revenue_tiers"), exports);
package/dist/logger.d.ts CHANGED
@@ -1,18 +1,50 @@
1
1
  export declare enum LogLevel {
2
- INFO = "INFO",
3
- WARN = "WARN",
4
- ERROR = "ERROR",
5
- DEBUG = "DEBUG"
2
+ DEBUG = 0,
3
+ INFO = 1,
4
+ WARN = 2,
5
+ ERROR = 3,
6
+ SILENT = 4
7
+ }
8
+ /**
9
+ * Custom log handler function type.
10
+ * @param level - The severity level of the log message.
11
+ * @param message - The formatted log message string.
12
+ */
13
+ export type LogHandler = (level: LogLevel, message: string) => void;
14
+ interface LoggerConfig {
15
+ level?: LogLevel;
16
+ handler?: LogHandler;
6
17
  }
7
18
  export declare class Logger {
8
19
  private static instance;
20
+ private static currentLevel;
21
+ private static currentHandler;
9
22
  private namespace;
10
23
  private constructor();
11
24
  static getInstance(namespace?: string): Logger;
25
+ /**
26
+ * Configure the global logger level and/or handler.
27
+ * @param config - Configuration object with optional level and handler.
28
+ *
29
+ * @example
30
+ * ```typescript
31
+ * Logger.configure({ level: LogLevel.DEBUG });
32
+ * Logger.configure({ handler: (level, msg) => myLogService.send(msg) });
33
+ * Logger.configure({ level: LogLevel.WARN, handler: customHandler });
34
+ * ```
35
+ */
36
+ static configure(config: LoggerConfig): void;
37
+ /**
38
+ * Convenience method to suppress all log output.
39
+ * Equivalent to `Logger.configure({ level: LogLevel.SILENT })`.
40
+ */
41
+ static silent(): void;
12
42
  private formatMessage;
43
+ private emit;
13
44
  info(message: string): void;
14
45
  warn(message: string): void;
15
46
  error(message: string, error?: Error): void;
16
47
  debug(message: string): void;
17
48
  }
18
49
  export declare const logger: Logger;
50
+ export {};
package/dist/logger.js CHANGED
@@ -4,13 +4,42 @@ Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.logger = exports.Logger = exports.LogLevel = void 0;
5
5
  var LogLevel;
6
6
  (function (LogLevel) {
7
- LogLevel["INFO"] = "INFO";
8
- LogLevel["WARN"] = "WARN";
9
- LogLevel["ERROR"] = "ERROR";
10
- LogLevel["DEBUG"] = "DEBUG";
7
+ LogLevel[LogLevel["DEBUG"] = 0] = "DEBUG";
8
+ LogLevel[LogLevel["INFO"] = 1] = "INFO";
9
+ LogLevel[LogLevel["WARN"] = 2] = "WARN";
10
+ LogLevel[LogLevel["ERROR"] = 3] = "ERROR";
11
+ LogLevel[LogLevel["SILENT"] = 4] = "SILENT";
11
12
  })(LogLevel || (exports.LogLevel = LogLevel = {}));
13
+ const LOG_LEVEL_NAMES = {
14
+ [LogLevel.DEBUG]: "DEBUG",
15
+ [LogLevel.INFO]: "INFO",
16
+ [LogLevel.WARN]: "WARN",
17
+ [LogLevel.ERROR]: "ERROR",
18
+ [LogLevel.SILENT]: "SILENT",
19
+ };
20
+ /**
21
+ * Default log handler that writes to the console.
22
+ */
23
+ const defaultHandler = (level, message) => {
24
+ switch (level) {
25
+ case LogLevel.DEBUG:
26
+ console.debug(message);
27
+ break;
28
+ case LogLevel.INFO:
29
+ console.log(message);
30
+ break;
31
+ case LogLevel.WARN:
32
+ console.warn(message);
33
+ break;
34
+ case LogLevel.ERROR:
35
+ console.error(message);
36
+ break;
37
+ }
38
+ };
12
39
  class Logger {
13
40
  static instance;
41
+ static currentLevel = LogLevel.INFO;
42
+ static currentHandler = defaultHandler;
14
43
  namespace;
15
44
  constructor(namespace = "TTT-SDK") {
16
45
  this.namespace = namespace;
@@ -21,15 +50,47 @@ class Logger {
21
50
  }
22
51
  return Logger.instance;
23
52
  }
53
+ /**
54
+ * Configure the global logger level and/or handler.
55
+ * @param config - Configuration object with optional level and handler.
56
+ *
57
+ * @example
58
+ * ```typescript
59
+ * Logger.configure({ level: LogLevel.DEBUG });
60
+ * Logger.configure({ handler: (level, msg) => myLogService.send(msg) });
61
+ * Logger.configure({ level: LogLevel.WARN, handler: customHandler });
62
+ * ```
63
+ */
64
+ static configure(config) {
65
+ if (config.level !== undefined) {
66
+ Logger.currentLevel = config.level;
67
+ }
68
+ if (config.handler !== undefined) {
69
+ Logger.currentHandler = config.handler;
70
+ }
71
+ }
72
+ /**
73
+ * Convenience method to suppress all log output.
74
+ * Equivalent to `Logger.configure({ level: LogLevel.SILENT })`.
75
+ */
76
+ static silent() {
77
+ Logger.currentLevel = LogLevel.SILENT;
78
+ }
24
79
  formatMessage(level, message) {
25
80
  const timestamp = new Date().toISOString();
26
- return `[${timestamp}] [${level}] [${this.namespace}] ${message}`;
81
+ return `[${timestamp}] [${LOG_LEVEL_NAMES[level]}] [${this.namespace}] ${message}`;
82
+ }
83
+ emit(level, message) {
84
+ if (level < Logger.currentLevel)
85
+ return;
86
+ const formatted = this.formatMessage(level, message);
87
+ Logger.currentHandler(level, formatted);
27
88
  }
28
89
  info(message) {
29
- console.log(this.formatMessage(LogLevel.INFO, message));
90
+ this.emit(LogLevel.INFO, message);
30
91
  }
31
92
  warn(message) {
32
- console.warn(this.formatMessage(LogLevel.WARN, message));
93
+ this.emit(LogLevel.WARN, message);
33
94
  }
34
95
  error(message, error) {
35
96
  let msg = message;
@@ -39,12 +100,10 @@ class Logger {
39
100
  msg += `\nStack: ${error.stack}`;
40
101
  }
41
102
  }
42
- console.error(this.formatMessage(LogLevel.ERROR, msg));
103
+ this.emit(LogLevel.ERROR, msg);
43
104
  }
44
105
  debug(message) {
45
- if (process.env.DEBUG) {
46
- console.debug(this.formatMessage(LogLevel.DEBUG, message));
47
- }
106
+ this.emit(LogLevel.DEBUG, message);
48
107
  }
49
108
  }
50
109
  exports.Logger = Logger;
@@ -8,6 +8,27 @@ export interface NetworkConfig {
8
8
  protocolFeeAddress: string;
9
9
  usdcAddress: string;
10
10
  }
11
+ /**
12
+ * Base Mainnet network preset.
13
+ *
14
+ * `tttAddress` and `protocolFeeAddress` are set to the zero address because the
15
+ * TTT contracts have not yet been deployed to Base Mainnet. These are
16
+ * placeholders only — using them without an override will cause a runtime error
17
+ * (validated in `TTTClient.create`).
18
+ *
19
+ * **Operators deploying to mainnet** must supply their own `contractAddress` and
20
+ * `feeCollectorAddress` via `TTTClientConfig` when calling `TTTClient.create()`.
21
+ * Example:
22
+ * ```ts
23
+ * const client = await TTTClient.create({
24
+ * network: "base",
25
+ * contractAddress: "0xYourDeployedTTTContract",
26
+ * feeCollectorAddress: "0xYourFeeCollector",
27
+ * // ...other config
28
+ * });
29
+ * ```
30
+ */
11
31
  export declare const BASE_MAINNET: NetworkConfig;
32
+ export declare const ETH_SEPOLIA: NetworkConfig;
12
33
  export declare const BASE_SEPOLIA: NetworkConfig;
13
34
  export declare const NETWORKS: Record<string, NetworkConfig>;
package/dist/networks.js CHANGED
@@ -1,8 +1,26 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.NETWORKS = exports.BASE_SEPOLIA = exports.BASE_MAINNET = void 0;
4
- // ⚠️ MAINNET ADDRESSES NOT YET DEPLOYED — must be overridden via TTTClientConfig.contractAddress
5
- // Using these defaults without override will throw at runtime (validated in TTTClient.create)
3
+ exports.NETWORKS = exports.BASE_SEPOLIA = exports.ETH_SEPOLIA = exports.BASE_MAINNET = void 0;
4
+ /**
5
+ * Base Mainnet network preset.
6
+ *
7
+ * `tttAddress` and `protocolFeeAddress` are set to the zero address because the
8
+ * TTT contracts have not yet been deployed to Base Mainnet. These are
9
+ * placeholders only — using them without an override will cause a runtime error
10
+ * (validated in `TTTClient.create`).
11
+ *
12
+ * **Operators deploying to mainnet** must supply their own `contractAddress` and
13
+ * `feeCollectorAddress` via `TTTClientConfig` when calling `TTTClient.create()`.
14
+ * Example:
15
+ * ```ts
16
+ * const client = await TTTClient.create({
17
+ * network: "base",
18
+ * contractAddress: "0xYourDeployedTTTContract",
19
+ * feeCollectorAddress: "0xYourFeeCollector",
20
+ * // ...other config
21
+ * });
22
+ * ```
23
+ */
6
24
  exports.BASE_MAINNET = {
7
25
  chainId: 8453,
8
26
  rpcUrl: "https://mainnet.base.org",
@@ -10,6 +28,13 @@ exports.BASE_MAINNET = {
10
28
  protocolFeeAddress: "0x0000000000000000000000000000000000000000",
11
29
  usdcAddress: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
12
30
  };
31
+ exports.ETH_SEPOLIA = {
32
+ chainId: 11155111,
33
+ rpcUrl: "https://ethereum-sepolia-rpc.publicnode.com",
34
+ tttAddress: "0x291b83F605F2dA95cf843d4a53983B413ef3B929",
35
+ protocolFeeAddress: "0x6b39D96741BB4Ce6283F824CC31c2931c75AEe64",
36
+ usdcAddress: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
37
+ };
13
38
  exports.BASE_SEPOLIA = {
14
39
  chainId: 84532,
15
40
  rpcUrl: "https://sepolia.base.org",
@@ -19,5 +44,6 @@ exports.BASE_SEPOLIA = {
19
44
  };
20
45
  exports.NETWORKS = {
21
46
  base: exports.BASE_MAINNET,
22
- sepolia: exports.BASE_SEPOLIA,
47
+ sepolia: exports.ETH_SEPOLIA,
48
+ baseSepolia: exports.BASE_SEPOLIA,
23
49
  };
@@ -55,4 +55,13 @@ export declare class PoolRegistry {
55
55
  * @returns Array of pool addresses.
56
56
  */
57
57
  listPools(): string[];
58
+ /**
59
+ * Serialize registry state to JSON for persistence across restarts.
60
+ * Allows operators to restore pool registrations and stats without re-registering.
61
+ */
62
+ serialize(): string;
63
+ /**
64
+ * Reconstruct a PoolRegistry from previously serialized JSON state.
65
+ */
66
+ static deserialize(json: string): PoolRegistry;
58
67
  }