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,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GrgInverse = void 0;
|
|
4
|
+
const crypto_1 = require("crypto");
|
|
5
|
+
const golay_1 = require("./golay");
|
|
6
|
+
const logger_1 = require("./logger");
|
|
7
|
+
const reed_solomon_1 = require("./reed_solomon");
|
|
8
|
+
class GrgInverse {
|
|
9
|
+
// 1. Golay Decoding & Integrity Check ๐ฑ
|
|
10
|
+
static golayDecodeWrapper(data) {
|
|
11
|
+
if (data.length < 8)
|
|
12
|
+
throw new Error("GRG shard too short for checksum");
|
|
13
|
+
// Split data and checksum (last 8 bytes)
|
|
14
|
+
const encoded = data.subarray(0, data.length - 8);
|
|
15
|
+
const checksum = data.subarray(data.length - 8);
|
|
16
|
+
// Verify SHA-256 Checksum (B1-5: 4 -> 8 bytes)
|
|
17
|
+
const hash = (0, crypto_1.createHash)("sha256").update(Buffer.from(encoded)).digest();
|
|
18
|
+
const expected = hash.subarray(0, 8);
|
|
19
|
+
if (!Buffer.from(checksum).equals(Buffer.from(expected))) {
|
|
20
|
+
throw new Error("GRG tamper detected: SHA-256 checksum mismatch");
|
|
21
|
+
}
|
|
22
|
+
// Proceed to Golay decode
|
|
23
|
+
const res = (0, golay_1.golayDecode)(encoded);
|
|
24
|
+
if (res.uncorrectable) {
|
|
25
|
+
throw new Error("GRG tamper detected: uncorrectable bit errors in Golay codeword");
|
|
26
|
+
}
|
|
27
|
+
return res.data;
|
|
28
|
+
}
|
|
29
|
+
// 2. RedStuff Decoding (Reed-Solomon GF(2^8))
|
|
30
|
+
static redstuffDecode(shards, dataShardCount = 4, parityShardCount = 2) {
|
|
31
|
+
return reed_solomon_1.ReedSolomon.decode(shards, dataShardCount, parityShardCount);
|
|
32
|
+
}
|
|
33
|
+
// 3. Golomb-Rice Decompression
|
|
34
|
+
// R4-P2-3: Max unary run length to prevent amplification DoS
|
|
35
|
+
static MAX_GOLOMB_Q = 1_000_000;
|
|
36
|
+
static golombDecode(data, m = 16) {
|
|
37
|
+
if (m < 2)
|
|
38
|
+
throw new Error("[GRG] Golomb parameter m must be >= 2");
|
|
39
|
+
const k = Math.log2(m);
|
|
40
|
+
let bits = "";
|
|
41
|
+
for (const byte of data) {
|
|
42
|
+
bits += byte.toString(2).padStart(8, "0");
|
|
43
|
+
}
|
|
44
|
+
const result = [];
|
|
45
|
+
let i = 0;
|
|
46
|
+
while (i < bits.length) {
|
|
47
|
+
let q = 0;
|
|
48
|
+
while (bits[i] === "1") {
|
|
49
|
+
q++;
|
|
50
|
+
i++;
|
|
51
|
+
if (q > this.MAX_GOLOMB_Q)
|
|
52
|
+
throw new Error(`[GRG] Golomb decode: unary run exceeds ${this.MAX_GOLOMB_Q} โ malformed or malicious input`);
|
|
53
|
+
}
|
|
54
|
+
if (bits[i] === "0")
|
|
55
|
+
i++;
|
|
56
|
+
const rStr = bits.substring(i, i + k);
|
|
57
|
+
if (rStr.length < k)
|
|
58
|
+
break;
|
|
59
|
+
const r = parseInt(rStr, 2);
|
|
60
|
+
result.push(q * m + r);
|
|
61
|
+
i += k;
|
|
62
|
+
}
|
|
63
|
+
return new Uint8Array(result);
|
|
64
|
+
}
|
|
65
|
+
static verify(data, originalShards) {
|
|
66
|
+
try {
|
|
67
|
+
const decodedShards = originalShards.map(s => {
|
|
68
|
+
try {
|
|
69
|
+
return this.golayDecodeWrapper(s);
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
const withLen = this.redstuffDecode(decodedShards);
|
|
76
|
+
// Extract original length
|
|
77
|
+
if (withLen.length < 4)
|
|
78
|
+
return false;
|
|
79
|
+
const origLen = (withLen[0] << 24) | (withLen[1] << 16) | (withLen[2] << 8) | withLen[3];
|
|
80
|
+
const compressed = withLen.subarray(4);
|
|
81
|
+
const decoded = this.golombDecode(compressed);
|
|
82
|
+
const final = decoded.subarray(0, origLen);
|
|
83
|
+
if (final.length !== data.length)
|
|
84
|
+
return false;
|
|
85
|
+
for (let i = 0; i < data.length; i++) {
|
|
86
|
+
if (final[i] !== data[i])
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
catch (e) {
|
|
92
|
+
logger_1.logger.warn(`[GRG Inverse] Verification failed: ${e}`);
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
exports.GrgInverse = GrgInverse;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare class GrgPipeline {
|
|
2
|
+
static readonly MAX_INPUT_SIZE: number;
|
|
3
|
+
/**
|
|
4
|
+
* Runs the full forward pipeline:
|
|
5
|
+
* Golomb-Rice -> RedStuff (Erasure) -> Golay(24,12)
|
|
6
|
+
*/
|
|
7
|
+
static processForward(data: Uint8Array): Uint8Array[];
|
|
8
|
+
/**
|
|
9
|
+
* Runs the full inverse pipeline:
|
|
10
|
+
* Golay(24,12) -> RedStuff (Reconstruction) -> Golomb-Rice Decompression
|
|
11
|
+
*/
|
|
12
|
+
static processInverse(shards: Uint8Array[], originalLength: number): Uint8Array;
|
|
13
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GrgPipeline = void 0;
|
|
4
|
+
const grg_forward_1 = require("./grg_forward");
|
|
5
|
+
const grg_inverse_1 = require("./grg_inverse");
|
|
6
|
+
const logger_1 = require("./logger");
|
|
7
|
+
class GrgPipeline {
|
|
8
|
+
// P1-3: Max input size to prevent OOM attacks (100 MB)
|
|
9
|
+
static MAX_INPUT_SIZE = 100 * 1024 * 1024;
|
|
10
|
+
/**
|
|
11
|
+
* Runs the full forward pipeline:
|
|
12
|
+
* Golomb-Rice -> RedStuff (Erasure) -> Golay(24,12)
|
|
13
|
+
*/
|
|
14
|
+
static processForward(data) {
|
|
15
|
+
if (data.length > this.MAX_INPUT_SIZE) {
|
|
16
|
+
throw new Error(`[GRG] Input size ${data.length} exceeds MAX_INPUT_SIZE ${this.MAX_INPUT_SIZE}`);
|
|
17
|
+
}
|
|
18
|
+
logger_1.logger.info("Starting GRG forward pipeline...");
|
|
19
|
+
try {
|
|
20
|
+
const shards = grg_forward_1.GrgForward.encode(data);
|
|
21
|
+
logger_1.logger.info(`GRG forward pipeline complete. Generated ${shards.length} shards.`);
|
|
22
|
+
return shards;
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
logger_1.logger.error(`GRG forward pipeline failed: ${error}`);
|
|
26
|
+
throw error;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Runs the full inverse pipeline:
|
|
31
|
+
* Golay(24,12) -> RedStuff (Reconstruction) -> Golomb-Rice Decompression
|
|
32
|
+
*/
|
|
33
|
+
static processInverse(shards, originalLength) {
|
|
34
|
+
logger_1.logger.info("Starting GRG inverse pipeline...");
|
|
35
|
+
try {
|
|
36
|
+
const decodedShards = shards.map(s => {
|
|
37
|
+
try {
|
|
38
|
+
return grg_inverse_1.GrgInverse.golayDecodeWrapper(s);
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
logger_1.logger.warn(`Golay decode failed for a shard: ${e}`);
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
const withLen = grg_inverse_1.GrgInverse.redstuffDecode(decodedShards);
|
|
46
|
+
// Extract original length from the first 4 bytes
|
|
47
|
+
const decodedLength = (withLen[0] << 24) | (withLen[1] << 16) | (withLen[2] << 8) | withLen[3];
|
|
48
|
+
const compressed = withLen.subarray(4);
|
|
49
|
+
const decompressed = grg_inverse_1.GrgInverse.golombDecode(compressed);
|
|
50
|
+
const final = decompressed.subarray(0, decodedLength);
|
|
51
|
+
// P1-4 FIX: Length mismatch is a corruption signal โ throw instead of warn
|
|
52
|
+
if (final.length !== originalLength) {
|
|
53
|
+
throw new Error(`[GRG] Length mismatch in inverse: expected ${originalLength}, got ${final.length}`);
|
|
54
|
+
}
|
|
55
|
+
logger_1.logger.info("GRG inverse pipeline complete.");
|
|
56
|
+
return final;
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
logger_1.logger.error(`GRG inverse pipeline failed: ${error}`);
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
exports.GrgPipeline = GrgPipeline;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export * from "./grg_forward";
|
|
2
|
+
export * from "./grg_inverse";
|
|
3
|
+
export * from "./adaptive_switch";
|
|
4
|
+
export * from "./evm_connector";
|
|
5
|
+
export * from "./x402_enforcer";
|
|
6
|
+
export * from "./ttt_builder";
|
|
7
|
+
export * from "./protocol_fee";
|
|
8
|
+
export * from "./pool_registry";
|
|
9
|
+
export * from "./v4_hook";
|
|
10
|
+
export * from "./logger";
|
|
11
|
+
export * from "./types";
|
|
12
|
+
export * from "./ttt_client";
|
|
13
|
+
export * from "./auto_mint";
|
|
14
|
+
export * from "./time_synthesis";
|
|
15
|
+
export * from "./dynamic_fee";
|
|
16
|
+
export * from "./golay";
|
|
17
|
+
export * from "./grg_pipeline";
|
|
18
|
+
export * from "./signer";
|
|
19
|
+
export * from "./networks";
|
|
20
|
+
export * from "./reed_solomon";
|
|
21
|
+
export * from "./errors";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
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 __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
// sdk/src/index.ts
|
|
18
|
+
__exportStar(require("./grg_forward"), exports);
|
|
19
|
+
__exportStar(require("./grg_inverse"), exports);
|
|
20
|
+
__exportStar(require("./adaptive_switch"), exports);
|
|
21
|
+
__exportStar(require("./evm_connector"), exports);
|
|
22
|
+
__exportStar(require("./x402_enforcer"), exports);
|
|
23
|
+
__exportStar(require("./ttt_builder"), exports);
|
|
24
|
+
__exportStar(require("./protocol_fee"), exports);
|
|
25
|
+
__exportStar(require("./pool_registry"), exports);
|
|
26
|
+
__exportStar(require("./v4_hook"), exports);
|
|
27
|
+
__exportStar(require("./logger"), exports);
|
|
28
|
+
__exportStar(require("./types"), exports);
|
|
29
|
+
__exportStar(require("./ttt_client"), exports);
|
|
30
|
+
__exportStar(require("./auto_mint"), exports);
|
|
31
|
+
__exportStar(require("./time_synthesis"), exports);
|
|
32
|
+
__exportStar(require("./dynamic_fee"), exports);
|
|
33
|
+
__exportStar(require("./golay"), exports);
|
|
34
|
+
__exportStar(require("./grg_pipeline"), exports);
|
|
35
|
+
__exportStar(require("./signer"), exports);
|
|
36
|
+
__exportStar(require("./networks"), exports);
|
|
37
|
+
__exportStar(require("./reed_solomon"), exports);
|
|
38
|
+
__exportStar(require("./errors"), exports);
|
package/dist/logger.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export declare enum LogLevel {
|
|
2
|
+
INFO = "INFO",
|
|
3
|
+
WARN = "WARN",
|
|
4
|
+
ERROR = "ERROR",
|
|
5
|
+
DEBUG = "DEBUG"
|
|
6
|
+
}
|
|
7
|
+
export declare class Logger {
|
|
8
|
+
private static instance;
|
|
9
|
+
private namespace;
|
|
10
|
+
private constructor();
|
|
11
|
+
static getInstance(namespace?: string): Logger;
|
|
12
|
+
private formatMessage;
|
|
13
|
+
info(message: string): void;
|
|
14
|
+
warn(message: string): void;
|
|
15
|
+
error(message: string, error?: Error): void;
|
|
16
|
+
debug(message: string): void;
|
|
17
|
+
}
|
|
18
|
+
export declare const logger: Logger;
|
package/dist/logger.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// sdk/src/logger.ts โ Structured Logging Wrapper
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.logger = exports.Logger = exports.LogLevel = void 0;
|
|
5
|
+
var LogLevel;
|
|
6
|
+
(function (LogLevel) {
|
|
7
|
+
LogLevel["INFO"] = "INFO";
|
|
8
|
+
LogLevel["WARN"] = "WARN";
|
|
9
|
+
LogLevel["ERROR"] = "ERROR";
|
|
10
|
+
LogLevel["DEBUG"] = "DEBUG";
|
|
11
|
+
})(LogLevel || (exports.LogLevel = LogLevel = {}));
|
|
12
|
+
class Logger {
|
|
13
|
+
static instance;
|
|
14
|
+
namespace;
|
|
15
|
+
constructor(namespace = "TTT-SDK") {
|
|
16
|
+
this.namespace = namespace;
|
|
17
|
+
}
|
|
18
|
+
static getInstance(namespace) {
|
|
19
|
+
if (!Logger.instance) {
|
|
20
|
+
Logger.instance = new Logger(namespace);
|
|
21
|
+
}
|
|
22
|
+
return Logger.instance;
|
|
23
|
+
}
|
|
24
|
+
formatMessage(level, message) {
|
|
25
|
+
const timestamp = new Date().toISOString();
|
|
26
|
+
return `[${timestamp}] [${level}] [${this.namespace}] ${message}`;
|
|
27
|
+
}
|
|
28
|
+
info(message) {
|
|
29
|
+
console.log(this.formatMessage(LogLevel.INFO, message));
|
|
30
|
+
}
|
|
31
|
+
warn(message) {
|
|
32
|
+
console.warn(this.formatMessage(LogLevel.WARN, message));
|
|
33
|
+
}
|
|
34
|
+
error(message, error) {
|
|
35
|
+
let msg = message;
|
|
36
|
+
if (error) {
|
|
37
|
+
msg += ` | Error: ${error.message}`;
|
|
38
|
+
if (error.stack) {
|
|
39
|
+
msg += `\nStack: ${error.stack}`;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
console.error(this.formatMessage(LogLevel.ERROR, msg));
|
|
43
|
+
}
|
|
44
|
+
debug(message) {
|
|
45
|
+
if (process.env.DEBUG) {
|
|
46
|
+
console.debug(this.formatMessage(LogLevel.DEBUG, message));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
exports.Logger = Logger;
|
|
51
|
+
exports.logger = Logger.getInstance();
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Network configuration for TTT SDK
|
|
3
|
+
*/
|
|
4
|
+
export interface NetworkConfig {
|
|
5
|
+
chainId: number;
|
|
6
|
+
rpcUrl: string;
|
|
7
|
+
tttAddress: string;
|
|
8
|
+
protocolFeeAddress: string;
|
|
9
|
+
usdcAddress: string;
|
|
10
|
+
}
|
|
11
|
+
export declare const BASE_MAINNET: NetworkConfig;
|
|
12
|
+
export declare const BASE_SEPOLIA: NetworkConfig;
|
|
13
|
+
export declare const NETWORKS: Record<string, NetworkConfig>;
|
package/dist/networks.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
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)
|
|
6
|
+
exports.BASE_MAINNET = {
|
|
7
|
+
chainId: 8453,
|
|
8
|
+
rpcUrl: "https://mainnet.base.org",
|
|
9
|
+
tttAddress: "0x0000000000000000000000000000000000000000",
|
|
10
|
+
protocolFeeAddress: "0x0000000000000000000000000000000000000000",
|
|
11
|
+
usdcAddress: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
12
|
+
};
|
|
13
|
+
exports.BASE_SEPOLIA = {
|
|
14
|
+
chainId: 84532,
|
|
15
|
+
rpcUrl: "https://sepolia.base.org",
|
|
16
|
+
tttAddress: "0xde357135cA493e59680182CDE9E1c6A4dA400811",
|
|
17
|
+
protocolFeeAddress: "0xE289337d3a79b22753BDA03510a8b8E4D1040F21",
|
|
18
|
+
usdcAddress: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
|
|
19
|
+
};
|
|
20
|
+
exports.NETWORKS = {
|
|
21
|
+
base: exports.BASE_MAINNET,
|
|
22
|
+
sepolia: exports.BASE_SEPOLIA,
|
|
23
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PoolRegistry - TTT Pool Registry
|
|
3
|
+
* Manages registration of pools and tracks minting/burning statistics.
|
|
4
|
+
*/
|
|
5
|
+
export declare class PoolRegistry {
|
|
6
|
+
private static readonly MAX_POOLS;
|
|
7
|
+
private pools;
|
|
8
|
+
private static readonly MAX_TOKEN_MAPPINGS;
|
|
9
|
+
private tokenToPool;
|
|
10
|
+
/**
|
|
11
|
+
* Register a new pool for TTT isolation.
|
|
12
|
+
* @param chainId The chain ID where the pool is located.
|
|
13
|
+
* @param poolAddress The address of the pool.
|
|
14
|
+
*/
|
|
15
|
+
registerPool(chainId: number, poolAddress: string): Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* Generate keccak256 tokenId based on chainId, poolAddress, timestamp and slotIndex.
|
|
18
|
+
* @param chainId The chain ID.
|
|
19
|
+
* @param poolAddress The pool address.
|
|
20
|
+
* @param timestamp The timestamp of the TTT.
|
|
21
|
+
* @param slotIndex The slot index.
|
|
22
|
+
* @returns The generated tokenId as a hex string.
|
|
23
|
+
*/
|
|
24
|
+
getTokenId(chainId: number, poolAddress: string, timestamp: bigint, slotIndex: number): string;
|
|
25
|
+
/**
|
|
26
|
+
* Verify if a tokenId belongs to a specific pool.
|
|
27
|
+
* @param tokenId The tokenId to verify.
|
|
28
|
+
* @param poolAddress The pool address to check against.
|
|
29
|
+
* @returns True if the tokenId belongs to the pool.
|
|
30
|
+
*/
|
|
31
|
+
isValidForPool(tokenId: string, poolAddress: string): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Get minting/burning statistics for a pool.
|
|
34
|
+
* @param poolAddress The pool address.
|
|
35
|
+
* @returns Statistics for the pool or null if not registered.
|
|
36
|
+
*/
|
|
37
|
+
getPoolStats(poolAddress: string): {
|
|
38
|
+
minted: bigint;
|
|
39
|
+
burned: bigint;
|
|
40
|
+
} | null;
|
|
41
|
+
/**
|
|
42
|
+
* Record a minting event for a pool. (Internal/Helper method)
|
|
43
|
+
* @param poolAddress The pool address.
|
|
44
|
+
* @param amount The amount minted.
|
|
45
|
+
*/
|
|
46
|
+
recordMint(poolAddress: string, amount: bigint): void;
|
|
47
|
+
/**
|
|
48
|
+
* Record a burning event for a pool. (Internal/Helper method)
|
|
49
|
+
* @param poolAddress The pool address.
|
|
50
|
+
* @param amount The amount burned.
|
|
51
|
+
*/
|
|
52
|
+
recordBurn(poolAddress: string, amount: bigint): void;
|
|
53
|
+
/**
|
|
54
|
+
* List all registered pool addresses.
|
|
55
|
+
* @returns Array of pool addresses.
|
|
56
|
+
*/
|
|
57
|
+
listPools(): string[];
|
|
58
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PoolRegistry = void 0;
|
|
4
|
+
const ethers_1 = require("ethers");
|
|
5
|
+
/**
|
|
6
|
+
* PoolRegistry - TTT Pool Registry
|
|
7
|
+
* Manages registration of pools and tracks minting/burning statistics.
|
|
8
|
+
*/
|
|
9
|
+
class PoolRegistry {
|
|
10
|
+
// R4-P1-4: Capacity limit to prevent Sybil DoS
|
|
11
|
+
static MAX_POOLS = 10000;
|
|
12
|
+
pools = new Map();
|
|
13
|
+
// R8-5: Bounded token-to-pool map to prevent unbounded memory growth
|
|
14
|
+
static MAX_TOKEN_MAPPINGS = 100000;
|
|
15
|
+
tokenToPool = new Map();
|
|
16
|
+
/**
|
|
17
|
+
* Register a new pool for TTT isolation.
|
|
18
|
+
* @param chainId The chain ID where the pool is located.
|
|
19
|
+
* @param poolAddress The address of the pool.
|
|
20
|
+
*/
|
|
21
|
+
async registerPool(chainId, poolAddress) {
|
|
22
|
+
if (!poolAddress || !ethers_1.ethers.isAddress(poolAddress.toLowerCase()))
|
|
23
|
+
throw new Error(`[PoolRegistry] Invalid pool address: ${poolAddress}`);
|
|
24
|
+
const key = poolAddress.toLowerCase();
|
|
25
|
+
if (!this.pools.has(key)) {
|
|
26
|
+
// R4-P1-4: Sybil DoS prevention
|
|
27
|
+
if (this.pools.size >= PoolRegistry.MAX_POOLS) {
|
|
28
|
+
throw new Error(`[PoolRegistry] Registry full (max ${PoolRegistry.MAX_POOLS} pools). Cannot register more.`);
|
|
29
|
+
}
|
|
30
|
+
this.pools.set(key, { chainId, minted: 0n, burned: 0n });
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Generate keccak256 tokenId based on chainId, poolAddress, timestamp and slotIndex.
|
|
35
|
+
* @param chainId The chain ID.
|
|
36
|
+
* @param poolAddress The pool address.
|
|
37
|
+
* @param timestamp The timestamp of the TTT.
|
|
38
|
+
* @param slotIndex The slot index.
|
|
39
|
+
* @returns The generated tokenId as a hex string.
|
|
40
|
+
*/
|
|
41
|
+
getTokenId(chainId, poolAddress, timestamp, slotIndex) {
|
|
42
|
+
if (!poolAddress || !ethers_1.ethers.isAddress(poolAddress.toLowerCase()))
|
|
43
|
+
throw new Error(`[PoolRegistry] Invalid pool address: ${poolAddress}`);
|
|
44
|
+
// R2-P2-4: Validate slotIndex fits in uint32
|
|
45
|
+
if (slotIndex < 0 || slotIndex > 0xFFFFFFFF || !Number.isInteger(slotIndex)) {
|
|
46
|
+
throw new Error(`[PoolRegistry] slotIndex must be integer in [0, ${0xFFFFFFFF}], got: ${slotIndex}`);
|
|
47
|
+
}
|
|
48
|
+
// uint64 max value check
|
|
49
|
+
if (timestamp > 18446744073709551615n) {
|
|
50
|
+
throw new Error(`[PoolRegistry] Timestamp overflow: ${timestamp} exceeds uint64 max`);
|
|
51
|
+
}
|
|
52
|
+
const normalizedAddress = poolAddress.toLowerCase();
|
|
53
|
+
const abiCoder = ethers_1.ethers.AbiCoder.defaultAbiCoder();
|
|
54
|
+
const tokenId = ethers_1.ethers.keccak256(abiCoder.encode(["uint256", "address", "uint64", "uint32"], [BigInt(chainId), normalizedAddress, timestamp, slotIndex]));
|
|
55
|
+
// Store mapping to support isValidForPool
|
|
56
|
+
// R8-5: Evict oldest entry if at capacity (FIFO via Map insertion order)
|
|
57
|
+
if (this.tokenToPool.size >= PoolRegistry.MAX_TOKEN_MAPPINGS) {
|
|
58
|
+
const oldest = this.tokenToPool.keys().next().value;
|
|
59
|
+
if (oldest !== undefined)
|
|
60
|
+
this.tokenToPool.delete(oldest);
|
|
61
|
+
}
|
|
62
|
+
this.tokenToPool.set(tokenId, normalizedAddress);
|
|
63
|
+
return tokenId;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Verify if a tokenId belongs to a specific pool.
|
|
67
|
+
* @param tokenId The tokenId to verify.
|
|
68
|
+
* @param poolAddress The pool address to check against.
|
|
69
|
+
* @returns True if the tokenId belongs to the pool.
|
|
70
|
+
*/
|
|
71
|
+
isValidForPool(tokenId, poolAddress) {
|
|
72
|
+
if (!poolAddress || !ethers_1.ethers.isAddress(poolAddress.toLowerCase()))
|
|
73
|
+
return false;
|
|
74
|
+
const normalizedAddress = poolAddress.toLowerCase();
|
|
75
|
+
const registeredPool = this.tokenToPool.get(tokenId);
|
|
76
|
+
return registeredPool === normalizedAddress;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Get minting/burning statistics for a pool.
|
|
80
|
+
* @param poolAddress The pool address.
|
|
81
|
+
* @returns Statistics for the pool or null if not registered.
|
|
82
|
+
*/
|
|
83
|
+
getPoolStats(poolAddress) {
|
|
84
|
+
if (!poolAddress || !ethers_1.ethers.isAddress(poolAddress.toLowerCase()))
|
|
85
|
+
return null;
|
|
86
|
+
const normalizedAddress = poolAddress.toLowerCase();
|
|
87
|
+
const stats = this.pools.get(normalizedAddress);
|
|
88
|
+
if (!stats) {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
return { minted: stats.minted, burned: stats.burned };
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Record a minting event for a pool. (Internal/Helper method)
|
|
95
|
+
* @param poolAddress The pool address.
|
|
96
|
+
* @param amount The amount minted.
|
|
97
|
+
*/
|
|
98
|
+
recordMint(poolAddress, amount) {
|
|
99
|
+
if (!poolAddress || !ethers_1.ethers.isAddress(poolAddress.toLowerCase()))
|
|
100
|
+
return;
|
|
101
|
+
const normalizedAddress = poolAddress.toLowerCase();
|
|
102
|
+
const stats = this.pools.get(normalizedAddress);
|
|
103
|
+
if (stats) {
|
|
104
|
+
stats.minted += amount;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Record a burning event for a pool. (Internal/Helper method)
|
|
109
|
+
* @param poolAddress The pool address.
|
|
110
|
+
* @param amount The amount burned.
|
|
111
|
+
*/
|
|
112
|
+
recordBurn(poolAddress, amount) {
|
|
113
|
+
if (!poolAddress || !ethers_1.ethers.isAddress(poolAddress.toLowerCase()))
|
|
114
|
+
return;
|
|
115
|
+
const normalizedAddress = poolAddress.toLowerCase();
|
|
116
|
+
const stats = this.pools.get(normalizedAddress);
|
|
117
|
+
if (stats) {
|
|
118
|
+
stats.burned += amount;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* List all registered pool addresses.
|
|
123
|
+
* @returns Array of pool addresses.
|
|
124
|
+
*/
|
|
125
|
+
listPools() {
|
|
126
|
+
return Array.from(this.pools.keys());
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
exports.PoolRegistry = PoolRegistry;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { FeeCalculation } from "./dynamic_fee";
|
|
2
|
+
import { EVMConnector } from "./evm_connector";
|
|
3
|
+
/**
|
|
4
|
+
* ProtocolFeeCollector - Helm ํ๋กํ ์ฝ ์์๋ฃ ์์ทจ ๋ฐ ๊ฒ์ฆ ๋ด๋น
|
|
5
|
+
* x402 ์ปดํ๋ผ์ด์ธ์ค๋ฅผ ์ํด EIP-712 ์๋ช
๊ฒ์ฆ์ ํฌํจ
|
|
6
|
+
*/
|
|
7
|
+
export declare class ProtocolFeeCollector {
|
|
8
|
+
private totalCollected;
|
|
9
|
+
private chainId;
|
|
10
|
+
private verifyingContract;
|
|
11
|
+
private usedSignatures;
|
|
12
|
+
private readonly MAX_REPLAY_CACHE;
|
|
13
|
+
private readonly REPLAY_TTL_MS;
|
|
14
|
+
private evmConnector;
|
|
15
|
+
private protocolFeeRecipient;
|
|
16
|
+
private feeContract;
|
|
17
|
+
constructor(chainId: number, verifyingContract: string, evmConnector: EVMConnector, protocolFeeRecipient: string);
|
|
18
|
+
/**
|
|
19
|
+
* R3-P0-2: Verify chainId matches the actual connected network.
|
|
20
|
+
* Must be called after EVMConnector.connect() to prevent cross-chain signature replay.
|
|
21
|
+
*/
|
|
22
|
+
validateChainId(): Promise<void>;
|
|
23
|
+
private getFeeContract;
|
|
24
|
+
/**
|
|
25
|
+
* ๋ฏผํ
์์๋ฃ ์์ทจ (Stablecoin)
|
|
26
|
+
* @param feeCalc - DynamicFeeEngine์์ ๊ณ์ฐ๋ ์์๋ฃ ์ ๋ณด
|
|
27
|
+
* @param signature - EIP-712 ์๋ช
(ํ์, x402 ๊ฒ์ฆ์ฉ)
|
|
28
|
+
* @param user - ์๋ช
์ ์ฃผ์
|
|
29
|
+
* @param nonce - ์ค๋ณต ๋ฐฉ์ง nonce
|
|
30
|
+
* @param deadline - ์๋ช
์ ํจ ๊ธฐํ
|
|
31
|
+
*/
|
|
32
|
+
collectMintFee(feeCalc: FeeCalculation, signature: string, user: string, nonce: bigint, deadline: number): Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* ์๊ฐ ์์๋ฃ ์์ทจ
|
|
35
|
+
* @param feeCalc - DynamicFeeEngine์์ ๊ณ์ฐ๋ ์์๋ฃ ์ ๋ณด
|
|
36
|
+
* @param signature - EIP-712 ์๋ช
(ํ์)
|
|
37
|
+
* @param user - ์๋ช
์ ์ฃผ์
|
|
38
|
+
* @param nonce - ์ค๋ณต ๋ฐฉ์ง nonce
|
|
39
|
+
* @param deadline - ์๋ช
์ ํจ ๊ธฐํ
|
|
40
|
+
*/
|
|
41
|
+
collectBurnFee(feeCalc: FeeCalculation, signature: string, user: string, nonce: bigint, deadline: number): Promise<void>;
|
|
42
|
+
/**
|
|
43
|
+
* ํ์ฌ๊น์ง ์์ทจํ ์ด ์์๋ฃ ๋ฐํ
|
|
44
|
+
*/
|
|
45
|
+
getCollectedFees(): Promise<bigint>;
|
|
46
|
+
/**
|
|
47
|
+
* P1-2: Prune expired signatures from replay cache
|
|
48
|
+
*/
|
|
49
|
+
private lastPruneTime;
|
|
50
|
+
private static readonly PRUNE_INTERVAL_MS;
|
|
51
|
+
private pruneExpiredSignatures;
|
|
52
|
+
/**
|
|
53
|
+
* EIP-712 ์๋ช
๊ฒ์ฆ (x402 compliance)
|
|
54
|
+
*/
|
|
55
|
+
private verifySignature;
|
|
56
|
+
}
|