[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6FXFSAAEP1B2G0QG8ZTDA0G":3,"skill-same:01M6FXFSAAEP1B2G0QG8ZTDA0G":62},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":25,"publishedAt":36,"linkOnly":37,"markdownContent":38,"markdownBody":39,"frontMatter":40,"refName":41,"commitSha":42,"skillDirectory":43,"forksCount":44,"repositoryHtmlUrl":45,"repositoryHomepageUrl":6,"ownerHtmlUrl":46,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":47,"install":48,"resources":61},"01M6FXFSAAEP1B2G0QG8ZTDA0G","jb-explorer-ui",null,"Etherscan-like contract explorer for Juicebox projects. Read contract state, write transactions, and decode events.","kbarbel640-del\u002Fskills","kbarbel640-del","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F255315490?v=4","01M6CJPPQGCCESXMG3K7BBGQ5J","01M6CJPPX2PB2BCZERFVM2C2B3",1,"MIT","https:\u002F\u002Fgithub.com\u002Fkbarbel640-del\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fmejango\u002Fjuicy\u002Fjb-explorer-ui\u002FSKILL.md",[17,22],{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000032","设计师","PROFESSION","适用职业",{"categoryNo":23,"categoryName":24,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000037","律师与法务",[26,27,28,29,30,31,32,33,34,35],"ui","explorer","jb explorer ui","state","transactions","projects","read","write","contract","events","2026-11-10 10:12:30",false,"---\nname: jb-explorer-ui\ndescription: Etherscan-like contract explorer for Juicebox projects. Read contract state, write transactions, and decode events.\n---\n\n# Juicebox V5 Contract Explorer UI\n\nBuild Etherscan-like interfaces for reading contract state, executing transactions, and exploring Juicebox project data.\n\n## Compatibility\n\n- **nana-core-v5**: v5.0.0+\n- **Last verified**: 2025-01-16\n\n## Uses Shared Components\n\nThis skill uses components from `\u002Fshared\u002F`:\n\n| Component | Purpose |\n|-----------|---------|\n| `styles.css` | Dark theme, buttons, cards, forms |\n| `wallet-utils.js` | Wallet connection, chain switching |\n| `chain-config.json` | RPC URLs, contract addresses |\n| `abis\u002F*.json` | JBController, JBMultiTerminal, JBProjects |\n\n## Features\n\n- **Read Tab**: Call any view\u002Fpure function, auto-decode results\n- **Write Tab**: Submit transactions with wallet signing\n- **Events Tab**: Browse and filter contract events\n- **Quick Actions**: One-click project overview, ruleset info\n\n## Template Structure\n\n```\n┌─────────────────────────────────────────┐\n│ Contract Explorer                       │\n├─────────────────────────────────────────┤\n│ [Contract Address] [Chain ▼] [Load]     │\n├─────────────────────────────────────────┤\n│ Wallet: [Connect] \u002F 0x1234...5678       │\n├─────────────────────────────────────────┤\n│ [Read] [Write] [Events]                 │\n├─────────────────────────────────────────┤\n│ Quick Actions:                          │\n│ [Project Overview] [Current Ruleset]    │\n│ [Token Supply] [Pending Reserved]       │\n├─────────────────────────────────────────┤\n│ Function List                           │\n│ ┌────────────────────────────────────┐  │\n│ │ functionName(param1, param2)       │  │\n│ │ [input] [input] [Query]            │  │\n│ │ Result: {...}                      │  │\n│ └────────────────────────────────────┘  │\n└─────────────────────────────────────────┘\n```\n\n## Core JavaScript Logic\n\n```javascript\n\u002F**\n * Contract Explorer - Core Logic\n * Requires: ethers.js v6, \u002Fshared\u002Fwallet-utils.js\n *\u002F\nclass ContractExplorer {\n  constructor() {\n    this.wallet = new JBWallet();\n    this.provider = null;\n    this.contract = null;\n    this.abi = null;\n    this.chainId = 1;\n  }\n\n  async load(address, chainId) {\n    this.chainId = chainId;\n    const config = await loadChainConfig();\n    this.provider = new ethers.JsonRpcProvider(config.chains[chainId].rpc);\n\n    \u002F\u002F Try to match known JB contracts, otherwise fetch from explorer\n    this.abi = await this.fetchABI(address, chainId, config);\n    this.contract = new ethers.Contract(address, this.abi, this.provider);\n\n    return this.categorize();\n  }\n\n  async fetchABI(address, chainId, config) {\n    \u002F\u002F Check if it's a known JB contract\n    const contracts = config.chains[chainId]?.contracts || {};\n    for (const [name, addr] of Object.entries(contracts)) {\n      if (addr.toLowerCase() === address.toLowerCase()) {\n        const res = await fetch(`\u002Fshared\u002Fabis\u002F${name}.json`);\n        if (res.ok) return res.json();\n      }\n    }\n\n    \u002F\u002F Fallback to Etherscan API\n    const explorers = {\n      1: 'api.etherscan.io', 10: 'api-optimistic.etherscan.io',\n      8453: 'api.basescan.org', 42161: 'api.arbiscan.io',\n      11155111: 'api-sepolia.etherscan.io'\n    };\n    const url = `https:\u002F\u002F${explorers[chainId]}\u002Fapi?module=contract&action=getabi&address=${address}`;\n    const data = await (await fetch(url)).json();\n    if (data.status === '1') return JSON.parse(data.result);\n    throw new Error('ABI not found');\n  }\n\n  categorize() {\n    const items = this.abi.filter(x => x.type === 'function');\n    return {\n      read: items.filter(f => ['view', 'pure'].includes(f.stateMutability)),\n      write: items.filter(f => !['view', 'pure'].includes(f.stateMutability)),\n      events: this.abi.filter(x => x.type === 'event')\n    };\n  }\n\n  async call(fnName, args = []) {\n    return await this.contract[fnName](...args);\n  }\n\n  async send(fnName, args = [], value = '0') {\n    if (!this.wallet.signer) await this.wallet.connect(this.chainId);\n    const connected = this.contract.connect(this.wallet.signer);\n    return await connected[fnName](...args, { value: ethers.parseEther(value) });\n  }\n\n  format(result) {\n    if (typeof result === 'bigint') return result.toString();\n    if (Array.isArray(result)) {\n      return result.map(r => this.format(r));\n    }\n    if (result && typeof result === 'object') {\n      const obj = {};\n      for (const key of Object.keys(result)) {\n        if (isNaN(key)) obj[key] = this.format(result[key]);\n      }\n      return obj;\n    }\n    return result;\n  }\n}\n\n\u002F\u002F Quick Actions for JB contracts\nconst QUICK_ACTIONS = [\n  {\n    name: 'Project Overview',\n    contract: 'JBController',\n    fn: 'currentRulesetOf',\n    args: (projectId) => [projectId]\n  },\n  {\n    name: 'Token Supply',\n    contract: 'JBController',\n    fn: 'totalTokenSupplyWithReservedTokensOf',\n    args: (projectId) => [projectId]\n  },\n  {\n    name: 'Pending Reserved',\n    contract: 'JBController',\n    fn: 'pendingReservedTokenBalanceOf',\n    args: (projectId) => [projectId]\n  }\n];\n```\n\n## HTML Template\n\n```html\n\u003C!DOCTYPE html>\n\u003Chtml lang=\"en\">\n\u003Chead>\n  \u003Cmeta charset=\"UTF-8\">\n  \u003Cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  \u003Ctitle>Juicebox Contract Explorer\u003C\u002Ftitle>\n  \u003Cscript src=\"https:\u002F\u002Fcdn.jsdelivr.net\u002Fnpm\u002Fethers@6\u002Fdist\u002Fethers.umd.min.js\">\u003C\u002Fscript>\n  \u003Cstyle>\n    \u002F* Load from \u002Fshared\u002Fstyles.css or inline the CSS variables and classes *\u002F\n    :root {\n      --jb-yellow: #ffcc00; --bg-primary: #0d0d0d; --bg-secondary: #1a1a1a;\n      --bg-tertiary: #2a2a2a; --text-primary: #fff; --text-muted: #888;\n      --border-color: #333; --font-mono: monospace;\n    }\n    * { margin: 0; padding: 0; box-sizing: border-box; }\n    body { font-family: -apple-system, sans-serif; background: var(--bg-primary); color: #e0e0e0; padding: 20px; }\n    .container { max-width: 1000px; margin: 0 auto; }\n    h1 { color: var(--jb-yellow); margin-bottom: 20px; }\n    .card { background: var(--bg-secondary); border-radius: 12px; padding: 20px; margin-bottom: 16px; }\n    .row { display: flex; gap: 10px; flex-wrap: wrap; }\n    input, select { flex: 1; min-width: 150px; padding: 12px; background: var(--bg-tertiary); border: 1px solid var(--border-color); border-radius: 8px; color: #fff; }\n    button { padding: 12px 20px; background: var(--jb-yellow); color: #000; border: none; border-radius: 8px; font-weight: 600; cursor: pointer; }\n    button:hover { background: #e6b800; }\n    button.secondary { background: var(--bg-tertiary); color: #fff; border: 1px solid var(--border-color); }\n    .tabs { display: flex; gap: 8px; margin-bottom: 16px; }\n    .tab { padding: 10px 20px; background: transparent; border: 1px solid var(--border-color); border-radius: 8px; color: var(--text-muted); cursor: pointer; }\n    .tab.active { background: var(--jb-yellow); color: #000; border-color: var(--jb-yellow); }\n    .fn-card { background: var(--bg-tertiary); border-radius: 8px; padding: 16px; margin-bottom: 12px; }\n    .fn-name { font-family: var(--font-mono); font-weight: 600; margin-bottom: 12px; }\n    .fn-inputs { margin-bottom: 12px; }\n    .fn-inputs input { margin-bottom: 8px; }\n    .result { background: var(--bg-primary); border-radius: 6px; padding: 12px; margin-top: 12px; font-family: var(--font-mono); font-size: 13px; white-space: pre-wrap; word-break: break-all; }\n    .quick-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 16px; }\n    .quick-btn { padding: 8px 16px; font-size: 13px; }\n    .label { font-size: 12px; color: var(--text-muted); margin-bottom: 4px; }\n    .loading { text-align: center; padding: 20px; color: var(--text-muted); }\n    .badge { display: inline-block; padding: 4px 10px; border-radius: 20px; font-size: 12px; }\n    .badge.success { background: rgba(0,255,136,0.2); color: #0f8; }\n    .badge.error { background: rgba(255,68,68,0.2); color: #f44; }\n  \u003C\u002Fstyle>\n\u003C\u002Fhead>\n\u003Cbody>\n  \u003Cdiv class=\"container\">\n    \u003Ch1>Contract Explorer\u003C\u002Fh1>\n\n    \u003Cdiv class=\"card\">\n      \u003Cdiv class=\"row\">\n        \u003Cinput type=\"text\" id=\"address\" placeholder=\"Contract address (0x...)\">\n        \u003Cselect id=\"chain\">\n          \u003Coption value=\"1\">Ethereum\u003C\u002Foption>\n          \u003Coption value=\"10\">Optimism\u003C\u002Foption>\n          \u003Coption value=\"8453\">Base\u003C\u002Foption>\n          \u003Coption value=\"42161\">Arbitrum\u003C\u002Foption>\n          \u003Coption value=\"11155111\">Sepolia\u003C\u002Foption>\n        \u003C\u002Fselect>\n        \u003Cbutton onclick=\"loadContract()\">Load Contract\u003C\u002Fbutton>\n      \u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n\n    \u003Cdiv class=\"card\" id=\"walletSection\" style=\"display:none;\">\n      \u003Cdiv class=\"row\" style=\"justify-content: space-between; align-items: center;\">\n        \u003Cspan id=\"walletStatus\">Not connected\u003C\u002Fspan>\n        \u003Cbutton class=\"secondary\" id=\"connectBtn\" onclick=\"connectWallet()\">Connect Wallet\u003C\u002Fbutton>\n      \u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n\n    \u003Cdiv id=\"quickActions\" class=\"quick-actions\" style=\"display:none;\">\u003C\u002Fdiv>\n\n    \u003Cdiv class=\"tabs\" id=\"tabs\" style=\"display:none;\">\n      \u003Cbutton class=\"tab active\" onclick=\"showTab('read')\">Read\u003C\u002Fbutton>\n      \u003Cbutton class=\"tab\" onclick=\"showTab('write')\">Write\u003C\u002Fbutton>\n      \u003Cbutton class=\"tab\" onclick=\"showTab('events')\">Events\u003C\u002Fbutton>\n    \u003C\u002Fdiv>\n\n    \u003Cdiv id=\"content\">\u003C\u002Fdiv>\n  \u003C\u002Fdiv>\n\n  \u003Cscript>\n    let explorer = null;\n    let functions = { read: [], write: [], events: [] };\n    let currentTab = 'read';\n\n    async function loadContract() {\n      const address = document.getElementById('address').value;\n      const chainId = parseInt(document.getElementById('chain').value);\n      if (!address) return alert('Enter a contract address');\n\n      document.getElementById('content').innerHTML = '\u003Cdiv class=\"loading\">Loading contract...\u003C\u002Fdiv>';\n\n      try {\n        explorer = new ContractExplorer();\n        functions = await explorer.load(address, chainId);\n\n        document.getElementById('walletSection').style.display = 'block';\n        document.getElementById('tabs').style.display = 'flex';\n        renderQuickActions();\n        showTab('read');\n      } catch (e) {\n        document.getElementById('content').innerHTML = `\u003Cdiv class=\"badge error\">${e.message}\u003C\u002Fdiv>`;\n      }\n    }\n\n    function renderQuickActions() {\n      const el = document.getElementById('quickActions');\n      el.style.display = 'flex';\n      el.innerHTML = `\n        \u003Cspan style=\"color:#888;margin-right:8px;\">Quick:\u003C\u002Fspan>\n        \u003Cinput type=\"number\" id=\"quickProjectId\" placeholder=\"Project ID\" style=\"width:120px;padding:8px;\">\n        ${QUICK_ACTIONS.map(a => `\u003Cbutton class=\"secondary quick-btn\" onclick=\"runQuickAction('${a.name}')\">${a.name}\u003C\u002Fbutton>`).join('')}\n      `;\n    }\n\n    async function runQuickAction(name) {\n      const action = QUICK_ACTIONS.find(a => a.name === name);\n      const projectId = document.getElementById('quickProjectId').value;\n      if (!projectId) return alert('Enter a project ID');\n\n      const config = await loadChainConfig();\n      const addr = config.chains[explorer.chainId].contracts[action.contract];\n      const tempExplorer = new ContractExplorer();\n      await tempExplorer.load(addr, explorer.chainId);\n\n      try {\n        const result = await tempExplorer.call(action.fn, action.args(projectId));\n        alert(JSON.stringify(tempExplorer.format(result), null, 2));\n      } catch (e) {\n        alert('Error: ' + e.message);\n      }\n    }\n\n    function showTab(tab) {\n      currentTab = tab;\n      document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));\n      document.querySelector(`.tab:nth-child(${tab === 'read' ? 1 : tab === 'write' ? 2 : 3})`).classList.add('active');\n      renderFunctions();\n    }\n\n    function renderFunctions() {\n      const list = currentTab === 'events' ? functions.events : functions[currentTab];\n      const content = document.getElementById('content');\n\n      if (currentTab === 'events') {\n        content.innerHTML = list.map(e => `\n          \u003Cdiv class=\"fn-card\">\n            \u003Cdiv class=\"fn-name\">${e.name}\u003C\u002Fdiv>\n            \u003Cdiv style=\"color:#888;font-size:13px;\">${e.inputs?.map(i => `${i.type} ${i.name}`).join(', ') || 'No parameters'}\u003C\u002Fdiv>\n          \u003C\u002Fdiv>\n        `).join('') || '\u003Cdiv class=\"loading\">No events found\u003C\u002Fdiv>';\n        return;\n      }\n\n      content.innerHTML = list.map((fn, idx) => `\n        \u003Cdiv class=\"fn-card\" id=\"fn-${idx}\">\n          \u003Cdiv class=\"fn-name\">${fn.name}(${fn.inputs?.map(i => i.type).join(', ') || ''})\u003C\u002Fdiv>\n          ${fn.inputs?.length ? `\u003Cdiv class=\"fn-inputs\">\n            ${fn.inputs.map((inp, i) => `\n              \u003Cdiv class=\"label\">${inp.name || 'param' + i} (${inp.type})\u003C\u002Fdiv>\n              \u003Cinput type=\"text\" data-fn=\"${idx}\" data-param=\"${i}\" placeholder=\"${inp.type}\">\n            `).join('')}\n          \u003C\u002Fdiv>` : ''}\n          ${currentTab === 'write' && fn.stateMutability === 'payable' ? `\n            \u003Cdiv class=\"label\">ETH Value\u003C\u002Fdiv>\n            \u003Cinput type=\"text\" data-fn=\"${idx}\" data-value=\"true\" placeholder=\"0.0\">\n          ` : ''}\n          \u003Cbutton ${currentTab === 'write' ? '' : 'class=\"secondary\"'} onclick=\"callFn(${idx})\">\n            ${currentTab === 'write' ? 'Write' : 'Query'}\n          \u003C\u002Fbutton>\n          \u003Cdiv class=\"result\" id=\"result-${idx}\" style=\"display:none;\">\u003C\u002Fdiv>\n        \u003C\u002Fdiv>\n      `).join('') || '\u003Cdiv class=\"loading\">No functions found\u003C\u002Fdiv>';\n    }\n\n    async function callFn(idx) {\n      const fn = functions[currentTab][idx];\n      const inputs = [...document.querySelectorAll(`[data-fn=\"${idx}\"][data-param]`)].map(el => el.value);\n      const valueEl = document.querySelector(`[data-fn=\"${idx}\"][data-value]`);\n      const value = valueEl?.value || '0';\n      const resultEl = document.getElementById(`result-${idx}`);\n\n      resultEl.style.display = 'block';\n      resultEl.textContent = 'Loading...';\n\n      try {\n        if (currentTab === 'write') {\n          const tx = await explorer.send(fn.name, inputs, value);\n          resultEl.innerHTML = `\u003Cspan class=\"badge success\">TX: ${tx.hash}\u003C\u002Fspan>`;\n          await tx.wait();\n          resultEl.innerHTML += `\u003Cbr>\u003Cspan class=\"badge success\">Confirmed!\u003C\u002Fspan>`;\n        } else {\n          const result = await explorer.call(fn.name, inputs);\n          resultEl.textContent = JSON.stringify(explorer.format(result), null, 2);\n        }\n      } catch (e) {\n        resultEl.innerHTML = `\u003Cspan class=\"badge error\">${e.message}\u003C\u002Fspan>`;\n      }\n    }\n\n    async function connectWallet() {\n      try {\n        await explorer.wallet.connect(explorer.chainId);\n        document.getElementById('walletStatus').textContent = `Connected: ${explorer.wallet.address.slice(0,6)}...${explorer.wallet.address.slice(-4)}`;\n        document.getElementById('connectBtn').textContent = 'Connected';\n      } catch (e) {\n        alert(e.message);\n      }\n    }\n\n    \u002F\u002F Inline ContractExplorer and loadChainConfig from above\n    class ContractExplorer {\n      constructor() { this.wallet = { signer: null, address: null, connect: async (chainId) => {\n        if (!window.ethereum) throw new Error('No wallet found');\n        const provider = new ethers.BrowserProvider(window.ethereum);\n        await provider.send('eth_requestAccounts', []);\n        try { await window.ethereum.request({ method: 'wallet_switchEthereumChain', params: [{ chainId: '0x' + chainId.toString(16) }] }); } catch(e) {}\n        this.signer = await provider.getSigner();\n        this.address = await this.signer.getAddress();\n      }}; this.provider = null; this.contract = null; this.abi = null; this.chainId = 1; }\n      async load(address, chainId) {\n        this.chainId = chainId;\n        const config = await loadChainConfig();\n        this.provider = new ethers.JsonRpcProvider(config.chains[chainId].rpc);\n        this.abi = await this.fetchABI(address, chainId, config);\n        this.contract = new ethers.Contract(address, this.abi, this.provider);\n        return this.categorize();\n      }\n      async fetchABI(address, chainId, config) {\n        const explorers = { 1: 'api.etherscan.io', 10: 'api-optimistic.etherscan.io', 8453: 'api.basescan.org', 42161: 'api.arbiscan.io', 11155111: 'api-sepolia.etherscan.io' };\n        const url = `https:\u002F\u002F${explorers[chainId]}\u002Fapi?module=contract&action=getabi&address=${address}`;\n        const data = await (await fetch(url)).json();\n        if (data.status === '1') return JSON.parse(data.result);\n        throw new Error('ABI not found - contract may not be verified');\n      }\n      categorize() {\n        const items = this.abi.filter(x => x.type === 'function');\n        return { read: items.filter(f => ['view', 'pure'].includes(f.stateMutability)), write: items.filter(f => !['view', 'pure'].includes(f.stateMutability)), events: this.abi.filter(x => x.type === 'event') };\n      }\n      async call(fnName, args = []) { return await this.contract[fnName](...args); }\n      async send(fnName, args = [], value = '0') {\n        if (!this.wallet.signer) await this.wallet.connect(this.chainId);\n        const connected = this.contract.connect(this.wallet.signer);\n        const opts = value !== '0' ? { value: ethers.parseEther(value) } : {};\n        return await connected[fnName](...args, opts);\n      }\n      format(result) {\n        if (typeof result === 'bigint') return result.toString();\n        if (Array.isArray(result)) return result.map(r => this.format(r));\n        if (result && typeof result === 'object') { const obj = {}; for (const k of Object.keys(result)) if (isNaN(k)) obj[k] = this.format(result[k]); return obj; }\n        return result;\n      }\n    }\n\n    async function loadChainConfig() {\n      return { chains: {\n        1: { rpc: 'https:\u002F\u002Feth.llamarpc.com', contracts: { JBController: '0x0Ae7403b3C3B4C5222bBbE664bdD8600C593b23e' }},\n        10: { rpc: 'https:\u002F\u002Foptimism.llamarpc.com', contracts: { JBController: '0x0Ae7403b3C3B4C5222bBbE664bdD8600C593b23e' }},\n        8453: { rpc: 'https:\u002F\u002Fbase.llamarpc.com', contracts: { JBController: '0x0Ae7403b3C3B4C5222bBbE664bdD8600C593b23e' }},\n        42161: { rpc: 'https:\u002F\u002Farbitrum.llamarpc.com', contracts: { JBController: '0x0Ae7403b3C3B4C5222bBbE664bdD8600C593b23e' }},\n        11155111: { rpc: 'https:\u002F\u002Fsepolia.drpc.org', contracts: { JBController: '0x0Ae7403b3C3B4C5222bBbE664bdD8600C593b23e' }}\n      }};\n    }\n\n    const QUICK_ACTIONS = [\n      { name: 'Project Overview', contract: 'JBController', fn: 'currentRulesetOf', args: (p) => [p] },\n      { name: 'Token Supply', contract: 'JBController', fn: 'totalTokenSupplyWithReservedTokensOf', args: (p) => [p] },\n      { name: 'Pending Reserved', contract: 'JBController', fn: 'pendingReservedTokenBalanceOf', args: (p) => [p] }\n    ];\n  \u003C\u002Fscript>\n\u003C\u002Fbody>\n\u003C\u002Fhtml>\n```\n\n## Customization Points\n\n| What | Where |\n|------|-------|\n| Add quick actions | Extend `QUICK_ACTIONS` array |\n| Change styling | Override CSS variables in `:root` |\n| Add ABI sources | Modify `fetchABI()` method |\n| Custom result formatting | Extend `format()` method |\n\n## Example Prompts\n\n- \"Create an explorer for JBController on Optimism\"\n- \"Build a read-only explorer (no write tab)\"\n- \"Add a quick action to check project splits\"\n\n## See Also\n\n- `\u002Fjb-event-explorer-ui` - Event-focused browsing\n- `\u002Fjb-hook-deploy-ui` - Deploy custom hooks\n- `\u002Fjb-ruleset-timeline-ui` - Ruleset history visualization\n","# Juicebox V5 Contract Explorer UI\n\nBuild Etherscan-like interfaces for reading contract state, executing transactions, and exploring Juicebox project data.\n\n## Compatibility\n\n- **nana-core-v5**: v5.0.0+\n- **Last verified**: 2025-01-16\n\n## Uses Shared Components\n\nThis skill uses components from `\u002Fshared\u002F`:\n\n| Component | Purpose |\n|-----------|---------|\n| `styles.css` | Dark theme, buttons, cards, forms |\n| `wallet-utils.js` | Wallet connection, chain switching |\n| `chain-config.json` | RPC URLs, contract addresses |\n| `abis\u002F*.json` | JBController, JBMultiTerminal, JBProjects |\n\n## Features\n\n- **Read Tab**: Call any view\u002Fpure function, auto-decode results\n- **Write Tab**: Submit transactions with wallet signing\n- **Events Tab**: Browse and filter contract events\n- **Quick Actions**: One-click project overview, ruleset info\n\n## Template Structure\n\n```\n┌─────────────────────────────────────────┐\n│ Contract Explorer                       │\n├─────────────────────────────────────────┤\n│ [Contract Address] [Chain ▼] [Load]     │\n├─────────────────────────────────────────┤\n│ Wallet: [Connect] \u002F 0x1234...5678       │\n├─────────────────────────────────────────┤\n│ [Read] [Write] [Events]                 │\n├─────────────────────────────────────────┤\n│ Quick Actions:                          │\n│ [Project Overview] [Current Ruleset]    │\n│ [Token Supply] [Pending Reserved]       │\n├─────────────────────────────────────────┤\n│ Function List                           │\n│ ┌────────────────────────────────────┐  │\n│ │ functionName(param1, param2)       │  │\n│ │ [input] [input] [Query]            │  │\n│ │ Result: {...}                      │  │\n│ └────────────────────────────────────┘  │\n└─────────────────────────────────────────┘\n```\n\n## Core JavaScript Logic\n\n```javascript\n\u002F**\n * Contract Explorer - Core Logic\n * Requires: ethers.js v6, \u002Fshared\u002Fwallet-utils.js\n *\u002F\nclass ContractExplorer {\n  constructor() {\n    this.wallet = new JBWallet();\n    this.provider = null;\n    this.contract = null;\n    this.abi = null;\n    this.chainId = 1;\n  }\n\n  async load(address, chainId) {\n    this.chainId = chainId;\n    const config = await loadChainConfig();\n    this.provider = new ethers.JsonRpcProvider(config.chains[chainId].rpc);\n\n    \u002F\u002F Try to match known JB contracts, otherwise fetch from explorer\n    this.abi = await this.fetchABI(address, chainId, config);\n    this.contract = new ethers.Contract(address, this.abi, this.provider);\n\n    return this.categorize();\n  }\n\n  async fetchABI(address, chainId, config) {\n    \u002F\u002F Check if it's a known JB contract\n    const contracts = config.chains[chainId]?.contracts || {};\n    for (const [name, addr] of Object.entries(contracts)) {\n      if (addr.toLowerCase() === address.toLowerCase()) {\n        const res = await fetch(`\u002Fshared\u002Fabis\u002F${name}.json`);\n        if (res.ok) return res.json();\n      }\n    }\n\n    \u002F\u002F Fallback to Etherscan API\n    const explorers = {\n      1: 'api.etherscan.io', 10: 'api-optimistic.etherscan.io',\n      8453: 'api.basescan.org', 42161: 'api.arbiscan.io',\n      11155111: 'api-sepolia.etherscan.io'\n    };\n    const url = `https:\u002F\u002F${explorers[chainId]}\u002Fapi?module=contract&action=getabi&address=${address}`;\n    const data = await (await fetch(url)).json();\n    if (data.status === '1') return JSON.parse(data.result);\n    throw new Error('ABI not found');\n  }\n\n  categorize() {\n    const items = this.abi.filter(x => x.type === 'function');\n    return {\n      read: items.filter(f => ['view', 'pure'].includes(f.stateMutability)),\n      write: items.filter(f => !['view', 'pure'].includes(f.stateMutability)),\n      events: this.abi.filter(x => x.type === 'event')\n    };\n  }\n\n  async call(fnName, args = []) {\n    return await this.contract[fnName](...args);\n  }\n\n  async send(fnName, args = [], value = '0') {\n    if (!this.wallet.signer) await this.wallet.connect(this.chainId);\n    const connected = this.contract.connect(this.wallet.signer);\n    return await connected[fnName](...args, { value: ethers.parseEther(value) });\n  }\n\n  format(result) {\n    if (typeof result === 'bigint') return result.toString();\n    if (Array.isArray(result)) {\n      return result.map(r => this.format(r));\n    }\n    if (result && typeof result === 'object') {\n      const obj = {};\n      for (const key of Object.keys(result)) {\n        if (isNaN(key)) obj[key] = this.format(result[key]);\n      }\n      return obj;\n    }\n    return result;\n  }\n}\n\n\u002F\u002F Quick Actions for JB contracts\nconst QUICK_ACTIONS = [\n  {\n    name: 'Project Overview',\n    contract: 'JBController',\n    fn: 'currentRulesetOf',\n    args: (projectId) => [projectId]\n  },\n  {\n    name: 'Token Supply',\n    contract: 'JBController',\n    fn: 'totalTokenSupplyWithReservedTokensOf',\n    args: (projectId) => [projectId]\n  },\n  {\n    name: 'Pending Reserved',\n    contract: 'JBController',\n    fn: 'pendingReservedTokenBalanceOf',\n    args: (projectId) => [projectId]\n  }\n];\n```\n\n## HTML Template\n\n```html\n\u003C!DOCTYPE html>\n\u003Chtml lang=\"en\">\n\u003Chead>\n  \u003Cmeta charset=\"UTF-8\">\n  \u003Cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  \u003Ctitle>Juicebox Contract Explorer\u003C\u002Ftitle>\n  \u003Cscript src=\"https:\u002F\u002Fcdn.jsdelivr.net\u002Fnpm\u002Fethers@6\u002Fdist\u002Fethers.umd.min.js\">\u003C\u002Fscript>\n  \u003Cstyle>\n    \u002F* Load from \u002Fshared\u002Fstyles.css or inline the CSS variables and classes *\u002F\n    :root {\n      --jb-yellow: #ffcc00; --bg-primary: #0d0d0d; --bg-secondary: #1a1a1a;\n      --bg-tertiary: #2a2a2a; --text-primary: #fff; --text-muted: #888;\n      --border-color: #333; --font-mono: monospace;\n    }\n    * { margin: 0; padding: 0; box-sizing: border-box; }\n    body { font-family: -apple-system, sans-serif; background: var(--bg-primary); color: #e0e0e0; padding: 20px; }\n    .container { max-width: 1000px; margin: 0 auto; }\n    h1 { color: var(--jb-yellow); margin-bottom: 20px; }\n    .card { background: var(--bg-secondary); border-radius: 12px; padding: 20px; margin-bottom: 16px; }\n    .row { display: flex; gap: 10px; flex-wrap: wrap; }\n    input, select { flex: 1; min-width: 150px; padding: 12px; background: var(--bg-tertiary); border: 1px solid var(--border-color); border-radius: 8px; color: #fff; }\n    button { padding: 12px 20px; background: var(--jb-yellow); color: #000; border: none; border-radius: 8px; font-weight: 600; cursor: pointer; }\n    button:hover { background: #e6b800; }\n    button.secondary { background: var(--bg-tertiary); color: #fff; border: 1px solid var(--border-color); }\n    .tabs { display: flex; gap: 8px; margin-bottom: 16px; }\n    .tab { padding: 10px 20px; background: transparent; border: 1px solid var(--border-color); border-radius: 8px; color: var(--text-muted); cursor: pointer; }\n    .tab.active { background: var(--jb-yellow); color: #000; border-color: var(--jb-yellow); }\n    .fn-card { background: var(--bg-tertiary); border-radius: 8px; padding: 16px; margin-bottom: 12px; }\n    .fn-name { font-family: var(--font-mono); font-weight: 600; margin-bottom: 12px; }\n    .fn-inputs { margin-bottom: 12px; }\n    .fn-inputs input { margin-bottom: 8px; }\n    .result { background: var(--bg-primary); border-radius: 6px; padding: 12px; margin-top: 12px; font-family: var(--font-mono); font-size: 13px; white-space: pre-wrap; word-break: break-all; }\n    .quick-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 16px; }\n    .quick-btn { padding: 8px 16px; font-size: 13px; }\n    .label { font-size: 12px; color: var(--text-muted); margin-bottom: 4px; }\n    .loading { text-align: center; padding: 20px; color: var(--text-muted); }\n    .badge { display: inline-block; padding: 4px 10px; border-radius: 20px; font-size: 12px; }\n    .badge.success { background: rgba(0,255,136,0.2); color: #0f8; }\n    .badge.error { background: rgba(255,68,68,0.2); color: #f44; }\n  \u003C\u002Fstyle>\n\u003C\u002Fhead>\n\u003Cbody>\n  \u003Cdiv class=\"container\">\n    \u003Ch1>Contract Explorer\u003C\u002Fh1>\n\n    \u003Cdiv class=\"card\">\n      \u003Cdiv class=\"row\">\n        \u003Cinput type=\"text\" id=\"address\" placeholder=\"Contract address (0x...)\">\n        \u003Cselect id=\"chain\">\n          \u003Coption value=\"1\">Ethereum\u003C\u002Foption>\n          \u003Coption value=\"10\">Optimism\u003C\u002Foption>\n          \u003Coption value=\"8453\">Base\u003C\u002Foption>\n          \u003Coption value=\"42161\">Arbitrum\u003C\u002Foption>\n          \u003Coption value=\"11155111\">Sepolia\u003C\u002Foption>\n        \u003C\u002Fselect>\n        \u003Cbutton onclick=\"loadContract()\">Load Contract\u003C\u002Fbutton>\n      \u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n\n    \u003Cdiv class=\"card\" id=\"walletSection\" style=\"display:none;\">\n      \u003Cdiv class=\"row\" style=\"justify-content: space-between; align-items: center;\">\n        \u003Cspan id=\"walletStatus\">Not connected\u003C\u002Fspan>\n        \u003Cbutton class=\"secondary\" id=\"connectBtn\" onclick=\"connectWallet()\">Connect Wallet\u003C\u002Fbutton>\n      \u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n\n    \u003Cdiv id=\"quickActions\" class=\"quick-actions\" style=\"display:none;\">\u003C\u002Fdiv>\n\n    \u003Cdiv class=\"tabs\" id=\"tabs\" style=\"display:none;\">\n      \u003Cbutton class=\"tab active\" onclick=\"showTab('read')\">Read\u003C\u002Fbutton>\n      \u003Cbutton class=\"tab\" onclick=\"showTab('write')\">Write\u003C\u002Fbutton>\n      \u003Cbutton class=\"tab\" onclick=\"showTab('events')\">Events\u003C\u002Fbutton>\n    \u003C\u002Fdiv>\n\n    \u003Cdiv id=\"content\">\u003C\u002Fdiv>\n  \u003C\u002Fdiv>\n\n  \u003Cscript>\n    let explorer = null;\n    let functions = { read: [], write: [], events: [] };\n    let currentTab = 'read';\n\n    async function loadContract() {\n      const address = document.getElementById('address').value;\n      const chainId = parseInt(document.getElementById('chain').value);\n      if (!address) return alert('Enter a contract address');\n\n      document.getElementById('content').innerHTML = '\u003Cdiv class=\"loading\">Loading contract...\u003C\u002Fdiv>';\n\n      try {\n        explorer = new ContractExplorer();\n        functions = await explorer.load(address, chainId);\n\n        document.getElementById('walletSection').style.display = 'block';\n        document.getElementById('tabs').style.display = 'flex';\n        renderQuickActions();\n        showTab('read');\n      } catch (e) {\n        document.getElementById('content').innerHTML = `\u003Cdiv class=\"badge error\">${e.message}\u003C\u002Fdiv>`;\n      }\n    }\n\n    function renderQuickActions() {\n      const el = document.getElementById('quickActions');\n      el.style.display = 'flex';\n      el.innerHTML = `\n        \u003Cspan style=\"color:#888;margin-right:8px;\">Quick:\u003C\u002Fspan>\n        \u003Cinput type=\"number\" id=\"quickProjectId\" placeholder=\"Project ID\" style=\"width:120px;padding:8px;\">\n        ${QUICK_ACTIONS.map(a => `\u003Cbutton class=\"secondary quick-btn\" onclick=\"runQuickAction('${a.name}')\">${a.name}\u003C\u002Fbutton>`).join('')}\n      `;\n    }\n\n    async function runQuickAction(name) {\n      const action = QUICK_ACTIONS.find(a => a.name === name);\n      const projectId = document.getElementById('quickProjectId').value;\n      if (!projectId) return alert('Enter a project ID');\n\n      const config = await loadChainConfig();\n      const addr = config.chains[explorer.chainId].contracts[action.contract];\n      const tempExplorer = new ContractExplorer();\n      await tempExplorer.load(addr, explorer.chainId);\n\n      try {\n        const result = await tempExplorer.call(action.fn, action.args(projectId));\n        alert(JSON.stringify(tempExplorer.format(result), null, 2));\n      } catch (e) {\n        alert('Error: ' + e.message);\n      }\n    }\n\n    function showTab(tab) {\n      currentTab = tab;\n      document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));\n      document.querySelector(`.tab:nth-child(${tab === 'read' ? 1 : tab === 'write' ? 2 : 3})`).classList.add('active');\n      renderFunctions();\n    }\n\n    function renderFunctions() {\n      const list = currentTab === 'events' ? functions.events : functions[currentTab];\n      const content = document.getElementById('content');\n\n      if (currentTab === 'events') {\n        content.innerHTML = list.map(e => `\n          \u003Cdiv class=\"fn-card\">\n            \u003Cdiv class=\"fn-name\">${e.name}\u003C\u002Fdiv>\n            \u003Cdiv style=\"color:#888;font-size:13px;\">${e.inputs?.map(i => `${i.type} ${i.name}`).join(', ') || 'No parameters'}\u003C\u002Fdiv>\n          \u003C\u002Fdiv>\n        `).join('') || '\u003Cdiv class=\"loading\">No events found\u003C\u002Fdiv>';\n        return;\n      }\n\n      content.innerHTML = list.map((fn, idx) => `\n        \u003Cdiv class=\"fn-card\" id=\"fn-${idx}\">\n          \u003Cdiv class=\"fn-name\">${fn.name}(${fn.inputs?.map(i => i.type).join(', ') || ''})\u003C\u002Fdiv>\n          ${fn.inputs?.length ? `\u003Cdiv class=\"fn-inputs\">\n            ${fn.inputs.map((inp, i) => `\n              \u003Cdiv class=\"label\">${inp.name || 'param' + i} (${inp.type})\u003C\u002Fdiv>\n              \u003Cinput type=\"text\" data-fn=\"${idx}\" data-param=\"${i}\" placeholder=\"${inp.type}\">\n            `).join('')}\n          \u003C\u002Fdiv>` : ''}\n          ${currentTab === 'write' && fn.stateMutability === 'payable' ? `\n            \u003Cdiv class=\"label\">ETH Value\u003C\u002Fdiv>\n            \u003Cinput type=\"text\" data-fn=\"${idx}\" data-value=\"true\" placeholder=\"0.0\">\n          ` : ''}\n          \u003Cbutton ${currentTab === 'write' ? '' : 'class=\"secondary\"'} onclick=\"callFn(${idx})\">\n            ${currentTab === 'write' ? 'Write' : 'Query'}\n          \u003C\u002Fbutton>\n          \u003Cdiv class=\"result\" id=\"result-${idx}\" style=\"display:none;\">\u003C\u002Fdiv>\n        \u003C\u002Fdiv>\n      `).join('') || '\u003Cdiv class=\"loading\">No functions found\u003C\u002Fdiv>';\n    }\n\n    async function callFn(idx) {\n      const fn = functions[currentTab][idx];\n      const inputs = [...document.querySelectorAll(`[data-fn=\"${idx}\"][data-param]`)].map(el => el.value);\n      const valueEl = document.querySelector(`[data-fn=\"${idx}\"][data-value]`);\n      const value = valueEl?.value || '0';\n      const resultEl = document.getElementById(`result-${idx}`);\n\n      resultEl.style.display = 'block';\n      resultEl.textContent = 'Loading...';\n\n      try {\n        if (currentTab === 'write') {\n          const tx = await explorer.send(fn.name, inputs, value);\n          resultEl.innerHTML = `\u003Cspan class=\"badge success\">TX: ${tx.hash}\u003C\u002Fspan>`;\n          await tx.wait();\n          resultEl.innerHTML += `\u003Cbr>\u003Cspan class=\"badge success\">Confirmed!\u003C\u002Fspan>`;\n        } else {\n          const result = await explorer.call(fn.name, inputs);\n          resultEl.textContent = JSON.stringify(explorer.format(result), null, 2);\n        }\n      } catch (e) {\n        resultEl.innerHTML = `\u003Cspan class=\"badge error\">${e.message}\u003C\u002Fspan>`;\n      }\n    }\n\n    async function connectWallet() {\n      try {\n        await explorer.wallet.connect(explorer.chainId);\n        document.getElementById('walletStatus').textContent = `Connected: ${explorer.wallet.address.slice(0,6)}...${explorer.wallet.address.slice(-4)}`;\n        document.getElementById('connectBtn').textContent = 'Connected';\n      } catch (e) {\n        alert(e.message);\n      }\n    }\n\n    \u002F\u002F Inline ContractExplorer and loadChainConfig from above\n    class ContractExplorer {\n      constructor() { this.wallet = { signer: null, address: null, connect: async (chainId) => {\n        if (!window.ethereum) throw new Error('No wallet found');\n        const provider = new ethers.BrowserProvider(window.ethereum);\n        await provider.send('eth_requestAccounts', []);\n        try { await window.ethereum.request({ method: 'wallet_switchEthereumChain', params: [{ chainId: '0x' + chainId.toString(16) }] }); } catch(e) {}\n        this.signer = await provider.getSigner();\n        this.address = await this.signer.getAddress();\n      }}; this.provider = null; this.contract = null; this.abi = null; this.chainId = 1; }\n      async load(address, chainId) {\n        this.chainId = chainId;\n        const config = await loadChainConfig();\n        this.provider = new ethers.JsonRpcProvider(config.chains[chainId].rpc);\n        this.abi = await this.fetchABI(address, chainId, config);\n        this.contract = new ethers.Contract(address, this.abi, this.provider);\n        return this.categorize();\n      }\n      async fetchABI(address, chainId, config) {\n        const explorers = { 1: 'api.etherscan.io', 10: 'api-optimistic.etherscan.io', 8453: 'api.basescan.org', 42161: 'api.arbiscan.io', 11155111: 'api-sepolia.etherscan.io' };\n        const url = `https:\u002F\u002F${explorers[chainId]}\u002Fapi?module=contract&action=getabi&address=${address}`;\n        const data = await (await fetch(url)).json();\n        if (data.status === '1') return JSON.parse(data.result);\n        throw new Error('ABI not found - contract may not be verified');\n      }\n      categorize() {\n        const items = this.abi.filter(x => x.type === 'function');\n        return { read: items.filter(f => ['view', 'pure'].includes(f.stateMutability)), write: items.filter(f => !['view', 'pure'].includes(f.stateMutability)), events: this.abi.filter(x => x.type === 'event') };\n      }\n      async call(fnName, args = []) { return await this.contract[fnName](...args); }\n      async send(fnName, args = [], value = '0') {\n        if (!this.wallet.signer) await this.wallet.connect(this.chainId);\n        const connected = this.contract.connect(this.wallet.signer);\n        const opts = value !== '0' ? { value: ethers.parseEther(value) } : {};\n        return await connected[fnName](...args, opts);\n      }\n      format(result) {\n        if (typeof result === 'bigint') return result.toString();\n        if (Array.isArray(result)) return result.map(r => this.format(r));\n        if (result && typeof result === 'object') { const obj = {}; for (const k of Object.keys(result)) if (isNaN(k)) obj[k] = this.format(result[k]); return obj; }\n        return result;\n      }\n    }\n\n    async function loadChainConfig() {\n      return { chains: {\n        1: { rpc: 'https:\u002F\u002Feth.llamarpc.com', contracts: { JBController: '0x0Ae7403b3C3B4C5222bBbE664bdD8600C593b23e' }},\n        10: { rpc: 'https:\u002F\u002Foptimism.llamarpc.com', contracts: { JBController: '0x0Ae7403b3C3B4C5222bBbE664bdD8600C593b23e' }},\n        8453: { rpc: 'https:\u002F\u002Fbase.llamarpc.com', contracts: { JBController: '0x0Ae7403b3C3B4C5222bBbE664bdD8600C593b23e' }},\n        42161: { rpc: 'https:\u002F\u002Farbitrum.llamarpc.com', contracts: { JBController: '0x0Ae7403b3C3B4C5222bBbE664bdD8600C593b23e' }},\n        11155111: { rpc: 'https:\u002F\u002Fsepolia.drpc.org', contracts: { JBController: '0x0Ae7403b3C3B4C5222bBbE664bdD8600C593b23e' }}\n      }};\n    }\n\n    const QUICK_ACTIONS = [\n      { name: 'Project Overview', contract: 'JBController', fn: 'currentRulesetOf', args: (p) => [p] },\n      { name: 'Token Supply', contract: 'JBController', fn: 'totalTokenSupplyWithReservedTokensOf', args: (p) => [p] },\n      { name: 'Pending Reserved', contract: 'JBController', fn: 'pendingReservedTokenBalanceOf', args: (p) => [p] }\n    ];\n  \u003C\u002Fscript>\n\u003C\u002Fbody>\n\u003C\u002Fhtml>\n```\n\n## Customization Points\n\n| What | Where |\n|------|-------|\n| Add quick actions | Extend `QUICK_ACTIONS` array |\n| Change styling | Override CSS variables in `:root` |\n| Add ABI sources | Modify `fetchABI()` method |\n| Custom result formatting | Extend `format()` method |\n\n## Example Prompts\n\n- \"Create an explorer for JBController on Optimism\"\n- \"Build a read-only explorer (no write tab)\"\n- \"Add a quick action to check project splits\"\n\n## See Also\n\n- `\u002Fjb-event-explorer-ui` - Event-focused browsing\n- `\u002Fjb-hook-deploy-ui` - Deploy custom hooks\n- `\u002Fjb-ruleset-timeline-ui` - Ruleset history visualization\n",{"name":5,"description":7},"main","e7e336ac628c3e3bd771aff1aaa469c93778f94a","skills\u002Fmejango\u002Fjuicy\u002Fjb-explorer-ui",0,"https:\u002F\u002Fgithub.com\u002Fkbarbel640-del\u002Fskills","https:\u002F\u002Fgithub.com\u002Fkbarbel640-del","2026-08-23 22:39:37",{"source":8,"skillName":5,"commands":49,"prompt":59,"zipAvailable":60},[50,53,56],{"tool":51,"command":52},"npx","npx skills add kbarbel640-del\u002Fskills --skill jb-explorer-ui",{"tool":54,"command":55},"bunx","bunx skills add kbarbel640-del\u002Fskills --skill jb-explorer-ui",{"tool":57,"command":58},"pnpm","pnpm dlx skills add kbarbel640-del\u002Fskills --skill jb-explorer-ui","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fkbarbel640-del\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fmejango\u002Fjuicy\u002Fjb-explorer-ui\u002FSKILL.md",true,[],[63,92,121,141,147,167],{"skillNo":64,"name":65,"nameZh":6,"description":66,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":67,"categories":68,"keywords":72,"publishedAt":36},"01M6G8J55967D6281ACMKAKTEQ","crowd-prompting","A marketplace where AI agents improve prompts, system instructions, tool descriptions, and other text-based content with domain expertise from real-world operations — and earn tokens for valuable contributions.","https:\u002F\u002Fgithub.com\u002Fkbarbel640-del\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fzzadrian\u002Fcrowd-prompting\u002FSKILL.md",[69],{"categoryNo":70,"categoryName":71,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000036","内容创作者",[73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91],"crowd prompting","prompting","api","with","tokens","domain","world","prompts","operations","instructions","text","tool","system","improve","productivity","marketplace","https","content","ai agents",{"skillNo":93,"name":94,"nameZh":6,"description":95,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":96,"categories":97,"keywords":103,"publishedAt":36},"01M6G8J0PKPAYVET6BWE8W4C26","dilemm-ai","Compete in DilemmAI, the prisoner's dilemma AI arena at dilemm.ai. Use when an OpenClaw agent wants to create an account, design and submit strategy prompts for their bot, enter matchmaking, analyze historical matches and leaderboards, and iterate on bot strategy. Requires headless browser automation (no API available yet).","https:\u002F\u002Fgithub.com\u002Fkbarbel640-del\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fzyori\u002Fdilemm-ai\u002FSKILL.md",[98],{"categoryNo":99,"categoryName":100,"taxonomyType":101,"taxonomyTypeDesc":102},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[104,75,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,80,120],"dilemm ai","arena","analyze","browser","openclaw agent","create","automation","browser automation","bot","openclaw","design","strategy","headless","compete","submit","matchmaking","use",{"skillNo":122,"name":123,"nameZh":6,"description":124,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":125,"categories":126,"keywords":130,"publishedAt":36},"01M6G8HWAF3K9SW7NP3Z83CPJ6","arxiv-paper-reviews","Interact with arXiv Crawler API to fetch papers, read reviews, and submit comments. Use when working with arXiv papers, fetching paper lists by date\u002Fcategory\u002Finterest, viewing paper details with comments, or submitting paper reviews via API at http:\u002F\u002F150.158.152.82:8000.","https:\u002F\u002Fgithub.com\u002Fkbarbel640-del\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fzxrys\u002Fweak-accept\u002FSKILL.md",[127],{"categoryNo":128,"categoryName":129,"taxonomyType":101,"taxonomyTypeDesc":102},"01JBSEEDCAT000000000000021","资料检索与研究",[131,132,133,134,135,136,137,76,138,118,120,32,75,139,140],"arxiv paper reviews","reviews","arxiv","paper","crawler","papers","comments","fetch","http","via",{"skillNo":142,"name":123,"nameZh":6,"description":124,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":143,"categories":144,"keywords":146,"publishedAt":36},"01M6G8HQJ7N7WMY6YV7D7SSJ16","https:\u002F\u002Fgithub.com\u002Fkbarbel640-del\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fzxrys\u002Farxiv-paper-reviews\u002FSKILL.md",[145],{"categoryNo":128,"categoryName":129,"taxonomyType":101,"taxonomyTypeDesc":102},[134,133,132,131,140,136,137,76,135,118,120,138,32,75,139],{"skillNo":148,"name":149,"nameZh":6,"description":150,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":151,"categories":152,"keywords":153,"publishedAt":36},"01M6G8HM95Y64JNVFC2HN4E2BG","git-summary","Get a quick summary of the current Git repository including status, recent commits, branches, and contributors.","https:\u002F\u002Fgithub.com\u002Fkbarbel640-del\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fzweack\u002Fgit-summary\u002FSKILL.md",[],[154,155,156,157,158,159,160,161,162,163,164,165,166],"git","summary","git summary","commits","repository","win32","status","linux","contributors","true","quick","get","branches",{"skillNo":168,"name":169,"nameZh":6,"description":170,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":171,"categories":172,"keywords":175,"publishedAt":36},"01M6G8HGEGF05CNEJCGVYFSC4R","agent-constitution","Interact with AgentConstitution governance contracts on Base Sepolia. Check compliance, read rules, log actions, query governance state.","https:\u002F\u002Fgithub.com\u002Fkbarbel640-del\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fztsalexey\u002Fagent-constitution\u002FSKILL.md",[173,174],{"categoryNo":99,"categoryName":100,"taxonomyType":101,"taxonomyTypeDesc":102},{"categoryNo":23,"categoryName":24,"taxonomyType":20,"taxonomyTypeDesc":21},[176,177,178,179,76,180,181,41,182,32,183,184,31,185,186,89,29,187,188],"agent constitution","constitution","contracts","tree","query","base","log","check","Rules","compliance","actions","governance","github"]