Skill: doctor-1017/aicash-miner (version 1.0.0)

--- name: aicash-miner description: AICash Network auto-miner for $CASH tokens on Base L2. Use when setting up automated Proof of Compute mining on the AICash mempool network. Supports multi-instance mining, systemd service management, and real-time stats tracking. --- # AICash Miner Automated mining agent for the AI CASH MEMPOOL network (aicash.network). ## Quick Start 1. Get API credentials from https://aicash.network (generate soul.md from browser) 2. Run the setup script: ```bash scripts/setup.sh --api-key <KEY> --wallet <WALLET> --endpoint <ENDPOINT> ``` This creates the miner script, systemd service, and starts mining. ## Configuration Required parameters: - `--api-key` — API key from aicash.network (format: `cash_xxx`) - `--wallet` — EVM wallet address for rewards - `--endpoint` — Supabase mining endpoint URL Optional: - `--name <name>` — Service name (default: `aicash-miner`) - `--instances <n>` — Number of parallel miners (default: 1) ## Multi-Instance Mining Run multiple miners to increase block capture rate: ```bash scripts/setup.sh --api-key <KEY> --wallet <WALLET> --endpoint <ENDPOINT> --instances 6 ``` Creates 6 independent systemd services: `aicash-miner`, `aicash-miner-2` through `aicash-miner-6`. ## Management ```bash # Check status scripts/status.sh # Stop all miners scripts/stop.sh # Start all miners scripts/start.sh # Update API credentials scripts/setup.sh --api-key <NEW_KEY> --wallet <WALLET> --endpoint <NEW_ENDPOINT> ``` ## How It Works 1. Probes API with invalid block number to discover current block 2. Submits Proof of Compute for current block 3. Logs real reward amount from API response 4. Auto-retries on errors, skips claimed blocks 5. Runs 24/7 via systemd with auto-restart

path: scripts/setup.sh
content: #!/bin/bash
set -e

# AICash Miner Setup Script
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
SKILL_DIR="$(dirname "$SCRIPT_DIR")"
NODE=$(which node)
INSTALL_DIR="/root/.openclaw/workspace/aicash"

API_KEY=""
WALLET=""
ENDPOINT=""
NAME="aicash-miner"
INSTANCES=1

while [[ $# -gt 0 ]]; do
  case $1 in
    --api-key) API_KEY="$2"; shift 2;;
    --wallet) WALLET="$2"; shift 2;;
    --endpoint) ENDPOINT="$2"; shift 2;;
    --name) NAME="$2"; shift 2;;
    --instances) INSTANCES="$2"; shift 2;;
    *) echo "Unknown: $1"; exit 1;;
  esac
done

if [[ -z "$API_KEY" || -z "$WALLET" || -z "$ENDPOINT" ]]; then
  echo "Usage: setup.sh --api-key <KEY> --wallet <WALLET> --endpoint <ENDPOINT> [--instances N]"
  exit 1
fi

mkdir -p "$INSTALL_DIR"

# Generate miner.js
cat > "$INSTALL_DIR/miner.js" << 'MINEREOF'
#!/usr/bin/env node
const https = require('https');
const fs = require('fs');
const path = require('path');

const CONFIG = {
  endpoint: process.env.AICASH_ENDPOINT || '__ENDPOINT__',
  [REDACTED_SECRET] || '__API_KEY__',
  wallet: process.env.AICASH_WALLET || '__WALLET__',
  requestTimeout: 180000,
  retryDelay: 5000,
  successDelay: 1000,
};

const STATS_FILE = process.env.AICASH_STATS || path.join(__dirname, 'stats.json');
let stats = { started: new Date().toISOString(), totalMined: 0, totalCash: 0, errors: 0, lastBlock: null, lastSuccess: null };

function log(msg) { console.log(`[${new Date().toISOString()}] ${msg}`); }

function postJSON(url, headers, body) {
  return new Promise((resolve, reject) => {
    const u = new URL(url);
    const data = JSON.stringify(body);
    const req = https.request({
      hostname: u.hostname, port: 443, path: u.pathname, method: 'POST',
      headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(data), ...headers },
      timeout: CONFIG.requestTimeout
    }, (res) => {
      let b = ''; res.on('data', c => b += c);
      res.on('end', () => { try { resolve({ status: res.statusCode, data: JSON.parse(b) }); } catch { resolve({ status: res.statusCode, data: b }); } });
    });
    req.on('error', reject);
    req.on('timeout', () => { req.destroy(); reject(new Error('Request timeout')); });
    req.write(data); req.end();
  });
}

async function findCurrentBlock() {
  try {
    const res = await postJSON(CONFIG.endpoint, { 'x-agent-api-key': CONFIG.apiKey }, { block_number: 999999999 });
    if (res.data.error) { const m = res.data.error.match(/Current:\s*#(\d+)/); if (m) return parseInt(m[1]); }
  } catch (e) { log(`Error probing: ${e.message}`); }
  return null;
}

async function mineBlock(n) {
  return (await postJSON(CONFIG.endpoint, { 'x-agent-api-key': CONFIG.apiKey }, { block_number: n })).data;
}

const sleep = ms => new Promise(r => setTimeout(r, ms));

async function main() {
  log('=== AICash Auto-Miner Started ===');
  log(`Wallet: ${CONFIG.wallet} | Endpoint: ${CONFIG.endpoint}`);
  while (true) {
    try {
      const block = await findCurrentBlock();
      if (!block) { log('Could not determine current block. Retrying in 10s...'); await sleep(10000); continue; }
      log(`Current block: #${block}. Mining...`);
      const result = await mineBlock(block);
      if (result.success) {
        const reward = result.reward?.amount || result.amount || 0;
        stats.totalMined++; stats.totalCash += reward;
        stats.lastBlock = block; stats.lastSuccess = new Date().toISOString();
        log(`✅ MINED Block #${block}! +${reward} $CASH | Total: ${stats.totalMined} blocks, ${stats.totalCash} $CASH`);
        log(`API: ${JSON.stringify(result)}`);
        fs.writeFileSync(STATS_FILE, JSON.stringify(stats, null, 2));
        await sleep(CONFIG.successDelay);
      } else if (result.error) {
        if (result.error.includes('too old') || result.error.includes('already')) { await sleep(2000); }
        else if (result.error.includes('future')) { await sleep(5000); }
        else { log(`Error: ${result.error}`); stats.errors++; await sleep(CONFIG.retryDelay); }
      }
    } catch (e) { log(`Exception: ${e.message}`); stats.errors++; await sleep(CONFIG.retryDelay); }
  }
}

process.on('SIGINT', () => { log(`Stopped. ${stats.totalMined} blocks, ${stats.totalCash} $CASH`); process.exit(0); });
process.on('SIGTERM', () => { log(`Stopped. ${stats.totalMined} blocks, ${stats.totalCash} $CASH`); process.exit(0); });
main().catch(e => { log(`Fatal: ${e.message}`); process.exit(1); });
MINEREOF

# Replace placeholders
sed -i "s|__ENDPOINT__|${ENDPOINT}|g; s|__API_KEY__|${API_KEY}|g; s|__WALLET__|${WALLET}|g" "$INSTALL_DIR/miner.js"

# Create systemd services
for i in $(seq 1 "$INSTANCES"); do
  SVC_NAME="${NAME}"
  [[ $i -gt 1 ]] && SVC_NAME="${NAME}-${i}"
  
  cat > "/etc/systemd/system/${SVC_NAME}.service" << EOF
[Unit]
Description=AICash Miner (${SVC_NAME})
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=${NODE} ${INSTALL_DIR}/miner.js
Restart=always
RestartSec=3
Environment=HOME=/root
[Install]
WantedBy=multi-user.target
EOF
  echo "Created: ${SVC_NAME}.service"
done

systemctl daemon-reload
for i in $(seq 1 "$INSTANCES"); do
  SVC_NAME="${NAME}"
  [[ $i -gt 1 ]] && SVC_NAME="${NAME}-${i}"
  systemctl enable --now "${SVC_NAME}" 2>&1 | grep -v "Created symlink"
  echo "Started: ${SVC_NAME}"
done

echo ""
echo "✅ AICash Miner deployed! ${INSTANCES} instance(s) running."
echo "Check status: systemctl status ${NAME}"


path: scripts/start.sh
content: #!/bin/bash
for svc in $(systemctl list-units --type=service --all --no-legend | grep "aicash-miner" | awk '{print $1}'); do
  systemctl enable --now "$svc" 2>/dev/null
  echo "Started: $svc"
done


path: scripts/status.sh
content: #!/bin/bash
# Check status of all AICash miners
echo "=== AICash Miners ==="
for svc in $(systemctl list-units --type=service --all --no-legend | grep "aicash-miner" | awk '{print $1}'); do
  name=$(echo "$svc" | sed 's/.service//')
  status=$(systemctl is-active "$svc")
  echo "${name}: ${status}"
done


path: scripts/stop.sh
content: #!/bin/bash
for svc in $(systemctl list-units --type=service --all --no-legend | grep "aicash-miner" | awk '{print $1}'); do
  systemctl stop "$svc" && systemctl disable "$svc" 2>/dev/null
  echo "Stopped: $svc"
done


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: suspicious | SkillSpector: suspicious (CRITICAL)