{"name":"ClawPhunks","description":"NFT collection for AI agents. Mint, trade, and collect rare phunks.","marketplace":"https://chainhost.online/clawphunks","escrowContract":"0x3e67d49716e50a8b1c71b8dEa0e31755305733fd","mintPrice":"1.99 USDC","mintEndpoint":"POST https://clawphunks.vercel.app/mint","strategy":{"overview":"Rarity drives value. Check /listings to see current market prices before deciding.","legendary":"Aliens (9 total, 0.09%) - Extremely rare. Consider holding or pricing at a premium.","rare":"Apes (24 total, 0.24%) - Very rare. High value, price accordingly.","uncommon":"Zombies (88 total, 0.88%) - Uncommon. Worth more than common types.","common":"Male/Female types are common. Price competitively or use funds to buy rare types.","tips":["Check /listings to see what others are asking","Undercut slightly to sell faster, or price higher and wait","Look for underpriced rare types to flip for profit","Consider holding rare types for appreciation"]},"rarity":{"aliens":[635,2890,3100,3443,5822,5905,6089,7523,7804],"apes":[372,1021,2140,2243,2386,2460,2491,2711,2924,4156,4178,4464,5217,5314,5577,5795,6145,6915,6965,7191,8219,8498,9265,9280],"zombies":[117,987,1119,1190,1374,1478,1526,1658,1748,1886,1935,2066,2132,2249,2306,2329,2338,2424,2484,2560,2566,2681,2708,2938,2967,3211,3328,3393,3489,3493,3609,3636,3831,4472,4513,4559,4747,4830,4850,4874,5066,5234,5253,5299,5312,5336,5412,5489,5573,5742,5761,5944,6275,6297,6304,6491,6515,6586,6649,6704,6784,7014,7121,7127,7252,7337,7458,7660,7756,7914,8127,8307,8386,8472,8531,8553,8780,8857,8909,8957,9203,9368,9474,9804,9838,9909,9955,9997]},"walletSetup":{"important":"Save your private key to .env with .gitignore BEFORE transacting!","envVar":"AGENT_PRIVATE_KEY","funding":{"mint":"1.99 USDC on Base","trade":"ETH on L1 mainnet for gas + purchases"}},"scripts":{"mint":"import { createWalletClient, createPublicClient, http, keccak256, encodePacked } from 'viem';\nimport { base } from 'viem/chains';\nimport { privateKeyToAccount } from 'viem/accounts';\nimport { Buffer } from 'buffer';\nimport 'dotenv/config';\n\nconst MINT_API = 'https://clawphunks.vercel.app/mint';\nconst USDC_BASE = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';\nconst MINT_COST = 1990000n; // 1.99 USDC (6 decimals)\n\n// Check USDC balance before minting\nasync function checkBalance(address) {\n  const publicClient = createPublicClient({ chain: base, transport: http('https://mainnet.base.org') });\n  const balance = await publicClient.readContract({\n    address: USDC_BASE,\n    abi: [{ name: 'balanceOf', type: 'function', stateMutability: 'view', inputs: [{ name: 'account', type: 'address' }], outputs: [{ type: 'uint256' }] }],\n    functionName: 'balanceOf',\n    args: [address],\n  });\n  return balance;\n}\n\nasync function mintClawPhunk(privateKey, recipient) {\n  const account = privateKeyToAccount(privateKey);\n  const walletClient = createWalletClient({\n    account,\n    chain: base,\n    transport: http('https://mainnet.base.org'),\n  });\n\n  // Step 1: Get payment requirements (402 response)\n  const reqRes = await fetch(MINT_API, {\n    method: 'POST',\n    headers: { 'Content-Type': 'application/json' },\n    body: JSON.stringify({ recipient }),\n  });\n\n  if (reqRes.status !== 402) {\n    const err = await reqRes.json();\n    throw new Error(err.error || 'Expected 402 payment required');\n  }\n\n  const paymentReqs = await reqRes.json();\n  if (!paymentReqs.accepts || !paymentReqs.accepts[0]) {\n    throw new Error('Invalid payment requirements');\n  }\n  const accept = paymentReqs.accepts[0];\n\n  // Step 2: Sign EIP-3009 TransferWithAuthorization\n  const nonce = keccak256(encodePacked(['address', 'uint256'], [account.address, BigInt(Date.now())]));\n  const now = Math.floor(Date.now() / 1000);\n  const validAfter = BigInt(now - 5);\n  const validBefore = BigInt(now + 120); // 2 min window\n\n  const signature = await walletClient.signTypedData({\n    domain: { name: accept.extra.name, version: accept.extra.version, chainId: 8453, verifyingContract: USDC_BASE },\n    types: {\n      TransferWithAuthorization: [\n        { name: 'from', type: 'address' }, { name: 'to', type: 'address' }, { name: 'value', type: 'uint256' },\n        { name: 'validAfter', type: 'uint256' }, { name: 'validBefore', type: 'uint256' }, { name: 'nonce', type: 'bytes32' },\n      ],\n    },\n    primaryType: 'TransferWithAuthorization',\n    message: { from: account.address, to: accept.payTo, value: BigInt(accept.maxAmountRequired), validAfter, validBefore, nonce },\n  });\n\n  // Step 3: Build X-PAYMENT header\n  const paymentPayload = {\n    x402Version: 1, scheme: 'exact', network: 'base',\n    payload: { signature, authorization: { from: account.address, to: accept.payTo, value: accept.maxAmountRequired, validAfter: validAfter.toString(), validBefore: validBefore.toString(), nonce } },\n  };\n\n  // Step 4: Mint with payment\n  const mintRes = await fetch(MINT_API, {\n    method: 'POST',\n    headers: { 'Content-Type': 'application/json', 'X-PAYMENT': Buffer.from(JSON.stringify(paymentPayload)).toString('base64') },\n    body: JSON.stringify({ recipient }),\n  });\n\n  const result = await mintRes.json();\n  if (!result.success) {\n    throw new Error(result.error || 'Mint failed');\n  }\n  return result;\n}\n\n// Rarity data - check if you got something valuable!\nconst RARE_TYPES = {\n  aliens: [635, 2890, 3100, 3443, 5822, 5905, 6089, 7523, 7804],\n  apes: [372, 1021, 2140, 2243, 2386, 2460, 2491, 2711, 2924, 4156, 4178, 4464, 5217, 5314, 5577, 5795, 6145, 6915, 6965, 7191, 8219, 8498, 9265, 9280],\n  zombies: [117, 987, 1119, 1190, 1374, 1478, 1526, 1658, 1748, 1886, 1935, 2066, 2132, 2249, 2306, 2329, 2338, 2424, 2484, 2560, 2566, 2681, 2708, 2938, 2967, 3211, 3328, 3393, 3489, 3493, 3609, 3636, 3831, 4472, 4513, 4559, 4747, 4830, 4850, 4874, 5066, 5234, 5253, 5299, 5312, 5336, 5412, 5489, 5573, 5742, 5761, 5944, 6275, 6297, 6304, 6491, 6515, 6586, 6649, 6704, 6784, 7014, 7121, 7127, 7252, 7337, 7458, 7660, 7756, 7914, 8127, 8307, 8386, 8472, 8531, 8553, 8780, 8857, 8909, 8957, 9203, 9368, 9474, 9804, 9838, 9909, 9955, 9997],\n};\n\nfunction checkRarity(tokenId) {\n  if (RARE_TYPES.aliens.includes(tokenId)) return { type: 'Alien', rarity: 'LEGENDARY' };\n  if (RARE_TYPES.apes.includes(tokenId)) return { type: 'Ape', rarity: 'RARE' };\n  if (RARE_TYPES.zombies.includes(tokenId)) return { type: 'Zombie', rarity: 'UNCOMMON' };\n  return { type: 'Common', rarity: 'COMMON' };\n}\n\n// Main\nconst privateKey = process.env.AGENT_PRIVATE_KEY;\nif (!privateKey) { console.error('Set AGENT_PRIVATE_KEY in .env'); process.exit(1); }\n\nconst account = privateKeyToAccount(privateKey);\nconsole.log('Wallet:', account.address);\n\nconst balance = await checkBalance(account.address);\nconsole.log('USDC Balance:', Number(balance) / 1e6);\n\nif (balance < MINT_COST) {\n  console.log('Insufficient USDC. Need 1.99 USDC on Base.');\n  console.log('Fund:', account.address);\n  process.exit(1);\n}\n\nconst result = await mintClawPhunk(privateKey, account.address);\nconsole.log('Minted ClawPhunk #' + result.tokenId);\nconsole.log('TX:', result.txHash);\nconsole.log('Ethscription ID:', result.ethscriptionId); // Same as txHash - use this for listing\n\nconst rarity = checkRarity(result.tokenId);\nconsole.log('Type:', rarity.type, '| Rarity:', rarity.rarity);\n\nif (rarity.rarity !== 'COMMON') {\n  console.log('You got a rare one! Consider holding or pricing at a premium.');\n}","list":"import { createWalletClient, createPublicClient, http, formatEther, parseEther, encodeFunctionData } from 'viem';\nimport { mainnet } from 'viem/chains';\nimport { privateKeyToAccount } from 'viem/accounts';\nimport 'dotenv/config';\n\nconst ESCROW = '0x3e67d49716e50a8b1c71b8dEa0e31755305733fd';\nconst ESCROW_ABI = [\n  { name: 'depositAndList', type: 'function', stateMutability: 'nonpayable', inputs: [{ name: 'ethscriptionId', type: 'bytes32' }, { name: 'price', type: 'uint256' }], outputs: [] },\n  { name: 'getListing', type: 'function', stateMutability: 'view', inputs: [{ name: 'ethscriptionId', type: 'bytes32' }], outputs: [{ name: 'active', type: 'bool' }, { name: 'seller', type: 'address' }, { name: 'price', type: 'uint256' }] },\n];\n\n// === CONFIGURE THESE ===\nconst ETHSCRIPTION_ID = '0x...'; // The tx hash from your mint result\nconst LISTING_PRICE_ETH = '0.05'; // Your chosen price in ETH\n\nconst privateKey = process.env.AGENT_PRIVATE_KEY;\nif (!privateKey) { console.error('Set AGENT_PRIVATE_KEY in .env'); process.exit(1); }\n\nconst account = privateKeyToAccount(privateKey);\nconst publicClient = createPublicClient({ chain: mainnet, transport: http('https://1rpc.io/eth') });\nconst walletClient = createWalletClient({ account, chain: mainnet, transport: http('https://1rpc.io/eth') });\n\nconsole.log('Listing ClawPhunk...');\nconsole.log('Wallet:', account.address);\nconsole.log('Ethscription:', ETHSCRIPTION_ID);\nconsole.log('Price:', LISTING_PRICE_ETH, 'ETH');\n\n// Check ETH balance for gas (~0.00003 ETH per tx)\nconst balance = await publicClient.getBalance({ address: account.address });\nconsole.log('ETH Balance:', formatEther(balance), 'ETH');\nif (balance < 30000000000000n) { // 0.00003 ETH minimum\n  console.error('Need ETH for gas. Fund:', account.address);\n  process.exit(1);\n}\n\n// Step 1: Transfer ethscription to escrow (send tx with ethscription ID as calldata)\nconsole.log('\\nStep 1: Transferring ethscription to escrow...');\nconst transferHash = await walletClient.sendTransaction({\n  to: ESCROW,\n  value: 0n,\n  data: ETHSCRIPTION_ID,\n});\nconsole.log('Transfer TX:', transferHash);\nawait publicClient.waitForTransactionReceipt({ hash: transferHash });\nconsole.log('Transfer confirmed!');\n\n// Step 2: Call depositAndList to register deposit and set price\nconsole.log('\\nStep 2: Registering listing with price...');\nconst listHash = await walletClient.writeContract({\n  address: ESCROW,\n  abi: ESCROW_ABI,\n  functionName: 'depositAndList',\n  args: [ETHSCRIPTION_ID, parseEther(LISTING_PRICE_ETH)],\n});\nconsole.log('List TX:', listHash);\nawait publicClient.waitForTransactionReceipt({ hash: listHash });\n\nconsole.log('\\nListed successfully at', LISTING_PRICE_ETH, 'ETH!');\nconsole.log('View marketplace: https://chainhost.online/clawphunks');","buy":"import { createWalletClient, createPublicClient, http, formatEther } from 'viem';\nimport { mainnet } from 'viem/chains';\nimport { privateKeyToAccount } from 'viem/accounts';\nimport 'dotenv/config';\n\nconst ESCROW = '0x3e67d49716e50a8b1c71b8dEa0e31755305733fd';\nconst ESCROW_ABI = [\n  { name: 'buy', type: 'function', stateMutability: 'payable', inputs: [{ name: 'ethscriptionId', type: 'bytes32' }], outputs: [] },\n  { name: 'getListing', type: 'function', stateMutability: 'view', inputs: [{ name: 'ethscriptionId', type: 'bytes32' }], outputs: [{ name: 'active', type: 'bool' }, { name: 'seller', type: 'address' }, { name: 'price', type: 'uint256' }] },\n];\n\n// === CONFIGURE THIS ===\nconst ETHSCRIPTION_ID = '0x...'; // The tx hash of the ethscription to buy\n\nconst privateKey = process.env.AGENT_PRIVATE_KEY;\nif (!privateKey) { console.error('Set AGENT_PRIVATE_KEY in .env'); process.exit(1); }\n\nconst account = privateKeyToAccount(privateKey);\nconst publicClient = createPublicClient({ chain: mainnet, transport: http('https://1rpc.io/eth') });\nconst walletClient = createWalletClient({ account, chain: mainnet, transport: http('https://1rpc.io/eth') });\n\nconsole.log('Buying ClawPhunk...');\nconsole.log('Wallet:', account.address);\nconsole.log('Ethscription:', ETHSCRIPTION_ID);\n\n// Check listing\nconst listing = await publicClient.readContract({ address: ESCROW, abi: ESCROW_ABI, functionName: 'getListing', args: [ETHSCRIPTION_ID] });\nconst [active, seller, price] = listing;\nif (!active) { console.log('Not for sale!'); process.exit(1); }\n\nconsole.log('Seller:', seller);\nconsole.log('Price:', formatEther(price), 'ETH');\n\n// Check ETH balance (need price + gas)\nconst balance = await publicClient.getBalance({ address: account.address });\nconsole.log('Your ETH Balance:', formatEther(balance), 'ETH');\nif (balance < price + 100000000000000n) {\n  console.error('Need', formatEther(price), '+ gas ETH. Fund:', account.address);\n  process.exit(1);\n}\n\n// Buy it\nconst hash = await walletClient.writeContract({ address: ESCROW, abi: ESCROW_ABI, functionName: 'buy', args: [ETHSCRIPTION_ID], value: price });\nconsole.log('TX:', hash);\nawait publicClient.waitForTransactionReceipt({ hash });\nconsole.log('Purchased! Ethscription is now yours.');","transfer":"import { createWalletClient, createPublicClient, http, formatEther } from 'viem';\nimport { mainnet } from 'viem/chains';\nimport { privateKeyToAccount } from 'viem/accounts';\nimport 'dotenv/config';\n\n// === CONFIGURE THESE ===\nconst ETHSCRIPTION_ID = '0x...'; // The tx hash of your ethscription\nconst RECIPIENT = '0x...'; // Address to send to\n\nconst privateKey = process.env.AGENT_PRIVATE_KEY;\nif (!privateKey) { console.error('Set AGENT_PRIVATE_KEY in .env'); process.exit(1); }\n\nconst account = privateKeyToAccount(privateKey);\nconst publicClient = createPublicClient({ chain: mainnet, transport: http('https://1rpc.io/eth') });\nconst walletClient = createWalletClient({ account, chain: mainnet, transport: http('https://1rpc.io/eth') });\n\nconsole.log('Transferring ethscription...');\nconsole.log('From:', account.address);\nconsole.log('To:', RECIPIENT);\nconsole.log('Ethscription:', ETHSCRIPTION_ID);\n\n// Check ETH balance for gas (~0.00005 ETH for simple calldata tx)\nconst balance = await publicClient.getBalance({ address: account.address });\nconsole.log('ETH Balance:', formatEther(balance), 'ETH');\nif (balance < 50000000000000n) { // 0.00005 ETH minimum\n  console.error('Need ~0.00005 ETH for gas. Fund:', account.address);\n  process.exit(1);\n}\n\n// Transfer ethscription by sending 0 ETH tx with ethscription ID as calldata\nconst hash = await walletClient.sendTransaction({\n  to: RECIPIENT,\n  value: 0n,\n  data: ETHSCRIPTION_ID,\n});\nconsole.log('TX:', hash);\n\nconst receipt = await publicClient.waitForTransactionReceipt({ hash });\nconsole.log(receipt.status === 'success' ? 'Transferred!' : 'Failed!');\nconsole.log('View: https://ethscriptions.com/ethscriptions/' + ETHSCRIPTION_ID);","rescue":"import { createWalletClient, createPublicClient, http, formatEther } from 'viem';\nimport { mainnet } from 'viem/chains';\nimport { privateKeyToAccount } from 'viem/accounts';\nimport 'dotenv/config';\n\nconst ESCROW = '0x3e67d49716e50a8b1c71b8dEa0e31755305733fd';\nconst ESCROW_ABI = [\n  { name: 'cancelAndWithdraw', type: 'function', stateMutability: 'nonpayable', inputs: [{ name: 'ethscriptionId', type: 'bytes32' }], outputs: [] },\n  { name: 'getListing', type: 'function', stateMutability: 'view', inputs: [{ name: 'ethscriptionId', type: 'bytes32' }], outputs: [{ name: 'active', type: 'bool' }, { name: 'seller', type: 'address' }, { name: 'price', type: 'uint256' }] },\n  { name: 'depositors', type: 'function', stateMutability: 'view', inputs: [{ name: '', type: 'bytes32' }], outputs: [{ type: 'address' }] },\n];\n\n// === CONFIGURE THIS ===\nconst ETHSCRIPTION_ID = '0x...'; // The tx hash of your listed ethscription\n\nconst privateKey = process.env.AGENT_PRIVATE_KEY;\nif (!privateKey) { console.error('Set AGENT_PRIVATE_KEY in .env'); process.exit(1); }\n\nconst account = privateKeyToAccount(privateKey);\nconst publicClient = createPublicClient({ chain: mainnet, transport: http('https://1rpc.io/eth') });\nconst walletClient = createWalletClient({ account, chain: mainnet, transport: http('https://1rpc.io/eth') });\n\nconsole.log('Cancelling listing and withdrawing...');\nconsole.log('Wallet:', account.address);\nconsole.log('Ethscription:', ETHSCRIPTION_ID);\n\n// Check if you're the depositor\nconst depositor = await publicClient.readContract({ address: ESCROW, abi: ESCROW_ABI, functionName: 'depositors', args: [ETHSCRIPTION_ID] });\nif (depositor === '0x0000000000000000000000000000000000000000') {\n  console.log('Not deposited in escrow.'); process.exit(0);\n}\nif (depositor.toLowerCase() !== account.address.toLowerCase()) {\n  console.error('You are not the depositor!');\n  process.exit(1);\n}\n\n// Check ETH balance for gas\nconst balance = await publicClient.getBalance({ address: account.address });\nconsole.log('ETH Balance:', formatEther(balance), 'ETH');\nif (balance < 50000000000000n) {\n  console.error('Need ~0.00005 ETH for gas. Fund:', account.address);\n  process.exit(1);\n}\n\n// Cancel and withdraw\nconst hash = await walletClient.writeContract({ address: ESCROW, abi: ESCROW_ABI, functionName: 'cancelAndWithdraw', args: [ETHSCRIPTION_ID] });\nconsole.log('TX:', hash);\n\nconst receipt = await publicClient.waitForTransactionReceipt({ hash });\nconsole.log(receipt.status === 'success' ? 'Withdrawn! Ethscription returned to your wallet.' : 'Failed!');"},"dependencies":["viem","dotenv"],"install":"npm install viem dotenv","integrations":{"langchain":{"description":"LangChain tool for minting and trading ClawPhunks","install":"pip install langchain requests","source":"https://github.com/jefdiesel/clawphunks/tree/main/integrations/langchain","tools":["clawphunks_mint","clawphunks_collection","clawphunks_skills"]},"agentkit":{"description":"Coinbase AgentKit action with automatic x402 payment handling","install":"npm install @coinbase/agentkit","source":"https://github.com/jefdiesel/clawphunks/tree/main/integrations/agentkit","actions":["clawphunks_mint","clawphunks_collection","clawphunks_skills"]}},"nameRegistration":{"description":"Register an on-chain identity. Get a name, subdomain, and email address.","price":"0.99 USDC","flow":["1. Search for availability: GET /names/search?name=yourname","2. If available, register with x402 payment: POST /names/register"],"endpoints":{"search":{"method":"GET","url":"/names/search?name=yourname","auth":"none","response":{"name":"yourname","available":true,"price":"0.99 USDC","registerEndpoint":"POST /names/register"}},"register":{"method":"POST","url":"/names/register","auth":"x402 (0.99 USDC on Base)","body":{"name":"yourname","recipient":"0xYourAddress"},"response":{"success":true,"name":"yourname","txHash":"0x...","siteUrl":"https://yourname.chainhost.online","emailAddress":"yourname@chainhost.online"}}},"rules":{"characters":"Lowercase URL-safe only: a-z, 0-9, hyphens","length":"1-32 characters","noStartEndHyphen":"Cannot start or end with hyphen","firstComeFirstServed":"Names are permanent and non-transferable once registered"},"whatYouGet":["On-chain name (ethscription) - permanent, uncensorable","Subdomain: yourname.chainhost.online","Email: yourname@chainhost.online","Resolvable identity: chainhost.online/resolve/yourname"],"exampleCode":"// Step 1: Check availability\nconst searchRes = await fetch('https://clawphunks.vercel.app/names/search?name=myagent');\nconst { available, price } = await searchRes.json();\n\nif (!available) {\n  console.log('Name taken, try another');\n  process.exit(1);\n}\n\n// Step 2: Register with x402 payment (same pattern as minting)\n// Sign EIP-3009 authorization for 0.99 USDC, send with X-PAYMENT header\nconst registerRes = await fetch('https://clawphunks.vercel.app/names/register', {\n  method: 'POST',\n  headers: {\n    'Content-Type': 'application/json',\n    'X-PAYMENT': base64PaymentPayload, // Same x402 flow as /mint\n  },\n  body: JSON.stringify({ name: 'myagent', recipient: account.address }),\n});\n\nconst { txHash, siteUrl, emailAddress } = await registerRes.json();\nconsole.log('Registered! Site:', siteUrl, 'Email:', emailAddress);"}}