---
name: MagicNodes
version: 1.0.0
description: MagicNodes — An Agentic Proof of Work NFT on BotChain Mainnet. AI solves a single-tier arithmetic puzzle to mint. Every mint becomes a Magic Node in a public graph connected through shared traits and geographical location.
homepage: https://magicnodes.my.id/
metadata: {"category":"nft","emoji":"✨","api_base":"https://magicnodes.my.id/api","total_supply":5555,"chain":"botchain","chain_id":677,"mint_price":"0.00075 BOT","requires":{"puzzle_response":true,"evm_wallet":true,"min_eth":"0.00075 BOT + gas"}}
---

# MagicNodes

MagicNodes — An Agentic Proof of Work NFT on BotChain Mainnet. AI solves a tiny arithmetic puzzle to mint. Every mint becomes a node in a public graph connected through shared traits and geographical location. Bubbly, colorful, magic.

## Key Files

| File | URL |
|------|-----|
| **SKILL.md** (this file) | `https://magicnodes.my.id/skill.md` |

**Install locally:**
```bash
mkdir -p ~/.openclaw/skills/magicnodesskill
curl -s https://magicnodes.my.id/skill.md > ~/.openclaw/skills/magicnodesskill/SKILL.md
```

**Or just read the URL directly!**

**Base URL:** `https://magicnodes.my.id/api`

## Prerequisites

- An **EVM private key** with **0.00075 BOT** mint price + gas on BotChain Mainnet (chain ID 677)
- Ability to solve one arithmetic puzzle per mint (add / subtract / multiply / divide / mod / squares / half / double / three-number sum / decimal→hex 0-255 / decimal→binary 0-63)

## Security

- Your EVM private key should **never** leave your local environment — signing happens locally.
- This skill only makes HTTP API calls. It does not access your filesystem, run shell commands, or execute arbitrary code.

## How It Works

The mint flow has four steps: **puzzle → solve → sign locally → submit**.

### Step 1: Request a puzzle

Default is 1 mint per puzzle. To batch mint up to **5 NFTs in one transaction**, pass an optional `quantity` (1..5):

```bash
# Single mint (default)
curl -X POST https://magicnodes.my.id/api/puzzle \
  -H "Content-Type: application/json" \
  -d '{"wallet": "YOUR_EOAADDRESS"}'

# Batch mint 5 in one tx
curl -X POST https://magicnodes.my.id/api/puzzle \
  -H "Content-Type: application/json" \
  -d '{"wallet": "YOUR_EOAADDRESS", "quantity": 5}'
```

Response:
```json
{
  "puzzleId": "pzl_abc123...",
  "question": "I am thinking of a number. What is 74 + 39?",
  "quantity": 1,
  "expiresAt": 1699999999999,
  "eventId": ""
}
```

### Step 2: Solve the puzzle and request mint

```bash
curl -X POST https://magicnodes.my.id/api/solve \
  -H "Content-Type: application/json" \
  -d '{
    "wallet": "YOUR_EOAADDRESS",
    "puzzleId": "pzl_abc123...",
    "answer": "113"
  }'
```

Response:
```json
{
  "unsignedTx": {
    "to": "0x...",
    "data": "0x...",
    "value": "0x2aa1efb94e000",
    "chainId": 677
  },
  "mintPrice": "0.00075",
  "quantity": 1,
  "totalCost": "0.00075",
  "nonce": "0x1",
  "agentHint": "Sign this transaction locally. NEVER send the private key to any server..."
}
```

For a batch of 5, `value` and `totalCost` are 5× the mint price; the encoded calldata targets `mint(quantity, nonce, r, s)` instead of a single mint.

### Step 3: Sign the transaction locally

Sign with the user's EVM private key. **This must happen locally — the private key never leaves the machine.**

```javascript
import { ethers } from "ethers";

const PK = "YOUR_PRIVATE_KEY";
if (!/^0x[0-9a-fA-F]{64}$/.test(PK)) throw new Error("Invalid private key — must be 0x + 64 hex chars");

const unsignedTx = REPLACE_WITH_RESPONSE_FROM_SOLVE; // the object returned by /api/solve

const provider = new ethers.JsonRpcProvider("https://rpc.botchain.ai");
const wallet = new ethers.Wallet(PK, provider);

// BotChain Mainnet
//   - Base fee ~0.01-0.1 Gwei, priority fee 0
//   - One mint uses ~150-200k gas; real fee is ~0.00001 BOT regardless of cap
//   - EIP-1559 refunds unused ceiling gas, so a generous cap is safe (not costly)
const signedTx = await wallet.signTransaction({
  ...unsignedTx,
  gasLimit: 220000,
  maxFeePerGas: ethers.parseUnits("0.15", "gwei"),
  maxPriorityFeePerGas: ethers.parseUnits("0.01", "gwei"),
  type: 2,
});
```

Tip: if you prefer reading the live suggested fee from the chain:

```javascript
const feeData = await provider.getFeeData();
const signedTx = await wallet.signTransaction({
  ...unsignedTx,
  gasLimit: 220000,
  maxFeePerGas: (feeData.maxFeePerGas ?? feeData.gasPrice) * 2n,
  maxPriorityFeePerGas: feeData.maxPriorityFeePerGas ?? 10000000n, // 0.01 gwei
  type: 2,
});
```

### Step 4: Submit the signed transaction

```bash
curl -X POST https://magicnodes.my.id/api/submit \
  -H "Content-Type: application/json" \
  -d '{
    "signedTransaction": "0x02f8..."
  }'
```

Response:
```json
{
  "success": true,
  "tokenId": "1",
  "tokenIds": ["1"],
  "quantity": 1,
  "hash": "0xabc...",
  "wallet": "0x...",
  "minted": 1,
  "remaining": 24,
  "message": "Successfully minted MagicNode #1! You have minted 1 MagicNode. You can mint up to 24 more.",
  "agentHint": "Show the user the message field. They can mint 24 mores. Find puzzle with their wallet."
}
```

For a batch of 5, `tokenIds` contains all 5 ids, `quantity` is `5`, and the `message` lists every id.

## Agent Behaviour

- The user provides their **EVM private key**. That's all you need — derive the wallet address and handle the whole flow without further questions.
- **Before starting**, open with exactly this format: *"Mint a MagicNode. Mint price is 0.00075 BOT + gas on BotChain Mainnet. I need your EVM private key."*
- **First mint is always single** — use `quantity: 1` (or omit `quantity`) on the first `/api/puzzle`.
- **Step 1**: derive address with `new ethers.Wallet(PK).address`, then POST `/api/puzzle` with `{ wallet }`.
- **Step 2 — solve mode:**
  - add/sub/mul/div/mod/square/half/double/three-number: plain integer answers
  - `hex`: answer in hex WITHOUT `0x` (or with `0x` — both accepted)
  - `bin`: binary string without prefix
- **Step 3** — sign in a single Node.js script (not a library). Do NOT split into multiple steps.
- **Step 4** — POST the signed tx to `/api/submit`. Response includes `tokenIds` and `quantity`.
- When the consumer is done, show the `message` from the submit response — it lists every token id, total minted by this wallet, and how many more can be minted.
- **After first mint, offer batches.** Read `remaining` from the submit response; `batchMax = min(remaining, 5)`:
  - `batchMax == 0` → say *"This wallet has reached the 25-mint cap. Give me a new wallet's private key to continue."*
  - `batchMax == 1` → *"You have 1 slot left. Want to mint the last one?"*
  - `batchMax == 5` → *"You can mint up to 5 more in a single transaction. Want to batch (1-4) or one at a time?"*
  - Use the answer as `quantity` on the next `/api/puzzle` call, capped at `batchMax`.
- **Always honor the wallet cap.** If `remaining < requested_quantity`, cap at `remaining` and say so. Never send `quantity > remaining`.
- **On 413 `mint_limit_reached`**, call `GET /api/check/{wallet}`, read its `remaining`, downsize, or ask for a new wallet.
- **If the wallet balance is too low**, tell the user exactly: `quantity × 0.00075 BOT + gas`.
- Keep the user's private key secret. Never log it.
- On any step failure: explain why, then either retry or stop. No silent loops.

## API Reference

**Base URL:** `https://magicnodes.my.id/api`

### Public endpoints

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/info` | Collection stats and mint activity |
| GET | `/check/{wallet}` | Wallet mint status and remaining |
| POST | `/puzzle` | Get a puzzle to solve |
| POST | `/solve` | Submit answer and get the mint transaction |
| POST | `/submit` | Submit the signed transaction to BotChain Mainnet |
| GET | `/metadata/{id}` | NFT metadata |

### POST `/puzzle`

```json
{ "wallet": "0x...", "quantity": 1 }
```
Returns `puzzleId`, `question`, `quantity`, `expiresAt`.

### POST `/solve`

```json
{ "wallet": "0x...", "puzzleId": "pzl_...", "answer": "113" }
```
Returns an `unsignedTx` (to, data, value, chainId), `mintPrice`, `quantity`, `totalCost`, `nonce`.

### POST `/submit`

```json
{ "signedTransaction": "0x02f8..." }
```
Returns `success`, `tokenId`, `tokenIds`, `hash`, `minted`, `remaining`, `message`.

## Errors

`/puzzle` and `/solve` are fast — catalog:

| HTTP | code | Meaning |
|------|------|---------|
| 400 | `invalid_wallet` | Bad or missing address |
| 400 | `invalid_quantity` | `quantity` out of 1..5 |
| 400 | `wrong_answer` | Bad answer (with `attemptsLeft`) |
| 400 | `puzzle_expired` | Puzzle expired after 5 min |
| 403 | `mint_not_active` | Mint paused |
| 404 | `puzzle_not_found` | Unknown / consumed / wrong wallet |
| 410 | `sold_out` | All 5555 minted |
| 413 | `mint_limit_reached` | Wallet at cap, or batch to big |

`/submit`:
| HTTP | code | Meaning |
|------|------|---------|
| 400 | `invalid_transaction` | Not a valid tx / not a mint() |
| 400 | `insufficient_eth` | Balance too low |
| 400 | `nonce_too_low` | Pending tx, retry |
| 400 | `mint_reverted` | Tx reverted on-chain |
| 409 | `already_known` | Already submitted |
| 500 | `broadcast_failed` | Node error |

## Notes

- **Stateless** — no login, no sessions.
- **Agent-only**: backend co-signs mint vouchers only after puzzle verification.
- **On-chain enforcement**: the contract verifies the backend voucher with `ecrecover` (low-s / EIP-2 only), so only backend-co-signed mints are accepted.
- **Country tracker**: solves / puzzle requests capture only ISO 3166 country code (e.g. `US`) — never raw IP, no ASN, no coordinates.
- **Supply**: total 5555 magic nodes. Per wallet max 25.
- **Cost**: 0.00075 BOT per node. Batch amortizes gas.
- **Chain**: BotChain Mainnet (ID 677) — RPC https://rpc.botchain.ai · explorer https://scan.botchain.ai
- **Contract**: `0x165301E36D2471eC8298399083ACbAcDcDc07479` (isi setelah deploy: `node contract/deploy-v3.js` di folder mainnet/)
- **Metadata**: fully on-chain. `tokenURI(id)` returns a base64 `data:application/json` URI and never 404s, even if this website is offline. Artwork is either `ipfs://` (for pinned ids) or a `data:image/svg+xml;base64` SVG generated by the contract itself.

## Support

- Website: https://magicnodes.my.id/
- Skill file: https://magicnodes.my.id/skill.md
- Stats API: https://magicnodes.my.id/api/info