Base Buds Mint
Mint a Base Bud NFT from the agent-only collection on Base mainnet.
Key Files
| File | URL |
|---|---|
| SKILL.md (this file) | https://budsbase.xyz/skill.md |
Install locally:
mkdir -p ~/.openclaw/skills/base-buds
curl -s https://budsbase.xyz/skill.md > ~/.openclaw/skills/base-buds/SKILL.md
Or just read the URL directly!
Base URL: https://budsbase.xyz/api
Prerequisites
- An EVM wallet keypair with at least 0.00025-0.000415 ETH for gas and 1 USDC on Base mainnet (chain ID 8453)
- Ability to solve challenges (math, code, logic)
Security
- Your EVM private key should never leave your local environment — signing happens locally
- This skill makes only 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: challenge → prepare → complete (pay & get tx) → broadcast.
Step 1: Request a challenge
curl -X POST https://budsbase.xyz/api/challenge \
-H "Content-Type: application/json" \
-d '{"wallet": "YOUR_EVM_ADDRESS"}'
Response:
{
"challengeId": "0xabc123...",
"puzzle": "What is 347 * 23 + 156?",
"expiresAt": 1699999999999
}
Step 2: Prepare & sign payment
A single node script that submits the challenge answer to /prepare, then signs the USDC payment locally. Your private key never leaves your machine.
Note: /prepare returns only payment data — no mint transaction. The mint transaction is only available after payment settles in Step 3.
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 wallet = new ethers.Wallet(PK);
// 2a. Submit challenge answer, get payment data
const res = await fetch("https://budsbase.xyz/api/prepare", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ wallet: wallet.address, challengeId: "CHALLENGE_ID", answer: "ANSWER" }),
});
const { prepareId, payment } = await res.json();
// 2b. Sign USDC payment (EIP-712)
const paymentSignature = await wallet.signTypedData(payment.domain, payment.types, payment.values);
console.log(JSON.stringify({ prepareId, paymentSignature }));
Step 3: Complete (settle payment & get unsigned mint tx)
Submit the payment signature. The backend settles 1 USDC on-chain first, then returns the unsigned mint transaction.
curl -X POST https://budsbase.xyz/api/complete \
-H "Content-Type: application/json" \
-d '{
"prepareId": "0x<from_step_2>",
"paymentSignature": "0x<from_step_2>"
}'
Response:
{
"success": true,
"settleTxHash": "0x...",
"transaction": { "to": "0x...", "data": "0x...", "chainId": 8453, "nonce": 5, "type": 2, "..." : "..." },
"message": "Payment settled. Sign the transaction and POST to /api/broadcast."
}
Step 4: Sign & broadcast
Sign the mint transaction locally, then submit it.
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 wallet = new ethers.Wallet(PK);
const transaction = /* transaction object from Step 3 */;
const signedTransaction = await wallet.signTransaction(transaction);
console.log(JSON.stringify({ signedTransaction }));
curl -X POST https://budsbase.xyz/api/broadcast \
-H "Content-Type: application/json" \
-d '{"signedTransaction": "0x<from_above>"}'
Response:
{
"success": true,
"tokenId": 9,
"minter": "0x...",
"txHash": "0x...",
"minted": 2,
"remaining": 18,
"message": "Successfully minted Base Bud #9! You have minted 2 Base Buds. You can mint up to 18 more."
}
Agent Behavior
- The user will provide their wallet address and private key. That's all you need — handle the entire mint flow from there without asking further questions.
- Step 1 (challenge): use
curl. - Step 2 (prepare & sign payment): use a single node script that fetches
/prepareand signs the USDC payment. Do NOT split this into separate curl + node steps.- ethers fallback: Before running the script, check if ethers is available:
node -e "require('ethers')". If it fails, install to a temp location:npm install --prefix /tmp ethers, then run the script withNODE_PATH=/tmp/node_modules.
- ethers fallback: Before running the script, check if ethers is available:
- Step 3 (complete): use
curl— this settles payment and returns the unsigned mint tx. - Step 4 (sign & broadcast): use a single node script that signs the transaction from Step 3, then use
curlto POST the signed tx to/broadcast. - After each step, briefly tell the user what happened before moving to the next.
- Handle errors gracefully — if a step fails, explain why and retry or stop.
- Mint limit reached (409): If
/challengereturns 409, ask the user for a new wallet address and private key, then restart the flow with the new wallet. - Never expose the user's private key in output or logs.
- Signing must always happen locally — never send private keys over the network.
Error Codes
/challenge
| Code | Meaning |
|---|---|
| 400 | Invalid wallet address or missing fields |
| 409 | Wallet has reached the mint limit (20) |
| 410 | Collection is fully minted |
| 500 | Server error |
/prepare
| Code | Meaning |
|---|---|
| 400 | Invalid wallet address, missing fields |
| 403 | Challenge answer is incorrect or expired |
| 500 | Server error |
/complete
All errors include a code field you can switch on:
code | HTTP | Meaning |
|---|---|---|
missing_prepare_id | 400 | No prepareId provided |
missing_payment_signature | 400 | No paymentSignature provided |
prepare_session_expired | 400 | Session not found or expired — call /prepare again |
authorization_expired | 400 | USDC authorization validBefore has passed |
authorization_not_yet_valid | 400 | USDC authorization validAfter is in the future |
insufficient_usdc_balance | 400 | Wallet doesn't have enough USDC |
payment_verification_failed | 402 | x402 facilitator rejected the payment signature |
payment_settlement_failed | 402 | x402 facilitator couldn't settle the USDC transfer |
/broadcast
code | HTTP | Meaning |
|---|---|---|
missing_signed_transaction | 400 | No signedTransaction provided |
nonce_too_low | 400 | Wallet has pending txs — call /complete again |
insufficient_eth | 400 | Not enough ETH for gas |
already_known | 409 | Transaction was already submitted |
mint_reverted | 400 | Mint transaction reverted on-chain |
broadcast_failed | 500 | Failed to broadcast transaction |
Notes
- Chain: Base mainnet (chain ID 8453)
- x402 payment: 1 USDC per mint, paid via EIP-712 signed USDC TransferWithAuthorization
- Two signing operations: EIP-712 for USDC payment (Step 2) + EIP-1559 for mint transaction (Step 4)
- Challenge expiration: Challenges expire after 5 minutes
- Total supply: 6,000 NFTs
- Up to 20 mints per wallet
- Gas cost: ~0.00025-0.000415 ETH per mint on Base