apow-cli 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.
@@ -0,0 +1,183 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.runPreflight = runPreflight;
40
+ const viem_1 = require("viem");
41
+ const AgentCoin_json_1 = __importDefault(require("./abi/AgentCoin.json"));
42
+ const config_1 = require("./config");
43
+ const wallet_1 = require("./wallet");
44
+ const ui = __importStar(require("./ui"));
45
+ const agentCoinAbi = AgentCoin_json_1.default;
46
+ async function runPreflight(level) {
47
+ const results = [];
48
+ // Check 1: Contract addresses set
49
+ const zeroAddr = "0x0000000000000000000000000000000000000000";
50
+ if (config_1.config.miningAgentAddress === zeroAddr || config_1.config.agentCoinAddress === zeroAddr) {
51
+ results.push({
52
+ label: "Contract addresses not configured",
53
+ passed: false,
54
+ fix: "Set MINING_AGENT_ADDRESS and AGENT_COIN_ADDRESS in .env",
55
+ });
56
+ }
57
+ else {
58
+ results.push({ label: "Contract addresses configured", passed: true });
59
+ }
60
+ // Check 2: RPC reachable + chain ID
61
+ try {
62
+ const chainId = await wallet_1.publicClient.getChainId();
63
+ const expectedId = config_1.config.chain.id;
64
+ if (chainId !== expectedId) {
65
+ results.push({
66
+ label: `RPC chain mismatch — expected ${expectedId}, got ${chainId}`,
67
+ passed: false,
68
+ fix: "Update RPC_URL to point to the correct network",
69
+ });
70
+ }
71
+ else {
72
+ results.push({ label: `RPC connected — ${config_1.config.chain.name}`, passed: true });
73
+ }
74
+ }
75
+ catch {
76
+ results.push({
77
+ label: `RPC unreachable — could not connect to ${config_1.config.rpcUrl}`,
78
+ passed: false,
79
+ fix: "Check internet connection or update RPC_URL in .env",
80
+ });
81
+ }
82
+ if (level === "wallet" || level === "mining") {
83
+ // Check 3: Private key valid
84
+ if (wallet_1.account) {
85
+ results.push({
86
+ label: `Private key valid (${wallet_1.account.address.slice(0, 6)}...${wallet_1.account.address.slice(-4)})`,
87
+ passed: true,
88
+ });
89
+ }
90
+ else {
91
+ results.push({
92
+ label: "Private key not configured",
93
+ passed: false,
94
+ fix: "Set PRIVATE_KEY in .env (0x-prefixed 32-byte hex)",
95
+ });
96
+ }
97
+ // Check 4: Wallet has ETH
98
+ if (wallet_1.account) {
99
+ try {
100
+ const balance = await wallet_1.publicClient.getBalance({ address: wallet_1.account.address });
101
+ const ethBalance = Number((0, viem_1.formatEther)(balance));
102
+ if (ethBalance < 0.001) {
103
+ results.push({
104
+ label: `Low ETH balance (${ethBalance.toFixed(6)} ETH)`,
105
+ passed: false,
106
+ fix: `Send ETH to ${wallet_1.account.address} on Base`,
107
+ });
108
+ }
109
+ else {
110
+ results.push({ label: `ETH balance: ${ethBalance.toFixed(6)} ETH`, passed: true });
111
+ }
112
+ }
113
+ catch {
114
+ results.push({
115
+ label: "Could not check ETH balance",
116
+ passed: false,
117
+ fix: "Verify RPC connection",
118
+ });
119
+ }
120
+ }
121
+ // Check 5: LLM key set
122
+ if (config_1.config.llmProvider === "ollama") {
123
+ results.push({ label: `LLM provider: ollama (${config_1.config.ollamaUrl})`, passed: true });
124
+ }
125
+ else if (config_1.config.llmProvider === "claude-code" || config_1.config.llmProvider === "codex") {
126
+ results.push({ label: `LLM provider: ${config_1.config.llmProvider} (local CLI)`, passed: true });
127
+ }
128
+ else if (config_1.config.llmApiKey) {
129
+ results.push({ label: `LLM provider: ${config_1.config.llmProvider} (key set)`, passed: true });
130
+ }
131
+ else {
132
+ results.push({
133
+ label: `LLM API key not set for ${config_1.config.llmProvider}`,
134
+ passed: false,
135
+ fix: "Set LLM_API_KEY (or OPENAI_API_KEY / ANTHROPIC_API_KEY / GEMINI_API_KEY) in .env, or run `apow setup`",
136
+ });
137
+ }
138
+ }
139
+ // Check 6: Contracts exist on-chain (bytecode check)
140
+ try {
141
+ const [miningAgentCode, agentCoinCode] = await Promise.all([
142
+ wallet_1.publicClient.getCode({ address: config_1.config.miningAgentAddress }),
143
+ wallet_1.publicClient.getCode({ address: config_1.config.agentCoinAddress }),
144
+ ]);
145
+ if (!miningAgentCode || miningAgentCode === "0x") {
146
+ results.push({
147
+ label: "MiningAgent contract not found on-chain",
148
+ passed: false,
149
+ fix: "Verify MINING_AGENT_ADDRESS is correct for this network",
150
+ });
151
+ }
152
+ else if (!agentCoinCode || agentCoinCode === "0x") {
153
+ results.push({
154
+ label: "AgentCoin contract not found on-chain",
155
+ passed: false,
156
+ fix: "Verify AGENT_COIN_ADDRESS is correct for this network",
157
+ });
158
+ }
159
+ else {
160
+ results.push({ label: "Contracts verified on-chain", passed: true });
161
+ }
162
+ }
163
+ catch {
164
+ // Skip if contract addresses not configured (already caught above)
165
+ }
166
+ const failures = results.filter((r) => !r.passed);
167
+ if (failures.length > 0) {
168
+ console.log("");
169
+ console.log(` ${ui.red("Pre-flight failed:")}`);
170
+ for (const r of results) {
171
+ if (r.passed) {
172
+ ui.ok(r.label);
173
+ }
174
+ else {
175
+ ui.fail(r.label);
176
+ if (r.fix)
177
+ ui.hint(`Fix: ${r.fix}`);
178
+ }
179
+ }
180
+ console.log("");
181
+ process.exit(1);
182
+ }
183
+ }
package/dist/smhl.js ADDED
@@ -0,0 +1,317 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.normalizeSmhlChallenge = normalizeSmhlChallenge;
7
+ exports.buildSmhlPrompt = buildSmhlPrompt;
8
+ exports.validateSmhlSolution = validateSmhlSolution;
9
+ exports.solveSmhlChallenge = solveSmhlChallenge;
10
+ const node_child_process_1 = require("node:child_process");
11
+ const openai_1 = __importDefault(require("openai"));
12
+ const config_1 = require("./config");
13
+ function normalizeSmhlChallenge(raw) {
14
+ if (Array.isArray(raw)) {
15
+ const [targetAsciiSum, firstNChars, wordCount, charPosition, charValue, totalLength] = raw;
16
+ return {
17
+ targetAsciiSum: Number(targetAsciiSum),
18
+ firstNChars: Number(firstNChars),
19
+ wordCount: Number(wordCount),
20
+ charPosition: Number(charPosition),
21
+ charValue: Number(charValue),
22
+ totalLength: Number(totalLength),
23
+ };
24
+ }
25
+ if (raw && typeof raw === "object") {
26
+ const challenge = raw;
27
+ return {
28
+ targetAsciiSum: Number(challenge.targetAsciiSum),
29
+ firstNChars: Number(challenge.firstNChars),
30
+ wordCount: Number(challenge.wordCount),
31
+ charPosition: Number(challenge.charPosition),
32
+ charValue: Number(challenge.charValue),
33
+ totalLength: Number(challenge.totalLength),
34
+ };
35
+ }
36
+ throw new Error("Unable to normalize SMHL challenge.");
37
+ }
38
+ function buildSmhlPrompt(challenge, feedback) {
39
+ const requiredChar = String.fromCharCode(challenge.charValue);
40
+ const spaces = Math.max(0, challenge.wordCount - 1);
41
+ const avgWordLen = Math.round((challenge.totalLength - spaces) / challenge.wordCount);
42
+ const minWordLen = Math.max(2, avgWordLen - 2);
43
+ const maxWordLen = avgWordLen + 2;
44
+ const lines = [
45
+ `Write exactly ${challenge.wordCount} lowercase English words separated by single spaces.`,
46
+ `Each word should be ${minWordLen} to ${maxWordLen} letters long.`,
47
+ `At least one word must contain the letter '${requiredChar}'.`,
48
+ `No punctuation, no quotes, no explanation — just the words.`,
49
+ ];
50
+ if (feedback) {
51
+ lines.push("", `Previous attempt was rejected: ${feedback}. Try completely different words.`);
52
+ }
53
+ return lines.join("\n");
54
+ }
55
+ function validateSmhlSolution(solution, challenge) {
56
+ const issues = [];
57
+ if (!solution) {
58
+ issues.push("empty response");
59
+ return issues;
60
+ }
61
+ const len = Buffer.byteLength(solution, "utf8");
62
+ if (Math.abs(len - challenge.totalLength) > 5) {
63
+ issues.push(`length ${len} not within ±5 of ${challenge.totalLength}`);
64
+ }
65
+ if (!/^[\x20-\x7E]+$/.test(solution)) {
66
+ issues.push("solution must use printable ASCII only");
67
+ }
68
+ const requiredChar = String.fromCharCode(challenge.charValue);
69
+ if (!solution.includes(requiredChar)) {
70
+ issues.push(`missing required char '${requiredChar}'`);
71
+ }
72
+ const words = solution.split(" ").filter(Boolean);
73
+ if (Math.abs(words.length - challenge.wordCount) > 2) {
74
+ issues.push(`word count ${words.length} not within ±2 of ${challenge.wordCount}`);
75
+ }
76
+ return issues;
77
+ }
78
+ /**
79
+ * Post-process LLM output to fit within SMHL constraints.
80
+ * The LLM generates roughly-correct text; this adjusts length, word count,
81
+ * and ensures the required character is present.
82
+ */
83
+ function adjustSolution(raw, challenge) {
84
+ const requiredChar = String.fromCharCode(challenge.charValue);
85
+ const minLen = challenge.totalLength - 5;
86
+ const maxLen = challenge.totalLength + 5;
87
+ const maxWords = challenge.wordCount + 2;
88
+ // Clean: lowercase, letters and spaces only, collapse whitespace
89
+ let words = raw
90
+ .toLowerCase()
91
+ .replace(/[^a-z ]/g, "")
92
+ .split(/\s+/)
93
+ .filter((w) => w.length > 0);
94
+ if (words.length === 0)
95
+ return raw;
96
+ // Trim excess words (prefer keeping words with the required char)
97
+ while (words.length > maxWords) {
98
+ let removeIdx = -1;
99
+ for (let i = words.length - 1; i >= 0; i--) {
100
+ if (!words[i].includes(requiredChar)) {
101
+ removeIdx = i;
102
+ break;
103
+ }
104
+ }
105
+ words.splice(removeIdx >= 0 ? removeIdx : words.length - 1, 1);
106
+ }
107
+ let result = words.join(" ");
108
+ let len = Buffer.byteLength(result, "utf8");
109
+ // Too long: trim at word boundary, then trim characters
110
+ if (len > maxLen) {
111
+ result = result.slice(0, maxLen);
112
+ const lastSpace = result.lastIndexOf(" ");
113
+ if (lastSpace >= minLen)
114
+ result = result.slice(0, lastSpace);
115
+ words = result.split(" ").filter(Boolean);
116
+ len = Buffer.byteLength(result, "utf8");
117
+ }
118
+ // Too short: add filler words
119
+ const fillers = ["and", "the", "not", "can", "run", "now", "old", "new"];
120
+ let fi = 0;
121
+ while (len < minLen && words.length < maxWords) {
122
+ words.push(fillers[fi % fillers.length]);
123
+ result = words.join(" ");
124
+ len = Buffer.byteLength(result, "utf8");
125
+ fi++;
126
+ }
127
+ // Still too short but can't add more words: extend last word
128
+ while (len < minLen && words.length > 0) {
129
+ words[words.length - 1] += "s";
130
+ result = words.join(" ");
131
+ len = Buffer.byteLength(result, "utf8");
132
+ }
133
+ // Ensure required char is present
134
+ if (!result.includes(requiredChar)) {
135
+ const last = words[words.length - 1];
136
+ words[words.length - 1] = last.slice(0, -1) + requiredChar;
137
+ result = words.join(" ");
138
+ }
139
+ return result;
140
+ }
141
+ function sanitizeResponse(text) {
142
+ let cleaned = text.replace(/\r/g, "").trim();
143
+ const fenceMatch = cleaned.match(/^```(?:text)?\n([\s\S]*?)\n```$/);
144
+ if (fenceMatch) {
145
+ cleaned = fenceMatch[1];
146
+ }
147
+ if ((cleaned.startsWith('"') && cleaned.endsWith('"')) ||
148
+ (cleaned.startsWith("'") && cleaned.endsWith("'"))) {
149
+ cleaned = cleaned.slice(1, -1);
150
+ }
151
+ return cleaned;
152
+ }
153
+ async function requestOpenAiSolution(prompt) {
154
+ const client = new openai_1.default({ apiKey: (0, config_1.requireLlmApiKey)() });
155
+ const response = await client.chat.completions.create({
156
+ model: config_1.config.llmModel,
157
+ temperature: 0.7,
158
+ messages: [
159
+ {
160
+ role: "system",
161
+ content: "You generate short lowercase word sequences that match exact constraints. Return only the words separated by spaces. Nothing else.",
162
+ },
163
+ { role: "user", content: prompt },
164
+ ],
165
+ }, { timeout: 15_000 });
166
+ return response.choices[0]?.message.content ?? "";
167
+ }
168
+ async function requestAnthropicSolution(prompt) {
169
+ const response = await fetch("https://api.anthropic.com/v1/messages", {
170
+ method: "POST",
171
+ signal: AbortSignal.timeout(15_000),
172
+ headers: {
173
+ "content-type": "application/json",
174
+ "x-api-key": (0, config_1.requireLlmApiKey)(),
175
+ "anthropic-version": "2023-06-01",
176
+ },
177
+ body: JSON.stringify({
178
+ model: config_1.config.llmModel,
179
+ max_tokens: 200,
180
+ temperature: 0.7,
181
+ system: "You generate short lowercase word sequences that match exact constraints. Return only the words separated by spaces. Nothing else.",
182
+ messages: [{ role: "user", content: prompt }],
183
+ }),
184
+ });
185
+ if (response.status === 429) {
186
+ const retryAfter = response.headers.get("retry-after");
187
+ const waitMs = retryAfter ? parseInt(retryAfter) * 1000 : 5000;
188
+ await new Promise((r) => setTimeout(r, waitMs));
189
+ throw new Error(`Rate limited by Anthropic — retrying`);
190
+ }
191
+ if (!response.ok) {
192
+ throw new Error(`Anthropic request failed: ${response.status} ${response.statusText}`);
193
+ }
194
+ const data = (await response.json());
195
+ return data.content?.find((item) => item.type === "text")?.text ?? "";
196
+ }
197
+ async function requestOllamaSolution(prompt) {
198
+ const response = await fetch(`${config_1.config.ollamaUrl}/api/generate`, {
199
+ method: "POST",
200
+ signal: AbortSignal.timeout(15_000),
201
+ headers: { "content-type": "application/json" },
202
+ body: JSON.stringify({
203
+ model: config_1.config.llmModel,
204
+ prompt: [
205
+ "You generate short lowercase word sequences that match exact constraints.",
206
+ "Return only the words separated by spaces. Nothing else.",
207
+ "",
208
+ prompt,
209
+ ].join("\n"),
210
+ stream: false,
211
+ options: { temperature: 0.7 },
212
+ }),
213
+ });
214
+ if (response.status === 429) {
215
+ const retryAfter = response.headers.get("retry-after");
216
+ const waitMs = retryAfter ? parseInt(retryAfter) * 1000 : 5000;
217
+ await new Promise((r) => setTimeout(r, waitMs));
218
+ throw new Error(`Rate limited by Ollama — retrying`);
219
+ }
220
+ if (!response.ok) {
221
+ throw new Error(`Ollama request failed: ${response.status} ${response.statusText}`);
222
+ }
223
+ const data = (await response.json());
224
+ return data.response ?? "";
225
+ }
226
+ async function requestGeminiSolution(prompt) {
227
+ const model = config_1.config.llmModel || "gemini-2.5-flash";
228
+ const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${(0, config_1.requireLlmApiKey)()}`, {
229
+ method: "POST",
230
+ signal: AbortSignal.timeout(15_000),
231
+ headers: { "content-type": "application/json" },
232
+ body: JSON.stringify({
233
+ systemInstruction: {
234
+ parts: [{ text: "You generate short lowercase word sequences that match exact constraints. Return only the words separated by spaces. Nothing else." }],
235
+ },
236
+ contents: [{ parts: [{ text: prompt }] }],
237
+ generationConfig: { temperature: 0.7 },
238
+ }),
239
+ });
240
+ if (response.status === 429) {
241
+ const retryAfter = response.headers.get("retry-after");
242
+ const waitMs = retryAfter ? parseInt(retryAfter) * 1000 : 5000;
243
+ await new Promise((r) => setTimeout(r, waitMs));
244
+ throw new Error(`Rate limited by Gemini — retrying`);
245
+ }
246
+ if (!response.ok) {
247
+ throw new Error(`Gemini request failed: ${response.status} ${response.statusText}`);
248
+ }
249
+ const data = (await response.json());
250
+ return data.candidates?.[0]?.content?.parts?.[0]?.text ?? "";
251
+ }
252
+ async function requestClaudeCodeSolution(prompt) {
253
+ return new Promise((resolve, reject) => {
254
+ const escaped = prompt.replace(/'/g, "'\\''");
255
+ (0, node_child_process_1.exec)(`claude -p '${escaped}'`, { timeout: 120_000 }, (error, stdout, stderr) => {
256
+ if (error) {
257
+ reject(new Error(`Claude Code error: ${error.message}${stderr ? `\nstderr: ${stderr}` : ""}${stdout ? `\nstdout: ${stdout}` : ""}`));
258
+ return;
259
+ }
260
+ resolve(stdout.trim());
261
+ });
262
+ });
263
+ }
264
+ async function requestCodexSolution(prompt) {
265
+ return new Promise((resolve, reject) => {
266
+ (0, node_child_process_1.execFile)("codex", ["exec", prompt, "--full-auto"], { timeout: 15_000 }, (error, stdout, stderr) => {
267
+ if (error) {
268
+ reject(new Error(`Codex error: ${error.message}`));
269
+ return;
270
+ }
271
+ resolve(stdout.trim());
272
+ });
273
+ });
274
+ }
275
+ async function requestProviderSolution(prompt) {
276
+ switch (config_1.config.llmProvider) {
277
+ case "anthropic":
278
+ return requestAnthropicSolution(prompt);
279
+ case "gemini":
280
+ return requestGeminiSolution(prompt);
281
+ case "ollama":
282
+ return requestOllamaSolution(prompt);
283
+ case "claude-code":
284
+ return requestClaudeCodeSolution(prompt);
285
+ case "codex":
286
+ return requestCodexSolution(prompt);
287
+ case "openai":
288
+ default:
289
+ return requestOpenAiSolution(prompt);
290
+ }
291
+ }
292
+ async function solveSmhlChallenge(challenge, onAttempt) {
293
+ let feedback;
294
+ let lastIssues = "provider did not return a valid response";
295
+ for (let attempt = 1; attempt <= 5; attempt += 1) {
296
+ if (onAttempt)
297
+ onAttempt(attempt);
298
+ try {
299
+ const prompt = buildSmhlPrompt(challenge, feedback);
300
+ const raw = await requestProviderSolution(prompt);
301
+ const sanitized = sanitizeResponse(raw);
302
+ const adjusted = adjustSolution(sanitized, challenge);
303
+ const issues = validateSmhlSolution(adjusted, challenge);
304
+ if (issues.length === 0) {
305
+ return adjusted;
306
+ }
307
+ feedback = issues.join(", ");
308
+ lastIssues = `attempt ${attempt}: ${feedback}`;
309
+ }
310
+ catch (err) {
311
+ const msg = err instanceof Error ? err.message : String(err);
312
+ feedback = msg;
313
+ lastIssues = `attempt ${attempt}: ${msg}`;
314
+ }
315
+ }
316
+ throw new Error(`SMHL solve failed after 5 attempts: ${lastIssues}`);
317
+ }
package/dist/stats.js ADDED
@@ -0,0 +1,166 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.displayStats = displayStats;
40
+ const viem_1 = require("viem");
41
+ const AgentCoin_json_1 = __importDefault(require("./abi/AgentCoin.json"));
42
+ const MiningAgent_json_1 = __importDefault(require("./abi/MiningAgent.json"));
43
+ const config_1 = require("./config");
44
+ const detect_1 = require("./detect");
45
+ const explorer_1 = require("./explorer");
46
+ const ui = __importStar(require("./ui"));
47
+ const wallet_1 = require("./wallet");
48
+ const miningAgentAbi = MiningAgent_json_1.default;
49
+ const agentCoinAbi = AgentCoin_json_1.default;
50
+ async function displayStats(tokenId) {
51
+ const [totalMines, totalMinted, miningTarget, mineableSupply, eraInterval, walletBalance] = await Promise.all([
52
+ wallet_1.publicClient.readContract({
53
+ address: config_1.config.agentCoinAddress,
54
+ abi: agentCoinAbi,
55
+ functionName: "totalMines",
56
+ }),
57
+ wallet_1.publicClient.readContract({
58
+ address: config_1.config.agentCoinAddress,
59
+ abi: agentCoinAbi,
60
+ functionName: "totalMinted",
61
+ }),
62
+ wallet_1.publicClient.readContract({
63
+ address: config_1.config.agentCoinAddress,
64
+ abi: agentCoinAbi,
65
+ functionName: "miningTarget",
66
+ }),
67
+ wallet_1.publicClient.readContract({
68
+ address: config_1.config.agentCoinAddress,
69
+ abi: agentCoinAbi,
70
+ functionName: "MINEABLE_SUPPLY",
71
+ }),
72
+ wallet_1.publicClient.readContract({
73
+ address: config_1.config.agentCoinAddress,
74
+ abi: agentCoinAbi,
75
+ functionName: "ERA_INTERVAL",
76
+ }),
77
+ wallet_1.account
78
+ ? wallet_1.publicClient.readContract({
79
+ address: config_1.config.agentCoinAddress,
80
+ abi: agentCoinAbi,
81
+ functionName: "balanceOf",
82
+ args: [wallet_1.account.address],
83
+ })
84
+ : Promise.resolve(0n),
85
+ ]);
86
+ const era = totalMines / eraInterval;
87
+ const minesUntilNextEra = eraInterval - (totalMines % eraInterval);
88
+ const supplyPct = Number(totalMinted * 10000n / mineableSupply) / 100;
89
+ const remainingSupply = mineableSupply - totalMinted;
90
+ // Base reward calculation (3 * 0.9^era)
91
+ let baseReward = 3;
92
+ for (let i = 0n; i < era; i++) {
93
+ baseReward *= 0.9;
94
+ }
95
+ let nextEraReward = baseReward * 0.9;
96
+ // Difficulty interpretation
97
+ const targetLog = Math.log2(Number(miningTarget));
98
+ const difficultyDesc = targetLog > 250 ? "very easy" : targetLog > 240 ? "easy" : targetLog > 220 ? "moderate" : targetLog > 200 ? "hard" : "very hard";
99
+ console.log("");
100
+ console.log(` ${ui.bold("Network")}`);
101
+ ui.table([
102
+ ["Total mines", totalMines.toLocaleString()],
103
+ ["Supply mined", `${Number((0, viem_1.formatEther)(totalMinted)).toLocaleString()} / ${Number((0, viem_1.formatEther)(mineableSupply)).toLocaleString()} AGENT (${supplyPct.toFixed(2)}%)`],
104
+ ["Remaining", `${Number((0, viem_1.formatEther)(remainingSupply)).toLocaleString()} AGENT`],
105
+ ["Era", `${era} (${minesUntilNextEra.toLocaleString()} mines until era ${(era + 1n).toString()})`],
106
+ ["Base reward", `${baseReward.toFixed(2)} AGENT (next era: ${nextEraReward.toFixed(2)})`],
107
+ ["Difficulty", `${difficultyDesc} (target: 2^${targetLog.toFixed(0)})`],
108
+ ]);
109
+ if (wallet_1.account && walletBalance > 0n) {
110
+ console.log("");
111
+ console.log(` ${ui.bold("Wallet")}`);
112
+ ui.table([
113
+ ["Address", `${wallet_1.account.address.slice(0, 6)}...${wallet_1.account.address.slice(-4)}`],
114
+ ["AGENT balance", `${Number((0, viem_1.formatEther)(walletBalance)).toLocaleString()} AGENT`],
115
+ ]);
116
+ }
117
+ if (tokenId === undefined) {
118
+ console.log("");
119
+ return;
120
+ }
121
+ const [tokenMineCount, tokenEarnings, rarityRaw, hashpowerRaw, mintBlock] = await Promise.all([
122
+ wallet_1.publicClient.readContract({
123
+ address: config_1.config.agentCoinAddress,
124
+ abi: agentCoinAbi,
125
+ functionName: "tokenMineCount",
126
+ args: [tokenId],
127
+ }),
128
+ wallet_1.publicClient.readContract({
129
+ address: config_1.config.agentCoinAddress,
130
+ abi: agentCoinAbi,
131
+ functionName: "tokenEarnings",
132
+ args: [tokenId],
133
+ }),
134
+ wallet_1.publicClient.readContract({
135
+ address: config_1.config.miningAgentAddress,
136
+ abi: miningAgentAbi,
137
+ functionName: "rarity",
138
+ args: [tokenId],
139
+ }),
140
+ wallet_1.publicClient.readContract({
141
+ address: config_1.config.miningAgentAddress,
142
+ abi: miningAgentAbi,
143
+ functionName: "hashpower",
144
+ args: [tokenId],
145
+ }),
146
+ wallet_1.publicClient.readContract({
147
+ address: config_1.config.miningAgentAddress,
148
+ abi: miningAgentAbi,
149
+ functionName: "mintBlock",
150
+ args: [tokenId],
151
+ }),
152
+ ]);
153
+ const rarity = Number(rarityRaw);
154
+ const hashpower = Number(hashpowerRaw);
155
+ const rewardPerMine = baseReward * (hashpower / 100);
156
+ console.log("");
157
+ console.log(` ${ui.bold(`Miner #${tokenId} (${detect_1.rarityLabels[rarity] ?? `Tier ${rarity}`}, ${(0, detect_1.formatHashpower)(hashpower)})`)}`);
158
+ ui.table([
159
+ ["Mine count", tokenMineCount.toLocaleString()],
160
+ ["Earnings", `${Number((0, viem_1.formatEther)(tokenEarnings)).toLocaleString()} AGENT`],
161
+ ["Reward/mine", `${rewardPerMine.toFixed(2)} AGENT`],
162
+ ["Mint block", mintBlock.toLocaleString()],
163
+ ["Basescan", (0, explorer_1.tokenUrl)(config_1.config.miningAgentAddress, tokenId)],
164
+ ]);
165
+ console.log("");
166
+ }