clawminer-skill 1.0.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/.env.example +19 -0
- package/LICENSE +21 -0
- package/README.md +60 -0
- package/SKILL.md +89 -0
- package/bin/cli.js +172 -0
- package/package.json +35 -0
- package/scripts/mine.js +435 -0
package/.env.example
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# ================================================
|
|
2
|
+
# ClawMiner Skill - Environment Configuration
|
|
3
|
+
# ================================================
|
|
4
|
+
# Copy this file to .env and fill in your values:
|
|
5
|
+
# cp .env.example .env
|
|
6
|
+
# ================================================
|
|
7
|
+
|
|
8
|
+
# [REQUIRED] Your wallet private key (with 0x prefix)
|
|
9
|
+
# ⚠️ Use a dedicated mining wallet, NOT your main wallet!
|
|
10
|
+
PRIVATE_KEY=0xYOUR_PRIVATE_KEY_HERE
|
|
11
|
+
|
|
12
|
+
# [OPTIONAL] RPC URL (defaults to BSC Testnet public node)
|
|
13
|
+
RPC_URL=https://bsc-testnet.publicnode.com
|
|
14
|
+
|
|
15
|
+
# [OPTIONAL] Contract address (defaults to BSC Testnet deployment)
|
|
16
|
+
CONTRACT_ADDRESS=0xCe9eAa062Ca1F6a8817f229921Ec79ac20705c38
|
|
17
|
+
|
|
18
|
+
# [OPTIONAL] Network: testnet or mainnet
|
|
19
|
+
NETWORK=testnet
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ClawMiner-Project
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# 🦞 ClawMiner Skill
|
|
2
|
+
|
|
3
|
+
> Automated PoW mining for **ClawMiner ($CLAWMINER)** on BSC.
|
|
4
|
+
|
|
5
|
+
ClawMiner is a fully decentralized, Bitcoin-inspired PoW token on Binance Smart Chain. No admin, no pre-mine, non-upgradeable. Mine it with pure computation.
|
|
6
|
+
|
|
7
|
+
## Quick Start
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# Initialize (install + configure wallet)
|
|
11
|
+
npx clawminer-skill init
|
|
12
|
+
|
|
13
|
+
# Start continuous mining
|
|
14
|
+
npx clawminer-skill loop
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Commands
|
|
18
|
+
|
|
19
|
+
| Command | Description |
|
|
20
|
+
|---------|-------------|
|
|
21
|
+
| `npx clawminer-skill init` | 初始化(安装依赖 + 配置钱包) |
|
|
22
|
+
| `npx clawminer-skill mine` | 单次挖矿 |
|
|
23
|
+
| `npx clawminer-skill loop` | 连续挖矿 24/7(推荐) |
|
|
24
|
+
| `npx clawminer-skill status` | 查看挖矿状态 |
|
|
25
|
+
| `npx clawminer-skill dry-run` | 模拟挖矿(不提交交易) |
|
|
26
|
+
|
|
27
|
+
## Tokenomics
|
|
28
|
+
|
|
29
|
+
| Parameter | Value |
|
|
30
|
+
|-----------|-------|
|
|
31
|
+
| Total Supply | 21,000,000 CLAWMINER |
|
|
32
|
+
| Initial Reward | 100 CLAWMINER / mine |
|
|
33
|
+
| Halving Cycle | Every 2,100,000 tokens |
|
|
34
|
+
| Max Halvings | 8 |
|
|
35
|
+
| Cooldown | 5 minutes |
|
|
36
|
+
| Difficulty | Starts at 1 byte, increases every 2,000 mines |
|
|
37
|
+
|
|
38
|
+
## Security
|
|
39
|
+
|
|
40
|
+
- ⚠️ **Use a dedicated mining wallet** — Never use your main wallet
|
|
41
|
+
- 🔒 **Private key stays local** — Stored in `.env`, never committed to git
|
|
42
|
+
- 🛡️ **Contract is immutable** — No admin functions, cannot be modified
|
|
43
|
+
|
|
44
|
+
## Network
|
|
45
|
+
|
|
46
|
+
Configure via `npx clawminer-skill init`:
|
|
47
|
+
|
|
48
|
+
| Network | Status |
|
|
49
|
+
|---------|--------|
|
|
50
|
+
| BSC Testnet | ✅ Available |
|
|
51
|
+
| BSC Mainnet | ⏳ Coming soon |
|
|
52
|
+
|
|
53
|
+
## Links
|
|
54
|
+
|
|
55
|
+
- 🌐 Website: [clawminer.xyz](https://clawminer.xyz) *(coming soon)*
|
|
56
|
+
- 📄 Contract (Testnet): [`0xCe9eAa062Ca1F6a8817f229921Ec79ac20705c38`](https://testnet.bscscan.com/address/0xCe9eAa062Ca1F6a8817f229921Ec79ac20705c38)
|
|
57
|
+
|
|
58
|
+
## License
|
|
59
|
+
|
|
60
|
+
MIT
|
package/SKILL.md
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: clawminer
|
|
3
|
+
description: PoW mining skill for ClawMiner ($CLAWMINER) on BSC — auto PoW, auto submit, auto retry
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# ClawMiner Mining Skill
|
|
7
|
+
|
|
8
|
+
You are operating the ClawMiner PoW mining skill. This skill allows the user to mine $CLAWMINER tokens on BNB Smart Chain (BSC) through Proof of Work computation.
|
|
9
|
+
|
|
10
|
+
## Available Commands
|
|
11
|
+
|
|
12
|
+
| Command | What it does |
|
|
13
|
+
|---------|-------------|
|
|
14
|
+
| `npx clawminer-skill init` | 初始化:安装依赖 + 交互式配置钱包 |
|
|
15
|
+
| `npx clawminer-skill mine` | 单次挖矿 |
|
|
16
|
+
| `npx clawminer-skill loop` | 连续挖矿 24/7(推荐)|
|
|
17
|
+
| `npx clawminer-skill status` | 查看挖矿状态 |
|
|
18
|
+
| `npx clawminer-skill dry-run` | 模拟挖矿(不提交交易)|
|
|
19
|
+
|
|
20
|
+
## Workflow
|
|
21
|
+
|
|
22
|
+
### Step 1: Install & Initialize
|
|
23
|
+
|
|
24
|
+
Run the init command. It will install dependencies and guide wallet configuration interactively:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npx clawminer-skill init
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
The init process will:
|
|
31
|
+
1. Install npm dependencies (ethers.js, dotenv)
|
|
32
|
+
2. Display a **security warning** about using a dedicated mining wallet
|
|
33
|
+
3. Ask for the user's **private key** (must be a dedicated mining wallet, NOT their main wallet)
|
|
34
|
+
4. Ask which network (testnet/mainnet)
|
|
35
|
+
5. Create `.env` configuration file
|
|
36
|
+
|
|
37
|
+
### Step 2: Start Mining
|
|
38
|
+
|
|
39
|
+
After init is complete, start mining:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# Recommended: continuous mining loop
|
|
43
|
+
npx clawminer-skill loop
|
|
44
|
+
|
|
45
|
+
# Or single mine attempt
|
|
46
|
+
npx clawminer-skill mine
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Step 3: Monitor
|
|
50
|
+
|
|
51
|
+
Check mining status anytime:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
npx clawminer-skill status
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## ⚠️ Critical Security Rules
|
|
58
|
+
|
|
59
|
+
When the user asks to configure or initialize this skill, you MUST:
|
|
60
|
+
|
|
61
|
+
1. **WARN** them to use a **dedicated mining wallet**, NOT their main wallet
|
|
62
|
+
2. **EXPLAIN** that the mining wallet only needs a small amount of BNB for gas fees (~0.001 BNB per mine)
|
|
63
|
+
3. **SUGGEST** creating a new account in MetaMask specifically for mining
|
|
64
|
+
4. **NEVER** store or log the private key anywhere other than the local `.env` file
|
|
65
|
+
5. **NEVER** transmit the private key over any network or API
|
|
66
|
+
|
|
67
|
+
## Mining Mechanics
|
|
68
|
+
|
|
69
|
+
- Each mine requires computing a PoW proof (hash with N leading zeros)
|
|
70
|
+
- Current difficulty: starts at 1 byte (2 hex chars), increases every 2,000 mines
|
|
71
|
+
- Reward: starts at 100 CLAWMINER per mine, halves every 2,100,000 tokens
|
|
72
|
+
- Cooldown: 5 minutes between mines
|
|
73
|
+
- Gas: ~0.001 BNB per mine transaction on BSC
|
|
74
|
+
|
|
75
|
+
## Troubleshooting
|
|
76
|
+
|
|
77
|
+
| Issue | Solution |
|
|
78
|
+
|-------|----------|
|
|
79
|
+
| "Cooldown active" | Normal. Wait 5 minutes between mines. Loop mode handles this automatically. |
|
|
80
|
+
| "Proof already used" | The script will auto-retry with a new proof. |
|
|
81
|
+
| Gas estimation failed | Network congestion. The script uses a default gas limit as fallback. |
|
|
82
|
+
| "PRIVATE_KEY not set" | Run `npx clawminer-skill init` first. |
|
|
83
|
+
|
|
84
|
+
## Contract Info
|
|
85
|
+
|
|
86
|
+
- Token: Claw Miner ($CLAWMINER)
|
|
87
|
+
- Total Supply: 21,000,000
|
|
88
|
+
- Network: BSC Testnet (Chain ID: 97) / BSC Mainnet (Chain ID: 56)
|
|
89
|
+
- Testnet Contract: `0xCe9eAa062Ca1F6a8817f229921Ec79ac20705c38`
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ClawMiner Skill CLI
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* npx clawminer-skill init # 初始化 Skill(安装依赖 + 引导配置)
|
|
8
|
+
* npx clawminer-skill mine # 单次挖矿
|
|
9
|
+
* npx clawminer-skill loop # 连续挖矿(自动冷却 + 重试)
|
|
10
|
+
* npx clawminer-skill status # 查看挖矿状态
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const { execSync, spawn } = require('child_process');
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const path = require('path');
|
|
16
|
+
const readline = require('readline');
|
|
17
|
+
|
|
18
|
+
const SKILL_DIR = path.resolve(__dirname, '..');
|
|
19
|
+
const ENV_FILE = path.join(SKILL_DIR, '.env');
|
|
20
|
+
const ENV_EXAMPLE = path.join(SKILL_DIR, '.env.example');
|
|
21
|
+
|
|
22
|
+
// ========== Helpers ==========
|
|
23
|
+
function ask(question) {
|
|
24
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
25
|
+
return new Promise(resolve => {
|
|
26
|
+
rl.question(question, answer => {
|
|
27
|
+
rl.close();
|
|
28
|
+
resolve(answer.trim());
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function log(emoji, msg) {
|
|
34
|
+
console.log(`${emoji} ${msg}`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// ========== Commands ==========
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* init — 初始化 Skill,引导用户配置钱包
|
|
41
|
+
*/
|
|
42
|
+
async function init() {
|
|
43
|
+
console.log('\n🦞 ClawMiner Skill 初始化\n');
|
|
44
|
+
|
|
45
|
+
// 1. Check if already initialized
|
|
46
|
+
if (fs.existsSync(ENV_FILE)) {
|
|
47
|
+
log('✅', '.env 文件已存在,Skill 已配置。');
|
|
48
|
+
log('💡', '如需重新配置,请删除 .env 文件后重试。');
|
|
49
|
+
console.log('');
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// 2. Install dependencies if needed
|
|
54
|
+
const nodeModules = path.join(SKILL_DIR, 'node_modules');
|
|
55
|
+
if (!fs.existsSync(nodeModules)) {
|
|
56
|
+
log('📦', '正在安装依赖...');
|
|
57
|
+
execSync('npm install', { cwd: SKILL_DIR, stdio: 'inherit' });
|
|
58
|
+
console.log('');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// 3. Wallet security warning
|
|
62
|
+
console.log(' ┌─────────────────────────────────────────────────┐');
|
|
63
|
+
console.log(' │ ⚠️ 安全提醒 │');
|
|
64
|
+
console.log(' │ │');
|
|
65
|
+
console.log(' │ 请使用【专用挖矿钱包】,不要使用你的主钱包! │');
|
|
66
|
+
console.log(' │ │');
|
|
67
|
+
console.log(' │ 挖矿钱包只需存放少量 BNB 作为 Gas 费。 │');
|
|
68
|
+
console.log(' │ 如果还没有挖矿钱包,请在 MetaMask 中新建一个。 │');
|
|
69
|
+
console.log(' │ │');
|
|
70
|
+
console.log(' │ ❌ 主钱包(存有大额资产) │');
|
|
71
|
+
console.log(' │ ✅ 专用挖矿钱包(仅存少量 BNB) │');
|
|
72
|
+
console.log(' └─────────────────────────────────────────────────┘\n');
|
|
73
|
+
|
|
74
|
+
// 4. Get private key
|
|
75
|
+
const privateKey = await ask(' 🔑 请输入你的【挖矿钱包】私钥(0x 开头):\n > ');
|
|
76
|
+
|
|
77
|
+
if (!privateKey || !privateKey.startsWith('0x')) {
|
|
78
|
+
log('❌', '私钥格式不正确,请确保以 0x 开头。');
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// 5. Ask for network
|
|
83
|
+
const network = await ask('\n 🌐 选择网络 [testnet/mainnet](默认 testnet):\n > ');
|
|
84
|
+
const selectedNetwork = (network === 'mainnet') ? 'mainnet' : 'testnet';
|
|
85
|
+
|
|
86
|
+
// 6. Set RPC and contract based on network
|
|
87
|
+
let rpcUrl, contractAddress;
|
|
88
|
+
if (selectedNetwork === 'mainnet') {
|
|
89
|
+
rpcUrl = 'https://bsc.publicnode.com';
|
|
90
|
+
contractAddress = await ask('\n 📄 请输入主网合约地址:\n > ');
|
|
91
|
+
} else {
|
|
92
|
+
rpcUrl = 'https://bsc-testnet.publicnode.com';
|
|
93
|
+
contractAddress = '0xCe9eAa062Ca1F6a8817f229921Ec79ac20705c38';
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// 7. Write .env
|
|
97
|
+
const envContent = `# ClawMiner Skill Configuration
|
|
98
|
+
# ⚠️ 此文件包含私钥,请勿分享或上传!
|
|
99
|
+
|
|
100
|
+
PRIVATE_KEY=${privateKey}
|
|
101
|
+
RPC_URL=${rpcUrl}
|
|
102
|
+
CONTRACT_ADDRESS=${contractAddress}
|
|
103
|
+
NETWORK=${selectedNetwork}
|
|
104
|
+
`;
|
|
105
|
+
|
|
106
|
+
fs.writeFileSync(ENV_FILE, envContent);
|
|
107
|
+
log('✅', '配置完成!.env 文件已创建。\n');
|
|
108
|
+
|
|
109
|
+
// 8. Show next steps
|
|
110
|
+
console.log(' 📋 接下来你可以:');
|
|
111
|
+
console.log(' ├─ npx clawminer-skill mine 单次挖矿');
|
|
112
|
+
console.log(' ├─ npx clawminer-skill loop 连续挖矿(推荐)');
|
|
113
|
+
console.log(' └─ npx clawminer-skill status 查看状态\n');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Run mining script with given args
|
|
118
|
+
*/
|
|
119
|
+
function runMine(args) {
|
|
120
|
+
const mineScript = path.join(SKILL_DIR, 'scripts', 'mine.js');
|
|
121
|
+
|
|
122
|
+
if (!fs.existsSync(ENV_FILE)) {
|
|
123
|
+
log('❌', '尚未配置!请先运行:npx clawminer-skill init');
|
|
124
|
+
process.exit(1);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const child = spawn('node', [mineScript, ...args], {
|
|
128
|
+
cwd: SKILL_DIR,
|
|
129
|
+
stdio: 'inherit',
|
|
130
|
+
env: { ...process.env }
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
child.on('close', code => process.exit(code));
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// ========== CLI Router ==========
|
|
137
|
+
const command = process.argv[2];
|
|
138
|
+
|
|
139
|
+
switch (command) {
|
|
140
|
+
case 'init':
|
|
141
|
+
init().catch(err => {
|
|
142
|
+
console.error('❌ 初始化错误:', err.message);
|
|
143
|
+
process.exit(1);
|
|
144
|
+
});
|
|
145
|
+
break;
|
|
146
|
+
case 'mine':
|
|
147
|
+
runMine([]);
|
|
148
|
+
break;
|
|
149
|
+
case 'loop':
|
|
150
|
+
runMine(['--loop']);
|
|
151
|
+
break;
|
|
152
|
+
case 'status':
|
|
153
|
+
runMine(['--status']);
|
|
154
|
+
break;
|
|
155
|
+
case 'dry-run':
|
|
156
|
+
runMine(['--dry-run']);
|
|
157
|
+
break;
|
|
158
|
+
default:
|
|
159
|
+
console.log(`
|
|
160
|
+
🦞 ClawMiner Skill v1.0.0
|
|
161
|
+
|
|
162
|
+
命令:
|
|
163
|
+
npx clawminer-skill init 初始化配置(首次使用)
|
|
164
|
+
npx clawminer-skill mine 单次挖矿
|
|
165
|
+
npx clawminer-skill loop 连续挖矿 24/7(推荐)
|
|
166
|
+
npx clawminer-skill status 查看挖矿状态
|
|
167
|
+
npx clawminer-skill dry-run 模拟挖矿(不提交交易)
|
|
168
|
+
|
|
169
|
+
GitHub: https://github.com/ClawMiner-Project/clawminer-skill
|
|
170
|
+
`);
|
|
171
|
+
break;
|
|
172
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "clawminer-skill",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "ClawMiner PoW Mining Skill for OpenClaw Agent",
|
|
5
|
+
"main": "scripts/mine.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"clawminer-skill": "bin/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin/",
|
|
11
|
+
"scripts/",
|
|
12
|
+
"SKILL.md",
|
|
13
|
+
"README.md",
|
|
14
|
+
".env.example"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"mine": "node scripts/mine.js",
|
|
18
|
+
"status": "node scripts/mine.js --status",
|
|
19
|
+
"loop": "node scripts/mine.js --loop"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"clawminer",
|
|
23
|
+
"pow",
|
|
24
|
+
"mining",
|
|
25
|
+
"bsc",
|
|
26
|
+
"web3",
|
|
27
|
+
"openclaw"
|
|
28
|
+
],
|
|
29
|
+
"author": "ClawMiner-Project",
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"ethers": "^6.13.0",
|
|
33
|
+
"dotenv": "^16.4.0"
|
|
34
|
+
}
|
|
35
|
+
}
|
package/scripts/mine.js
ADDED
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ClawMiner Skill - PoW Mining Script
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* node scripts/mine.js # Mine once
|
|
6
|
+
* node scripts/mine.js --status # Show status only
|
|
7
|
+
* node scripts/mine.js --loop # Continuous mining loop
|
|
8
|
+
* node scripts/mine.js --dry-run # PoW only, no transaction
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
require('dotenv').config();
|
|
12
|
+
const { ethers } = require('ethers');
|
|
13
|
+
|
|
14
|
+
// ========== Configuration ==========
|
|
15
|
+
const CONFIG = {
|
|
16
|
+
// From environment or defaults
|
|
17
|
+
PRIVATE_KEY: process.env.PRIVATE_KEY,
|
|
18
|
+
RPC_URL: process.env.RPC_URL || 'https://bsc-testnet.publicnode.com',
|
|
19
|
+
CONTRACT_ADDRESS: process.env.CONTRACT_ADDRESS || '0xCe9eAa062Ca1F6a8817f229921Ec79ac20705c38',
|
|
20
|
+
NETWORK: process.env.NETWORK || 'testnet',
|
|
21
|
+
|
|
22
|
+
// Mining parameters
|
|
23
|
+
MAX_ATTEMPTS: 500000,
|
|
24
|
+
MAX_RETRY: 5,
|
|
25
|
+
RETRY_DELAY_MS: 2000,
|
|
26
|
+
GAS_LIMIT: 300000,
|
|
27
|
+
GAS_BUFFER_PERCENT: 20,
|
|
28
|
+
COOLDOWN_SECONDS: 300, // 5 minutes
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// ========== ABI ==========
|
|
32
|
+
const ABI = [
|
|
33
|
+
"function currentDifficulty() view returns (uint256)",
|
|
34
|
+
"function cooldownRemaining(address) view returns (uint256)",
|
|
35
|
+
"function mine(bytes32,uint256,uint256)",
|
|
36
|
+
"function balanceOf(address) view returns (uint256)",
|
|
37
|
+
"function getMiningInfo(address) view returns (uint256,uint256,uint256,uint256,uint256,uint256)",
|
|
38
|
+
"function currentBlockReward() view returns (uint256)",
|
|
39
|
+
"function totalMined() view returns (uint256)",
|
|
40
|
+
"function miningProgress() view returns (uint256)",
|
|
41
|
+
"function currentPhase() view returns (uint256)",
|
|
42
|
+
"function remainingSupply() view returns (uint256)"
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
// ========== Utility Functions ==========
|
|
46
|
+
function log(emoji, message) {
|
|
47
|
+
const timestamp = new Date().toLocaleTimeString();
|
|
48
|
+
console.log(`[${timestamp}] ${emoji} ${message}`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function formatTime(seconds) {
|
|
52
|
+
if (seconds < 60) return `${seconds}秒`;
|
|
53
|
+
const mins = Math.floor(seconds / 60);
|
|
54
|
+
const secs = seconds % 60;
|
|
55
|
+
return `${mins}分${secs > 0 ? secs + '秒' : ''}`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function formatNumber(n) {
|
|
59
|
+
return Number(n).toLocaleString();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ========== ClawMiner Agent ==========
|
|
63
|
+
class ClawMinerAgent {
|
|
64
|
+
constructor() {
|
|
65
|
+
if (!CONFIG.PRIVATE_KEY) {
|
|
66
|
+
console.error('❌ 错误:未设置 PRIVATE_KEY!');
|
|
67
|
+
console.error(' 请复制 .env.example 为 .env 并填入你的私钥:');
|
|
68
|
+
console.error(' cp .env.example .env');
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
this.provider = new ethers.JsonRpcProvider(CONFIG.RPC_URL);
|
|
73
|
+
this.wallet = new ethers.Wallet(CONFIG.PRIVATE_KEY, this.provider);
|
|
74
|
+
this.address = this.wallet.address;
|
|
75
|
+
this.contract = new ethers.Contract(CONFIG.CONTRACT_ADDRESS, ABI, this.wallet);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Get full mining status
|
|
80
|
+
*/
|
|
81
|
+
async getStatus() {
|
|
82
|
+
try {
|
|
83
|
+
const [info, progress, cooldown, balance] = await Promise.all([
|
|
84
|
+
this.contract.getMiningInfo(this.address),
|
|
85
|
+
this.contract.miningProgress(),
|
|
86
|
+
this.contract.cooldownRemaining(this.address),
|
|
87
|
+
this.contract.balanceOf(this.address)
|
|
88
|
+
]);
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
totalMined: ethers.formatEther(info[0]),
|
|
92
|
+
remaining: ethers.formatEther(info[1]),
|
|
93
|
+
difficulty: Number(info[2]),
|
|
94
|
+
reward: ethers.formatEther(info[3]),
|
|
95
|
+
cooldownEnd: Number(info[4]),
|
|
96
|
+
nextHalving: ethers.formatEther(info[5]),
|
|
97
|
+
progress: (Number(progress) / 100).toFixed(2) + '%',
|
|
98
|
+
cooldownRemaining: Number(cooldown),
|
|
99
|
+
balance: ethers.formatEther(balance)
|
|
100
|
+
};
|
|
101
|
+
} catch (error) {
|
|
102
|
+
throw new Error(`Failed to get status: ${error.message}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Display mining status
|
|
108
|
+
*/
|
|
109
|
+
async showStatus() {
|
|
110
|
+
log('📊', '正在获取挖矿状态...\n');
|
|
111
|
+
|
|
112
|
+
const status = await this.getStatus();
|
|
113
|
+
const explorerBase = CONFIG.NETWORK === 'mainnet'
|
|
114
|
+
? 'https://bscscan.com'
|
|
115
|
+
: 'https://testnet.bscscan.com';
|
|
116
|
+
|
|
117
|
+
console.log(' ┌─────────────────────────────────────────┐');
|
|
118
|
+
console.log(' │ 🦞 ClawMiner 挖矿状态 │');
|
|
119
|
+
console.log(' ├─────────────────────────────────────────┤');
|
|
120
|
+
console.log(` │ 钱包: ${this.address.substring(0, 6)}...${this.address.substring(38)}`);
|
|
121
|
+
console.log(` │ 网络: BSC ${CONFIG.NETWORK}`);
|
|
122
|
+
console.log(` │ 余额: ${formatNumber(status.balance)} CLAWMINER`);
|
|
123
|
+
console.log(' ├─────────────────────────────────────────┤');
|
|
124
|
+
console.log(` │ 难度: ${status.difficulty} 字节`);
|
|
125
|
+
console.log(` │ 奖励: ${status.reward} CLAWMINER / 次`);
|
|
126
|
+
console.log(` │ 进度: ${status.progress}`);
|
|
127
|
+
console.log(` │ 剩余可挖: ${formatNumber(status.remaining)} CLAWMINER`);
|
|
128
|
+
console.log(' ├─────────────────────────────────────────┤');
|
|
129
|
+
|
|
130
|
+
if (status.cooldownRemaining > 0) {
|
|
131
|
+
console.log(` │ 冷却: ⏱️ 还需 ${formatTime(status.cooldownRemaining)}`);
|
|
132
|
+
} else {
|
|
133
|
+
console.log(` │ 冷却: ✅ 可以挖矿!`);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
console.log(' └─────────────────────────────────────────┘');
|
|
137
|
+
console.log(`\n 🔍 ${explorerBase}/address/${this.address}\n`);
|
|
138
|
+
|
|
139
|
+
return status;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Check if mining is possible
|
|
144
|
+
*/
|
|
145
|
+
async canMine() {
|
|
146
|
+
const cooldown = await this.contract.cooldownRemaining(this.address);
|
|
147
|
+
return {
|
|
148
|
+
canMine: cooldown === 0n,
|
|
149
|
+
remaining: Number(cooldown)
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Find a valid PoW proof
|
|
155
|
+
*/
|
|
156
|
+
async findProof(blockNumber, timestamp, difficulty) {
|
|
157
|
+
const targetHexChars = difficulty * 2;
|
|
158
|
+
const startTime = Date.now();
|
|
159
|
+
|
|
160
|
+
let nonce = 0;
|
|
161
|
+
|
|
162
|
+
for (let i = 0; i < CONFIG.MAX_ATTEMPTS; i++) {
|
|
163
|
+
const hash = ethers.solidityPackedKeccak256(
|
|
164
|
+
['uint256', 'uint256', 'address', 'uint256'],
|
|
165
|
+
[blockNumber, timestamp, this.address, nonce]
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
const prefix = hash.substring(2, 2 + targetHexChars);
|
|
169
|
+
const allZeros = prefix.split('').every(c => c === '0');
|
|
170
|
+
|
|
171
|
+
if (allZeros) {
|
|
172
|
+
return {
|
|
173
|
+
success: true,
|
|
174
|
+
proof: hash,
|
|
175
|
+
nonce: nonce,
|
|
176
|
+
attempts: i + 1,
|
|
177
|
+
elapsed: Date.now() - startTime
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
nonce++;
|
|
182
|
+
|
|
183
|
+
// Progress report every 100k attempts
|
|
184
|
+
if (i % 100000 === 0 && i > 0) {
|
|
185
|
+
log('⛏️', `正在计算 PoW... 已尝试 ${formatNumber(i)} 次(${Math.floor((Date.now() - startTime) / 1000)}秒)`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return {
|
|
190
|
+
success: false,
|
|
191
|
+
attempts: CONFIG.MAX_ATTEMPTS,
|
|
192
|
+
elapsed: Date.now() - startTime
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Execute single mine
|
|
198
|
+
*/
|
|
199
|
+
async mine(dryRun = false) {
|
|
200
|
+
log('🦞', 'ClawMiner — 开始挖矿\n');
|
|
201
|
+
|
|
202
|
+
// 1. Check cooldown
|
|
203
|
+
log('⏱️', '检查冷却状态...');
|
|
204
|
+
const canMineResult = await this.canMine();
|
|
205
|
+
|
|
206
|
+
if (!canMineResult.canMine) {
|
|
207
|
+
log('⏱️', `冷却中 — 还需 ${formatTime(canMineResult.remaining)}`);
|
|
208
|
+
return {
|
|
209
|
+
status: 'cooling',
|
|
210
|
+
remaining: canMineResult.remaining,
|
|
211
|
+
message: `冷却中,还需 ${formatTime(canMineResult.remaining)}`
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// 2. Get current state
|
|
216
|
+
const status = await this.getStatus();
|
|
217
|
+
log('📊', `难度:${status.difficulty} | 奖励:${status.reward} CLAWMINER | 进度:${status.progress}`);
|
|
218
|
+
|
|
219
|
+
// 3. Get block info
|
|
220
|
+
const [blockNumber, block] = await Promise.all([
|
|
221
|
+
this.provider.getBlockNumber(),
|
|
222
|
+
this.provider.getBlock()
|
|
223
|
+
]);
|
|
224
|
+
const timestamp = block.timestamp;
|
|
225
|
+
log('🔗', `区块:${blockNumber} | 时间戳:${timestamp}`);
|
|
226
|
+
|
|
227
|
+
// 4. Find proof
|
|
228
|
+
log('⛏️', '正在计算 PoW 证明...');
|
|
229
|
+
const proofResult = await this.findProof(blockNumber, Number(timestamp), status.difficulty);
|
|
230
|
+
|
|
231
|
+
if (!proofResult.success) {
|
|
232
|
+
log('❌', `尝试 ${formatNumber(proofResult.attempts)} 次后未找到有效解(${Math.floor(proofResult.elapsed / 1000)}秒)`);
|
|
233
|
+
return {
|
|
234
|
+
status: 'failed',
|
|
235
|
+
message: `尝试 ${formatNumber(proofResult.attempts)} 次未找到有效 Proof`,
|
|
236
|
+
attempts: proofResult.attempts,
|
|
237
|
+
elapsed: proofResult.elapsed
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
log('✅', `找到有效 Proof!尝试:${formatNumber(proofResult.attempts)} 次 | 耗时:${Math.floor(proofResult.elapsed / 1000)}秒`);
|
|
242
|
+
log('🔑', `Hash: ${proofResult.proof.substring(0, 22)}...`);
|
|
243
|
+
|
|
244
|
+
// Dry run stops here
|
|
245
|
+
if (dryRun) {
|
|
246
|
+
log('🧪', '模拟运行 — 跳过交易提交');
|
|
247
|
+
return {
|
|
248
|
+
status: 'dry-run',
|
|
249
|
+
proof: proofResult.proof,
|
|
250
|
+
nonce: proofResult.nonce,
|
|
251
|
+
attempts: proofResult.attempts,
|
|
252
|
+
elapsed: proofResult.elapsed,
|
|
253
|
+
message: '模拟运行完成 — Proof 有效'
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// 5. Estimate gas
|
|
258
|
+
log('📤', '准备提交交易...');
|
|
259
|
+
let gasLimit = CONFIG.GAS_LIMIT;
|
|
260
|
+
|
|
261
|
+
try {
|
|
262
|
+
const estimate = await this.contract.mine.estimateGas(
|
|
263
|
+
proofResult.proof, blockNumber, timestamp
|
|
264
|
+
);
|
|
265
|
+
gasLimit = Math.floor(Number(estimate) * (1 + CONFIG.GAS_BUFFER_PERCENT / 100));
|
|
266
|
+
log('⛽', `Gas 估算:${estimate} → ${gasLimit}(含缓冲)`);
|
|
267
|
+
} catch (e) {
|
|
268
|
+
log('⛽', `Gas 估算失败,使用默认值:${gasLimit}`);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// 6. Submit transaction
|
|
272
|
+
try {
|
|
273
|
+
const tx = await this.contract.mine(
|
|
274
|
+
proofResult.proof, blockNumber, timestamp,
|
|
275
|
+
{ gasLimit }
|
|
276
|
+
);
|
|
277
|
+
|
|
278
|
+
log('📤', `交易哈希:${tx.hash}`);
|
|
279
|
+
log('⏳', '等待链上确认...');
|
|
280
|
+
|
|
281
|
+
const receipt = await tx.wait();
|
|
282
|
+
const explorerBase = CONFIG.NETWORK === 'mainnet'
|
|
283
|
+
? 'https://bscscan.com'
|
|
284
|
+
: 'https://testnet.bscscan.com';
|
|
285
|
+
|
|
286
|
+
if (receipt.status === 1) {
|
|
287
|
+
const balance = await this.contract.balanceOf(this.address);
|
|
288
|
+
|
|
289
|
+
log('🎉', `挖矿成功!获得 ${status.reward} CLAWMINER`);
|
|
290
|
+
log('💰', `总余额:${ethers.formatEther(balance)} CLAWMINER`);
|
|
291
|
+
log('🔍', `${explorerBase}/tx/${tx.hash}`);
|
|
292
|
+
|
|
293
|
+
return {
|
|
294
|
+
status: 'success',
|
|
295
|
+
txHash: tx.hash,
|
|
296
|
+
gasUsed: Number(receipt.gasUsed),
|
|
297
|
+
reward: status.reward,
|
|
298
|
+
balance: ethers.formatEther(balance),
|
|
299
|
+
proof: proofResult.proof,
|
|
300
|
+
attempts: proofResult.attempts,
|
|
301
|
+
message: `挖矿成功!获得 ${status.reward} CLAWMINER`
|
|
302
|
+
};
|
|
303
|
+
} else {
|
|
304
|
+
log('❌', `交易失败 (status=0)`);
|
|
305
|
+
return { status: 'failed', txHash: tx.hash, message: '交易被回滚' };
|
|
306
|
+
}
|
|
307
|
+
} catch (error) {
|
|
308
|
+
if (error.message.includes('Cooldown')) {
|
|
309
|
+
log('⏱️', '冷却中 — 请稍后重试');
|
|
310
|
+
return { status: 'cooling', message: '冷却中,请稍后重试' };
|
|
311
|
+
}
|
|
312
|
+
if (error.message.includes('Proof already used')) {
|
|
313
|
+
log('⚠️', 'Proof 已被使用 — 将使用新的 Proof 重试');
|
|
314
|
+
return { status: 'retry', message: 'Proof 已被使用' };
|
|
315
|
+
}
|
|
316
|
+
throw error;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Mine with retry
|
|
322
|
+
*/
|
|
323
|
+
async mineWithRetry(dryRun = false) {
|
|
324
|
+
let retryCount = 0;
|
|
325
|
+
|
|
326
|
+
while (retryCount <= CONFIG.MAX_RETRY) {
|
|
327
|
+
try {
|
|
328
|
+
const result = await this.mine(dryRun);
|
|
329
|
+
|
|
330
|
+
if (result.status === 'success' || result.status === 'cooling' || result.status === 'dry-run') {
|
|
331
|
+
return result;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
retryCount++;
|
|
335
|
+
if (retryCount > CONFIG.MAX_RETRY) {
|
|
336
|
+
log('❌', `已达最大重试次数 (${CONFIG.MAX_RETRY})`);
|
|
337
|
+
return { status: 'exhausted', message: `重试 ${CONFIG.MAX_RETRY} 次后仍失败` };
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
log('🔄', `${CONFIG.RETRY_DELAY_MS / 1000}秒后重试 (${retryCount}/${CONFIG.MAX_RETRY})...`);
|
|
341
|
+
await new Promise(r => setTimeout(r, CONFIG.RETRY_DELAY_MS));
|
|
342
|
+
|
|
343
|
+
} catch (error) {
|
|
344
|
+
retryCount++;
|
|
345
|
+
if (retryCount > CONFIG.MAX_RETRY) {
|
|
346
|
+
log('❌', `Error: ${error.message}`);
|
|
347
|
+
return { status: 'error', message: error.message };
|
|
348
|
+
}
|
|
349
|
+
log('⚠️', `错误:${error.message}`);
|
|
350
|
+
log('🔄', `${CONFIG.RETRY_DELAY_MS / 1000}秒后重试 (${retryCount}/${CONFIG.MAX_RETRY})...`);
|
|
351
|
+
await new Promise(r => setTimeout(r, CONFIG.RETRY_DELAY_MS));
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Continuous mining loop
|
|
358
|
+
*/
|
|
359
|
+
async loop() {
|
|
360
|
+
log('🔁', '启动连续挖矿循环(按 Ctrl+C 停止)\n');
|
|
361
|
+
let mineCount = 0;
|
|
362
|
+
|
|
363
|
+
while (true) {
|
|
364
|
+
mineCount++;
|
|
365
|
+
log('🔁', `\n${'='.repeat(50)}`);
|
|
366
|
+
log('🔁', `第 ${mineCount} 轮挖矿`);
|
|
367
|
+
log('🔁', `${'='.repeat(50)}\n`);
|
|
368
|
+
|
|
369
|
+
const result = await this.mineWithRetry();
|
|
370
|
+
|
|
371
|
+
if (result.status === 'cooling') {
|
|
372
|
+
const waitTime = result.remaining || CONFIG.COOLDOWN_SECONDS;
|
|
373
|
+
log('⏱️', `等待冷却 ${formatTime(waitTime)}...\n`);
|
|
374
|
+
|
|
375
|
+
// Countdown display
|
|
376
|
+
let remaining = waitTime;
|
|
377
|
+
while (remaining > 0) {
|
|
378
|
+
process.stdout.write(`\r ⏱️ 冷却中:还需 ${formatTime(remaining)}... `);
|
|
379
|
+
await new Promise(r => setTimeout(r, 10000)); // Update every 10s
|
|
380
|
+
remaining -= 10;
|
|
381
|
+
}
|
|
382
|
+
console.log('\n');
|
|
383
|
+
} else if (result.status === 'success') {
|
|
384
|
+
// Wait for cooldown after successful mine
|
|
385
|
+
log('⏱️', `等待冷却 ${formatTime(CONFIG.COOLDOWN_SECONDS)}...\n`);
|
|
386
|
+
let remaining = CONFIG.COOLDOWN_SECONDS;
|
|
387
|
+
while (remaining > 0) {
|
|
388
|
+
process.stdout.write(`\r ⏱️ 冷却中:还需 ${formatTime(remaining)}... `);
|
|
389
|
+
await new Promise(r => setTimeout(r, 10000));
|
|
390
|
+
remaining -= 10;
|
|
391
|
+
}
|
|
392
|
+
console.log('\n');
|
|
393
|
+
} else {
|
|
394
|
+
// On error, wait a bit before retrying
|
|
395
|
+
log('⚠️', '30秒后重新尝试...');
|
|
396
|
+
await new Promise(r => setTimeout(r, 30000));
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// ========== CLI Entry Point ==========
|
|
403
|
+
async function main() {
|
|
404
|
+
const args = process.argv.slice(2);
|
|
405
|
+
const agent = new ClawMinerAgent();
|
|
406
|
+
|
|
407
|
+
console.log('\n 🦞 ClawMiner Skill v1.0.0');
|
|
408
|
+
console.log(` 📍 网络:BSC ${CONFIG.NETWORK}`);
|
|
409
|
+
console.log(` 👛 钱包:${agent.address}\n`);
|
|
410
|
+
|
|
411
|
+
if (args.includes('--status')) {
|
|
412
|
+
await agent.showStatus();
|
|
413
|
+
} else if (args.includes('--loop')) {
|
|
414
|
+
await agent.loop();
|
|
415
|
+
} else if (args.includes('--dry-run')) {
|
|
416
|
+
await agent.mineWithRetry(true);
|
|
417
|
+
} else {
|
|
418
|
+
const result = await agent.mineWithRetry();
|
|
419
|
+
console.log('\n' + '─'.repeat(50));
|
|
420
|
+
console.log(` 结果:${result.status}`);
|
|
421
|
+
console.log(` ${result.message}`);
|
|
422
|
+
if (result.txHash) {
|
|
423
|
+
const explorerBase = CONFIG.NETWORK === 'mainnet'
|
|
424
|
+
? 'https://bscscan.com'
|
|
425
|
+
: 'https://testnet.bscscan.com';
|
|
426
|
+
console.log(` 🔍 ${explorerBase}/tx/${result.txHash}`);
|
|
427
|
+
}
|
|
428
|
+
console.log('─'.repeat(50) + '\n');
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
main().catch(error => {
|
|
433
|
+
console.error('\n❌ 致命错误:', error.message);
|
|
434
|
+
process.exit(1);
|
|
435
|
+
});
|