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.
- package/README.md +52 -30
- package/dist/adaptive_switch.d.ts +22 -7
- package/dist/adaptive_switch.js +52 -15
- package/dist/auto_mint.d.ts +22 -7
- package/dist/auto_mint.js +107 -30
- package/dist/ct_log.d.ts +47 -0
- package/dist/ct_log.js +107 -0
- package/dist/dynamic_fee.d.ts +13 -2
- package/dist/dynamic_fee.js +62 -11
- package/dist/errors.d.ts +44 -25
- package/dist/errors.js +58 -42
- package/dist/evm_connector.d.ts +28 -1
- package/dist/evm_connector.js +124 -32
- package/dist/grg_api_client.d.ts +41 -0
- package/dist/grg_api_client.js +116 -0
- package/dist/http_client.d.ts +98 -0
- package/dist/http_client.js +252 -0
- package/dist/index.d.ts +5 -5
- package/dist/index.js +5 -5
- package/dist/logger.d.ts +36 -4
- package/dist/logger.js +70 -11
- package/dist/networks.d.ts +21 -0
- package/dist/networks.js +30 -4
- package/dist/pool_registry.d.ts +9 -0
- package/dist/pool_registry.js +37 -0
- package/dist/pot_signer.d.ts +15 -0
- package/dist/pot_signer.js +28 -0
- package/dist/protocol_fee.d.ts +42 -26
- package/dist/protocol_fee.js +77 -54
- package/dist/revenue_tiers.d.ts +36 -0
- package/dist/revenue_tiers.js +83 -0
- package/dist/signer.d.ts +1 -2
- package/dist/signer.js +72 -14
- package/dist/time_synthesis.d.ts +38 -0
- package/dist/time_synthesis.js +131 -21
- package/dist/trust_store.d.ts +49 -0
- package/dist/trust_store.js +89 -0
- package/dist/ttt_builder.d.ts +1 -1
- package/dist/ttt_builder.js +2 -2
- package/dist/ttt_client.d.ts +42 -29
- package/dist/ttt_client.js +117 -28
- package/dist/types.d.ts +46 -3
- package/dist/v4_hook.d.ts +10 -2
- package/dist/v4_hook.js +10 -2
- package/dist/x402_enforcer.d.ts +17 -2
- package/dist/x402_enforcer.js +27 -2
- package/package.json +6 -2
- package/dist/golay.d.ts +0 -6
- package/dist/golay.js +0 -166
- package/dist/grg_forward.d.ts +0 -11
- package/dist/grg_forward.js +0 -74
- package/dist/grg_inverse.d.ts +0 -7
- package/dist/grg_inverse.js +0 -100
- package/dist/grg_pipeline.d.ts +0 -13
- package/dist/grg_pipeline.js +0 -64
- package/dist/reed_solomon.d.ts +0 -12
- package/dist/reed_solomon.js +0 -179
- package/vendor/helm-crypto/golay.d.ts +0 -6
- package/vendor/helm-crypto/golay.js +0 -167
- package/vendor/helm-crypto/golay.js.map +0 -1
- package/vendor/helm-crypto/grg_forward.d.ts +0 -22
- package/vendor/helm-crypto/grg_forward.js +0 -89
- package/vendor/helm-crypto/grg_forward.js.map +0 -1
- package/vendor/helm-crypto/grg_inverse.d.ts +0 -16
- package/vendor/helm-crypto/grg_inverse.js +0 -118
- package/vendor/helm-crypto/grg_inverse.js.map +0 -1
- package/vendor/helm-crypto/grg_pipeline.d.ts +0 -13
- package/vendor/helm-crypto/grg_pipeline.js +0 -66
- package/vendor/helm-crypto/grg_pipeline.js.map +0 -1
- package/vendor/helm-crypto/index.d.ts +0 -5
- package/vendor/helm-crypto/index.js +0 -17
- package/vendor/helm-crypto/index.js.map +0 -1
- package/vendor/helm-crypto/logger.d.ts +0 -6
- package/vendor/helm-crypto/logger.js +0 -11
- package/vendor/helm-crypto/logger.js.map +0 -1
- package/vendor/helm-crypto/reed_solomon.d.ts +0 -37
- package/vendor/helm-crypto/reed_solomon.js +0 -210
- package/vendor/helm-crypto/reed_solomon.js.map +0 -1
package/dist/errors.js
CHANGED
|
@@ -1,18 +1,67 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.TTTFeeError = exports.TTTTimeSynthesisError = exports.TTTContractError = exports.TTTNetworkError = exports.TTTSignerError = exports.TTTConfigError = exports.TTTBaseError = void 0;
|
|
3
|
+
exports.TTTFeeError = exports.TTTTimeSynthesisError = exports.TTTContractError = exports.TTTNetworkError = exports.TTTSignerError = exports.TTTConfigError = exports.TTTBaseError = exports.ERROR_CODES = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Structured error codes for OpenTTT SDK.
|
|
6
|
+
* TTT_E001-E009: Config, TTT_E010-E019: Signer, TTT_E020-E029: Network,
|
|
7
|
+
* TTT_E030-E039: Contract, TTT_E040-E049: TimeSynthesis, TTT_E050-E059: Fee
|
|
8
|
+
*/
|
|
9
|
+
exports.ERROR_CODES = {
|
|
10
|
+
// Config errors (TTT_E001-E009)
|
|
11
|
+
CONFIG_MISSING_SIGNER: "TTT_E001",
|
|
12
|
+
CONFIG_INVALID: "TTT_E002",
|
|
13
|
+
// Signer errors (TTT_E010-E019)
|
|
14
|
+
SIGNER_NOT_INITIALIZED: "TTT_E010",
|
|
15
|
+
SIGNER_MISSING_KEY: "TTT_E011",
|
|
16
|
+
SIGNER_INVALID_KEY_FORMAT: "TTT_E012",
|
|
17
|
+
SIGNER_NO_EIP712: "TTT_E013",
|
|
18
|
+
SIGNER_PRIVY_NOT_IMPLEMENTED: "TTT_E014",
|
|
19
|
+
SIGNER_KMS_AWS_INIT_FAILED: "TTT_E015",
|
|
20
|
+
SIGNER_KMS_GCP_MISSING_FIELDS: "TTT_E016",
|
|
21
|
+
SIGNER_KMS_GCP_INIT_FAILED: "TTT_E017",
|
|
22
|
+
SIGNER_KMS_UNSUPPORTED_PROVIDER: "TTT_E018",
|
|
23
|
+
SIGNER_UNSUPPORTED_TYPE: "TTT_E019",
|
|
24
|
+
// Network errors (TTT_E020-E029)
|
|
25
|
+
NETWORK_INVALID_RPC: "TTT_E020",
|
|
26
|
+
NETWORK_CONNECTION_FAILED: "TTT_E021",
|
|
27
|
+
NETWORK_CANNOT_RECONNECT: "TTT_E022",
|
|
28
|
+
NETWORK_RECONNECTION_EXHAUSTED: "TTT_E023",
|
|
29
|
+
NETWORK_TX_DROPPED: "TTT_E024",
|
|
30
|
+
NETWORK_PROVIDER_NOT_CONNECTED: "TTT_E025",
|
|
31
|
+
NETWORK_BLOCK_NOT_FOUND: "TTT_E026",
|
|
32
|
+
// Contract errors (TTT_E030-E039)
|
|
33
|
+
CONTRACT_SIGNER_NOT_CONNECTED: "TTT_E030",
|
|
34
|
+
CONTRACT_INVALID_ADDRESS: "TTT_E031",
|
|
35
|
+
CONTRACT_NOT_ATTACHED: "TTT_E032",
|
|
36
|
+
CONTRACT_BURN_FAILED: "TTT_E033",
|
|
37
|
+
CONTRACT_MINT_FAILED: "TTT_E034",
|
|
38
|
+
CONTRACT_BALANCE_QUERY_FAILED: "TTT_E035",
|
|
39
|
+
CONTRACT_SWAP_FAILED: "TTT_E036",
|
|
40
|
+
CONTRACT_INVALID_KEY_FORMAT: "TTT_E037",
|
|
41
|
+
// TimeSynthesis errors (TTT_E040-E049)
|
|
42
|
+
TIME_SYNTHESIS_INTEGRITY_FAILED: "TTT_E040",
|
|
43
|
+
TIME_SYNTHESIS_INSUFFICIENT_CONFIDENCE: "TTT_E041",
|
|
44
|
+
TIME_SYNTHESIS_SOURCE_NOT_FOUND: "TTT_E042",
|
|
45
|
+
TIME_SYNTHESIS_ALL_SOURCES_FAILED: "TTT_E043",
|
|
46
|
+
TIME_SYNTHESIS_POT_ALL_FAILED: "TTT_E044",
|
|
47
|
+
TIME_SYNTHESIS_SELF_VERIFY_FAILED: "TTT_E045",
|
|
48
|
+
// Fee errors (TTT_E050-E059)
|
|
49
|
+
FEE_CALCULATION_FAILED: "TTT_E050",
|
|
50
|
+
};
|
|
4
51
|
/**
|
|
5
52
|
* Base error class for TTT SDK errors with storytelling capabilities.
|
|
6
53
|
*/
|
|
7
54
|
class TTTBaseError extends Error {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
55
|
+
code;
|
|
56
|
+
constructor(codeOrMessage, messageOrReason, reasonOrFix, fix) {
|
|
57
|
+
// Support both old (message, reason, fix) and new (code, message, reason, fix) signatures
|
|
58
|
+
const hasCode = fix !== undefined;
|
|
59
|
+
const code = hasCode ? codeOrMessage : "TTT_E002";
|
|
60
|
+
const message = hasCode ? messageOrReason : codeOrMessage;
|
|
61
|
+
const reason = hasCode ? reasonOrFix : messageOrReason;
|
|
62
|
+
const fixStr = hasCode ? fix : reasonOrFix;
|
|
63
|
+
super(`[${code}] ${message} (Reason: ${reason}. Fix: ${fixStr})`);
|
|
64
|
+
this.code = code;
|
|
16
65
|
this.name = this.constructor.name;
|
|
17
66
|
Object.setPrototypeOf(this, new.target.prototype);
|
|
18
67
|
}
|
|
@@ -22,53 +71,20 @@ exports.TTTBaseError = TTTBaseError;
|
|
|
22
71
|
* Errors related to SDK or Engine configuration.
|
|
23
72
|
*/
|
|
24
73
|
class TTTConfigError extends TTTBaseError {
|
|
25
|
-
constructor(message, reason, fix) {
|
|
26
|
-
super(message, reason, fix);
|
|
27
|
-
}
|
|
28
74
|
}
|
|
29
75
|
exports.TTTConfigError = TTTConfigError;
|
|
30
|
-
/**
|
|
31
|
-
* Errors related to Signer (PrivateKey, Turnkey, Privy, KMS) acquisition or usage.
|
|
32
|
-
*/
|
|
33
76
|
class TTTSignerError extends TTTBaseError {
|
|
34
|
-
constructor(message, reason, fix) {
|
|
35
|
-
super(message, reason, fix);
|
|
36
|
-
}
|
|
37
77
|
}
|
|
38
78
|
exports.TTTSignerError = TTTSignerError;
|
|
39
|
-
/**
|
|
40
|
-
* Errors related to Network (RPC, ChainID, Connectivity).
|
|
41
|
-
*/
|
|
42
79
|
class TTTNetworkError extends TTTBaseError {
|
|
43
|
-
constructor(message, reason, fix) {
|
|
44
|
-
super(message, reason, fix);
|
|
45
|
-
}
|
|
46
80
|
}
|
|
47
81
|
exports.TTTNetworkError = TTTNetworkError;
|
|
48
|
-
/**
|
|
49
|
-
* Errors related to Smart Contract interaction (TTT.sol, ProtocolFee.sol).
|
|
50
|
-
*/
|
|
51
82
|
class TTTContractError extends TTTBaseError {
|
|
52
|
-
constructor(message, reason, fix) {
|
|
53
|
-
super(message, reason, fix);
|
|
54
|
-
}
|
|
55
83
|
}
|
|
56
84
|
exports.TTTContractError = TTTContractError;
|
|
57
|
-
/**
|
|
58
|
-
* Errors related to NTP/KTSat Time Synthesis.
|
|
59
|
-
*/
|
|
60
85
|
class TTTTimeSynthesisError extends TTTBaseError {
|
|
61
|
-
constructor(message, reason, fix) {
|
|
62
|
-
super(message, reason, fix);
|
|
63
|
-
}
|
|
64
86
|
}
|
|
65
87
|
exports.TTTTimeSynthesisError = TTTTimeSynthesisError;
|
|
66
|
-
/**
|
|
67
|
-
* Errors related to Dynamic Fee Engine or Protocol Fee collection.
|
|
68
|
-
*/
|
|
69
88
|
class TTTFeeError extends TTTBaseError {
|
|
70
|
-
constructor(message, reason, fix) {
|
|
71
|
-
super(message, reason, fix);
|
|
72
|
-
}
|
|
73
89
|
}
|
|
74
90
|
exports.TTTFeeError = TTTFeeError;
|
package/dist/evm_connector.d.ts
CHANGED
|
@@ -7,6 +7,10 @@ export interface VerificationResult {
|
|
|
7
7
|
txCount: number;
|
|
8
8
|
latency: number;
|
|
9
9
|
}
|
|
10
|
+
export interface EVMConnectorOptions {
|
|
11
|
+
fallbackRpcUrls?: string[];
|
|
12
|
+
maxReconnectAttempts?: number;
|
|
13
|
+
}
|
|
10
14
|
export declare class EVMConnector {
|
|
11
15
|
private provider;
|
|
12
16
|
private signer;
|
|
@@ -14,7 +18,12 @@ export declare class EVMConnector {
|
|
|
14
18
|
private protocolFeeContract;
|
|
15
19
|
private eventListeners;
|
|
16
20
|
private static readonly GAS_TIMEOUT_MS;
|
|
17
|
-
|
|
21
|
+
private primaryRpcUrl;
|
|
22
|
+
private fallbackRpcUrls;
|
|
23
|
+
private signerOrKey;
|
|
24
|
+
private maxReconnectAttempts;
|
|
25
|
+
private connected;
|
|
26
|
+
constructor(options?: EVMConnectorOptions);
|
|
18
27
|
/**
|
|
19
28
|
* P1-7: Race estimateGas against timeout to prevent DoS
|
|
20
29
|
*/
|
|
@@ -23,6 +32,24 @@ export declare class EVMConnector {
|
|
|
23
32
|
* Connect to an EVM chain using either a private key or a pre-configured signer.
|
|
24
33
|
*/
|
|
25
34
|
connect(rpcUrl: string, signerOrKey: string | Signer): Promise<void>;
|
|
35
|
+
/**
|
|
36
|
+
* Reconnect using stored credentials. Tries primary first, then fallbacks.
|
|
37
|
+
*/
|
|
38
|
+
reconnect(): Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* Disconnect and release all resources.
|
|
41
|
+
*/
|
|
42
|
+
disconnect(): void;
|
|
43
|
+
/**
|
|
44
|
+
* Check if the connector is currently connected.
|
|
45
|
+
*/
|
|
46
|
+
isConnected(): boolean;
|
|
47
|
+
/**
|
|
48
|
+
* CT Log Equivalent: PoTAnchored event ABI fragment.
|
|
49
|
+
* Every Proof-of-Time anchor is publicly auditable on-chain,
|
|
50
|
+
* analogous to Certificate Transparency logs in TLS.
|
|
51
|
+
*/
|
|
52
|
+
static readonly POT_ANCHORED_EVENT_ABI = "event PoTAnchored(uint64 indexed timestamp, bytes32 grgHash, uint8 stratum, bytes32 potHash)";
|
|
26
53
|
/**
|
|
27
54
|
* Attach the TTT Token contract.
|
|
28
55
|
*/
|
package/dist/evm_connector.js
CHANGED
|
@@ -14,14 +14,22 @@ class EVMConnector {
|
|
|
14
14
|
eventListeners = []; // P2-6: Track listeners for cleanup
|
|
15
15
|
// P1-7: Timeout wrapper for gas estimation
|
|
16
16
|
static GAS_TIMEOUT_MS = 5000;
|
|
17
|
-
|
|
17
|
+
primaryRpcUrl = "";
|
|
18
|
+
fallbackRpcUrls = [];
|
|
19
|
+
signerOrKey = null;
|
|
20
|
+
maxReconnectAttempts;
|
|
21
|
+
connected = false;
|
|
22
|
+
constructor(options) {
|
|
23
|
+
this.fallbackRpcUrls = options?.fallbackRpcUrls ?? [];
|
|
24
|
+
this.maxReconnectAttempts = options?.maxReconnectAttempts ?? 3;
|
|
25
|
+
}
|
|
18
26
|
/**
|
|
19
27
|
* P1-7: Race estimateGas against timeout to prevent DoS
|
|
20
28
|
*/
|
|
21
29
|
async withTimeout(promise, ms = EVMConnector.GAS_TIMEOUT_MS) {
|
|
22
30
|
return Promise.race([
|
|
23
31
|
promise,
|
|
24
|
-
new Promise((_, reject) => setTimeout(() => reject(new errors_1.TTTNetworkError(`[EVM] Operation timed out`, `RPC did not respond within ${ms}ms`, `Check your RPC provider status or increase timeout.`)), ms))
|
|
32
|
+
new Promise((_, reject) => setTimeout(() => reject(new errors_1.TTTNetworkError(errors_1.ERROR_CODES.NETWORK_CONNECTION_FAILED, `[EVM] Operation timed out`, `RPC did not respond within ${ms}ms`, `Check your RPC provider status or increase timeout.`)), ms))
|
|
25
33
|
]);
|
|
26
34
|
}
|
|
27
35
|
/**
|
|
@@ -29,36 +37,116 @@ class EVMConnector {
|
|
|
29
37
|
*/
|
|
30
38
|
async connect(rpcUrl, signerOrKey) {
|
|
31
39
|
if (!rpcUrl || typeof rpcUrl !== "string")
|
|
32
|
-
throw new errors_1.TTTNetworkError("[EVM] Invalid RPC URL", "The provided RPC URL is empty or not a string", "Pass a valid RPC URL (e.g., https://mainnet.base.org)");
|
|
40
|
+
throw new errors_1.TTTNetworkError(errors_1.ERROR_CODES.NETWORK_INVALID_RPC, "[EVM] Invalid RPC URL", "The provided RPC URL is empty or not a string", "Pass a valid RPC URL (e.g., https://mainnet.base.org)");
|
|
41
|
+
this.primaryRpcUrl = rpcUrl;
|
|
42
|
+
this.signerOrKey = signerOrKey;
|
|
33
43
|
try {
|
|
34
44
|
this.provider = new ethers_1.JsonRpcProvider(rpcUrl);
|
|
35
45
|
if (typeof signerOrKey === "string") {
|
|
36
46
|
if (!signerOrKey.startsWith("0x") || signerOrKey.length !== 66) {
|
|
37
|
-
throw new errors_1.TTTContractError("[EVM] Invalid Private Key format", "Private key must be 0x + 64 hex characters", "Provide a valid 32-byte hex private key.");
|
|
47
|
+
throw new errors_1.TTTContractError(errors_1.ERROR_CODES.CONTRACT_INVALID_KEY_FORMAT, "[EVM] Invalid Private Key format", "Private key must be 0x + 64 hex characters", "Provide a valid 32-byte hex private key.");
|
|
38
48
|
}
|
|
39
49
|
this.signer = new ethers_1.ethers.Wallet(signerOrKey, this.provider);
|
|
40
50
|
}
|
|
41
51
|
else {
|
|
42
|
-
// Signer might already be connected to a provider, but we ensure it's linked to ours
|
|
43
52
|
this.signer = signerOrKey.connect ? signerOrKey.connect(this.provider) : signerOrKey;
|
|
44
53
|
}
|
|
45
54
|
const network = await this.provider.getNetwork();
|
|
55
|
+
this.connected = true;
|
|
46
56
|
logger_1.logger.info(`[EVM] Connected to Chain ID: ${network.chainId}`);
|
|
47
57
|
}
|
|
48
58
|
catch (error) {
|
|
49
59
|
if (error instanceof errors_1.TTTContractError || error instanceof errors_1.TTTNetworkError)
|
|
50
60
|
throw error;
|
|
51
|
-
|
|
61
|
+
// Try fallback RPCs before giving up
|
|
62
|
+
for (const fallback of this.fallbackRpcUrls) {
|
|
63
|
+
try {
|
|
64
|
+
logger_1.logger.warn(`[EVM] Primary RPC failed, trying fallback: ${fallback}`);
|
|
65
|
+
this.provider = new ethers_1.JsonRpcProvider(fallback);
|
|
66
|
+
if (typeof signerOrKey === "string") {
|
|
67
|
+
this.signer = new ethers_1.ethers.Wallet(signerOrKey, this.provider);
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
this.signer = signerOrKey.connect ? signerOrKey.connect(this.provider) : signerOrKey;
|
|
71
|
+
}
|
|
72
|
+
const network = await this.provider.getNetwork();
|
|
73
|
+
this.connected = true;
|
|
74
|
+
logger_1.logger.info(`[EVM] Connected via fallback to Chain ID: ${network.chainId}`);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
throw new errors_1.TTTNetworkError(errors_1.ERROR_CODES.NETWORK_CONNECTION_FAILED, `[EVM] Connection failed`, error instanceof Error ? error.message : String(error), `Verify your RPC URL and network connectivity.`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Reconnect using stored credentials. Tries primary first, then fallbacks.
|
|
86
|
+
*/
|
|
87
|
+
async reconnect() {
|
|
88
|
+
if (!this.signerOrKey)
|
|
89
|
+
throw new errors_1.TTTNetworkError(errors_1.ERROR_CODES.NETWORK_CANNOT_RECONNECT, "[EVM] Cannot reconnect", "No previous connection credentials stored", "Call connect() first.");
|
|
90
|
+
this.disconnect();
|
|
91
|
+
const allUrls = [this.primaryRpcUrl, ...this.fallbackRpcUrls].filter(Boolean);
|
|
92
|
+
for (let attempt = 0; attempt < Math.min(this.maxReconnectAttempts, allUrls.length); attempt++) {
|
|
93
|
+
try {
|
|
94
|
+
const url = allUrls[attempt % allUrls.length];
|
|
95
|
+
logger_1.logger.info(`[EVM] Reconnect attempt ${attempt + 1}/${this.maxReconnectAttempts} → ${url}`);
|
|
96
|
+
this.provider = new ethers_1.JsonRpcProvider(url);
|
|
97
|
+
if (typeof this.signerOrKey === "string") {
|
|
98
|
+
this.signer = new ethers_1.ethers.Wallet(this.signerOrKey, this.provider);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
this.signer = this.signerOrKey.connect ? this.signerOrKey.connect(this.provider) : this.signerOrKey;
|
|
102
|
+
}
|
|
103
|
+
await this.provider.getNetwork();
|
|
104
|
+
this.connected = true;
|
|
105
|
+
logger_1.logger.info(`[EVM] Reconnected successfully`);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
logger_1.logger.warn(`[EVM] Reconnect attempt ${attempt + 1} failed`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
this.connected = false;
|
|
113
|
+
throw new errors_1.TTTNetworkError(errors_1.ERROR_CODES.NETWORK_RECONNECTION_EXHAUSTED, "[EVM] Reconnection failed", `All ${this.maxReconnectAttempts} attempts exhausted`, "Check RPC provider status and network connectivity.");
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Disconnect and release all resources.
|
|
117
|
+
*/
|
|
118
|
+
disconnect() {
|
|
119
|
+
this.unsubscribeAll();
|
|
120
|
+
if (this.provider) {
|
|
121
|
+
this.provider.destroy();
|
|
52
122
|
}
|
|
123
|
+
this.provider = null;
|
|
124
|
+
this.signer = null;
|
|
125
|
+
this.tttContract = null;
|
|
126
|
+
this.protocolFeeContract = null;
|
|
127
|
+
this.connected = false;
|
|
128
|
+
logger_1.logger.info("[EVM] Disconnected and resources released");
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Check if the connector is currently connected.
|
|
132
|
+
*/
|
|
133
|
+
isConnected() {
|
|
134
|
+
return this.connected && this.provider !== null;
|
|
53
135
|
}
|
|
136
|
+
/**
|
|
137
|
+
* CT Log Equivalent: PoTAnchored event ABI fragment.
|
|
138
|
+
* Every Proof-of-Time anchor is publicly auditable on-chain,
|
|
139
|
+
* analogous to Certificate Transparency logs in TLS.
|
|
140
|
+
*/
|
|
141
|
+
static POT_ANCHORED_EVENT_ABI = "event PoTAnchored(uint64 indexed timestamp, bytes32 grgHash, uint8 stratum, bytes32 potHash)";
|
|
54
142
|
/**
|
|
55
143
|
* Attach the TTT Token contract.
|
|
56
144
|
*/
|
|
57
145
|
attachContract(address, abi) {
|
|
58
146
|
if (!this.signer)
|
|
59
|
-
throw new errors_1.TTTContractError("Not connected to signer", "EVMConnector.connect() must be called first", "Initialize connection before attaching contracts.");
|
|
147
|
+
throw new errors_1.TTTContractError(errors_1.ERROR_CODES.CONTRACT_SIGNER_NOT_CONNECTED, "Not connected to signer", "EVMConnector.connect() must be called first", "Initialize connection before attaching contracts.");
|
|
60
148
|
if (!address || !ethers_1.ethers.isAddress(address))
|
|
61
|
-
throw new errors_1.TTTContractError(`[EVM] Invalid contract address`, `Address '${address}' is not a valid EVM address`, `Check your config and provide a valid checksummed address.`);
|
|
149
|
+
throw new errors_1.TTTContractError(errors_1.ERROR_CODES.CONTRACT_INVALID_ADDRESS, `[EVM] Invalid contract address`, `Address '${address}' is not a valid EVM address`, `Check your config and provide a valid checksummed address.`);
|
|
62
150
|
this.tttContract = new ethers_1.Contract(address, abi, this.signer);
|
|
63
151
|
}
|
|
64
152
|
/**
|
|
@@ -66,9 +154,9 @@ class EVMConnector {
|
|
|
66
154
|
*/
|
|
67
155
|
attachProtocolFeeContract(address, abi) {
|
|
68
156
|
if (!this.signer)
|
|
69
|
-
throw new errors_1.TTTContractError("Not connected to signer", "EVMConnector.connect() must be called first", "Initialize connection before attaching contracts.");
|
|
157
|
+
throw new errors_1.TTTContractError(errors_1.ERROR_CODES.CONTRACT_SIGNER_NOT_CONNECTED, "Not connected to signer", "EVMConnector.connect() must be called first", "Initialize connection before attaching contracts.");
|
|
70
158
|
if (!address || !ethers_1.ethers.isAddress(address))
|
|
71
|
-
throw new errors_1.TTTContractError(`[EVM] Invalid contract address`, `Address '${address}' is not a valid EVM address`, `Check your config and provide a valid checksummed address.`);
|
|
159
|
+
throw new errors_1.TTTContractError(errors_1.ERROR_CODES.CONTRACT_INVALID_ADDRESS, `[EVM] Invalid contract address`, `Address '${address}' is not a valid EVM address`, `Check your config and provide a valid checksummed address.`);
|
|
72
160
|
this.protocolFeeContract = new ethers_1.Contract(address, abi, this.signer);
|
|
73
161
|
}
|
|
74
162
|
/**
|
|
@@ -76,7 +164,7 @@ class EVMConnector {
|
|
|
76
164
|
*/
|
|
77
165
|
async submitTTTRecord(record, amount, tier) {
|
|
78
166
|
if (!this.tttContract)
|
|
79
|
-
throw new errors_1.TTTContractError("Contract not attached", "TTT contract instance is null", "Call attachContract() with valid TTT address before burning.");
|
|
167
|
+
throw new errors_1.TTTContractError(errors_1.ERROR_CODES.CONTRACT_NOT_ATTACHED, "Contract not attached", "TTT contract instance is null", "Call attachContract() with valid TTT address before burning.");
|
|
80
168
|
const grgHash = ethers_1.ethers.keccak256(ethers_1.ethers.concat(record.grgPayload));
|
|
81
169
|
try {
|
|
82
170
|
// P1-7: Gas estimation with timeout
|
|
@@ -88,14 +176,14 @@ class EVMConnector {
|
|
|
88
176
|
const receipt = await tx.wait();
|
|
89
177
|
// P2-5: Null check for dropped transactions
|
|
90
178
|
if (!receipt)
|
|
91
|
-
throw new errors_1.TTTNetworkError(`[EVM] Transaction failed`, `Transaction was dropped from mempool or null receipt`, `Check block explorer for tx status.`);
|
|
179
|
+
throw new errors_1.TTTNetworkError(errors_1.ERROR_CODES.NETWORK_TX_DROPPED, `[EVM] Transaction failed`, `Transaction was dropped from mempool or null receipt`, `Check block explorer for tx status.`);
|
|
92
180
|
return receipt;
|
|
93
181
|
}
|
|
94
182
|
catch (error) {
|
|
95
183
|
if (error instanceof errors_1.TTTNetworkError || error instanceof errors_1.TTTContractError)
|
|
96
184
|
throw error;
|
|
97
185
|
const reason = this.extractRevertReason(error);
|
|
98
|
-
throw new errors_1.TTTContractError(`[EVM] Burn failed`, reason, `Verify your TTT balance and tier parameters.`);
|
|
186
|
+
throw new errors_1.TTTContractError(errors_1.ERROR_CODES.CONTRACT_BURN_FAILED, `[EVM] Burn failed`, reason, `Verify your TTT balance and tier parameters.`);
|
|
99
187
|
}
|
|
100
188
|
}
|
|
101
189
|
/**
|
|
@@ -103,22 +191,26 @@ class EVMConnector {
|
|
|
103
191
|
*/
|
|
104
192
|
async mintTTT(to, amount, grgHash, potHash) {
|
|
105
193
|
if (!this.tttContract)
|
|
106
|
-
throw new errors_1.TTTContractError("Contract not attached", "TTT contract instance is null", "Call attachContract() before minting.");
|
|
194
|
+
throw new errors_1.TTTContractError(errors_1.ERROR_CODES.CONTRACT_NOT_ATTACHED, "Contract not attached", "TTT contract instance is null", "Call attachContract() before minting.");
|
|
107
195
|
if (!to || !ethers_1.ethers.isAddress(to))
|
|
108
|
-
throw new errors_1.TTTContractError(`[EVM] Invalid recipient address`, `Address '${to}' is not a valid EVM address`, `Provide a valid destination address.`);
|
|
196
|
+
throw new errors_1.TTTContractError(errors_1.ERROR_CODES.CONTRACT_INVALID_ADDRESS, `[EVM] Invalid recipient address`, `Address '${to}' is not a valid EVM address`, `Provide a valid destination address.`);
|
|
109
197
|
try {
|
|
110
198
|
if (potHash) {
|
|
111
199
|
logger_1.logger.info(`[EVM] Recording PoT fingerprint: ${potHash}`);
|
|
112
200
|
}
|
|
113
|
-
|
|
201
|
+
// Gas estimation with 20% buffer and timeout (matching burnTTT pattern)
|
|
202
|
+
const gasLimit = await this.withTimeout(this.tttContract.mint.estimateGas(to, amount, grgHash));
|
|
203
|
+
const tx = await this.tttContract.mint(to, amount, grgHash, {
|
|
204
|
+
gasLimit: (gasLimit * 120n) / 100n
|
|
205
|
+
});
|
|
114
206
|
const receipt = await tx.wait();
|
|
115
207
|
if (!receipt)
|
|
116
|
-
throw new errors_1.TTTNetworkError(`[EVM] Mint TX dropped`, `Transaction was dropped from mempool`, `Check operator account for nonce collisions.`);
|
|
208
|
+
throw new errors_1.TTTNetworkError(errors_1.ERROR_CODES.NETWORK_TX_DROPPED, `[EVM] Mint TX dropped`, `Transaction was dropped from mempool`, `Check operator account for nonce collisions.`);
|
|
117
209
|
return receipt;
|
|
118
210
|
}
|
|
119
211
|
catch (error) {
|
|
120
212
|
const reason = this.extractRevertReason(error);
|
|
121
|
-
throw new errors_1.TTTContractError(`[EVM] Mint failed`, reason, `Ensure operator has minter role and sufficient gas.`);
|
|
213
|
+
throw new errors_1.TTTContractError(errors_1.ERROR_CODES.CONTRACT_MINT_FAILED, `[EVM] Mint failed`, reason, `Ensure operator has minter role and sufficient gas.`);
|
|
122
214
|
}
|
|
123
215
|
}
|
|
124
216
|
/**
|
|
@@ -126,17 +218,17 @@ class EVMConnector {
|
|
|
126
218
|
*/
|
|
127
219
|
async burnTTT(amount, grgHash, tierLevel) {
|
|
128
220
|
if (!this.tttContract)
|
|
129
|
-
throw new errors_1.TTTContractError("Contract not attached", "TTT contract instance is null", "Call attachContract() before burning.");
|
|
221
|
+
throw new errors_1.TTTContractError(errors_1.ERROR_CODES.CONTRACT_NOT_ATTACHED, "Contract not attached", "TTT contract instance is null", "Call attachContract() before burning.");
|
|
130
222
|
try {
|
|
131
223
|
const tx = await this.tttContract.burn(amount, grgHash, tierLevel);
|
|
132
224
|
const receipt = await tx.wait();
|
|
133
225
|
if (!receipt)
|
|
134
|
-
throw new errors_1.TTTNetworkError(`[EVM] Burn TX dropped`, `Transaction was dropped from mempool`, `Verify account balance.`);
|
|
226
|
+
throw new errors_1.TTTNetworkError(errors_1.ERROR_CODES.NETWORK_TX_DROPPED, `[EVM] Burn TX dropped`, `Transaction was dropped from mempool`, `Verify account balance.`);
|
|
135
227
|
return { hash: receipt.hash };
|
|
136
228
|
}
|
|
137
229
|
catch (error) {
|
|
138
230
|
const reason = this.extractRevertReason(error);
|
|
139
|
-
throw new errors_1.TTTContractError(`[EVM] Burn failed`, reason, `Check TTT balance.`);
|
|
231
|
+
throw new errors_1.TTTContractError(errors_1.ERROR_CODES.CONTRACT_BURN_FAILED, `[EVM] Burn failed`, reason, `Check TTT balance.`);
|
|
140
232
|
}
|
|
141
233
|
}
|
|
142
234
|
/**
|
|
@@ -144,13 +236,13 @@ class EVMConnector {
|
|
|
144
236
|
*/
|
|
145
237
|
async getTTTBalance(user, tokenId) {
|
|
146
238
|
if (!this.tttContract)
|
|
147
|
-
throw new errors_1.TTTContractError("Contract not attached", "TTT contract instance is null", "Call attachContract() before querying balance.");
|
|
239
|
+
throw new errors_1.TTTContractError(errors_1.ERROR_CODES.CONTRACT_NOT_ATTACHED, "Contract not attached", "TTT contract instance is null", "Call attachContract() before querying balance.");
|
|
148
240
|
try {
|
|
149
241
|
return await this.tttContract.balanceOf(user, tokenId);
|
|
150
242
|
}
|
|
151
243
|
catch (error) {
|
|
152
244
|
const reason = this.extractRevertReason(error);
|
|
153
|
-
throw new errors_1.TTTContractError(`[EVM] Balance query failed`, reason, `Check RPC connection and contract address.`);
|
|
245
|
+
throw new errors_1.TTTContractError(errors_1.ERROR_CODES.CONTRACT_BALANCE_QUERY_FAILED, `[EVM] Balance query failed`, reason, `Check RPC connection and contract address.`);
|
|
154
246
|
}
|
|
155
247
|
}
|
|
156
248
|
/**
|
|
@@ -158,9 +250,9 @@ class EVMConnector {
|
|
|
158
250
|
*/
|
|
159
251
|
async swap(routerAddress, tokenIn, tokenOut, amountIn, minAmountOut) {
|
|
160
252
|
if (!this.signer)
|
|
161
|
-
throw new errors_1.TTTContractError("Not connected to signer", "Signer is null", "Initialize connection.");
|
|
253
|
+
throw new errors_1.TTTContractError(errors_1.ERROR_CODES.CONTRACT_SIGNER_NOT_CONNECTED, "Not connected to signer", "Signer is null", "Initialize connection.");
|
|
162
254
|
if (!routerAddress || !ethers_1.ethers.isAddress(routerAddress))
|
|
163
|
-
throw new errors_1.TTTContractError(`[EVM] Invalid router address`, `Address '${routerAddress}' is invalid`, `Provide valid V4 SwapRouter address.`);
|
|
255
|
+
throw new errors_1.TTTContractError(errors_1.ERROR_CODES.CONTRACT_INVALID_ADDRESS, `[EVM] Invalid router address`, `Address '${routerAddress}' is invalid`, `Provide valid V4 SwapRouter address.`);
|
|
164
256
|
logger_1.logger.info(`[EVM] Swapping ${amountIn} of ${tokenIn} for ${tokenOut} via ${routerAddress}`);
|
|
165
257
|
// Realistic Uniswap V4-like SwapRouter ABI for simulation/integration
|
|
166
258
|
const swapRouterAbi = [
|
|
@@ -176,14 +268,14 @@ class EVMConnector {
|
|
|
176
268
|
logger_1.logger.info(`[EVM] Swap TX Sent: ${tx.hash}`);
|
|
177
269
|
const receipt = await tx.wait();
|
|
178
270
|
if (!receipt)
|
|
179
|
-
throw new errors_1.TTTNetworkError(`[EVM] Swap TX dropped`, `Transaction dropped`, `Check gas price.`);
|
|
271
|
+
throw new errors_1.TTTNetworkError(errors_1.ERROR_CODES.NETWORK_TX_DROPPED, `[EVM] Swap TX dropped`, `Transaction dropped`, `Check gas price.`);
|
|
180
272
|
return receipt;
|
|
181
273
|
}
|
|
182
274
|
catch (error) {
|
|
183
275
|
if (error instanceof errors_1.TTTNetworkError || error instanceof errors_1.TTTContractError)
|
|
184
276
|
throw error;
|
|
185
277
|
const reason = this.extractRevertReason(error);
|
|
186
|
-
throw new errors_1.TTTContractError(`[EVM] Swap failed`, reason, `Verify slippage and token balances.`);
|
|
278
|
+
throw new errors_1.TTTContractError(errors_1.ERROR_CODES.CONTRACT_SWAP_FAILED, `[EVM] Swap failed`, reason, `Verify slippage and token balances.`);
|
|
187
279
|
}
|
|
188
280
|
}
|
|
189
281
|
/**
|
|
@@ -241,10 +333,10 @@ class EVMConnector {
|
|
|
241
333
|
*/
|
|
242
334
|
async verifyBlock(blockNum) {
|
|
243
335
|
if (!this.provider)
|
|
244
|
-
throw new errors_1.TTTNetworkError("Provider not connected", "RPC provider is null", "Call connect() first.");
|
|
336
|
+
throw new errors_1.TTTNetworkError(errors_1.ERROR_CODES.NETWORK_PROVIDER_NOT_CONNECTED, "Provider not connected", "RPC provider is null", "Call connect() first.");
|
|
245
337
|
const block = await this.provider.getBlock(blockNum);
|
|
246
338
|
if (!block)
|
|
247
|
-
throw new errors_1.TTTNetworkError(`Block not found`, `RPC returned null for block ${blockNum}`, `Verify if block number exists on chain.`);
|
|
339
|
+
throw new errors_1.TTTNetworkError(errors_1.ERROR_CODES.NETWORK_BLOCK_NOT_FOUND, `Block not found`, `RPC returned null for block ${blockNum}`, `Verify if block number exists on chain.`);
|
|
248
340
|
return {
|
|
249
341
|
valid: true,
|
|
250
342
|
blockNumber: blockNum,
|
|
@@ -258,7 +350,7 @@ class EVMConnector {
|
|
|
258
350
|
*/
|
|
259
351
|
async getPendingTransactions() {
|
|
260
352
|
if (!this.provider)
|
|
261
|
-
throw new errors_1.TTTNetworkError("Provider not connected", "RPC provider is null", "Call connect() first.");
|
|
353
|
+
throw new errors_1.TTTNetworkError(errors_1.ERROR_CODES.NETWORK_PROVIDER_NOT_CONNECTED, "Provider not connected", "RPC provider is null", "Call connect() first.");
|
|
262
354
|
const block = await this.provider.send("eth_getBlockByNumber", ["pending", false]);
|
|
263
355
|
return block ? block.transactions : [];
|
|
264
356
|
}
|
|
@@ -267,7 +359,7 @@ class EVMConnector {
|
|
|
267
359
|
*/
|
|
268
360
|
getProvider() {
|
|
269
361
|
if (!this.provider)
|
|
270
|
-
throw new errors_1.TTTNetworkError("Provider not connected", "RPC provider is null", "Call connect() first.");
|
|
362
|
+
throw new errors_1.TTTNetworkError(errors_1.ERROR_CODES.NETWORK_PROVIDER_NOT_CONNECTED, "Provider not connected", "RPC provider is null", "Call connect() first.");
|
|
271
363
|
return this.provider;
|
|
272
364
|
}
|
|
273
365
|
/**
|
|
@@ -275,7 +367,7 @@ class EVMConnector {
|
|
|
275
367
|
*/
|
|
276
368
|
getSigner() {
|
|
277
369
|
if (!this.signer)
|
|
278
|
-
throw new errors_1.TTTContractError("Signer not connected", "Signer is null", "Call connect() first.");
|
|
370
|
+
throw new errors_1.TTTContractError(errors_1.ERROR_CODES.CONTRACT_SIGNER_NOT_CONNECTED, "Signer not connected", "Signer is null", "Call connect() first.");
|
|
279
371
|
return this.signer;
|
|
280
372
|
}
|
|
281
373
|
/**
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GRG API Client — replaces local GRG computation with server-side API call.
|
|
3
|
+
* GRG source code stays in Helm private repo. Only API calls go through npm SDK.
|
|
4
|
+
*
|
|
5
|
+
* Drop-in replacement interface for local GrgForward.encode() and GrgInverse.verify().
|
|
6
|
+
*/
|
|
7
|
+
export interface GrgEncodeResult {
|
|
8
|
+
/** Hex-encoded serialized shards (joined as JSON array of hex strings) */
|
|
9
|
+
shards: string[];
|
|
10
|
+
}
|
|
11
|
+
export interface GrgVerifyResult {
|
|
12
|
+
valid: boolean;
|
|
13
|
+
}
|
|
14
|
+
export declare class GrgApiClient {
|
|
15
|
+
private baseUrl;
|
|
16
|
+
private timeoutMs;
|
|
17
|
+
constructor(baseUrl?: string, options?: {
|
|
18
|
+
timeoutMs?: number;
|
|
19
|
+
});
|
|
20
|
+
/**
|
|
21
|
+
* Forward pass: encode data through GRG pipeline (server-side).
|
|
22
|
+
* Same interface contract as local GrgForward.encode().
|
|
23
|
+
*
|
|
24
|
+
* @returns Array of Uint8Array shards (same shape as GrgForward.encode())
|
|
25
|
+
*/
|
|
26
|
+
encode(data: Uint8Array, chainId: number, poolAddress: string): Promise<Uint8Array[]>;
|
|
27
|
+
/**
|
|
28
|
+
* Verify: check GRG integrity (server-side).
|
|
29
|
+
* Same interface contract as local GrgInverse.verify().
|
|
30
|
+
*
|
|
31
|
+
* @returns boolean — true if data matches the original shards
|
|
32
|
+
*/
|
|
33
|
+
verify(data: Uint8Array, originalShards: Uint8Array[], chainId: number, poolAddress: string): Promise<boolean>;
|
|
34
|
+
/**
|
|
35
|
+
* Health check — ping the GRG API server.
|
|
36
|
+
* Returns true if reachable within timeoutMs.
|
|
37
|
+
*/
|
|
38
|
+
isReachable(): Promise<boolean>;
|
|
39
|
+
}
|
|
40
|
+
export declare function getDefaultGrgApiClient(): GrgApiClient;
|
|
41
|
+
export declare function setDefaultGrgApiClient(client: GrgApiClient): void;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* GRG API Client — replaces local GRG computation with server-side API call.
|
|
4
|
+
* GRG source code stays in Helm private repo. Only API calls go through npm SDK.
|
|
5
|
+
*
|
|
6
|
+
* Drop-in replacement interface for local GrgForward.encode() and GrgInverse.verify().
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.GrgApiClient = void 0;
|
|
10
|
+
exports.getDefaultGrgApiClient = getDefaultGrgApiClient;
|
|
11
|
+
exports.setDefaultGrgApiClient = setDefaultGrgApiClient;
|
|
12
|
+
class GrgApiClient {
|
|
13
|
+
baseUrl;
|
|
14
|
+
timeoutMs;
|
|
15
|
+
constructor(baseUrl = "https://grg.helmprotocol.com/api/v1", options) {
|
|
16
|
+
this.baseUrl = baseUrl.replace(/\/$/, "");
|
|
17
|
+
this.timeoutMs = options?.timeoutMs ?? 5000;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Forward pass: encode data through GRG pipeline (server-side).
|
|
21
|
+
* Same interface contract as local GrgForward.encode().
|
|
22
|
+
*
|
|
23
|
+
* @returns Array of Uint8Array shards (same shape as GrgForward.encode())
|
|
24
|
+
*/
|
|
25
|
+
async encode(data, chainId, poolAddress) {
|
|
26
|
+
const controller = new AbortController();
|
|
27
|
+
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
28
|
+
try {
|
|
29
|
+
const resp = await fetch(`${this.baseUrl}/encode`, {
|
|
30
|
+
method: "POST",
|
|
31
|
+
headers: { "Content-Type": "application/json" },
|
|
32
|
+
body: JSON.stringify({
|
|
33
|
+
data: Buffer.from(data).toString("hex"),
|
|
34
|
+
chainId: chainId,
|
|
35
|
+
poolAddress,
|
|
36
|
+
}),
|
|
37
|
+
signal: controller.signal,
|
|
38
|
+
});
|
|
39
|
+
if (!resp.ok) {
|
|
40
|
+
throw new Error(`GRG API error: ${resp.status} ${resp.statusText}`);
|
|
41
|
+
}
|
|
42
|
+
const result = await resp.json();
|
|
43
|
+
return result.shards.map((hex) => Buffer.from(hex, "hex"));
|
|
44
|
+
}
|
|
45
|
+
finally {
|
|
46
|
+
clearTimeout(timer);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Verify: check GRG integrity (server-side).
|
|
51
|
+
* Same interface contract as local GrgInverse.verify().
|
|
52
|
+
*
|
|
53
|
+
* @returns boolean — true if data matches the original shards
|
|
54
|
+
*/
|
|
55
|
+
async verify(data, originalShards, chainId, poolAddress) {
|
|
56
|
+
const controller = new AbortController();
|
|
57
|
+
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
58
|
+
try {
|
|
59
|
+
const resp = await fetch(`${this.baseUrl}/verify`, {
|
|
60
|
+
method: "POST",
|
|
61
|
+
headers: { "Content-Type": "application/json" },
|
|
62
|
+
body: JSON.stringify({
|
|
63
|
+
data: Buffer.from(data).toString("hex"),
|
|
64
|
+
shards: originalShards.map((s) => Buffer.from(s).toString("hex")),
|
|
65
|
+
chainId: chainId,
|
|
66
|
+
poolAddress,
|
|
67
|
+
}),
|
|
68
|
+
signal: controller.signal,
|
|
69
|
+
});
|
|
70
|
+
if (!resp.ok) {
|
|
71
|
+
throw new Error(`GRG API error: ${resp.status} ${resp.statusText}`);
|
|
72
|
+
}
|
|
73
|
+
const result = await resp.json();
|
|
74
|
+
return result.valid;
|
|
75
|
+
}
|
|
76
|
+
finally {
|
|
77
|
+
clearTimeout(timer);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Health check — ping the GRG API server.
|
|
82
|
+
* Returns true if reachable within timeoutMs.
|
|
83
|
+
*/
|
|
84
|
+
async isReachable() {
|
|
85
|
+
const controller = new AbortController();
|
|
86
|
+
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
87
|
+
try {
|
|
88
|
+
const resp = await fetch(`${this.baseUrl}/health`, {
|
|
89
|
+
method: "GET",
|
|
90
|
+
signal: controller.signal,
|
|
91
|
+
});
|
|
92
|
+
return resp.ok;
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
finally {
|
|
98
|
+
clearTimeout(timer);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
exports.GrgApiClient = GrgApiClient;
|
|
103
|
+
/**
|
|
104
|
+
* Singleton default client — uses production GRG API endpoint.
|
|
105
|
+
* Can be overridden via setDefaultGrgApiClient() for testing/staging.
|
|
106
|
+
*/
|
|
107
|
+
let _defaultClient = null;
|
|
108
|
+
function getDefaultGrgApiClient() {
|
|
109
|
+
if (!_defaultClient) {
|
|
110
|
+
_defaultClient = new GrgApiClient();
|
|
111
|
+
}
|
|
112
|
+
return _defaultClient;
|
|
113
|
+
}
|
|
114
|
+
function setDefaultGrgApiClient(client) {
|
|
115
|
+
_defaultClient = client;
|
|
116
|
+
}
|