openttt 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +391 -0
- package/dist/adaptive_switch.d.ts +44 -0
- package/dist/adaptive_switch.js +108 -0
- package/dist/auto_mint.d.ts +45 -0
- package/dist/auto_mint.js +244 -0
- package/dist/dynamic_fee.d.ts +64 -0
- package/dist/dynamic_fee.js +203 -0
- package/dist/errors.d.ts +45 -0
- package/dist/errors.js +74 -0
- package/dist/evm_connector.d.ts +88 -0
- package/dist/evm_connector.js +297 -0
- package/dist/golay.d.ts +6 -0
- package/dist/golay.js +166 -0
- package/dist/grg_forward.d.ts +6 -0
- package/dist/grg_forward.js +59 -0
- package/dist/grg_inverse.d.ts +7 -0
- package/dist/grg_inverse.js +97 -0
- package/dist/grg_pipeline.d.ts +13 -0
- package/dist/grg_pipeline.js +64 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +38 -0
- package/dist/logger.d.ts +18 -0
- package/dist/logger.js +51 -0
- package/dist/networks.d.ts +13 -0
- package/dist/networks.js +23 -0
- package/dist/pool_registry.d.ts +58 -0
- package/dist/pool_registry.js +129 -0
- package/dist/protocol_fee.d.ts +56 -0
- package/dist/protocol_fee.js +176 -0
- package/dist/reed_solomon.d.ts +12 -0
- package/dist/reed_solomon.js +179 -0
- package/dist/signer.d.ts +76 -0
- package/dist/signer.js +329 -0
- package/dist/time_synthesis.d.ts +49 -0
- package/dist/time_synthesis.js +372 -0
- package/dist/ttt_builder.d.ts +32 -0
- package/dist/ttt_builder.js +84 -0
- package/dist/ttt_client.d.ts +118 -0
- package/dist/ttt_client.js +352 -0
- package/dist/types.d.ts +141 -0
- package/dist/types.js +9 -0
- package/dist/v4_hook.d.ts +43 -0
- package/dist/v4_hook.js +115 -0
- package/dist/x402_enforcer.d.ts +29 -0
- package/dist/x402_enforcer.js +67 -0
- package/package.json +51 -0
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.TimeSynthesis = exports.NTPSource = void 0;
|
|
37
|
+
const dgram = __importStar(require("dgram"));
|
|
38
|
+
const buffer_1 = require("buffer");
|
|
39
|
+
const ethers_1 = require("ethers");
|
|
40
|
+
const logger_1 = require("./logger");
|
|
41
|
+
const errors_1 = require("./errors");
|
|
42
|
+
const NTP_OFFSET_1900_TO_1970 = 2208988800n;
|
|
43
|
+
class NTPSource {
|
|
44
|
+
name;
|
|
45
|
+
host;
|
|
46
|
+
port;
|
|
47
|
+
constructor(name, host, port = 123) {
|
|
48
|
+
this.name = name;
|
|
49
|
+
this.host = host;
|
|
50
|
+
this.port = port;
|
|
51
|
+
}
|
|
52
|
+
async getTime() {
|
|
53
|
+
return new Promise((resolve, reject) => {
|
|
54
|
+
const client = dgram.createSocket('udp4');
|
|
55
|
+
const packet = buffer_1.Buffer.alloc(48);
|
|
56
|
+
// LI=0, VN=4, Mode=3 (Client)
|
|
57
|
+
packet[0] = 0x23;
|
|
58
|
+
const t1 = BigInt(Date.now()) * 1000000n; // Local originate timestamp (ns)
|
|
59
|
+
const timeout = setTimeout(() => {
|
|
60
|
+
client.close();
|
|
61
|
+
reject(new errors_1.TTTTimeSynthesisError(`NTP timeout for ${this.host}`, "Server did not respond within 2000ms", "Check your firewall (UDP port 123) or try a different NTP server."));
|
|
62
|
+
}, 2000); // 2s timeout for speed
|
|
63
|
+
client.on('error', (err) => {
|
|
64
|
+
clearTimeout(timeout);
|
|
65
|
+
client.close();
|
|
66
|
+
reject(new errors_1.TTTTimeSynthesisError(`NTP socket error for ${this.host}`, err.message, "Ensure network connectivity and that UDP 123 is outbound-allowed."));
|
|
67
|
+
});
|
|
68
|
+
client.on('message', (msg) => {
|
|
69
|
+
const t4 = BigInt(Date.now()) * 1000000n; // Local receive timestamp (ns)
|
|
70
|
+
clearTimeout(timeout);
|
|
71
|
+
client.close();
|
|
72
|
+
if (msg.length < 48) {
|
|
73
|
+
reject(new errors_1.TTTTimeSynthesisError(`Invalid NTP response from ${this.host}`, "Packet length < 48 bytes", "The NTP server returned a malformed response. Check the server address."));
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const stratum = msg[1];
|
|
77
|
+
if (stratum === 0 || stratum > 15) {
|
|
78
|
+
reject(new errors_1.TTTTimeSynthesisError(`Invalid Stratum from ${this.host}`, `Stratum: ${stratum}`, "The NTP server is unsynchronized. Use a stratum 1 or 2 server."));
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
// T2: Receive Timestamp (Server received request)
|
|
82
|
+
const t2_sec = BigInt(msg.readUInt32BE(32));
|
|
83
|
+
const t2_frac = BigInt(msg.readUInt32BE(36));
|
|
84
|
+
if (t2_sec <= NTP_OFFSET_1900_TO_1970) {
|
|
85
|
+
reject(new errors_1.TTTTimeSynthesisError(`[NTP] Invalid T2 timestamp from ${this.host}`, `${t2_sec} <= NTP epoch offset`, "The NTP server returned a pre-1970 timestamp. Use a reliable NTP server."));
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const t2 = (t2_sec - NTP_OFFSET_1900_TO_1970) * 1000000000n + (t2_frac * 1000000000n) / (1n << 32n);
|
|
89
|
+
// T3: Transmit Timestamp (Server sent response)
|
|
90
|
+
const t3_sec = BigInt(msg.readUInt32BE(40));
|
|
91
|
+
const t3_frac = BigInt(msg.readUInt32BE(44));
|
|
92
|
+
if (t3_sec <= NTP_OFFSET_1900_TO_1970) {
|
|
93
|
+
reject(new errors_1.TTTTimeSynthesisError(`[NTP] Invalid T3 timestamp from ${this.host}`, `${t3_sec} <= NTP epoch offset`, "The NTP server returned a pre-1970 timestamp. Use a reliable NTP server."));
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const t3 = (t3_sec - NTP_OFFSET_1900_TO_1970) * 1000000000n + (t3_frac * 1000000000n) / (1n << 32n);
|
|
97
|
+
// offset = ((T2-T1) + (T3-T4)) / 2
|
|
98
|
+
const offset = ((t2 - t1) + (t3 - t4)) / 2n;
|
|
99
|
+
const delay = (t4 - t1) - (t3 - t2);
|
|
100
|
+
// Root dispersion at offset 8 (4 bytes, fixed point 16.16)
|
|
101
|
+
const rootDispersionRaw = msg.readUInt32BE(8);
|
|
102
|
+
const rootDispersion = Number(rootDispersionRaw) / (1 << 16) * 1000; // ms
|
|
103
|
+
const rtt = Number(delay) / 1_000_000; // ms
|
|
104
|
+
const uncertainty = rootDispersion + rtt / 2;
|
|
105
|
+
resolve({
|
|
106
|
+
timestamp: t4 + offset,
|
|
107
|
+
uncertainty,
|
|
108
|
+
stratum,
|
|
109
|
+
source: this.name
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
try {
|
|
113
|
+
client.send(packet, 0, packet.length, this.port, this.host, (err) => {
|
|
114
|
+
if (err) {
|
|
115
|
+
clearTimeout(timeout);
|
|
116
|
+
client.close();
|
|
117
|
+
reject(new errors_1.TTTTimeSynthesisError(`Failed to send NTP packet to ${this.host}`, err.message, "Check network settings."));
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
catch (sendErr) {
|
|
122
|
+
clearTimeout(timeout);
|
|
123
|
+
try {
|
|
124
|
+
client.close();
|
|
125
|
+
}
|
|
126
|
+
catch { /* already closed */ }
|
|
127
|
+
reject(new errors_1.TTTTimeSynthesisError(`Synchronous send error for ${this.host}`, sendErr instanceof Error ? sendErr.message : String(sendErr), "Check runtime environment."));
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
exports.NTPSource = NTPSource;
|
|
133
|
+
class TimeSynthesis {
|
|
134
|
+
sources = [];
|
|
135
|
+
constructor(config) {
|
|
136
|
+
const sourceNames = config?.sources || ['nist', 'kriss', 'google'];
|
|
137
|
+
for (const s of sourceNames) {
|
|
138
|
+
if (s === 'nist') {
|
|
139
|
+
this.sources.push(new NTPSource('nist', 'time.nist.gov'));
|
|
140
|
+
}
|
|
141
|
+
else if (s === 'kriss') {
|
|
142
|
+
this.sources.push(new NTPSource('kriss', 'time.kriss.re.kr'));
|
|
143
|
+
}
|
|
144
|
+
else if (s === 'google' || s === 'galileo') {
|
|
145
|
+
this.sources.push(new NTPSource('google', 'time.google.com'));
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
async getFromSource(name) {
|
|
150
|
+
const source = this.sources.find(s => s.name === name);
|
|
151
|
+
if (!source)
|
|
152
|
+
throw new errors_1.TTTTimeSynthesisError(`Source ${name} not found`, "Requested source name is not configured", "Configure the source in the TimeSynthesis constructor.");
|
|
153
|
+
return source.getTime();
|
|
154
|
+
}
|
|
155
|
+
async synthesize() {
|
|
156
|
+
const readings = [];
|
|
157
|
+
const results = await Promise.allSettled(this.sources.map(s => s.getTime()));
|
|
158
|
+
for (const res of results) {
|
|
159
|
+
if (res.status === 'fulfilled') {
|
|
160
|
+
readings.push(res.value);
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
logger_1.logger.warn(`[TimeSynthesis] NTP request failed: ${res.reason}`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (readings.length === 0) {
|
|
167
|
+
throw new errors_1.TTTTimeSynthesisError('[TimeSynthesis] CRITICAL: All NTP sources failed.', "Zero readings returned from all configured NTP sources", "Ensure UDP port 123 is open and NTP servers are reachable.");
|
|
168
|
+
}
|
|
169
|
+
if (readings.length === 1) {
|
|
170
|
+
logger_1.logger.warn(`[TimeSynthesis] WARNING: Only 1 NTP source available (${readings[0].source}). Time may be unreliable.`);
|
|
171
|
+
return {
|
|
172
|
+
timestamp: readings[0].timestamp,
|
|
173
|
+
confidence: 1 / this.sources.length,
|
|
174
|
+
uncertainty: readings[0].uncertainty,
|
|
175
|
+
sources: 1,
|
|
176
|
+
stratum: readings[0].stratum
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
readings.sort((a, b) => (a.timestamp < b.timestamp ? -1 : a.timestamp > b.timestamp ? 1 : 0));
|
|
180
|
+
let finalTimestamp;
|
|
181
|
+
let finalUncertainty;
|
|
182
|
+
let finalStratum;
|
|
183
|
+
if (readings.length === 2) {
|
|
184
|
+
finalTimestamp = (readings[0].timestamp + readings[1].timestamp) / 2n;
|
|
185
|
+
finalUncertainty = (readings[0].uncertainty + readings[1].uncertainty) / 2;
|
|
186
|
+
finalStratum = Math.min(readings[0].stratum, readings[1].stratum);
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
const mid = Math.floor(readings.length / 2);
|
|
190
|
+
finalTimestamp = readings[mid].timestamp;
|
|
191
|
+
finalUncertainty = readings[mid].uncertainty;
|
|
192
|
+
finalStratum = readings[mid].stratum;
|
|
193
|
+
}
|
|
194
|
+
return {
|
|
195
|
+
timestamp: finalTimestamp,
|
|
196
|
+
confidence: readings.length / this.sources.length,
|
|
197
|
+
uncertainty: finalUncertainty,
|
|
198
|
+
sources: readings.length,
|
|
199
|
+
stratum: finalStratum
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Generates a Proof of Time (PoT) with verification of source readings.
|
|
204
|
+
*/
|
|
205
|
+
async generateProofOfTime() {
|
|
206
|
+
const readings = [];
|
|
207
|
+
const results = await Promise.allSettled(this.sources.map(s => s.getTime()));
|
|
208
|
+
for (const res of results) {
|
|
209
|
+
if (res.status === 'fulfilled') {
|
|
210
|
+
readings.push(res.value);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
if (readings.length === 0) {
|
|
214
|
+
throw new errors_1.TTTTimeSynthesisError('[TimeSynthesis] Cannot generate PoT: All NTP sources failed.', "No successful readings from NTP servers.", "Check internet connection and UDP 123 access.");
|
|
215
|
+
}
|
|
216
|
+
readings.sort((a, b) => (a.timestamp < b.timestamp ? -1 : a.timestamp > b.timestamp ? 1 : 0));
|
|
217
|
+
let finalTimestamp;
|
|
218
|
+
let finalUncertainty;
|
|
219
|
+
let finalStratum;
|
|
220
|
+
if (readings.length === 1) {
|
|
221
|
+
finalTimestamp = readings[0].timestamp;
|
|
222
|
+
finalUncertainty = readings[0].uncertainty;
|
|
223
|
+
finalStratum = readings[0].stratum;
|
|
224
|
+
}
|
|
225
|
+
else if (readings.length === 2) {
|
|
226
|
+
finalTimestamp = (readings[0].timestamp + readings[1].timestamp) / 2n;
|
|
227
|
+
finalUncertainty = (readings[0].uncertainty + readings[1].uncertainty) / 2;
|
|
228
|
+
finalStratum = Math.min(readings[0].stratum, readings[1].stratum);
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
const mid = Math.floor(readings.length / 2);
|
|
232
|
+
finalTimestamp = readings[mid].timestamp;
|
|
233
|
+
finalUncertainty = readings[mid].uncertainty;
|
|
234
|
+
finalStratum = readings[mid].stratum;
|
|
235
|
+
}
|
|
236
|
+
const signatures = readings.map(r => ({
|
|
237
|
+
source: r.source,
|
|
238
|
+
timestamp: r.timestamp,
|
|
239
|
+
uncertainty: r.uncertainty
|
|
240
|
+
}));
|
|
241
|
+
const pot = {
|
|
242
|
+
timestamp: finalTimestamp,
|
|
243
|
+
uncertainty: finalUncertainty,
|
|
244
|
+
sources: readings.length,
|
|
245
|
+
stratum: finalStratum,
|
|
246
|
+
confidence: readings.length / this.sources.length,
|
|
247
|
+
signatures
|
|
248
|
+
};
|
|
249
|
+
// Verification Logic: Ensure all source timestamps are within tolerance of synthesized median
|
|
250
|
+
if (!this.verifyProofOfTime(pot)) {
|
|
251
|
+
throw new errors_1.TTTTimeSynthesisError("[PoT] Self-verification failed", "Source readings are too far apart from synthesized median", "Check for high network jitter or malicious NTP spoofing.");
|
|
252
|
+
}
|
|
253
|
+
return pot;
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Verify Proof of Time integrity.
|
|
257
|
+
*/
|
|
258
|
+
verifyProofOfTime(pot) {
|
|
259
|
+
const TOLERANCE_NS = 100000000n; // 100ms
|
|
260
|
+
if (pot.signatures.length === 0)
|
|
261
|
+
return false;
|
|
262
|
+
if (pot.confidence <= 0)
|
|
263
|
+
return false;
|
|
264
|
+
for (const sig of pot.signatures) {
|
|
265
|
+
const diff = sig.timestamp > pot.timestamp ? sig.timestamp - pot.timestamp : pot.timestamp - sig.timestamp;
|
|
266
|
+
if (diff > TOLERANCE_NS) {
|
|
267
|
+
logger_1.logger.warn(`[TimeSynthesis] Signature from ${sig.source} outside tolerance: ${diff}ns`);
|
|
268
|
+
return false;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return true;
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Generates a bytes32 hash of the PoT for on-chain submission.
|
|
275
|
+
*/
|
|
276
|
+
static getOnChainHash(pot) {
|
|
277
|
+
return (0, ethers_1.keccak256)(ethers_1.AbiCoder.defaultAbiCoder().encode(["uint64", "uint32", "uint8", "uint8", "uint32"], [
|
|
278
|
+
pot.timestamp / 1000000n, // Convert to ms for storage efficiency
|
|
279
|
+
Math.round(pot.uncertainty * 1000), // Scale uncertainty
|
|
280
|
+
pot.sources,
|
|
281
|
+
pot.stratum,
|
|
282
|
+
Math.round(pot.confidence * 1000000)
|
|
283
|
+
]));
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Serializes PoT to JSON string.
|
|
287
|
+
*/
|
|
288
|
+
static serializeToJSON(pot) {
|
|
289
|
+
return JSON.stringify(pot, (key, value) => typeof value === 'bigint' ? value.toString() : value);
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Deserializes PoT from JSON string.
|
|
293
|
+
*/
|
|
294
|
+
static deserializeFromJSON(json) {
|
|
295
|
+
const data = JSON.parse(json);
|
|
296
|
+
return {
|
|
297
|
+
...data,
|
|
298
|
+
timestamp: BigInt(data.timestamp),
|
|
299
|
+
signatures: data.signatures.map((s) => ({
|
|
300
|
+
...s,
|
|
301
|
+
timestamp: BigInt(s.timestamp)
|
|
302
|
+
}))
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Serializes PoT to compact binary format.
|
|
307
|
+
*/
|
|
308
|
+
static serializeToBinary(pot) {
|
|
309
|
+
// Header: timestamp(8), uncertainty(4), sources(1), stratum(1), confidence(4), sigCount(1) = 19 bytes
|
|
310
|
+
let size = 19;
|
|
311
|
+
for (const sig of pot.signatures) {
|
|
312
|
+
size += 1 + sig.source.length + 8 + 4; // nameLen(1) + name(N) + ts(8) + unc(4)
|
|
313
|
+
}
|
|
314
|
+
const buf = buffer_1.Buffer.alloc(size);
|
|
315
|
+
let offset = 0;
|
|
316
|
+
buf.writeBigUInt64BE(pot.timestamp, offset);
|
|
317
|
+
offset += 8;
|
|
318
|
+
buf.writeFloatBE(pot.uncertainty, offset);
|
|
319
|
+
offset += 4;
|
|
320
|
+
buf.writeUInt8(pot.sources, offset);
|
|
321
|
+
offset += 1;
|
|
322
|
+
buf.writeUInt8(pot.stratum, offset);
|
|
323
|
+
offset += 1;
|
|
324
|
+
buf.writeFloatBE(pot.confidence, offset);
|
|
325
|
+
offset += 4;
|
|
326
|
+
buf.writeUInt8(pot.signatures.length, offset);
|
|
327
|
+
offset += 1;
|
|
328
|
+
for (const sig of pot.signatures) {
|
|
329
|
+
buf.writeUInt8(sig.source.length, offset);
|
|
330
|
+
offset += 1;
|
|
331
|
+
buf.write(sig.source, offset);
|
|
332
|
+
offset += sig.source.length;
|
|
333
|
+
buf.writeBigUInt64BE(sig.timestamp, offset);
|
|
334
|
+
offset += 8;
|
|
335
|
+
buf.writeFloatBE(sig.uncertainty, offset);
|
|
336
|
+
offset += 4;
|
|
337
|
+
}
|
|
338
|
+
return buf;
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Deserializes PoT from compact binary format.
|
|
342
|
+
*/
|
|
343
|
+
static deserializeFromBinary(buf) {
|
|
344
|
+
let offset = 0;
|
|
345
|
+
const timestamp = buf.readBigUInt64BE(offset);
|
|
346
|
+
offset += 8;
|
|
347
|
+
const uncertainty = buf.readFloatBE(offset);
|
|
348
|
+
offset += 4;
|
|
349
|
+
const sources = buf.readUInt8(offset);
|
|
350
|
+
offset += 1;
|
|
351
|
+
const stratum = buf.readUInt8(offset);
|
|
352
|
+
offset += 1;
|
|
353
|
+
const confidence = buf.readFloatBE(offset);
|
|
354
|
+
offset += 4;
|
|
355
|
+
const sigCount = buf.readUInt8(offset);
|
|
356
|
+
offset += 1;
|
|
357
|
+
const signatures = [];
|
|
358
|
+
for (let i = 0; i < sigCount; i++) {
|
|
359
|
+
const nameLen = buf.readUInt8(offset);
|
|
360
|
+
offset += 1;
|
|
361
|
+
const source = buf.toString('utf8', offset, offset + nameLen);
|
|
362
|
+
offset += nameLen;
|
|
363
|
+
const ts = buf.readBigUInt64BE(offset);
|
|
364
|
+
offset += 8;
|
|
365
|
+
const unc = buf.readFloatBE(offset);
|
|
366
|
+
offset += 4;
|
|
367
|
+
signatures.push({ source, timestamp: ts, uncertainty: unc });
|
|
368
|
+
}
|
|
369
|
+
return { timestamp, uncertainty, sources, stratum, confidence, signatures };
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
exports.TimeSynthesis = TimeSynthesis;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { AdaptiveMode, Block, TTTRecord } from "./adaptive_switch";
|
|
2
|
+
import { EVMConnector } from "./evm_connector";
|
|
3
|
+
export declare class TTTBuilder {
|
|
4
|
+
private mode;
|
|
5
|
+
private connector;
|
|
6
|
+
private tttBalance;
|
|
7
|
+
private adaptiveSwitch;
|
|
8
|
+
constructor(connector?: EVMConnector);
|
|
9
|
+
/**
|
|
10
|
+
* Purchase TTT from the market using EVMConnector.
|
|
11
|
+
* P1-5: Router/token addresses configurable. P1-6: 5% slippage protection.
|
|
12
|
+
*/
|
|
13
|
+
purchaseTTT(poolAddress: string, amount: bigint, tokenInAddress?: string, routerAddress?: string, slippageBps?: bigint): Promise<void>;
|
|
14
|
+
/**
|
|
15
|
+
* Apply TTT to a block by burning it.
|
|
16
|
+
* This signals the intention to use TTT for prioritized processing.
|
|
17
|
+
*/
|
|
18
|
+
consumeTick(tokenId: string, tier?: string): Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* Verify block data using the AdaptiveSwitch pipeline.
|
|
21
|
+
* Updates the current mode based on verification results.
|
|
22
|
+
*/
|
|
23
|
+
verifyBlock(blockData: Block, tttRecord: TTTRecord): Promise<AdaptiveMode>;
|
|
24
|
+
/**
|
|
25
|
+
* Return the current TURBO/FULL mode.
|
|
26
|
+
*/
|
|
27
|
+
getMode(): AdaptiveMode;
|
|
28
|
+
/**
|
|
29
|
+
* Helper to get current balance (for testing).
|
|
30
|
+
*/
|
|
31
|
+
getBalance(): bigint;
|
|
32
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// sdk/src/ttt_builder.ts — TTT Builder Implementation
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.TTTBuilder = void 0;
|
|
5
|
+
const ethers_1 = require("ethers");
|
|
6
|
+
const adaptive_switch_1 = require("./adaptive_switch");
|
|
7
|
+
const evm_connector_1 = require("./evm_connector");
|
|
8
|
+
const dynamic_fee_1 = require("./dynamic_fee");
|
|
9
|
+
const logger_1 = require("./logger");
|
|
10
|
+
class TTTBuilder {
|
|
11
|
+
mode = adaptive_switch_1.AdaptiveMode.FULL;
|
|
12
|
+
connector;
|
|
13
|
+
tttBalance = 0n;
|
|
14
|
+
adaptiveSwitch;
|
|
15
|
+
constructor(connector) {
|
|
16
|
+
this.connector = connector || new evm_connector_1.EVMConnector();
|
|
17
|
+
this.adaptiveSwitch = new adaptive_switch_1.AdaptiveSwitch();
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Purchase TTT from the market using EVMConnector.
|
|
21
|
+
* P1-5: Router/token addresses configurable. P1-6: 5% slippage protection.
|
|
22
|
+
*/
|
|
23
|
+
async purchaseTTT(poolAddress, amount, tokenInAddress = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", routerAddress = "0x11C9e42994625A0F52906852A9b91e1B69B79B22", slippageBps = 500n // 5% default slippage protection
|
|
24
|
+
) {
|
|
25
|
+
logger_1.logger.info(`[TTTBuilder] Purchasing ${amount} TTT from pool: ${poolAddress}`);
|
|
26
|
+
// P1-6: Calculate minimum output with slippage protection
|
|
27
|
+
const minAmountOut = (amount * (10000n - slippageBps)) / 10000n;
|
|
28
|
+
const receipt = await this.connector.swap(routerAddress, tokenInAddress, poolAddress, amount, minAmountOut);
|
|
29
|
+
this.tttBalance += amount;
|
|
30
|
+
logger_1.logger.info(`[TTTBuilder] Purchase successful. TX: ${receipt.hash}. Current balance: ${this.tttBalance}`);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Apply TTT to a block by burning it.
|
|
34
|
+
* This signals the intention to use TTT for prioritized processing.
|
|
35
|
+
*/
|
|
36
|
+
async consumeTick(tokenId, tier = "T1_block") {
|
|
37
|
+
logger_1.logger.info(`[TTTBuilder] Consuming TTT tick for token: ${tokenId} (Tier: ${tier})`);
|
|
38
|
+
// Look up tier-based costs from TIER_USD_MICRO
|
|
39
|
+
// Use BigInt to avoid floating point precision issues (Scale: 1e18)
|
|
40
|
+
const usdCostFactor = dynamic_fee_1.TIER_USD_MICRO[tier] || 10000n; // default 0.01 USD
|
|
41
|
+
let costTTT = (usdCostFactor * (10n ** 12n)); // Convert to 18 decimals (1e6 * 1e12 = 1e18)
|
|
42
|
+
// Apply discount if in TURBO mode (Economic incentive for honest builders)
|
|
43
|
+
// R2-P2-3: Use integer-only discount map to avoid float→BigInt precision loss
|
|
44
|
+
const DISCOUNT_PERMILLE = { "TURBO": 200n, "FULL": 0n };
|
|
45
|
+
const discountPermille = DISCOUNT_PERMILLE[this.adaptiveSwitch.getCurrentMode()] ?? 0n;
|
|
46
|
+
if (discountPermille > 0n) {
|
|
47
|
+
costTTT = (costTTT * (1000n - discountPermille)) / 1000n;
|
|
48
|
+
logger_1.logger.info(`[TTTBuilder] TURBO discount applied: ${Number(discountPermille) / 10}%. Final cost: ${costTTT}`);
|
|
49
|
+
}
|
|
50
|
+
if (this.tttBalance < costTTT) {
|
|
51
|
+
throw new Error(`[TTTBuilder] Insufficient TTT balance to consume tick. Needed: ${costTTT}, Balance: ${this.tttBalance}`);
|
|
52
|
+
}
|
|
53
|
+
// Call the actual burn function on the TTT contract via the connector.
|
|
54
|
+
const grgHash = ethers_1.ethers.keccak256(ethers_1.ethers.toUtf8Bytes(tokenId));
|
|
55
|
+
const tierLevel = parseInt(tier.substring(1, 2)) || 1;
|
|
56
|
+
await this.connector.burnTTT(costTTT, grgHash, tierLevel);
|
|
57
|
+
this.tttBalance -= costTTT;
|
|
58
|
+
logger_1.logger.info(`[TTTBuilder] Tick consumed. Cost: ${costTTT}. Remaining balance: ${this.tttBalance}`);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Verify block data using the AdaptiveSwitch pipeline.
|
|
62
|
+
* Updates the current mode based on verification results.
|
|
63
|
+
*/
|
|
64
|
+
async verifyBlock(blockData, tttRecord) {
|
|
65
|
+
logger_1.logger.info(`[TTTBuilder] Verifying block at timestamp: ${blockData.timestamp}`);
|
|
66
|
+
const result = this.adaptiveSwitch.verifyBlock(blockData, tttRecord);
|
|
67
|
+
this.mode = result;
|
|
68
|
+
logger_1.logger.info(`[TTTBuilder] Verification complete. Mode: ${this.mode}`);
|
|
69
|
+
return this.mode;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Return the current TURBO/FULL mode.
|
|
73
|
+
*/
|
|
74
|
+
getMode() {
|
|
75
|
+
return this.mode;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Helper to get current balance (for testing).
|
|
79
|
+
*/
|
|
80
|
+
getBalance() {
|
|
81
|
+
return this.tttBalance;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
exports.TTTBuilder = TTTBuilder;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { AutoMintConfig, TTTClientConfig } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Health status returned by getHealth()
|
|
4
|
+
*/
|
|
5
|
+
export interface HealthStatus {
|
|
6
|
+
healthy: boolean;
|
|
7
|
+
checks: {
|
|
8
|
+
initialized: boolean;
|
|
9
|
+
rpcConnected: boolean;
|
|
10
|
+
signerAvailable: boolean;
|
|
11
|
+
balanceSufficient: boolean;
|
|
12
|
+
ntpSourcesOk: boolean;
|
|
13
|
+
};
|
|
14
|
+
metrics: {
|
|
15
|
+
mintCount: number;
|
|
16
|
+
mintFailures: number;
|
|
17
|
+
successRate: number;
|
|
18
|
+
totalFeesPaid: string;
|
|
19
|
+
avgMintLatencyMs: number;
|
|
20
|
+
lastMintAt: string | null;
|
|
21
|
+
uptimeMs: number;
|
|
22
|
+
};
|
|
23
|
+
alerts: string[];
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* TTTClient - DEX 운영자용 SDK 진입점
|
|
27
|
+
* 모든 내부 모듈을 초기화하고 자동 민팅 프로세스를 관리
|
|
28
|
+
*/
|
|
29
|
+
export declare class TTTClient {
|
|
30
|
+
private config;
|
|
31
|
+
private autoMintEngine;
|
|
32
|
+
private poolRegistry;
|
|
33
|
+
private isInitialized;
|
|
34
|
+
private mintCount;
|
|
35
|
+
private mintFailures;
|
|
36
|
+
private totalFeesPaid;
|
|
37
|
+
private signer;
|
|
38
|
+
private lastTokenId;
|
|
39
|
+
private mintLatencies;
|
|
40
|
+
private lastMintAt;
|
|
41
|
+
private startedAt;
|
|
42
|
+
private minBalanceWei;
|
|
43
|
+
private onAlertCallback?;
|
|
44
|
+
constructor(config: AutoMintConfig);
|
|
45
|
+
/**
|
|
46
|
+
* Static factory for Base Mainnet
|
|
47
|
+
*/
|
|
48
|
+
static forBase(config: Omit<TTTClientConfig, 'network'>): Promise<TTTClient>;
|
|
49
|
+
/**
|
|
50
|
+
* Static factory for Base Sepolia
|
|
51
|
+
*/
|
|
52
|
+
static forSepolia(config: Omit<TTTClientConfig, 'network'>): Promise<TTTClient>;
|
|
53
|
+
/**
|
|
54
|
+
* Universal factory to create and initialize a client
|
|
55
|
+
*/
|
|
56
|
+
static create(config: TTTClientConfig): Promise<TTTClient>;
|
|
57
|
+
/**
|
|
58
|
+
* Gracefully shuts down the SDK, stopping all background processes and listeners.
|
|
59
|
+
*/
|
|
60
|
+
destroy(): Promise<void>;
|
|
61
|
+
/**
|
|
62
|
+
* SDK 초기화: RPC 연결, 시간 소스 설정, 수수료 엔진 연결
|
|
63
|
+
*/
|
|
64
|
+
initialize(): Promise<void>;
|
|
65
|
+
/**
|
|
66
|
+
* 자동 민팅 프로세스 시작
|
|
67
|
+
*/
|
|
68
|
+
startAutoMint(): void;
|
|
69
|
+
/**
|
|
70
|
+
* 자동 민팅 프로세스 정지
|
|
71
|
+
*/
|
|
72
|
+
stopAutoMint(): void;
|
|
73
|
+
/**
|
|
74
|
+
* List registered pools.
|
|
75
|
+
*/
|
|
76
|
+
listPools(): string[];
|
|
77
|
+
/**
|
|
78
|
+
* Get stats for a specific pool.
|
|
79
|
+
*/
|
|
80
|
+
getPoolStats(poolAddress: string): {
|
|
81
|
+
minted: bigint;
|
|
82
|
+
burned: bigint;
|
|
83
|
+
} | null;
|
|
84
|
+
/**
|
|
85
|
+
* Set minimum ETH balance threshold for health alerts.
|
|
86
|
+
*/
|
|
87
|
+
setMinBalance(weiAmount: bigint): void;
|
|
88
|
+
/**
|
|
89
|
+
* Register alert callback for real-time notifications.
|
|
90
|
+
*/
|
|
91
|
+
onAlert(callback: (alert: string) => void): void;
|
|
92
|
+
private emitAlert;
|
|
93
|
+
/**
|
|
94
|
+
* Record a mint failure (called internally or externally).
|
|
95
|
+
*/
|
|
96
|
+
recordMintFailure(): void;
|
|
97
|
+
/**
|
|
98
|
+
* Record mint latency in ms (called from auto-mint wrapper).
|
|
99
|
+
*/
|
|
100
|
+
recordMintLatency(ms: number): void;
|
|
101
|
+
/**
|
|
102
|
+
* Production health check — liveness + readiness + metrics.
|
|
103
|
+
* No exceptions: always returns a HealthStatus object.
|
|
104
|
+
*/
|
|
105
|
+
getHealth(): Promise<HealthStatus>;
|
|
106
|
+
/**
|
|
107
|
+
* 현재 SDK 상태 및 통계 반환 (잔고, 민팅 수, 수수료 등)
|
|
108
|
+
*/
|
|
109
|
+
getStatus(): Promise<{
|
|
110
|
+
isInitialized: boolean;
|
|
111
|
+
tier: string;
|
|
112
|
+
mintCount: number;
|
|
113
|
+
totalFeesPaid: string;
|
|
114
|
+
balance: string;
|
|
115
|
+
tttBalance: string;
|
|
116
|
+
lastTokenId: string | null;
|
|
117
|
+
}>;
|
|
118
|
+
}
|