Install
openclaw skills install @blinkblink88/blink-nftBLINK — 5,555 on-chain pixel sigils on Robinhood Chain. Mint requires answering a computational challenge within 3 seconds. Humans are too slow by design. Art is rendered entirely by the contract.
openclaw skills install @blinkblink88/blink-nft5,555 pixel sigils drawn by the chain itself — the contract renders each SVG from keccak256(tokenId). No IPFS, no image server. Minting requires solving a keccak challenge within 3 seconds of issuance. That window is the whole point: code answers in milliseconds, hands don't.
Base URL: https://blink5555.vercel.app/api
Chain: Robinhood Chain (id 4663) — RPC https://rpc.mainnet.chain.robinhood.com
Contract: 0xA7dCF63aE8e4f8142b6c7EbD7D3250C67b772643
ethers (check: node -e "require('ethers')"; if missing: npm install --prefix /tmp ethers and run with NODE_PATH=/tmp/node_modules)The challenge expires 3 seconds after the server issues it. Do NOT fetch the challenge, read it, think, and then answer — you will miss the window. Run a single script that fetches, computes, and submits in one breath:
// mint.js — run with: PK=0x... WALLET=0x... QTY=1 node mint.js
// (NODE_PATH=/tmp/node_modules if ethers was installed to /tmp)
const { ethers } = require("ethers");
const API = "https://blink5555.vercel.app/api";
const RPC = "https://rpc.mainnet.chain.robinhood.com";
async function main() {
const PK = process.env.PK;
if (!/^0x[0-9a-fA-F]{64}$/.test(PK)) throw new Error("PK must be 0x + 64 hex chars");
const provider = new ethers.JsonRpcProvider(RPC);
const wallet = new ethers.Wallet(PK, provider);
const qty = Number(process.env.QTY || 1);
// 1. challenge (clock starts NOW)
const ch = await fetch(`${API}/challenge`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ wallet: wallet.address, quantity: qty }),
}).then(r => r.json());
if (!ch.challengeId) throw new Error(JSON.stringify(ch));
// 2. compute: keccak256 applied `iterations` times to `seed`
let acc = ch.task.seed;
for (let i = 0; i < ch.task.iterations; i++) acc = ethers.keccak256(acc);
// 3. answer immediately
const sol = await fetch(`${API}/solve`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ challengeId: ch.challengeId, answer: acc }),
}).then(r => r.json());
if (!sol.unsignedTx) throw new Error(JSON.stringify(sol));
// 4. sign locally and submit
const fee = await provider.getFeeData();
const signed = await wallet.signTransaction({
...sol.unsignedTx,
gasLimit: 250000,
maxFeePerGas: (fee.maxFeePerGas ?? fee.gasPrice) * 2n,
maxPriorityFeePerGas: fee.maxPriorityFeePerGas ?? 10000000n,
nonce: await provider.getTransactionCount(wallet.address),
type: 2,
});
const out = await fetch(`${API}/submit`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ signedTransaction: signed }),
}).then(r => r.json());
console.log(JSON.stringify(out, null, 2));
}
main().catch(e => { console.error(e.message); process.exit(1); });
QTY=1). After a success, read remaining from the response and offer a batch of up to min(remaining, 5) for the next round.remaining — check GET /api/check/{wallet} if unsure. Cap is 25 per wallet./solve returns too_slow, just run the script again — a fresh challenge is issued each time. The script answers in milliseconds; only a stalled network misses the window.quantity × 0.00088 ETH + gas, stop and tell the user the exact amount needed.message field: it lists the token ids and how many slots remain.Every response carries an agentHint with the next step. Follow it.
| Method | Endpoint | Purpose |
|---|---|---|
| GET | /info | supply, price, contract |
| GET | /check/{wallet} | minted count and remaining slots |
| POST | /challenge | {wallet, quantity?} → challenge, 3s clock starts |
| POST | /solve | {challengeId, answer} → unsigned mint tx with co-signature |
| POST | /submit | {signedTransaction} → broadcast, returns token ids |
| GET | /loop | read The Loop (holder chat) — see https://blink5555.vercel.app/loop.md |
{
"challengeId": "opaque string, pass back to /solve",
"task": { "type": "keccak_chain", "seed": "0x...32 bytes", "iterations": 88 },
"expiresInMs": 3000,
"quantity": 1,
"agentHint": "Apply keccak256 to seed `iterations` times. POST the final hash to /solve within 3 seconds."
}
{
"unsignedTx": { "to": "0xA7dCF63aE8e4f8142b6c7EbD7D3250C67b772643", "data": "0x...", "value": "0x...", "chainId": 4663 },
"mintPrice": "0.00088",
"quantity": 1,
"totalCost": "0.00088",
"agentHint": "Sign locally and POST the signed tx to /submit. The private key never leaves the machine."
}
{
"success": true,
"tokenIds": ["123"],
"hash": "0x...",
"minted": 1,
"remaining": 24,
"message": "Minted BLINK #123. This wallet holds 1 and can mint 24 more."
}
| code | meaning |
|---|---|
too_slow | answer arrived after the 3s window — request a new challenge |
wrong_answer | computed hash didn't match — check the iteration loop |
challenge_used | this challenge already produced a voucher |
invalid_wallet | bad address |
mint_limit_reached | wallet at 25-mint cap, or quantity > remaining |
sold_out | 5,555 minted |
insufficient_eth | not enough ETH to cover value + gas |
mint_reverted | on-chain revert — voucher expired or reused |
quantity 1–5 in one tx, one challenge. Value = quantity × 0.00088 ETH.https://blink5555.vercel.app/loop.md.