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,352 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TTTClient = void 0;
|
|
4
|
+
const ethers_1 = require("ethers");
|
|
5
|
+
const auto_mint_1 = require("./auto_mint");
|
|
6
|
+
const logger_1 = require("./logger");
|
|
7
|
+
const pool_registry_1 = require("./pool_registry");
|
|
8
|
+
const networks_1 = require("./networks");
|
|
9
|
+
const signer_1 = require("./signer");
|
|
10
|
+
/**
|
|
11
|
+
* TTTClient - DEX 운영자용 SDK 진입점
|
|
12
|
+
* 모든 내부 모듈을 초기화하고 자동 민팅 프로세스를 관리
|
|
13
|
+
*/
|
|
14
|
+
class TTTClient {
|
|
15
|
+
config;
|
|
16
|
+
autoMintEngine;
|
|
17
|
+
poolRegistry;
|
|
18
|
+
isInitialized = false;
|
|
19
|
+
mintCount = 0;
|
|
20
|
+
mintFailures = 0;
|
|
21
|
+
totalFeesPaid = 0n;
|
|
22
|
+
signer = null;
|
|
23
|
+
lastTokenId = null;
|
|
24
|
+
mintLatencies = [];
|
|
25
|
+
lastMintAt = null;
|
|
26
|
+
startedAt = new Date();
|
|
27
|
+
minBalanceWei = ethers_1.ethers.parseEther("0.01"); // 0.01 ETH alert threshold
|
|
28
|
+
onAlertCallback;
|
|
29
|
+
constructor(config) {
|
|
30
|
+
this.config = config;
|
|
31
|
+
this.autoMintEngine = new auto_mint_1.AutoMintEngine(config);
|
|
32
|
+
this.poolRegistry = new pool_registry_1.PoolRegistry();
|
|
33
|
+
// Set up callback to update stats + metrics
|
|
34
|
+
this.autoMintEngine.setOnMint((result) => {
|
|
35
|
+
this.mintCount++;
|
|
36
|
+
this.totalFeesPaid += result.protocolFeePaid;
|
|
37
|
+
this.lastTokenId = result.tokenId;
|
|
38
|
+
this.lastMintAt = new Date();
|
|
39
|
+
// Record in registry
|
|
40
|
+
this.poolRegistry.recordMint(this.config.poolAddress, 1n);
|
|
41
|
+
});
|
|
42
|
+
// H2: Wire failure/latency metrics from AutoMint loop to TTTClient
|
|
43
|
+
this.autoMintEngine.setOnFailure((_error) => {
|
|
44
|
+
this.mintFailures++;
|
|
45
|
+
});
|
|
46
|
+
this.autoMintEngine.setOnLatency((ms) => {
|
|
47
|
+
this.mintLatencies.push(ms);
|
|
48
|
+
if (this.mintLatencies.length > 100) {
|
|
49
|
+
this.mintLatencies.shift();
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Static factory for Base Mainnet
|
|
55
|
+
*/
|
|
56
|
+
static async forBase(config) {
|
|
57
|
+
return this.create({ ...config, network: 'base' });
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Static factory for Base Sepolia
|
|
61
|
+
*/
|
|
62
|
+
static async forSepolia(config) {
|
|
63
|
+
return this.create({ ...config, network: 'sepolia' });
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Universal factory to create and initialize a client
|
|
67
|
+
*/
|
|
68
|
+
static async create(config) {
|
|
69
|
+
// 1. Resolve network defaults
|
|
70
|
+
let net;
|
|
71
|
+
if (typeof config.network === 'string') {
|
|
72
|
+
net = networks_1.NETWORKS[config.network] || networks_1.NETWORKS.base;
|
|
73
|
+
}
|
|
74
|
+
else if (config.network) {
|
|
75
|
+
net = config.network;
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
net = networks_1.NETWORKS.base;
|
|
79
|
+
}
|
|
80
|
+
// 2. Create signer via abstraction
|
|
81
|
+
const abstractSigner = await (0, signer_1.createSigner)(config.signer);
|
|
82
|
+
const signer = abstractSigner.inner;
|
|
83
|
+
// 2.5. Validate addresses — prevent accidental mainnet zero-address usage
|
|
84
|
+
const contractAddr = config.contractAddress || net.tttAddress;
|
|
85
|
+
if (contractAddr === "0x0000000000000000000000000000000000000000") {
|
|
86
|
+
throw new Error("[TTTClient] TTT contract address is zero address. On Base Mainnet, you must provide contractAddress in config (contracts not yet deployed).");
|
|
87
|
+
}
|
|
88
|
+
// 3. Build AutoMintConfig from TTTClientConfig + defaults
|
|
89
|
+
const autoMintConfig = {
|
|
90
|
+
chainId: net.chainId,
|
|
91
|
+
rpcUrl: config.rpcUrl || net.rpcUrl,
|
|
92
|
+
signer: signer,
|
|
93
|
+
contractAddress: config.contractAddress || net.tttAddress,
|
|
94
|
+
feeCollectorAddress: net.protocolFeeAddress,
|
|
95
|
+
poolAddress: config.poolAddress || "0x0000000000000000000000000000000000000000",
|
|
96
|
+
tier: config.tier || "T1_block",
|
|
97
|
+
timeSources: config.timeSources || ["nist", "kriss", "google"],
|
|
98
|
+
protocolFeeRate: config.protocolFeeRate || 0.05,
|
|
99
|
+
protocolFeeRecipient: config.protocolFeeRecipient || "0x0000000000000000000000000000000000000000",
|
|
100
|
+
fallbackPriceUsd: config.fallbackPriceUsd || 10000n,
|
|
101
|
+
};
|
|
102
|
+
// 4. Instantiate and initialize
|
|
103
|
+
const client = new TTTClient(autoMintConfig);
|
|
104
|
+
await client.initialize();
|
|
105
|
+
if (config.enableGracefulShutdown) {
|
|
106
|
+
process.on('SIGINT', async () => {
|
|
107
|
+
logger_1.logger.info("[TTTClient] SIGINT received, shutting down gracefully...");
|
|
108
|
+
await client.destroy();
|
|
109
|
+
process.exit(0);
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
return client;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Gracefully shuts down the SDK, stopping all background processes and listeners.
|
|
116
|
+
*/
|
|
117
|
+
async destroy() {
|
|
118
|
+
if (!this.isInitialized)
|
|
119
|
+
return;
|
|
120
|
+
logger_1.logger.info("[TTTClient] Destroying client...");
|
|
121
|
+
// 1. Stop auto-mint engine
|
|
122
|
+
this.autoMintEngine.stop();
|
|
123
|
+
// 2. Unsubscribe all event listeners
|
|
124
|
+
this.autoMintEngine.getEvmConnector().unsubscribeAll();
|
|
125
|
+
// 3. Clear local state
|
|
126
|
+
this.isInitialized = false;
|
|
127
|
+
this.signer = null;
|
|
128
|
+
logger_1.logger.info("[TTTClient] Client destroyed successfully.");
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* SDK 초기화: RPC 연결, 시간 소스 설정, 수수료 엔진 연결
|
|
132
|
+
*/
|
|
133
|
+
async initialize() {
|
|
134
|
+
if (this.isInitialized)
|
|
135
|
+
return;
|
|
136
|
+
try {
|
|
137
|
+
logger_1.logger.info(`[TTTClient] Initializing for chain ${this.config.chainId}...`);
|
|
138
|
+
await this.autoMintEngine.initialize();
|
|
139
|
+
const connector = this.autoMintEngine.getEvmConnector();
|
|
140
|
+
this.signer = connector.getSigner();
|
|
141
|
+
// Register initial pool
|
|
142
|
+
await this.poolRegistry.registerPool(this.config.chainId, this.config.poolAddress);
|
|
143
|
+
// R2-P2-2: Validate feeCollectorAddress early before use
|
|
144
|
+
if (this.config.feeCollectorAddress && !ethers_1.ethers.isAddress(this.config.feeCollectorAddress)) {
|
|
145
|
+
throw new Error(`[TTTClient] Invalid feeCollectorAddress: ${this.config.feeCollectorAddress}`);
|
|
146
|
+
}
|
|
147
|
+
// Attach ProtocolFee contract if address is provided
|
|
148
|
+
if (this.config.feeCollectorAddress) {
|
|
149
|
+
const protocolFeeAbi = [
|
|
150
|
+
"event FeeCollected(address indexed payer, uint256 amount, uint256 nonce)"
|
|
151
|
+
];
|
|
152
|
+
connector.attachProtocolFeeContract(this.config.feeCollectorAddress, protocolFeeAbi);
|
|
153
|
+
}
|
|
154
|
+
// Subscribe to events
|
|
155
|
+
await connector.subscribeToEvents({
|
|
156
|
+
onMinted: async (to, tokenId, amount) => {
|
|
157
|
+
logger_1.logger.info(`[TTTClient] Event: TTTMinted to ${to}, tokenId: ${tokenId}, amount: ${amount}`);
|
|
158
|
+
const myAddr = await this.signer?.getAddress();
|
|
159
|
+
if (to.toLowerCase() === myAddr?.toLowerCase()) {
|
|
160
|
+
// Logic could go here to update local state
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
onBurned: (from, tokenId, amount, tier) => {
|
|
164
|
+
logger_1.logger.info(`[TTTClient] Event: TTTBurned from ${from}, tokenId: ${tokenId}, amount: ${amount}, tier: ${tier}`);
|
|
165
|
+
this.poolRegistry.recordBurn(this.config.poolAddress, amount);
|
|
166
|
+
},
|
|
167
|
+
onFeeCollected: (payer, amount, nonce) => {
|
|
168
|
+
logger_1.logger.info(`[TTTClient] Event: FeeCollected from ${payer}, amount: ${amount}, nonce: ${nonce}`);
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
this.isInitialized = true;
|
|
172
|
+
logger_1.logger.info(`[TTTClient] SDK initialized successfully`);
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
this.isInitialized = false;
|
|
176
|
+
this.signer = null;
|
|
177
|
+
logger_1.logger.error(`[TTTClient] Initialization failed, state rolled back: ${error}`);
|
|
178
|
+
throw error;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* 자동 민팅 프로세스 시작
|
|
183
|
+
*/
|
|
184
|
+
startAutoMint() {
|
|
185
|
+
if (!this.isInitialized) {
|
|
186
|
+
throw new Error("SDK must be initialized before starting auto-mint");
|
|
187
|
+
}
|
|
188
|
+
this.autoMintEngine.start();
|
|
189
|
+
logger_1.logger.info(`[TTTClient] Auto-minting started for tier ${this.config.tier}`);
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* 자동 민팅 프로세스 정지
|
|
193
|
+
*/
|
|
194
|
+
stopAutoMint() {
|
|
195
|
+
this.autoMintEngine.stop();
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* List registered pools.
|
|
199
|
+
*/
|
|
200
|
+
listPools() {
|
|
201
|
+
return this.poolRegistry.listPools();
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Get stats for a specific pool.
|
|
205
|
+
*/
|
|
206
|
+
getPoolStats(poolAddress) {
|
|
207
|
+
return this.poolRegistry.getPoolStats(poolAddress);
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Set minimum ETH balance threshold for health alerts.
|
|
211
|
+
*/
|
|
212
|
+
setMinBalance(weiAmount) {
|
|
213
|
+
this.minBalanceWei = weiAmount;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Register alert callback for real-time notifications.
|
|
217
|
+
*/
|
|
218
|
+
onAlert(callback) {
|
|
219
|
+
this.onAlertCallback = callback;
|
|
220
|
+
}
|
|
221
|
+
emitAlert(alert) {
|
|
222
|
+
logger_1.logger.warn(`[TTTClient] ALERT: ${alert}`);
|
|
223
|
+
if (this.onAlertCallback) {
|
|
224
|
+
try {
|
|
225
|
+
this.onAlertCallback(alert);
|
|
226
|
+
}
|
|
227
|
+
catch (_) { /* swallow */ }
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Record a mint failure (called internally or externally).
|
|
232
|
+
*/
|
|
233
|
+
recordMintFailure() {
|
|
234
|
+
this.mintFailures++;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Record mint latency in ms (called from auto-mint wrapper).
|
|
238
|
+
*/
|
|
239
|
+
recordMintLatency(ms) {
|
|
240
|
+
this.mintLatencies.push(ms);
|
|
241
|
+
// Keep last 100 entries
|
|
242
|
+
if (this.mintLatencies.length > 100) {
|
|
243
|
+
this.mintLatencies.shift();
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Production health check — liveness + readiness + metrics.
|
|
248
|
+
* No exceptions: always returns a HealthStatus object.
|
|
249
|
+
*/
|
|
250
|
+
async getHealth() {
|
|
251
|
+
const alerts = [];
|
|
252
|
+
let rpcConnected = false;
|
|
253
|
+
let balanceSufficient = false;
|
|
254
|
+
let ntpSourcesOk = true; // Assume ok unless we can verify
|
|
255
|
+
// 1. RPC connectivity check
|
|
256
|
+
if (this.isInitialized && this.signer?.provider) {
|
|
257
|
+
try {
|
|
258
|
+
const blockNumber = await this.signer.provider.getBlockNumber();
|
|
259
|
+
rpcConnected = blockNumber > 0;
|
|
260
|
+
}
|
|
261
|
+
catch {
|
|
262
|
+
rpcConnected = false;
|
|
263
|
+
alerts.push("RPC connection failed");
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
// 2. Balance check
|
|
267
|
+
if (this.isInitialized && this.signer?.provider) {
|
|
268
|
+
try {
|
|
269
|
+
const address = await this.signer.getAddress();
|
|
270
|
+
const balance = await this.signer.provider.getBalance(address);
|
|
271
|
+
balanceSufficient = balance >= this.minBalanceWei;
|
|
272
|
+
if (!balanceSufficient) {
|
|
273
|
+
alerts.push(`ETH balance low: ${ethers_1.ethers.formatEther(balance)} ETH (min: ${ethers_1.ethers.formatEther(this.minBalanceWei)})`);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
catch {
|
|
277
|
+
alerts.push("Balance check failed");
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
// 3. Consecutive failure check
|
|
281
|
+
const total = this.mintCount + this.mintFailures;
|
|
282
|
+
const successRate = total > 0 ? this.mintCount / total : 1;
|
|
283
|
+
if (this.mintFailures > 5 && successRate < 0.8) {
|
|
284
|
+
alerts.push(`High mint failure rate: ${this.mintFailures} failures, ${(successRate * 100).toFixed(1)}% success`);
|
|
285
|
+
}
|
|
286
|
+
// 4. Avg latency
|
|
287
|
+
const avgLatency = this.mintLatencies.length > 0
|
|
288
|
+
? this.mintLatencies.reduce((a, b) => a + b, 0) / this.mintLatencies.length
|
|
289
|
+
: 0;
|
|
290
|
+
// 5. Uptime
|
|
291
|
+
const uptimeMs = Date.now() - this.startedAt.getTime();
|
|
292
|
+
// Emit alerts
|
|
293
|
+
for (const alert of alerts) {
|
|
294
|
+
this.emitAlert(alert);
|
|
295
|
+
}
|
|
296
|
+
const healthy = this.isInitialized && rpcConnected && balanceSufficient && alerts.length === 0;
|
|
297
|
+
return {
|
|
298
|
+
healthy,
|
|
299
|
+
checks: {
|
|
300
|
+
initialized: this.isInitialized,
|
|
301
|
+
rpcConnected,
|
|
302
|
+
signerAvailable: this.signer !== null,
|
|
303
|
+
balanceSufficient,
|
|
304
|
+
ntpSourcesOk,
|
|
305
|
+
},
|
|
306
|
+
metrics: {
|
|
307
|
+
mintCount: this.mintCount,
|
|
308
|
+
mintFailures: this.mintFailures,
|
|
309
|
+
successRate: Math.round(successRate * 1000) / 1000,
|
|
310
|
+
totalFeesPaid: this.totalFeesPaid.toString(),
|
|
311
|
+
avgMintLatencyMs: Math.round(avgLatency),
|
|
312
|
+
lastMintAt: this.lastMintAt?.toISOString() ?? null,
|
|
313
|
+
uptimeMs,
|
|
314
|
+
},
|
|
315
|
+
alerts,
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* 현재 SDK 상태 및 통계 반환 (잔고, 민팅 수, 수수료 등)
|
|
320
|
+
*/
|
|
321
|
+
async getStatus() {
|
|
322
|
+
if (!this.isInitialized || !this.signer) {
|
|
323
|
+
throw new Error("SDK must be initialized before getting status");
|
|
324
|
+
}
|
|
325
|
+
const provider = this.signer.provider;
|
|
326
|
+
if (!provider) {
|
|
327
|
+
throw new Error("Signer provider is not available");
|
|
328
|
+
}
|
|
329
|
+
const address = await this.signer.getAddress();
|
|
330
|
+
const balance = await provider.getBalance(address);
|
|
331
|
+
const formattedBalance = ethers_1.ethers.formatEther(balance);
|
|
332
|
+
let tttBalance = 0n;
|
|
333
|
+
if (this.lastTokenId) {
|
|
334
|
+
try {
|
|
335
|
+
tttBalance = await this.autoMintEngine.getEvmConnector().getTTTBalance(address, BigInt(this.lastTokenId));
|
|
336
|
+
}
|
|
337
|
+
catch (e) {
|
|
338
|
+
logger_1.logger.error(`[TTTClient] Failed to fetch TTT balance: ${e}`);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
return {
|
|
342
|
+
isInitialized: this.isInitialized,
|
|
343
|
+
tier: this.config.tier,
|
|
344
|
+
mintCount: this.mintCount,
|
|
345
|
+
totalFeesPaid: this.totalFeesPaid.toString(),
|
|
346
|
+
balance: formattedBalance,
|
|
347
|
+
tttBalance: tttBalance.toString(),
|
|
348
|
+
lastTokenId: this.lastTokenId
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
exports.TTTClient = TTTClient;
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { Signer } from "ethers";
|
|
2
|
+
import { SignerConfig } from "./signer";
|
|
3
|
+
import { NetworkConfig } from "./networks";
|
|
4
|
+
export type TierType = "T0_epoch" | "T1_block" | "T2_slot" | "T3_micro";
|
|
5
|
+
export declare const TierIntervals: Record<TierType, number>;
|
|
6
|
+
/**
|
|
7
|
+
* High-level configuration for TTTClient
|
|
8
|
+
*/
|
|
9
|
+
export interface TTTClientConfig {
|
|
10
|
+
/**
|
|
11
|
+
* Required: Signer configuration (PrivateKey, Turnkey, Privy, KMS)
|
|
12
|
+
*/
|
|
13
|
+
signer: SignerConfig;
|
|
14
|
+
/**
|
|
15
|
+
* Optional: Network selection (preset "base", "sepolia" or custom NetworkConfig)
|
|
16
|
+
* Default: "base" (Base Mainnet)
|
|
17
|
+
*/
|
|
18
|
+
network?: string | NetworkConfig;
|
|
19
|
+
/**
|
|
20
|
+
* Optional: Tier resolution (T0 to T3)
|
|
21
|
+
* Default: "T1_block"
|
|
22
|
+
*/
|
|
23
|
+
tier?: TierType;
|
|
24
|
+
/**
|
|
25
|
+
* Optional: Override RPC URL provided by network default
|
|
26
|
+
*/
|
|
27
|
+
rpcUrl?: string;
|
|
28
|
+
/**
|
|
29
|
+
* Optional: Overwrite default NTP/KTSat sources
|
|
30
|
+
* Default: ["nist", "kriss", "google"]
|
|
31
|
+
*/
|
|
32
|
+
timeSources?: string[];
|
|
33
|
+
/**
|
|
34
|
+
* Optional: Override contract address for TTT token
|
|
35
|
+
*/
|
|
36
|
+
contractAddress?: string;
|
|
37
|
+
/**
|
|
38
|
+
* Optional: Override protocol fee rate (0.01 ~ 0.10)
|
|
39
|
+
* Default: 0.05
|
|
40
|
+
*/
|
|
41
|
+
protocolFeeRate?: number;
|
|
42
|
+
/**
|
|
43
|
+
* Optional: Fallback price for TTT tokens in USD (scaled 1e6)
|
|
44
|
+
* Default: 10000n ($0.01)
|
|
45
|
+
*/
|
|
46
|
+
fallbackPriceUsd?: bigint;
|
|
47
|
+
/**
|
|
48
|
+
* Optional: Pool address for DEX operations
|
|
49
|
+
*/
|
|
50
|
+
poolAddress?: string;
|
|
51
|
+
/**
|
|
52
|
+
* Optional: Recipient for protocol fees
|
|
53
|
+
*/
|
|
54
|
+
protocolFeeRecipient?: string;
|
|
55
|
+
/**
|
|
56
|
+
* Optional: Automatically register SIGINT handler for graceful shutdown
|
|
57
|
+
*/
|
|
58
|
+
enableGracefulShutdown?: boolean;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Internal configuration used by engines.
|
|
62
|
+
* Kept for backwards compatibility.
|
|
63
|
+
*/
|
|
64
|
+
export interface AutoMintConfig {
|
|
65
|
+
chainId: number;
|
|
66
|
+
poolAddress: string;
|
|
67
|
+
rpcUrl: string;
|
|
68
|
+
privateKey?: string;
|
|
69
|
+
signer?: Signer;
|
|
70
|
+
contractAddress: string;
|
|
71
|
+
feeCollectorAddress?: string;
|
|
72
|
+
tier: TierType;
|
|
73
|
+
timeSources: string[];
|
|
74
|
+
protocolFeeRate: number;
|
|
75
|
+
protocolFeeRecipient: string;
|
|
76
|
+
fallbackPriceUsd?: bigint;
|
|
77
|
+
}
|
|
78
|
+
export interface MintResult {
|
|
79
|
+
tokenId: string;
|
|
80
|
+
grgHash: string;
|
|
81
|
+
timestamp: bigint;
|
|
82
|
+
txHash: string;
|
|
83
|
+
protocolFeePaid: bigint;
|
|
84
|
+
proofOfTime?: ProofOfTime;
|
|
85
|
+
}
|
|
86
|
+
export interface TimeReading {
|
|
87
|
+
timestamp: bigint;
|
|
88
|
+
uncertainty: number;
|
|
89
|
+
stratum: number;
|
|
90
|
+
source: string;
|
|
91
|
+
}
|
|
92
|
+
export interface SynthesizedTime {
|
|
93
|
+
timestamp: bigint;
|
|
94
|
+
confidence: number;
|
|
95
|
+
uncertainty: number;
|
|
96
|
+
sources: number;
|
|
97
|
+
stratum: number;
|
|
98
|
+
}
|
|
99
|
+
export interface PoolKey {
|
|
100
|
+
currency0: string;
|
|
101
|
+
currency1: string;
|
|
102
|
+
fee: number;
|
|
103
|
+
tickSpacing: number;
|
|
104
|
+
hooks: string;
|
|
105
|
+
}
|
|
106
|
+
export interface BeforeSwapParams {
|
|
107
|
+
sender: string;
|
|
108
|
+
key: PoolKey;
|
|
109
|
+
params: {
|
|
110
|
+
zeroForOne: boolean;
|
|
111
|
+
amountSpecified: bigint;
|
|
112
|
+
sqrtPriceLimitX96: bigint;
|
|
113
|
+
};
|
|
114
|
+
hookData: string;
|
|
115
|
+
}
|
|
116
|
+
export interface AfterSwapParams {
|
|
117
|
+
sender: string;
|
|
118
|
+
key: PoolKey;
|
|
119
|
+
params: {
|
|
120
|
+
zeroForOne: boolean;
|
|
121
|
+
amountSpecified: bigint;
|
|
122
|
+
sqrtPriceLimitX96: bigint;
|
|
123
|
+
};
|
|
124
|
+
delta: {
|
|
125
|
+
amount0: bigint;
|
|
126
|
+
amount1: bigint;
|
|
127
|
+
};
|
|
128
|
+
hookData: string;
|
|
129
|
+
}
|
|
130
|
+
export interface ProofOfTime {
|
|
131
|
+
timestamp: bigint;
|
|
132
|
+
uncertainty: number;
|
|
133
|
+
sources: number;
|
|
134
|
+
stratum: number;
|
|
135
|
+
confidence: number;
|
|
136
|
+
signatures: {
|
|
137
|
+
source: string;
|
|
138
|
+
timestamp: bigint;
|
|
139
|
+
uncertainty: number;
|
|
140
|
+
}[];
|
|
141
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TierIntervals = void 0;
|
|
4
|
+
exports.TierIntervals = {
|
|
5
|
+
T0_epoch: 384000, // 6.4 min
|
|
6
|
+
T1_block: 2000, // 2 sec (Base L2)
|
|
7
|
+
T2_slot: 12000, // 12 sec (Ethereum)
|
|
8
|
+
T3_micro: 100, // 100 ms (IoT)
|
|
9
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { EVMConnector } from "./evm_connector";
|
|
2
|
+
import { ProtocolFeeCollector } from "./protocol_fee";
|
|
3
|
+
import { FeeCalculation } from "./dynamic_fee";
|
|
4
|
+
import { BeforeSwapParams, AfterSwapParams } from "./types";
|
|
5
|
+
/**
|
|
6
|
+
* UniswapV4Hook - TTT-based Uniswap V4 Hook Simulation
|
|
7
|
+
* Provides actual logic for TTT balance verification and fee management.
|
|
8
|
+
*/
|
|
9
|
+
export declare class UniswapV4Hook {
|
|
10
|
+
private evmConnector;
|
|
11
|
+
private hookAddress;
|
|
12
|
+
private tttTokenAddress;
|
|
13
|
+
private minTTTBalance;
|
|
14
|
+
private swapFeeTTT;
|
|
15
|
+
private tttContract;
|
|
16
|
+
private feeCollector?;
|
|
17
|
+
private stats;
|
|
18
|
+
constructor(evmConnector: EVMConnector, hookAddress: string, tttTokenAddress: string, minTTTBalance?: bigint, swapFeeTTT?: bigint, feeCollector?: ProtocolFeeCollector);
|
|
19
|
+
/**
|
|
20
|
+
* beforeSwap(params: BeforeSwapParams): Promise<void>
|
|
21
|
+
* Check TTT balance and deduct fees before a swap.
|
|
22
|
+
*/
|
|
23
|
+
beforeSwap(params: BeforeSwapParams): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* afterSwap(params: AfterSwapParams): Promise<void>
|
|
26
|
+
* Record results and update statistics after a swap.
|
|
27
|
+
*/
|
|
28
|
+
afterSwap(params: AfterSwapParams, burnFeeCalc?: FeeCalculation, signature?: string, nonce?: bigint, deadline?: number): Promise<void>;
|
|
29
|
+
/**
|
|
30
|
+
* getHookAddress(): string
|
|
31
|
+
* Return the hook contract address.
|
|
32
|
+
*/
|
|
33
|
+
getHookAddress(): string;
|
|
34
|
+
/**
|
|
35
|
+
* Return current statistics for the hook.
|
|
36
|
+
*/
|
|
37
|
+
getStats(): {
|
|
38
|
+
totalSwaps: number;
|
|
39
|
+
totalFeesCollected: string;
|
|
40
|
+
lastSwapTimestamp: number;
|
|
41
|
+
failedBurns: number;
|
|
42
|
+
};
|
|
43
|
+
}
|
package/dist/v4_hook.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.UniswapV4Hook = void 0;
|
|
4
|
+
const ethers_1 = require("ethers");
|
|
5
|
+
const logger_1 = require("./logger");
|
|
6
|
+
/**
|
|
7
|
+
* UniswapV4Hook - TTT-based Uniswap V4 Hook Simulation
|
|
8
|
+
* Provides actual logic for TTT balance verification and fee management.
|
|
9
|
+
*/
|
|
10
|
+
class UniswapV4Hook {
|
|
11
|
+
evmConnector;
|
|
12
|
+
hookAddress;
|
|
13
|
+
tttTokenAddress;
|
|
14
|
+
minTTTBalance;
|
|
15
|
+
swapFeeTTT;
|
|
16
|
+
tttContract = null;
|
|
17
|
+
feeCollector;
|
|
18
|
+
stats = {
|
|
19
|
+
totalSwaps: 0,
|
|
20
|
+
totalFeesCollected: 0n,
|
|
21
|
+
lastSwapTimestamp: 0,
|
|
22
|
+
failedBurns: 0
|
|
23
|
+
};
|
|
24
|
+
constructor(evmConnector, hookAddress, tttTokenAddress, minTTTBalance = ethers_1.ethers.parseEther("1.0"), swapFeeTTT = ethers_1.ethers.parseEther("0.1"), feeCollector) {
|
|
25
|
+
this.evmConnector = evmConnector;
|
|
26
|
+
this.hookAddress = hookAddress;
|
|
27
|
+
this.tttTokenAddress = tttTokenAddress;
|
|
28
|
+
this.minTTTBalance = minTTTBalance;
|
|
29
|
+
this.swapFeeTTT = swapFeeTTT;
|
|
30
|
+
this.feeCollector = feeCollector;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* beforeSwap(params: BeforeSwapParams): Promise<void>
|
|
34
|
+
* Check TTT balance and deduct fees before a swap.
|
|
35
|
+
*/
|
|
36
|
+
async beforeSwap(params) {
|
|
37
|
+
logger_1.logger.info(`[UniswapV4Hook] beforeSwap called for sender: ${params.sender}`);
|
|
38
|
+
const provider = this.evmConnector.getProvider();
|
|
39
|
+
// ABI for TTT balance check
|
|
40
|
+
if (!this.tttContract) {
|
|
41
|
+
const tttAbi = ["function balanceOf(address, uint256) view returns (uint256)"];
|
|
42
|
+
this.tttContract = new ethers_1.ethers.Contract(this.tttTokenAddress, tttAbi, provider);
|
|
43
|
+
}
|
|
44
|
+
// 1. Check TTT balance of the sender
|
|
45
|
+
try {
|
|
46
|
+
// ERC-1155: balanceOf(address, uint256) — tokenId 0 = default TTT token
|
|
47
|
+
const balance = await this.tttContract.balanceOf(params.sender, 0);
|
|
48
|
+
if (balance < this.minTTTBalance) {
|
|
49
|
+
throw new Error(`[UniswapV4Hook] Insufficient TTT balance for ${params.sender}. ` +
|
|
50
|
+
`Required: ${ethers_1.ethers.formatEther(this.minTTTBalance)}, Actual: ${ethers_1.ethers.formatEther(balance)}`);
|
|
51
|
+
}
|
|
52
|
+
logger_1.logger.info(`[UniswapV4Hook] TTT balance verified: ${ethers_1.ethers.formatEther(balance)} TTT`);
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
if ((error instanceof Error ? error.message : String(error)).includes("Insufficient TTT balance"))
|
|
56
|
+
throw error;
|
|
57
|
+
throw new Error(`[UniswapV4Hook] Failed to check TTT balance: ${(error instanceof Error ? error.message : String(error))}`);
|
|
58
|
+
}
|
|
59
|
+
// 2. Deduct fees (Simulated record-keeping)
|
|
60
|
+
// In a production hook, this would be an on-chain state update or burn.
|
|
61
|
+
this.stats.totalFeesCollected += this.swapFeeTTT;
|
|
62
|
+
logger_1.logger.info(`[UniswapV4Hook] TTT fee deducted: ${ethers_1.ethers.formatEther(this.swapFeeTTT)} TTT`);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* afterSwap(params: AfterSwapParams): Promise<void>
|
|
66
|
+
* Record results and update statistics after a swap.
|
|
67
|
+
*/
|
|
68
|
+
async afterSwap(params, burnFeeCalc, signature, nonce, deadline) {
|
|
69
|
+
logger_1.logger.info(`[UniswapV4Hook] afterSwap called for sender: ${params.sender}`);
|
|
70
|
+
// Implement actual fee burn/transfer logic using EVMConnector
|
|
71
|
+
try {
|
|
72
|
+
const grgHash = ethers_1.ethers.keccak256(ethers_1.ethers.toUtf8Bytes(`burn-${params.sender}-${Date.now()}`));
|
|
73
|
+
// Assuming tier 1 for simplicity in this hook
|
|
74
|
+
await this.evmConnector.burnTTT(this.swapFeeTTT, grgHash, 1);
|
|
75
|
+
logger_1.logger.info(`[UniswapV4Hook] Fee burn executed on-chain for ${ethers_1.ethers.formatEther(this.swapFeeTTT)} TTT`);
|
|
76
|
+
// Collect protocol burn fee if feeCollector and required params are provided
|
|
77
|
+
if (this.feeCollector && burnFeeCalc && signature && nonce !== undefined && deadline !== undefined) {
|
|
78
|
+
try {
|
|
79
|
+
await this.feeCollector.collectBurnFee(burnFeeCalc, signature, params.sender, nonce, deadline);
|
|
80
|
+
}
|
|
81
|
+
catch (feeError) {
|
|
82
|
+
logger_1.logger.error(`[UniswapV4Hook] Burn fee collection failed but burn was successful: ${feeError instanceof Error ? feeError.message : feeError}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
this.stats.failedBurns += 1;
|
|
88
|
+
logger_1.logger.error(`[UniswapV4Hook] Failed to execute fee burn: ${(error instanceof Error ? error.message : String(error))}`);
|
|
89
|
+
}
|
|
90
|
+
this.stats.totalSwaps += 1;
|
|
91
|
+
this.stats.lastSwapTimestamp = Math.floor(Date.now() / 1000);
|
|
92
|
+
// Log swap results
|
|
93
|
+
const { amount0, amount1 } = params.delta;
|
|
94
|
+
logger_1.logger.info(`[UniswapV4Hook] Swap recorded: Delta0=${amount0}, Delta1=${amount1}`);
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* getHookAddress(): string
|
|
98
|
+
* Return the hook contract address.
|
|
99
|
+
*/
|
|
100
|
+
getHookAddress() {
|
|
101
|
+
return this.hookAddress;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Return current statistics for the hook.
|
|
105
|
+
*/
|
|
106
|
+
getStats() {
|
|
107
|
+
return {
|
|
108
|
+
totalSwaps: this.stats.totalSwaps,
|
|
109
|
+
totalFeesCollected: ethers_1.ethers.formatEther(this.stats.totalFeesCollected),
|
|
110
|
+
lastSwapTimestamp: this.stats.lastSwapTimestamp,
|
|
111
|
+
failedBurns: this.stats.failedBurns
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
exports.UniswapV4Hook = UniswapV4Hook;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { AdaptiveMode } from "./adaptive_switch";
|
|
2
|
+
import { EVMConnector } from "./evm_connector";
|
|
3
|
+
import { ProtocolFeeCollector } from "./protocol_fee";
|
|
4
|
+
import { DynamicFeeEngine, FeeCalculation } from "./dynamic_fee";
|
|
5
|
+
export interface SwapDetails {
|
|
6
|
+
user: string;
|
|
7
|
+
tokenIn: string;
|
|
8
|
+
tokenOut: string;
|
|
9
|
+
amount: bigint;
|
|
10
|
+
}
|
|
11
|
+
export declare class X402Enforcer {
|
|
12
|
+
private static getCost;
|
|
13
|
+
/**
|
|
14
|
+
* Deducts TTT ticks from local balance and determines Adaptive Mode.
|
|
15
|
+
*/
|
|
16
|
+
static deductTick(feeEngine: DynamicFeeEngine, swap: SwapDetails, balance: bigint, tier: number, mode: AdaptiveMode): Promise<{
|
|
17
|
+
success: boolean;
|
|
18
|
+
remaining: bigint;
|
|
19
|
+
mode: AdaptiveMode;
|
|
20
|
+
}>;
|
|
21
|
+
/**
|
|
22
|
+
* Executes on-chain TTT burn via EVMConnector.
|
|
23
|
+
*/
|
|
24
|
+
static deductOnChain(connector: EVMConnector, feeEngine: DynamicFeeEngine, swap: SwapDetails, grgHash: string, tier: number, feeCollector?: ProtocolFeeCollector, burnFeeCalc?: FeeCalculation, signature?: string, nonce?: bigint, deadline?: number): Promise<string>;
|
|
25
|
+
/**
|
|
26
|
+
* Static validation rule.
|
|
27
|
+
*/
|
|
28
|
+
static enforcePool(feeEngine: DynamicFeeEngine, swap: SwapDetails, tttBalance: bigint, tier: number): Promise<boolean>;
|
|
29
|
+
}
|