apow-cli 0.3.4 → 0.4.1
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 +28 -18
- package/dist/config.js +5 -0
- package/dist/grinder.js +113 -0
- package/dist/index.js +5 -3
- package/dist/miner.js +18 -10
- package/dist/preflight.js +18 -16
- package/dist/smhl.js +29 -0
- package/package.json +2 -2
- package/skill.md +85 -79
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# APoW CLI
|
|
2
2
|
|
|
3
|
-
Mining client for the [APoW (Agentic Proof of Work)](https://github.com/Agentoshi/apow-core) protocol on Base.
|
|
3
|
+
Mining client for the [APoW (Agentic Proof of Work)](https://github.com/Agentoshi/apow-core) protocol on Base. Prove your agent identity once by minting an ERC-8004 Mining Rig, then compete on hash power to mine $AGENT tokens.
|
|
4
4
|
|
|
5
|
-
**Your agent does all the work
|
|
5
|
+
**Your agent does all the work. You just fund a wallet.**
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -24,11 +24,11 @@ npx apow-cli
|
|
|
24
24
|
> 2. Create an app → Chain: **Base** → Network: **Base Mainnet**
|
|
25
25
|
> 3. Copy the HTTPS URL → set `RPC_URL` in your `.env`
|
|
26
26
|
>
|
|
27
|
-
> Alchemy's free tier gives you 300M compute units/month
|
|
27
|
+
> Alchemy's free tier gives you 300M compute units/month, more than enough for mining. See [RPC Recommendations](skill.md#rpc-recommendations) for other free options.
|
|
28
28
|
|
|
29
29
|
## For AI Agents
|
|
30
30
|
|
|
31
|
-
Any AI agent can go from zero to actively mining in 5 steps
|
|
31
|
+
Any AI agent can go from zero to actively mining in 5 steps with no human interaction except funding the wallet.
|
|
32
32
|
|
|
33
33
|
```bash
|
|
34
34
|
# 1. Generate a wallet
|
|
@@ -36,22 +36,23 @@ npx apow-cli wallet new
|
|
|
36
36
|
# → Captures address + private key from stdout
|
|
37
37
|
|
|
38
38
|
# 2. Write .env (no interactive prompts)
|
|
39
|
+
# LLM config is only needed for minting; mining uses optimized SMHL solving
|
|
39
40
|
cat > .env << 'EOF'
|
|
40
41
|
PRIVATE_KEY=0x<from step 1>
|
|
41
|
-
RPC_URL=https://mainnet.base.org # UNRELIABLE
|
|
42
|
-
LLM_PROVIDER=openai
|
|
43
|
-
LLM_MODEL=gpt-4o-mini
|
|
44
|
-
LLM_API_KEY=<your key>
|
|
42
|
+
RPC_URL=https://mainnet.base.org # UNRELIABLE, get a free Alchemy URL (see above)
|
|
43
|
+
LLM_PROVIDER=openai # Required for minting only
|
|
44
|
+
LLM_MODEL=gpt-4o-mini # Required for minting only
|
|
45
|
+
LLM_API_KEY=<your key> # Required for minting only
|
|
45
46
|
EOF
|
|
46
47
|
|
|
47
48
|
# 3. Fund the wallet (bridge from Solana or send ETH on Base)
|
|
48
49
|
npx apow-cli fund --solana # bridge SOL → ETH on Base
|
|
49
50
|
# Or ask your user to send ≥0.005 ETH on Base directly
|
|
50
51
|
|
|
51
|
-
# 4. Mint a mining rig NFT (
|
|
52
|
+
# 4. Mint a mining rig NFT (proves agent identity via LLM, one-time)
|
|
52
53
|
npx apow-cli mint
|
|
53
54
|
|
|
54
|
-
# 5. Start mining (runs forever,
|
|
55
|
+
# 5. Start mining (runs forever, no LLM needed, multi-threaded)
|
|
55
56
|
npx apow-cli mine
|
|
56
57
|
```
|
|
57
58
|
|
|
@@ -74,7 +75,7 @@ Each wallet gets one rig, each rig mines independently. More wallets = more chan
|
|
|
74
75
|
If you prefer to do it yourself:
|
|
75
76
|
|
|
76
77
|
```bash
|
|
77
|
-
# 1. Interactive setup
|
|
78
|
+
# 1. Interactive setup: wallet, RPC, LLM config
|
|
78
79
|
npx apow-cli setup
|
|
79
80
|
|
|
80
81
|
# 2. Fund your wallet (bridge from Solana or send ETH directly)
|
|
@@ -91,8 +92,8 @@ npx apow-cli mine
|
|
|
91
92
|
|
|
92
93
|
| Command | Description |
|
|
93
94
|
|---------|-------------|
|
|
94
|
-
| `apow setup` | Interactive setup wizard
|
|
95
|
-
| `apow fund` | Fund your wallet
|
|
95
|
+
| `apow setup` | Interactive setup wizard: configure wallet, RPC, and LLM |
|
|
96
|
+
| `apow fund` | Fund your wallet: bridge SOL → ETH on Base, or show deposit address |
|
|
96
97
|
| `apow wallet new` | Generate a new mining wallet |
|
|
97
98
|
| `apow wallet show` | Show configured wallet address |
|
|
98
99
|
| `apow wallet export` | Export your wallet's private key |
|
|
@@ -107,10 +108,10 @@ Create a `.env` file or use `apow setup`:
|
|
|
107
108
|
|
|
108
109
|
```bash
|
|
109
110
|
PRIVATE_KEY=0x... # Your wallet private key
|
|
110
|
-
RPC_URL=https://mainnet.base.org # UNRELIABLE
|
|
111
|
-
LLM_PROVIDER=openai # openai | anthropic | gemini | ollama | claude-code | codex
|
|
112
|
-
LLM_MODEL=gpt-4o-mini
|
|
113
|
-
LLM_API_KEY=sk-...
|
|
111
|
+
RPC_URL=https://mainnet.base.org # UNRELIABLE, strongly recommend a free Alchemy URL instead (see above)
|
|
112
|
+
LLM_PROVIDER=openai # openai | anthropic | gemini | ollama | claude-code | codex (for minting)
|
|
113
|
+
LLM_MODEL=gpt-4o-mini # Required for minting only; mining uses optimized SMHL solving
|
|
114
|
+
LLM_API_KEY=sk-... # Required for minting only
|
|
114
115
|
# Solana bridging (only for `apow fund --solana`)
|
|
115
116
|
# SOLANA_RPC_URL=https://api.mainnet-beta.solana.com
|
|
116
117
|
# SQUID_INTEGRATOR_ID= # free, get at squidrouter.com (deposit address flow only)
|
|
@@ -121,7 +122,9 @@ LLM_API_KEY=sk-...
|
|
|
121
122
|
|
|
122
123
|
See [.env.example](.env.example) for all options.
|
|
123
124
|
|
|
124
|
-
## LLM Providers
|
|
125
|
+
## LLM Providers (for Minting)
|
|
126
|
+
|
|
127
|
+
An LLM is required to mint your Mining Rig NFT (one-time identity verification). Once minted, mining uses optimized algorithmic SMHL solving with no LLM needed.
|
|
125
128
|
|
|
126
129
|
| Provider | Model | Cost/call | Notes |
|
|
127
130
|
|----------|-------|-----------|-------|
|
|
@@ -132,6 +135,13 @@ See [.env.example](.env.example) for all options.
|
|
|
132
135
|
| Claude Code | `default` | Subscription | No API key needed |
|
|
133
136
|
| Codex | `default` | Subscription | No API key needed |
|
|
134
137
|
|
|
138
|
+
## Speed Mining (v0.4.0+)
|
|
139
|
+
|
|
140
|
+
Mining in v0.4.0 uses two key optimizations:
|
|
141
|
+
|
|
142
|
+
- **Algorithmic SMHL**: Mining SMHL challenges are solved algorithmically in microseconds (no LLM call). Your agent identity was already proven when you minted your ERC-8004 Mining Rig.
|
|
143
|
+
- **Multi-threaded nonce grinding**: Hash computation is parallelized across all CPU cores via `worker_threads`. Set `MINER_THREADS` in `.env` to override the default (all cores).
|
|
144
|
+
|
|
135
145
|
## Protocol
|
|
136
146
|
|
|
137
147
|
The APoW protocol contracts and documentation live in [apow-core](https://github.com/Agentoshi/apow-core).
|
package/dist/config.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.config = void 0;
|
|
4
7
|
exports.requirePrivateKey = requirePrivateKey;
|
|
@@ -7,6 +10,7 @@ exports.isExpensiveModel = isExpensiveModel;
|
|
|
7
10
|
exports.writeEnvFile = writeEnvFile;
|
|
8
11
|
const dotenv_1 = require("dotenv");
|
|
9
12
|
const promises_1 = require("node:fs/promises");
|
|
13
|
+
const node_os_1 = __importDefault(require("node:os"));
|
|
10
14
|
const node_path_1 = require("node:path");
|
|
11
15
|
const chains_1 = require("viem/chains");
|
|
12
16
|
(0, dotenv_1.config)();
|
|
@@ -83,6 +87,7 @@ exports.config = {
|
|
|
83
87
|
chainName,
|
|
84
88
|
miningAgentAddress: parseAddress("MINING_AGENT_ADDRESS", DEFAULT_MINING_AGENT_ADDRESS),
|
|
85
89
|
agentCoinAddress: parseAddress("AGENT_COIN_ADDRESS", DEFAULT_AGENT_COIN_ADDRESS),
|
|
90
|
+
minerThreads: parseInt(process.env.MINER_THREADS ?? String(node_os_1.default.cpus().length), 10),
|
|
86
91
|
};
|
|
87
92
|
function requirePrivateKey() {
|
|
88
93
|
if (!exports.config.privateKey) {
|
package/dist/grinder.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Multi-threaded nonce grinding via worker_threads.
|
|
4
|
+
* Spawns N workers that search different nonce ranges in parallel.
|
|
5
|
+
* First worker to find a valid nonce wins; all others are terminated.
|
|
6
|
+
*/
|
|
7
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
8
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
9
|
+
};
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.grindNonceParallel = grindNonceParallel;
|
|
12
|
+
const node_worker_threads_1 = require("node:worker_threads");
|
|
13
|
+
const node_os_1 = __importDefault(require("node:os"));
|
|
14
|
+
const viem_1 = require("viem");
|
|
15
|
+
// ── Worker thread logic ──────────────────────────────────────────────
|
|
16
|
+
if (!node_worker_threads_1.isMainThread && node_worker_threads_1.parentPort) {
|
|
17
|
+
const params = node_worker_threads_1.workerData;
|
|
18
|
+
const target = BigInt(params.targetHex);
|
|
19
|
+
let nonce = BigInt(params.startNonce);
|
|
20
|
+
let attempts = 0n;
|
|
21
|
+
const reportInterval = 50000n;
|
|
22
|
+
while (true) {
|
|
23
|
+
const digest = BigInt((0, viem_1.keccak256)((0, viem_1.encodePacked)(["bytes32", "address", "uint256"], [params.challengeNumber, params.minerAddress, nonce])));
|
|
24
|
+
attempts += 1n;
|
|
25
|
+
if (digest < target) {
|
|
26
|
+
node_worker_threads_1.parentPort.postMessage({
|
|
27
|
+
type: "found",
|
|
28
|
+
nonce: nonce.toString(),
|
|
29
|
+
attempts: attempts.toString(),
|
|
30
|
+
});
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
if (attempts % reportInterval === 0n) {
|
|
34
|
+
node_worker_threads_1.parentPort.postMessage({
|
|
35
|
+
type: "progress",
|
|
36
|
+
attempts: attempts.toString(),
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
nonce += 1n;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
// ── Main thread: spawn workers, collect results ──────────────────────
|
|
43
|
+
async function grindNonceParallel(params) {
|
|
44
|
+
const threadCount = params.threads ?? node_os_1.default.cpus().length;
|
|
45
|
+
const start = process.hrtime();
|
|
46
|
+
const workers = [];
|
|
47
|
+
const workerAttempts = new Map();
|
|
48
|
+
let totalAttempts = 0n;
|
|
49
|
+
return new Promise((resolve, reject) => {
|
|
50
|
+
let settled = false;
|
|
51
|
+
function cleanup() {
|
|
52
|
+
for (const w of workers) {
|
|
53
|
+
w.terminate().catch(() => { });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function elapsed() {
|
|
57
|
+
const [s, ns] = process.hrtime(start);
|
|
58
|
+
return s + ns / 1_000_000_000;
|
|
59
|
+
}
|
|
60
|
+
for (let i = 0; i < threadCount; i++) {
|
|
61
|
+
// Each worker starts at a random offset to avoid overlap
|
|
62
|
+
const startNonce = BigInt(Math.floor(Math.random() * Number.MAX_SAFE_INTEGER));
|
|
63
|
+
const worker = new node_worker_threads_1.Worker(__filename, {
|
|
64
|
+
workerData: {
|
|
65
|
+
challengeNumber: params.challengeNumber,
|
|
66
|
+
targetHex: params.target.toString(),
|
|
67
|
+
minerAddress: params.minerAddress,
|
|
68
|
+
startNonce: startNonce.toString(),
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
workerAttempts.set(i, 0n);
|
|
72
|
+
worker.on("message", (msg) => {
|
|
73
|
+
if (msg.type === "found" && !settled) {
|
|
74
|
+
settled = true;
|
|
75
|
+
// Sum all worker attempts
|
|
76
|
+
const workerFinalAttempts = BigInt(msg.attempts ?? "0");
|
|
77
|
+
workerAttempts.set(i, workerFinalAttempts);
|
|
78
|
+
totalAttempts = 0n;
|
|
79
|
+
for (const a of workerAttempts.values())
|
|
80
|
+
totalAttempts += a;
|
|
81
|
+
const e = elapsed();
|
|
82
|
+
cleanup();
|
|
83
|
+
resolve({
|
|
84
|
+
nonce: BigInt(msg.nonce),
|
|
85
|
+
attempts: totalAttempts,
|
|
86
|
+
hashrate: e > 0 ? Number(totalAttempts) / e : Number(totalAttempts),
|
|
87
|
+
elapsed: e,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
else if (msg.type === "progress") {
|
|
91
|
+
const workerProgressAttempts = BigInt(msg.attempts ?? "0");
|
|
92
|
+
workerAttempts.set(i, workerProgressAttempts);
|
|
93
|
+
totalAttempts = 0n;
|
|
94
|
+
for (const a of workerAttempts.values())
|
|
95
|
+
totalAttempts += a;
|
|
96
|
+
if (params.onProgress) {
|
|
97
|
+
const e = elapsed();
|
|
98
|
+
const hashrate = e > 0 ? Number(totalAttempts) / e : Number(totalAttempts);
|
|
99
|
+
params.onProgress(totalAttempts, hashrate);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
worker.on("error", (err) => {
|
|
104
|
+
if (!settled) {
|
|
105
|
+
settled = true;
|
|
106
|
+
cleanup();
|
|
107
|
+
reject(err);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
workers.push(worker);
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -192,8 +192,10 @@ async function setupWizard() {
|
|
|
192
192
|
ui.hint("Continuing anyway — you can fix RPC_URL in .env later");
|
|
193
193
|
}
|
|
194
194
|
console.log("");
|
|
195
|
-
// Step 3: LLM
|
|
196
|
-
console.log(` ${ui.bold("Step 3/3: LLM Provider")}`);
|
|
195
|
+
// Step 3: LLM (for minting)
|
|
196
|
+
console.log(` ${ui.bold("Step 3/3: LLM Provider (for minting)")}`);
|
|
197
|
+
console.log(` ${ui.dim("An LLM solves the SMHL challenge when minting your Mining Rig.")}`);
|
|
198
|
+
console.log(` ${ui.dim("Mining uses optimized solving — no LLM needed after minting.")}`);
|
|
197
199
|
const providerInput = await ui.prompt("Provider (openai/anthropic/gemini/ollama/deepseek/qwen/claude-code/codex)", "openai");
|
|
198
200
|
const provider = (["openai", "anthropic", "gemini", "ollama", "deepseek", "qwen", "claude-code", "codex"].includes(providerInput) ? providerInput : "openai");
|
|
199
201
|
values.LLM_PROVIDER = provider;
|
|
@@ -265,7 +267,7 @@ async function main() {
|
|
|
265
267
|
});
|
|
266
268
|
program
|
|
267
269
|
.name("apow")
|
|
268
|
-
.description("Mine AGENT tokens on Base L2 with
|
|
270
|
+
.description("Mine AGENT tokens on Base L2 with Agentic Proof of Work")
|
|
269
271
|
.version(version);
|
|
270
272
|
program
|
|
271
273
|
.command("setup")
|
package/dist/miner.js
CHANGED
|
@@ -44,6 +44,7 @@ const config_1 = require("./config");
|
|
|
44
44
|
const detect_1 = require("./detect");
|
|
45
45
|
const errors_1 = require("./errors");
|
|
46
46
|
const explorer_1 = require("./explorer");
|
|
47
|
+
const grinder_1 = require("./grinder");
|
|
47
48
|
const smhl_1 = require("./smhl");
|
|
48
49
|
const ui = __importStar(require("./ui"));
|
|
49
50
|
const wallet_1 = require("./wallet");
|
|
@@ -234,19 +235,26 @@ async function startMining(tokenId) {
|
|
|
234
235
|
}));
|
|
235
236
|
const [challengeNumber, target, rawSmhl] = miningChallenge;
|
|
236
237
|
const smhl = (0, smhl_1.normalizeSmhlChallenge)(rawSmhl);
|
|
237
|
-
// Solve SMHL
|
|
238
|
-
const smhlSpinner = ui.spinner("Solving SMHL challenge...");
|
|
238
|
+
// Solve SMHL algorithmically (sub-millisecond)
|
|
239
239
|
const smhlStart = process.hrtime();
|
|
240
|
-
const smhlSolution =
|
|
241
|
-
|
|
242
|
-
|
|
240
|
+
const smhlSolution = (0, smhl_1.solveSmhlAlgorithmic)(smhl);
|
|
241
|
+
const smhlIssues = (0, smhl_1.validateSmhlSolution)(smhlSolution, smhl);
|
|
242
|
+
if (smhlIssues.length > 0) {
|
|
243
|
+
throw new Error(`SMHL generation failed: ${smhlIssues.join(", ")}`);
|
|
244
|
+
}
|
|
243
245
|
const smhlElapsed = elapsedSeconds(smhlStart);
|
|
244
|
-
|
|
245
|
-
// Grind nonce with spinner
|
|
246
|
+
console.log(` ${ui.dim(`SMHL solved (${(smhlElapsed * 1000).toFixed(1)}ms)`)}`);
|
|
247
|
+
// Grind nonce with multi-threaded spinner
|
|
246
248
|
const nonceSpinner = ui.spinner("Grinding nonce...");
|
|
247
|
-
const grind = await
|
|
248
|
-
|
|
249
|
-
|
|
249
|
+
const grind = await (0, grinder_1.grindNonceParallel)({
|
|
250
|
+
challengeNumber,
|
|
251
|
+
target,
|
|
252
|
+
minerAddress: account.address,
|
|
253
|
+
threads: config_1.config.minerThreads,
|
|
254
|
+
onProgress: (attempts, hashrate) => {
|
|
255
|
+
const khs = (hashrate / 1000).toFixed(0);
|
|
256
|
+
nonceSpinner.update(`Grinding nonce... ${khs}k H/s (${attempts.toLocaleString()} attempts)`);
|
|
257
|
+
},
|
|
250
258
|
});
|
|
251
259
|
const khs = (grind.hashrate / 1000).toFixed(0);
|
|
252
260
|
nonceSpinner.stop(`Grinding nonce... done (${grind.elapsed.toFixed(1)}s, ${khs}k H/s)`);
|
package/dist/preflight.js
CHANGED
|
@@ -118,22 +118,24 @@ async function runPreflight(level) {
|
|
|
118
118
|
});
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
|
-
// Check 5: LLM key set
|
|
122
|
-
if (
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
121
|
+
// Check 5: LLM key set (only required for minting, not mining)
|
|
122
|
+
if (level === "wallet") {
|
|
123
|
+
if (config_1.config.llmProvider === "ollama") {
|
|
124
|
+
results.push({ label: `LLM provider: ollama (${config_1.config.ollamaUrl})`, passed: true });
|
|
125
|
+
}
|
|
126
|
+
else if (config_1.config.llmProvider === "claude-code" || config_1.config.llmProvider === "codex") {
|
|
127
|
+
results.push({ label: `LLM provider: ${config_1.config.llmProvider} (local CLI)`, passed: true });
|
|
128
|
+
}
|
|
129
|
+
else if (config_1.config.llmApiKey) {
|
|
130
|
+
results.push({ label: `LLM provider: ${config_1.config.llmProvider} (key set)`, passed: true });
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
results.push({
|
|
134
|
+
label: `LLM API key not set for ${config_1.config.llmProvider}`,
|
|
135
|
+
passed: false,
|
|
136
|
+
fix: "Set LLM_API_KEY (or OPENAI_API_KEY / ANTHROPIC_API_KEY / GEMINI_API_KEY) in .env, or run `apow setup`",
|
|
137
|
+
});
|
|
138
|
+
}
|
|
137
139
|
}
|
|
138
140
|
}
|
|
139
141
|
// Check 6: Contracts exist on-chain (bytecode check)
|
package/dist/smhl.js
CHANGED
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.normalizeSmhlChallenge = normalizeSmhlChallenge;
|
|
7
7
|
exports.buildSmhlPrompt = buildSmhlPrompt;
|
|
8
8
|
exports.validateSmhlSolution = validateSmhlSolution;
|
|
9
|
+
exports.solveSmhlAlgorithmic = solveSmhlAlgorithmic;
|
|
9
10
|
exports.solveSmhlChallenge = solveSmhlChallenge;
|
|
10
11
|
const node_child_process_1 = require("node:child_process");
|
|
11
12
|
const openai_1 = __importDefault(require("openai"));
|
|
@@ -329,6 +330,34 @@ async function requestProviderSolution(prompt) {
|
|
|
329
330
|
return requestOpenAiSolution(prompt);
|
|
330
331
|
}
|
|
331
332
|
}
|
|
333
|
+
/**
|
|
334
|
+
* Solve SMHL challenge algorithmically (no LLM needed).
|
|
335
|
+
* On-chain _verifySMHL only checks: totalLength ±5, charValue anywhere, wordCount ±2.
|
|
336
|
+
* Generates a valid solution in microseconds.
|
|
337
|
+
*/
|
|
338
|
+
function solveSmhlAlgorithmic(challenge) {
|
|
339
|
+
const requiredChar = String.fromCharCode(challenge.charValue);
|
|
340
|
+
const targetWords = challenge.wordCount;
|
|
341
|
+
const targetLen = challenge.totalLength;
|
|
342
|
+
const spaces = targetWords - 1;
|
|
343
|
+
const letterBudget = targetLen - spaces;
|
|
344
|
+
if (letterBudget <= 0 || targetWords <= 0) {
|
|
345
|
+
return requiredChar.repeat(targetLen);
|
|
346
|
+
}
|
|
347
|
+
const baseWordLen = Math.floor(letterBudget / targetWords);
|
|
348
|
+
const extraChars = letterBudget - baseWordLen * targetWords;
|
|
349
|
+
const words = [];
|
|
350
|
+
for (let i = 0; i < targetWords; i++) {
|
|
351
|
+
const len = Math.max(1, baseWordLen + (i < extraChars ? 1 : 0));
|
|
352
|
+
if (i === 0) {
|
|
353
|
+
words.push(requiredChar + "a".repeat(len - 1));
|
|
354
|
+
}
|
|
355
|
+
else {
|
|
356
|
+
words.push("a".repeat(len));
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
return words.join(" ");
|
|
360
|
+
}
|
|
332
361
|
async function solveSmhlChallenge(challenge, onAttempt) {
|
|
333
362
|
let feedback;
|
|
334
363
|
let lastIssues = "provider did not return a valid response";
|
package/package.json
CHANGED
package/skill.md
CHANGED
|
@@ -21,11 +21,11 @@ metadata:
|
|
|
21
21
|
|
|
22
22
|
> A self-contained guide for any AI agent to go from zero knowledge to actively mining AGENT tokens on Base.
|
|
23
23
|
|
|
24
|
-
**For AI agents:** You are the operator. You can do everything autonomously
|
|
24
|
+
**For AI agents:** You are the operator. You can do everything autonomously: generate wallets,
|
|
25
25
|
configure the miner, mint, and mine. The only step that requires your user is funding the wallet
|
|
26
26
|
with ETH on Base. Everything else you handle yourself.
|
|
27
27
|
|
|
28
|
-
> **IMPORTANT
|
|
28
|
+
> **IMPORTANT: You need a dedicated RPC endpoint.** The default public RPC (`https://mainnet.base.org`) has aggressive rate limits and **will cause minting and mining to fail**. Before starting, get a free Alchemy endpoint:
|
|
29
29
|
>
|
|
30
30
|
> 1. Go to [alchemy.com](https://www.alchemy.com/) and sign up (free, no credit card)
|
|
31
31
|
> 2. Click **Create new app** → Name: `apow-miner` → Chain: **Base** → Network: **Base Mainnet**
|
|
@@ -40,8 +40,8 @@ with ETH on Base. Everything else you handle yourself.
|
|
|
40
40
|
|
|
41
41
|
**Via npm (recommended):**
|
|
42
42
|
```bash
|
|
43
|
-
npx apow-cli setup # interactive wizard
|
|
44
|
-
npx apow-cli fund # fund wallet
|
|
43
|
+
npx apow-cli setup # interactive wizard: wallet, RPC, LLM config
|
|
44
|
+
npx apow-cli fund # fund wallet: bridge from Solana or show deposit address
|
|
45
45
|
npx apow-cli mint # mint a mining rig NFT
|
|
46
46
|
npx apow-cli mine # start mining (auto-detects your best rig)
|
|
47
47
|
```
|
|
@@ -65,15 +65,15 @@ npx apow-cli wallet new
|
|
|
65
65
|
# Captures address + private key from output (also saved to wallet-<address>.txt)
|
|
66
66
|
|
|
67
67
|
# 2. Write .env directly (no interactive prompts needed)
|
|
68
|
-
#
|
|
69
|
-
#
|
|
70
|
-
# IMPORTANT: The public Base RPC is unreliable
|
|
68
|
+
# LLM config is only needed for minting (one-time); mining uses algorithmic SMHL.
|
|
69
|
+
# IMPORTANT: Use an API-based provider for minting (openai/anthropic/gemini/deepseek/qwen).
|
|
70
|
+
# IMPORTANT: The public Base RPC is unreliable. Get a free Alchemy URL (see RPC Recommendations).
|
|
71
71
|
cat > .env << 'EOF'
|
|
72
72
|
PRIVATE_KEY=0x<from step 1>
|
|
73
73
|
RPC_URL=https://mainnet.base.org
|
|
74
|
-
LLM_PROVIDER=openai
|
|
75
|
-
LLM_MODEL=gpt-4o-mini
|
|
76
|
-
LLM_API_KEY=<your key>
|
|
74
|
+
LLM_PROVIDER=openai # Required for minting only
|
|
75
|
+
LLM_MODEL=gpt-4o-mini # Required for minting only
|
|
76
|
+
LLM_API_KEY=<your key> # Required for minting only
|
|
77
77
|
MINING_AGENT_ADDRESS=0xB7caD3ca5F2BD8aEC2Eb67d6E8D448099B3bC03D
|
|
78
78
|
AGENT_COIN_ADDRESS=0x12577CF0D8a07363224D6909c54C056A183e13b3
|
|
79
79
|
EOF
|
|
@@ -92,19 +92,22 @@ npx apow-cli mine
|
|
|
92
92
|
|
|
93
93
|
## 1. What is APoW?
|
|
94
94
|
|
|
95
|
-
Agent Proof-of-Work (APoW) is a mining protocol where AI agents
|
|
95
|
+
Agent Proof-of-Work (APoW) is a mining protocol on Base L2 where AI agents prove their identity once by minting an ERC-8004 Mining Rig NFT (requires LLM to solve an SMHL challenge), then compete on hash power to mine AGENT tokens. Mining requires owning a Miner NFT (ERC-721 with rarity-based hashpower) and no LLM is needed after minting. Rewards start at 3 AGENT per mine (scaled by hashpower) and decay by 10% every 500,000 total network mines, with a hard cap of 21,000,000 AGENT.
|
|
96
96
|
|
|
97
97
|
### SMHL Challenge Format
|
|
98
98
|
|
|
99
|
-
|
|
99
|
+
SMHL ("Show Me Human Language") serves two different roles in APoW:
|
|
100
|
+
|
|
101
|
+
**SMHL for Minting (identity verification):** When minting a Mining Rig, your LLM solves an SMHL challenge to prove agent capability. This is the "prove yourself" gate: your agent demonstrates it can solve constrained text generation. The LLM receives a prompt like: "Generate a sentence that is approximately N characters long, contains approximately W words, and includes the letter 'X'."
|
|
100
102
|
|
|
101
|
-
|
|
103
|
+
**SMHL for Mining (algorithmic):** During mining, SMHL solutions are generated algorithmically in microseconds, with no LLM needed. Your agent identity was already established when you minted your ERC-8004 Mining Rig. Mining is a hash power competition, not a language puzzle.
|
|
104
|
+
|
|
105
|
+
On-chain verification checks (both minting and mining):
|
|
102
106
|
1. **Length** (in bytes): within ±5 of the target
|
|
103
107
|
2. **Word count**: within ±2 of the target
|
|
104
108
|
3. **Character presence**: the specified letter appears at least once
|
|
105
|
-
4. **ASCII only** (client-side convention): all characters should be printable ASCII (bytes 32-126). Note: this is NOT enforced on-chain — the contract's `_verifySMHL` does not check for ASCII-only characters. The miner client validates this locally to improve reliability.
|
|
106
109
|
|
|
107
|
-
The miner client validates locally before submitting.
|
|
110
|
+
The miner client validates locally before submitting.
|
|
108
111
|
|
|
109
112
|
---
|
|
110
113
|
|
|
@@ -114,7 +117,7 @@ The miner client validates locally before submitting. If validation fails, it re
|
|
|
114
117
|
|---|---|
|
|
115
118
|
| **Node.js** | v18 or higher |
|
|
116
119
|
| **Base wallet** | A private key with ETH on Base (for gas + mint fee) |
|
|
117
|
-
| **LLM access** | API key (OpenAI, Anthropic, Gemini, DeepSeek, Qwen), local Ollama, or Claude Code / Codex CLI |
|
|
120
|
+
| **LLM access** | API key (OpenAI, Anthropic, Gemini, DeepSeek, Qwen), local Ollama, or Claude Code / Codex CLI (**required for minting only**) |
|
|
118
121
|
| **git** | Only if installing from source (not needed for npm) |
|
|
119
122
|
|
|
120
123
|
---
|
|
@@ -128,7 +131,7 @@ npx apow-cli setup
|
|
|
128
131
|
# Select "No" when asked if you have a wallet → generates one automatically
|
|
129
132
|
```
|
|
130
133
|
|
|
131
|
-
Or generate one directly (useful for agents
|
|
134
|
+
Or generate one directly (useful for agents, no prompts):
|
|
132
135
|
|
|
133
136
|
```bash
|
|
134
137
|
npx apow-cli wallet new
|
|
@@ -192,7 +195,7 @@ Phantom natively supports Base. Tell your user:
|
|
|
192
195
|
#### From an Exchange (Coinbase, Binance, etc.)
|
|
193
196
|
1. Buy ETH on Base (Coinbase supports Base withdrawals natively)
|
|
194
197
|
2. Withdraw to the mining wallet address
|
|
195
|
-
3. Select **Base** as the network
|
|
198
|
+
3. Select **Base** as the network. Do NOT send on Ethereum mainnet
|
|
196
199
|
|
|
197
200
|
#### From Ethereum Mainnet
|
|
198
201
|
Bridge ETH to Base via [bridge.base.org](https://bridge.base.org):
|
|
@@ -206,7 +209,7 @@ Send ETH directly to the mining wallet address on Base.
|
|
|
206
209
|
After funding, verify the balance:
|
|
207
210
|
```bash
|
|
208
211
|
npx apow-cli stats
|
|
209
|
-
# Shows wallet balance
|
|
212
|
+
# Shows wallet balance; must be ≥0.005 ETH to proceed
|
|
210
213
|
```
|
|
211
214
|
|
|
212
215
|
---
|
|
@@ -217,7 +220,7 @@ npx apow-cli stats
|
|
|
217
220
|
```bash
|
|
218
221
|
npx apow-cli setup
|
|
219
222
|
```
|
|
220
|
-
All `apow` commands work via `npx`
|
|
223
|
+
All `apow` commands work via `npx` with no global install required.
|
|
221
224
|
|
|
222
225
|
**Via source (for developers):**
|
|
223
226
|
```bash
|
|
@@ -242,7 +245,7 @@ PRIVATE_KEY=0xYOUR_PRIVATE_KEY_HERE
|
|
|
242
245
|
MINING_AGENT_ADDRESS=0xB7caD3ca5F2BD8aEC2Eb67d6E8D448099B3bC03D
|
|
243
246
|
AGENT_COIN_ADDRESS=0x12577CF0D8a07363224D6909c54C056A183e13b3
|
|
244
247
|
|
|
245
|
-
# === LLM Configuration ===
|
|
248
|
+
# === LLM Configuration (required for minting only; mining uses optimized solving) ===
|
|
246
249
|
|
|
247
250
|
# Provider: "openai" | "anthropic" | "ollama" | "gemini" | "deepseek" | "qwen" | "claude-code" | "codex"
|
|
248
251
|
LLM_PROVIDER=openai
|
|
@@ -255,7 +258,7 @@ LLM_MODEL=gpt-4o-mini
|
|
|
255
258
|
|
|
256
259
|
# === Network ===
|
|
257
260
|
|
|
258
|
-
# Base RPC endpoint
|
|
261
|
+
# Base RPC endpoint. The public default is unreliable for sustained mining.
|
|
259
262
|
# Strongly recommend a free Alchemy key: https://www.alchemy.com/ (no credit card)
|
|
260
263
|
RPC_URL=https://mainnet.base.org
|
|
261
264
|
|
|
@@ -267,20 +270,23 @@ CHAIN=base
|
|
|
267
270
|
|
|
268
271
|
| Variable | Required | Default | Description |
|
|
269
272
|
|---|---|---|---|
|
|
270
|
-
| `PRIVATE_KEY` | Yes |
|
|
271
|
-
| `MINING_AGENT_ADDRESS` | Yes |
|
|
272
|
-
| `AGENT_COIN_ADDRESS` | Yes |
|
|
273
|
-
| `LLM_PROVIDER` |
|
|
274
|
-
| `LLM_API_KEY` |
|
|
275
|
-
| `LLM_MODEL` |
|
|
276
|
-
| `
|
|
273
|
+
| `PRIVATE_KEY` | Yes | - | Wallet private key (0x + 64 hex chars) |
|
|
274
|
+
| `MINING_AGENT_ADDRESS` | Yes | - | Deployed MiningAgent contract address |
|
|
275
|
+
| `AGENT_COIN_ADDRESS` | Yes | - | Deployed AgentCoin contract address |
|
|
276
|
+
| `LLM_PROVIDER` | For minting | `openai` | LLM provider for minting: `openai`, `anthropic`, `ollama`, `gemini`, `deepseek`, `qwen`, `claude-code`, or `codex`. Not needed for mining. |
|
|
277
|
+
| `LLM_API_KEY` | For minting | - | API key for minting. Falls back to `OPENAI_API_KEY` / `ANTHROPIC_API_KEY` / `GEMINI_API_KEY` / `DEEPSEEK_API_KEY` / `DASHSCOPE_API_KEY` per provider. Not needed for `ollama`, `claude-code`, `codex`, or mining. |
|
|
278
|
+
| `LLM_MODEL` | For minting | `gpt-4o-mini` | Model identifier passed to the provider (minting only) |
|
|
279
|
+
| `MINER_THREADS` | No | All CPU cores | Number of threads for parallel nonce grinding |
|
|
280
|
+
| `RPC_URL` | **Strongly recommended** | `https://mainnet.base.org` | Base JSON-RPC endpoint. **The default public RPC is unreliable. Use Alchemy (free) or another dedicated provider.** |
|
|
277
281
|
| `CHAIN` | No | `base` | Network selector; auto-detects `baseSepolia` if RPC URL contains "sepolia" |
|
|
278
282
|
| `SOLANA_RPC_URL` | No | `https://api.mainnet-beta.solana.com` | Solana RPC endpoint (only for `apow fund --solana`) |
|
|
279
|
-
| `SQUID_INTEGRATOR_ID` | No |
|
|
283
|
+
| `SQUID_INTEGRATOR_ID` | No | - | Squid Router integrator ID for deposit address flow (free at [squidrouter.com](https://app.squidrouter.com/)) |
|
|
280
284
|
|
|
281
|
-
### LLM Provider Recommendations
|
|
285
|
+
### LLM Provider Recommendations (for Minting)
|
|
282
286
|
|
|
283
|
-
>
|
|
287
|
+
> An LLM is only needed for **minting** your Mining Rig NFT (one-time identity verification). Mining uses optimized algorithmic SMHL solving, with no LLM needed.
|
|
288
|
+
>
|
|
289
|
+
> **For AI agents:** Use an API-based provider (OpenAI, Anthropic, Gemini, DeepSeek, or Qwen) for minting. Session-based providers (`claude-code`, `codex`) spawn a CLI subprocess and are too slow to reliably complete the 20-second mint window.
|
|
284
290
|
|
|
285
291
|
| Provider | Model | Cost per call | Notes |
|
|
286
292
|
|---|---|---|---|
|
|
@@ -291,8 +297,8 @@ CHAIN=base
|
|
|
291
297
|
| DeepSeek | `deepseek-chat` | ~$0.001 | Fast, accessible in China |
|
|
292
298
|
| Qwen | `qwen-plus` | ~$0.002 | Alibaba Cloud, accessible in China |
|
|
293
299
|
| Ollama | `llama3.1` | Free (local) | Requires local GPU; variable accuracy |
|
|
294
|
-
| Claude Code | `default` | Subscription | **Not recommended for minting
|
|
295
|
-
| Codex | `default` | Subscription | **Not recommended for minting
|
|
300
|
+
| Claude Code | `default` | Subscription | **Not recommended for minting.** CLI startup too slow for 20s window |
|
|
301
|
+
| Codex | `default` | Subscription | **Not recommended for minting.** CLI startup too slow for 20s window |
|
|
296
302
|
|
|
297
303
|
### RPC Recommendations
|
|
298
304
|
|
|
@@ -349,7 +355,7 @@ The default `https://mainnet.base.org` is a public RPC with aggressive rate limi
|
|
|
349
355
|
|
|
350
356
|
## 7. Step 4: Mint a Mining Rig
|
|
351
357
|
|
|
352
|
-
**One rig per wallet.** The CLI enforces a one-rig-per-wallet rule. Only one rig can mine competitively per wallet (one mine per block globally), so extra rigs in the same wallet waste ETH. To scale, create additional wallets
|
|
358
|
+
**One rig per wallet.** The CLI enforces a one-rig-per-wallet rule. Only one rig can mine competitively per wallet (one mine per block globally), so extra rigs in the same wallet waste ETH. To scale, create additional wallets (see [Scaling with Multiple Wallets](#scaling-with-multiple-wallets) below).
|
|
353
359
|
|
|
354
360
|
```bash
|
|
355
361
|
npx apow-cli mint
|
|
@@ -361,9 +367,9 @@ npx apow-cli mint
|
|
|
361
367
|
3. The LLM generates a sentence matching the constraints (approximate length, approximate word count, must contain a specific letter).
|
|
362
368
|
4. The client calls `mint(solution)` with the mint fee attached. The contract verifies the SMHL solution on-chain.
|
|
363
369
|
5. On success, an ERC-721 Miner NFT is minted to your wallet with a randomly determined rarity and hashpower.
|
|
364
|
-
6. The mint fee is forwarded to the LPVault (used for AGENT/USDC liquidity
|
|
370
|
+
6. The mint fee is forwarded to the LPVault (used for AGENT/USDC liquidity: initial LP deployment at threshold, then ongoing `addLiquidity()` to deepen the position).
|
|
365
371
|
|
|
366
|
-
**Challenge expiry:** 20 seconds from `getChallenge` to `mint`. The LLM must solve quickly. Use an API-based provider (openai/anthropic/gemini/deepseek/qwen)
|
|
372
|
+
**Challenge expiry:** 20 seconds from `getChallenge` to `mint`. The LLM must solve quickly. Use an API-based provider (openai/anthropic/gemini/deepseek/qwen). Session-based providers (claude-code/codex) are too slow and will fail.
|
|
367
373
|
|
|
368
374
|
### Mint Price
|
|
369
375
|
|
|
@@ -395,21 +401,21 @@ npx apow-cli mine <tokenId> # or specify a rig by token ID
|
|
|
395
401
|
|
|
396
402
|
### What Each Mining Cycle Does
|
|
397
403
|
|
|
398
|
-
1. **Ownership check
|
|
399
|
-
2. **Supply check
|
|
400
|
-
3. **Fetch challenge
|
|
401
|
-
- `challengeNumber` (bytes32)
|
|
402
|
-
- `miningTarget` (uint256)
|
|
403
|
-
- `smhl
|
|
404
|
-
4. **Solve SMHL
|
|
405
|
-
5. **Grind nonce
|
|
406
|
-
6. **Submit proof
|
|
407
|
-
7. **Collect reward
|
|
408
|
-
8. **Wait for next block
|
|
404
|
+
1. **Ownership check:** verifies your wallet owns the specified token.
|
|
405
|
+
2. **Supply check:** confirms mineable supply is not exhausted.
|
|
406
|
+
3. **Fetch challenge:** reads `getMiningChallenge()` from the AgentCoin contract, which returns:
|
|
407
|
+
- `challengeNumber` (bytes32): the current PoW challenge hash
|
|
408
|
+
- `miningTarget` (uint256): the difficulty target
|
|
409
|
+
- `smhl`: the SMHL format challenge
|
|
410
|
+
4. **Solve SMHL:** generates a valid SMHL solution algorithmically (sub-millisecond, no LLM needed).
|
|
411
|
+
5. **Grind nonce:** multi-threaded brute-force search for a `nonce` where `keccak256(challengeNumber, minerAddress, nonce) < miningTarget`. Uses all CPU cores by default.
|
|
412
|
+
6. **Submit proof:** calls `mine(nonce, smhlSolution, tokenId)` on AgentCoin. The contract verifies both the hash and SMHL solution on-chain.
|
|
413
|
+
7. **Collect reward:** AGENT tokens are minted directly to your wallet.
|
|
414
|
+
8. **Wait for next block:** the protocol enforces one mine per block network-wide. The client waits for block advancement before the next cycle.
|
|
409
415
|
|
|
410
416
|
### Reward Economics
|
|
411
417
|
|
|
412
|
-
**One mine per block, network-wide.** The protocol allows exactly one successful `mine()` per Base block across the entire network
|
|
418
|
+
**One mine per block, network-wide.** The protocol allows exactly one successful `mine()` per Base block across the entire network, not per wallet. All miners compete for each block's reward. If two miners submit in the same block, only the first transaction to be included succeeds; the other reverts (and still costs gas).
|
|
413
419
|
|
|
414
420
|
| Parameter | Value |
|
|
415
421
|
|---|---|
|
|
@@ -424,18 +430,18 @@ npx apow-cli mine <tokenId> # or specify a rig by token ID
|
|
|
424
430
|
|
|
425
431
|
| Era | Total Network Mines | Reward per Mine |
|
|
426
432
|
|---|---|---|
|
|
427
|
-
| 0 | 0
|
|
428
|
-
| 1 | 500,000
|
|
429
|
-
| 2 | 1,000,000
|
|
430
|
-
| 3 | 1,500,000
|
|
433
|
+
| 0 | 0 to 499,999 | 3.00 AGENT |
|
|
434
|
+
| 1 | 500,000 to 999,999 | 2.70 AGENT |
|
|
435
|
+
| 2 | 1,000,000 to 1,499,999 | 2.43 AGENT |
|
|
436
|
+
| 3 | 1,500,000 to 1,999,999 | 2.187 AGENT |
|
|
431
437
|
|
|
432
438
|
A Mythic miner (5.00x) earns 15.00 AGENT per mine in Era 0.
|
|
433
439
|
|
|
434
440
|
### Cost Per Mine
|
|
435
441
|
|
|
436
442
|
- **Gas:** ~0.001 ETH per `mine()` transaction on Base
|
|
437
|
-
- **LLM:**
|
|
438
|
-
- **Total:** ~$0.
|
|
443
|
+
- **LLM:** $0 (mining uses algorithmic SMHL, no LLM calls)
|
|
444
|
+
- **Total:** ~$0.003 to $0.005 per mining cycle (gas only)
|
|
439
445
|
|
|
440
446
|
### Error Handling
|
|
441
447
|
|
|
@@ -473,15 +479,15 @@ npx apow-cli stats <tokenId> # stats for a specific rig
|
|
|
473
479
|
|
|
474
480
|
### Competitive Mining & Scaling
|
|
475
481
|
|
|
476
|
-
**How mining competition works:** The protocol enforces exactly ONE successful mine per block, network-wide. This is controlled by `lastMineBlockNumber
|
|
482
|
+
**How mining competition works:** The protocol enforces exactly ONE successful mine per block, network-wide. This is controlled by `lastMineBlockNumber`: once any miner's `mine()` transaction is confirmed in a block, all other `mine()` calls in that same block revert. This is the same winner-takes-all model as Bitcoin: every miner competes for each block's reward, and only one wins.
|
|
477
483
|
|
|
478
484
|
**One rig per wallet (CLI-enforced).** The CLI prevents minting more than one rig per wallet. Only one rig can mine competitively per wallet since only one `mine()` can succeed per block and all rigs in the same wallet share the same address. Extra rigs waste ETH on mint fees with no advantage.
|
|
479
485
|
|
|
480
486
|
### Scaling with Multiple Wallets
|
|
481
487
|
|
|
482
|
-
To increase your chances of winning each block, run separate wallets
|
|
488
|
+
To increase your chances of winning each block, run separate wallets, each with its own rig and its own miner process. More wallets = more independent chances to land the winning transaction in each block.
|
|
483
489
|
|
|
484
|
-
**The user only funds one wallet.** Use `apow wallet fund` to distribute ETH from the main wallet to sub-wallets. The default amount covers the current mint price + gas buffer
|
|
490
|
+
**The user only funds one wallet.** Use `apow wallet fund` to distribute ETH from the main wallet to sub-wallets. The default amount covers the current mint price + gas buffer, so no manual calculation is needed.
|
|
485
491
|
|
|
486
492
|
**Autonomous agent flow for multi-wallet mining:**
|
|
487
493
|
|
|
@@ -525,7 +531,7 @@ pm2 start ecosystem.config.cjs
|
|
|
525
531
|
pm2 logs
|
|
526
532
|
```
|
|
527
533
|
|
|
528
|
-
**Economics of multi-wallet mining:** Failed `mine()` calls still cost gas (~0.001 ETH). As more miners compete for each block, the probability of winning decreases while gas costs stay constant. This creates a natural economic equilibrium
|
|
534
|
+
**Economics of multi-wallet mining:** Failed `mine()` calls still cost gas (~0.001 ETH). As more miners compete for each block, the probability of winning decreases while gas costs stay constant. This creates a natural economic equilibrium: scaling is profitable only when the expected reward exceeds the gas cost of losing.
|
|
529
535
|
|
|
530
536
|
**RPC rate limits:** For 3+ concurrent miners, use a dedicated RPC endpoint (Alchemy, Infura, QuickNode) instead of the default `https://mainnet.base.org`.
|
|
531
537
|
|
|
@@ -550,12 +556,12 @@ Ollama runs on `http://127.0.0.1:11434` by default. The miner connects there aut
|
|
|
550
556
|
|
|
551
557
|
### Session Mining (Claude Code / Codex)
|
|
552
558
|
|
|
553
|
-
Mine using your existing Claude Code or Codex subscription
|
|
559
|
+
Mine using your existing Claude Code or Codex subscription (no API key required):
|
|
554
560
|
|
|
555
561
|
```bash
|
|
556
562
|
# In your .env
|
|
557
563
|
LLM_PROVIDER=claude-code
|
|
558
|
-
# No LLM_API_KEY needed
|
|
564
|
+
# No LLM_API_KEY needed; the miner shells out to your local CLI
|
|
559
565
|
```
|
|
560
566
|
|
|
561
567
|
Or with Codex:
|
|
@@ -581,13 +587,13 @@ Set `RPC_URL` in `.env` to any Base-compatible JSON-RPC endpoint. The `CHAIN` va
|
|
|
581
587
|
Each Miner NFT supports an on-chain agent wallet via the ERC-8004 standard. This creates a one-rig-one-agent identity model: an NFT owner can delegate mining operations to a separate hot wallet without transferring ownership of the rig.
|
|
582
588
|
|
|
583
589
|
**Functions:**
|
|
584
|
-
- `getAgentWallet(tokenId)
|
|
585
|
-
- `setAgentWallet(tokenId, newWallet, deadline, signature)
|
|
586
|
-
- `unsetAgentWallet(tokenId)
|
|
590
|
+
- `getAgentWallet(tokenId)`: returns the registered agent wallet address
|
|
591
|
+
- `setAgentWallet(tokenId, newWallet, deadline, signature)`: sets a new agent wallet (requires EIP-712 signature from the new wallet)
|
|
592
|
+
- `unsetAgentWallet(tokenId)`: removes the agent wallet
|
|
587
593
|
|
|
588
|
-
**What survives NFT transfer:** rarity, hashpower, total mine count, total AGENT earned, and the on-chain pixel art
|
|
594
|
+
**What survives NFT transfer:** rarity, hashpower, total mine count, total AGENT earned, and the on-chain pixel art. All permanent metadata is baked into the token.
|
|
589
595
|
|
|
590
|
-
**What gets cleared on transfer:** ONLY the agent wallet binding. This is a security measure
|
|
596
|
+
**What gets cleared on transfer:** ONLY the agent wallet binding. This is a security measure: when a rig is sold or transferred, the old owner's delegated access is automatically revoked so they can't continue mining with the new owner's rig.
|
|
591
597
|
|
|
592
598
|
**Trading:** Miner NFTs are fully tradeable (standard ERC-721). They are NOT soulbound. You can buy, sell, and transfer them on OpenSea or any NFT marketplace. The new owner simply sets their own agent wallet after receiving the rig.
|
|
593
599
|
|
|
@@ -636,13 +642,13 @@ Use the corresponding testnet contract addresses.
|
|
|
636
642
|
|
|
637
643
|
This section addresses the security model of apow-cli head-on. Every claim below is verified against the actual source code and can be independently confirmed by reading the repository.
|
|
638
644
|
|
|
639
|
-
### Private Key Generation
|
|
645
|
+
### Private Key Generation (Local Only)
|
|
640
646
|
|
|
641
|
-
Keys are generated via `viem/accounts` `generatePrivateKey()`, which uses Node.js `crypto.randomBytes(32)
|
|
647
|
+
Keys are generated via `viem/accounts` `generatePrivateKey()`, which uses Node.js `crypto.randomBytes(32)`, a cryptographically secure random number generator. Generation happens entirely in-process with no network calls involved. The private key is displayed once to the terminal and saved to `wallet-<address>.txt` with file permissions `0o600` (owner-read-write only).
|
|
642
648
|
|
|
643
649
|
### Private Key Is NEVER Transmitted
|
|
644
650
|
|
|
645
|
-
Exhaustive audit confirms: the private key string is never included in any `fetch()` call, HTTP request body, URL parameter, or header anywhere in the codebase. viem's signing architecture means the key is used locally for ECDSA signatures
|
|
651
|
+
Exhaustive audit confirms: the private key string is never included in any `fetch()` call, HTTP request body, URL parameter, or header anywhere in the codebase. viem's signing architecture means the key is used locally for ECDSA signatures, and only the signed transaction (not the key) is sent to the RPC node. This is the same architecture used by MetaMask, Rabby, and every other non-custodial wallet.
|
|
646
652
|
|
|
647
653
|
### Zero Telemetry
|
|
648
654
|
|
|
@@ -654,15 +660,15 @@ The CLI contains no analytics, no error reporting, and no phone-home behavior of
|
|
|
654
660
|
|
|
655
661
|
The CLI makes only these network calls:
|
|
656
662
|
|
|
657
|
-
1. **Blockchain RPC** (to user-configured RPC URL, default: `mainnet.base.org`)
|
|
658
|
-
2. **LLM API** (to user-configured provider)
|
|
663
|
+
1. **Blockchain RPC** (to user-configured RPC URL, default: `mainnet.base.org`): standard `eth_call`, `eth_sendRawTransaction`, etc.
|
|
664
|
+
2. **LLM API** (to user-configured provider): sends only word-puzzle prompts for SMHL solving, never wallet data
|
|
659
665
|
3. **Bridge APIs** (only when using `apow fund --solana`):
|
|
660
|
-
- **CoinGecko** (`api.coingecko.com`)
|
|
661
|
-
- **deBridge DLN** (`dln.debridge.finance`)
|
|
662
|
-
- **Squid Router** (`v2.api.squidrouter.com`)
|
|
663
|
-
- **Solana RPC** (`api.mainnet-beta.solana.com` or custom)
|
|
666
|
+
- **CoinGecko** (`api.coingecko.com`): SOL/ETH price quotes
|
|
667
|
+
- **deBridge DLN** (`dln.debridge.finance`): bridge order creation and status (direct signing flow)
|
|
668
|
+
- **Squid Router** (`v2.api.squidrouter.com`): deposit address generation (deposit address flow)
|
|
669
|
+
- **Solana RPC** (`api.mainnet-beta.solana.com` or custom): balance checks and tx submission
|
|
664
670
|
|
|
665
|
-
No private keys are transmitted to bridge providers. deBridge returns a serialized Solana transaction that is signed locally. Squid generates a deposit address
|
|
671
|
+
No private keys are transmitted to bridge providers. deBridge returns a serialized Solana transaction that is signed locally. Squid generates a deposit address, and the user sends SOL themselves.
|
|
666
672
|
|
|
667
673
|
### LLM Calls Are Data-Isolated
|
|
668
674
|
|
|
@@ -672,13 +678,13 @@ The SMHL solver sends only generic word-generation prompts to the LLM (e.g., "Wr
|
|
|
672
678
|
|
|
673
679
|
- Full source code: [github.com/Agentoshi/apow-cli](https://github.com/Agentoshi/apow-cli)
|
|
674
680
|
- MIT licensed
|
|
675
|
-
- Every line is auditable
|
|
681
|
+
- Every line is auditable. There are no obfuscated modules, no binary blobs, no minified dependencies performing network calls
|
|
676
682
|
- Smart contracts are separately auditable: [github.com/Agentoshi/apow-core](https://github.com/Agentoshi/apow-core)
|
|
677
683
|
|
|
678
684
|
### npm Package Integrity
|
|
679
685
|
|
|
680
686
|
- Published as `apow-cli` on npm
|
|
681
|
-
- Package contents match the GitHub source
|
|
687
|
+
- Package contents match the GitHub source (verify with `npm pack --dry-run` or compare against the repo)
|
|
682
688
|
- No `postinstall` scripts that execute arbitrary code
|
|
683
689
|
- The `package.json` `scripts` section contains only standard build/dev commands
|
|
684
690
|
|
|
@@ -703,7 +709,7 @@ Every statement above can be independently verified:
|
|
|
703
709
|
# Clone the source
|
|
704
710
|
git clone https://github.com/Agentoshi/apow-cli && cd apow-cli
|
|
705
711
|
|
|
706
|
-
# Search for any outbound network calls
|
|
712
|
+
# Search for any outbound network calls (you'll find only RPC and LLM calls)
|
|
707
713
|
grep -r "fetch\|axios\|http\|request" src/
|
|
708
714
|
|
|
709
715
|
# Confirm private key is never in any network payload
|