<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Boar Developer]]></title><description><![CDATA[The official blog of Boar Network — a blockchain infrastructure provider. We write about protocol development, validator operations, node monitoring, and the engineering behind reliable blockchain infrastructure. 
Built by a team of protocol specialists contributing to governance and pushing the boundaries of decentralized systems.]]></description><link>https://blog.boar.network</link><image><url>https://cdn.hashnode.com/uploads/logos/69ce504b0ff860b6dea7546e/8b1e7b9b-460d-49e2-bbbb-33fdba837629.png</url><title>Boar Developer</title><link>https://blog.boar.network</link></image><generator>RSS for Node</generator><lastBuildDate>Sat, 17 Oct 2026 15:31:42 GMT</lastBuildDate><atom:link href="https://blog.boar.network/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Running an Arbitrum One RPC Node on Kubernetes]]></title><description><![CDATA[We needed an Arbitrum One RPC node in our own infrastructure. The setup looked straightforward: deploy Nitro v3.9.8 as a GKE StatefulSet using a Helm chart, point it at our internal Geth for L1, confi]]></description><link>https://blog.boar.network/running-an-arbitrum-one-rpc-node-on-kubernetes</link><guid isPermaLink="true">https://blog.boar.network/running-an-arbitrum-one-rpc-node-on-kubernetes</guid><category><![CDATA[Web3]]></category><dc:creator><![CDATA[Krzysztof Łuczak]]></dc:creator><pubDate>Fri, 26 Jun 2026 10:15:01 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/69d65ddb707c1ce7683d066b/49ccdf20-77d4-4ea0-a115-cf8e674e0e61.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>We needed an Arbitrum One RPC node in our own infrastructure. The setup looked straightforward: deploy Nitro v3.9.8 as a GKE StatefulSet using a Helm chart, point it at our internal Geth for L1, configure init.latest: "pruned" to pull the latest snapshot, and let it do its thing. Chain ID 42161, non-validator, standard pruned sync. Nothing exotic.</p>
<p>The snapshot download ran for about 15 hours. Five parts, roughly 500 GiB each (the last one 163 GiB), totalling ~2.1 TiB. We watched the logs tick through <code>pruned.tar.part0000</code> through <code>pruned.tar.part0004</code> and waited. Then the pod crashed mid-extraction.</p>
<p>On the next restart, the node refused to initialize. The error was this:</p>
<pre><code class="language-plaintext">ERROR error initializing database  err="found 1 unexpected files in database directory, including: tmp"
</code></pre>
<p>That was it. No further detail. <code>l2chaindata</code> was never created - the database directory contained only the <code>LOCK</code> file and the <code>tmp/</code> staging directory left behind from the download. Every restart produced the same error. Every restart ended in the same crash loop. There was no flag to skip the check, no <code>--force-init</code>, no obvious way to tell the node to proceed anyway.</p>
<p>To understand why this is not a bug you can work around but a design choice you have to accommodate, it helps to look at how Nitro decides whether it is safe to initialize at all.</p>
<h2>How Nitro initializes from a snapshot</h2>
<h3>The init flags</h3>
<p>Nitro's snapshot init system is configured through <code>--init.*</code> flags (or the JSON config equivalent):</p>
<ul>
<li><p><code>init.latest: "pruned"</code> - downloads the latest pruned snapshot automatically</p>
</li>
<li><p><code>init.url: "https://..."</code> - pins to a specific snapshot URL</p>
</li>
<li><p><code>init.download-path</code> - where to stage downloaded tar parts, separate from the database directory; if not set, Nitro defaults to <code>&lt;persistent.chain&gt;/nitro/tmp/</code></p>
</li>
</ul>
<h3>Download flow</h3>
<p>The sequence, from <a href="https://github.com/OffchainLabs/nitro/blob/v3.9.8/cmd/nitro/init.go"><code>cmd/nitro/init.go</code></a>:</p>
<ol>
<li><p>Fetches the manifest from the snapshot URL</p>
</li>
<li><p>Downloads multipart tar files (<code>.part0000</code>, <code>.part0001</code>, ...) to <code>download-path</code></p>
</li>
<li><p>Joins the parts into a single archive</p>
</li>
<li><p>Extracts the archive directly into <code>&lt;persistent.chain&gt;/nitro/l2chaindata/</code></p>
</li>
<li><p>Cleans up the <code>tmp/</code> staging directory - but only if <code>download-path</code> was not explicitly set</p>
</li>
</ol>
<h3><code>checkEmptyDatabaseDir</code></h3>
<p>Before any initialization work begins, Nitro checks the database directory against a strict allowlist in <a href="https://github.com/OffchainLabs/nitro/blob/v3.9.8/cmd/nitro/init.go#L399-L420"><code>checkEmptyDatabaseDir</code></a>:</p>
<pre><code class="language-go">// cmd/nitro/init.go
allowedFiles := map[string]bool{
    "LOCK": true, "classic-msg": true, "l2chaindata": true,
}
for _, entry := range entries {
    if !allowedFiles[entry.Name()] {
        unexpectedFiles = append(unexpectedFiles, entry.Name())
    }
}
if len(unexpectedFiles) &gt; 0 {
    return fmt.Errorf("found %d unexpected files in database directory, including: %s", ...)
}
</code></pre>
<p>When <code>download-path</code> is not set (the default), Nitro creates <code>&lt;persistent.chain&gt;/nitro/tmp/</code> as the staging directory. If the pod crashes mid-download, <code>tmp/</code> is left behind with whatever partial tar parts had been written. On the next restart, <code>checkEmptyDatabaseDir</code> finds <code>tmp/</code> in the database directory - it is not in the allowlist - and the node refuses to start.</p>
<p>This is the most common failure mode when setting up a new Nitro node.</p>
<blockquote>
<p>The check runs before any initialization work. There is no flag to skip it, no recovery mode. The only way forward is to either remove the unexpected files manually or prevent them from being there in the first place.</p>
</blockquote>
<h2>The fix: init.download-path</h2>
<p>Set <code>init.download-path</code> to a path on a separate PVC. Nitro stages all download artifacts there, leaving the main data PVC containing only <code>LOCK</code> (and eventually <code>l2chaindata</code>). If the pod crashes, <code>checkEmptyDatabaseDir</code> passes on restart and the download resumes.</p>
<h3>Resuming downloads</h3>
<p>The download library Nitro uses - <code>github.com/cavaliergopher/grab</code> - detects already-downloaded files by comparing local file size to the server's <code>Content-Length</code>. Existing complete parts are skipped. This makes crash recovery essentially free - you lose at most one in-progress part.</p>
<h3>Cleanup is manual</h3>
<p>When <code>download-path</code> is set, Nitro does not clean up the download artifacts after extraction completes. The parts and joined archive remain on the init PVC. Delete them manually once you have confirmed the node is syncing.</p>
<h3>Disk space math</h3>
<table>
<thead>
<tr>
<th>Item</th>
<th>Size</th>
<th>Location</th>
</tr>
</thead>
<tbody><tr>
<td>Compressed tar parts (5 parts)</td>
<td>~2.1 TiB</td>
<td>Init PVC</td>
</tr>
<tr>
<td>Joined archive (before extraction)</td>
<td>~2.1 TiB</td>
<td>Init PVC (peak, simultaneous with parts)</td>
</tr>
<tr>
<td>Extracted pebble database (<code>l2chaindata</code>)</td>
<td>~600-700 GiB</td>
<td>Main data PVC</td>
</tr>
</tbody></table>
<p>Peak init PVC usage: ~4.2 TiB (parts + joined archive simultaneously). We used 6 TiB for headroom.</p>
<p>PVC sizing:</p>
<ul>
<li><p>Init PVC: 6 TiB on cheaper HDD-backed storage - sufficient because downloads only need sequential throughput, not live-database IOPS</p>
</li>
<li><p>Main data PVC: 4 TiB on SSD-backed storage - needed for the IOPS of a live pebble database</p>
</li>
</ul>
<h3>Helm chart implementation</h3>
<p><code>init.download-path</code> is auto-injected into <code>config.json</code> via the Helm template when <code>initStorage.enabled: true</code>. When disabled, the init PVC is removed by Helm automatically - it is a standalone PVC, not a <code>volumeClaimTemplate</code>, so Helm can delete it without touching stateful data.</p>
<blockquote>
<p>The extraction destination is always &lt;persistent.chain&gt;/nitro/l2chaindata/ regardless of download-path. Setting download-path only affects where the parts and joined archive live during the download phase.</p>
</blockquote>
<h2>Resource sizing</h2>
<p>Observed on an 8-core node, April 2026:</p>
<table>
<thead>
<tr>
<th>Phase</th>
<th>CPU</th>
<th>Memory</th>
</tr>
</thead>
<tbody><tr>
<td>Downloading snapshot parts</td>
<td>~1 core</td>
<td>1-5 GiB</td>
</tr>
<tr>
<td>Joining + extracting snapshot</td>
<td>~1 core</td>
<td>1-5 GiB</td>
</tr>
<tr>
<td>Syncing to head (feed catchup)</td>
<td>3-6 cores</td>
<td>~15 GiB</td>
</tr>
<tr>
<td>Steady state (synced, live)</td>
<td>~0.5 cores</td>
<td>~14 GiB</td>
</tr>
</tbody></table>
<p>Memory stays elevated after sync completes - pebble keeps a large portion of the database in the block cache. The jump to 3-6 cores during feed catchup is from processing the backlog of L2 transactions.</p>
<p>Our Kubernetes resource requests: <code>cpu: "4", memory: "8Gi"</code>. The 8 GiB request is conservative relative to the ~14-15 GiB actual usage during and after sync. Set limits accordingly or leave memory unlimited.</p>
<blockquote>
<p>During feed catchup the node is CPU-heavy: it executes the backlog of L2 transactions as fast as it can. The 4-core request lets the pod schedule on a reasonably sized node while allowing burst up to 6 cores. If your cluster enforces CPU limits, expect catchup to take longer.</p>
</blockquote>
<h2>Sync phases</h2>
<h3>Phase 1: Blob-fetching sync (historical)</h3>
<p>Nitro replays historical L1 batches from the snapshot date forward. Batches posted after Dencun (EIP-4844, March 2024) are stored as blobs on the beacon chain rather than as calldata. Nitro fetches them from the beacon chain as it processes each batch.</p>
<p>Because our local Prysm node was still backfilling at this point and did not yet cover the older blob range, these historical blobs were served by Chainstack, the external beacon endpoint we ran as the primary during sync. Chainstack saw ~150-200 req/min during this phase. The phase ends once the node catches up near the chain head and switches to the sequencer feed.</p>
<p>(The beacon URL configuration - Chainstack as primary, local Prysm as fallback - is covered in the next section.)</p>
<h3>Phase 2: Feed-driven sync (near head)</h3>
<p>Once close enough to the chain head, Nitro switches to receiving transactions from the sequencer feed (<code>wss://arb1.arbitrum.io/feed</code>). No blob fetching is needed in this mode - the feed delivers transaction data directly.</p>
<p>Observable: Chainstack beacon requests drop to ~0.</p>
<p>The log line <code>Reading message result remotely</code> repeating on the same <code>msgIdx</code> is normal during this phase - it is polling the sequencer feed while waiting for the next message to be confirmed:</p>
<pre><code class="language-plaintext">INFO Reading message result remotely.  msgIdx=432935973
INFO Reading message result remotely.  msgIdx=432935973
</code></pre>
<h3>Phase 3: Live (synced)</h3>
<p><code>created block</code> logs appear at real-time cadence. The node is at head.</p>
<h3>Timing</h3>
<p>Real measurements, pruned snapshot, April 2026:</p>
<table>
<thead>
<tr>
<th>Phase</th>
<th>Duration</th>
</tr>
</thead>
<tbody><tr>
<td>Download ~2.1 TiB (5 parts)</td>
<td>~15 hours</td>
</tr>
<tr>
<td>Verification/hashing of existing parts</td>
<td>~45 min (CPU-bound, ~2K IOPS reads)</td>
</tr>
<tr>
<td>Joining parts into archive</td>
<td>~3 hours</td>
</tr>
<tr>
<td>Extraction to l2chaindata</td>
<td><del>3 hours (</del>10.6 GiB/min on SSD-backed storage)</td>
</tr>
<tr>
<td>Catch-up from snapshot date to head</td>
<td>~10 hours (1.5M blocks at ~2,500 blocks/min)</td>
</tr>
</tbody></table>
<p>Total from scratch to synced: ~31 hours, of which ~15 hours is the download.</p>
<h3>Background cleanup during catchup</h3>
<p>During Phase 1 and 2 you will see log lines like these:</p>
<pre><code class="language-plaintext">INFO Unindexing transactions  blocks=44,635,000 txs=75,903,733 total=327,775,881 elapsed=14m33s
INFO Deleting tail epoch #1 in progress...
</code></pre>
<p>These are normal - not errors. Nitro is pruning old transaction indexes from the snapshot data in the background while it syncs.</p>
<h2>Beacon and blob setup</h2>
<p>Nitro needs a beacon endpoint to fetch EIP-4844 blobs. We run two: an external Chainstack endpoint and a local Prysm instance. During the initial sync we set Chainstack as the primary and local Prysm as the fallback, because Prysm had only just been deployed and did not yet hold historical blobs. Nitro queries the primary first and falls back to the secondary when the primary does not have the blob.</p>
<pre><code class="language-yaml">parent-chain:
  blob-client:
    beacon-url: "https://chainstack-url/beacon/..."  # primary (external Chainstack)
    secondary-beacon-url: "http://prysm.svc:3500"    # fallback (local Prysm)
</code></pre>
<p>The field names are <code>beacon-url</code> (primary) and <code>secondary-beacon-url</code> (fallback). During sync we pointed the primary at Chainstack so historical blobs resolved immediately, with local Prysm as the fallback.</p>
<p><strong>Why two URLs, and the switch:</strong> local Prysm was deployed on April 20, 2026 with <code>--enable-backfill</code>. It takes approximately 18 days (~May 8) to backfill full blob history. Until then, Prysm returns <code>404</code> for pre-April-20 slots, so keeping Chainstack as the primary kept the sync from stalling on those misses. After ~May 8, once Prysm had full history, we flipped the primary to local Prysm and dropped the Chainstack <code>secondary-beacon-url</code>, relying on local Prysm alone.</p>
<h3>Monitoring blob fetches</h3>
<p>Prysm exposes HTTP API request counts. Three useful queries:</p>
<pre><code class="language-promql"># Blob request rate by status code
rate(http_request_count{exported_endpoint="blob.Blobs", job="prysm-beacon"}[5m])

# Blob request latency p99
histogram_quantile(0.99,
  rate(http_request_latency_seconds_bucket{exported_endpoint="blob.Blobs", job="prysm-beacon"}[5m])
)

# Backfill progress
backfill_blobs_download_count{job="prysm-beacon"}
</code></pre>
<blockquote>
<p>Prysm's HTTP metric label is exported_endpoint, not endpoint. Prometheus relabeling renames it during scrape. A 200 status means Prysm has that blob locally; 404 means it has not backfilled that slot yet. While Prysm is still backfilling, Chainstack stays the primary so those gaps do not stall the sync.</p>
</blockquote>
<h2>Kubernetes operational notes</h2>
<h3>Diagnostic mode</h3>
<p>We added a <code>diagnosticMode</code> flag to the Helm chart. When <code>true</code>, it replaces the Nitro command with a signal-handled sleep:</p>
<pre><code class="language-yaml">command:
  - /bin/sh
  - -c
  - "trap 'exit 0' TERM INT; sleep infinity &amp; wait"
</code></pre>
<p>The <code>sleep infinity &amp; wait</code> form matters. <code>wait</code> is a shell builtin and is interrupted by signals immediately. Bare <code>sleep infinity</code> ignores SIGTERM, so <code>kubectl delete pod</code> hangs until the grace period expires - and if you resort to <code>--force --grace-period=0</code>, network teardown is skipped, leaving stale interfaces on the host.</p>
<h3>Stale veth after force-delete</h3>
<p>Force-deleting a pod (<code>--force --grace-period=0</code>) bypasses network teardown. The host-side veth interface remains, causing the replacement pod to fail with:</p>
<pre><code class="language-plaintext">Failed to create pod sandbox: ... container veth name ("eth0") peer provided ("gke33b862248e7") already exists
</code></pre>
<p>GKE kubelet GC resolves this automatically in ~10 minutes (observed). If urgent: drain and uncordon the node, or SSH in and <code>ip link delete &lt;veth-name&gt;</code>.</p>
<blockquote>
<p>Use --force --grace-period=0 only when you are certain the pod is truly stuck. The stale veth can prevent the replacement pod from starting on the same node for up to 10 minutes.</p>
</blockquote>
<h3>Moving files between PVCs</h3>
<p><code>mv</code> across different PVCs falls back to copy+delete (different filesystems). Use rsync for large transfers:</p>
<pre><code class="language-bash">rsync -a --info=progress2 /source/ /dest/
</code></pre>
<p><code>--info=progress2</code> shows overall bytes/speed/ETA instead of per-file output - much cleaner for multi-TiB transfers.</p>
<h2>Snapshots</h2>
<p>Browse available snapshots at <a href="https://snapshot-explorer.arbitrum.io/">Arbitrum snapshot explorer</a></p>
<p>Available types for Arbitrum One (<code>arb1</code>):</p>
<ul>
<li><p><strong>Pruned</strong> (recommended for RPC nodes): ~2.1 TiB compressed, new snapshot every ~12 days</p>
</li>
<li><p><strong>Archive-path</strong>: much larger, for full history access</p>
</li>
<li><p><strong>Archive</strong>: LevelDB format (legacy)</p>
</li>
</ul>
<p><code>init.latest: "pruned"</code> always picks the newest available pruned snapshot. To pin a specific snapshot:</p>
<pre><code class="language-yaml">init:
  url: "https://snapshot.arbitrum.io/arb1/2026-04-08-a3b0e3cf/"
</code></pre>
<p>Pinning is useful when you have already downloaded parts and want to resume a specific snapshot after a crash. Set <code>init.url</code> to the same snapshot URL and <code>grab</code> will detect the existing parts by Content-Length and skip re-downloading them.</p>
<p>The <a href="https://github.com/OffchainLabs/community-helm-charts/tree/main/charts/nitro">community Helm chart</a> ships with an empty <code>nitro.initContainers</code> stub - no built-in snapshot handling. The <code>tmp/</code> problem exists there too with no upstream workaround. Custom init containers can be injected via <code>initContainers:</code> (raw YAML passthrough).</p>
<blockquote>
<p>To verify what snapshot a running node initialized from, check the logs for INFO Downloading initial database url=... or INFO Successfully joined parts into archive. These appear once during the init phase and are not repeated on subsequent restarts.</p>
</blockquote>
<p>The non-obvious part of this setup is that Nitro's default staging location for snapshot downloads sits inside the same directory it checks for unexpected files before initializing - so a crash leaves behind a file that permanently blocks the next start. Setting <code>init.download-path</code> to a separate PVC moves the staging artifacts out of that directory, making downloads crash-safe and resumable without manual intervention. For a closer look at the logic, <a href="https://github.com/OffchainLabs/nitro/blob/v3.9.8/cmd/nitro/init.go"><code>cmd/nitro/init.go</code></a> is the place to start.</p>
]]></content:encoded></item><item><title><![CDATA[Boar’s Hackathon Starter Pack for the Mezo Hackathon: Building Bitcoin’s Future]]></title><description><![CDATA[🎁 Special gifts for hackathon builders! 🎁

The Mezo Hackathon: Building Bitcoin’s Future, organized by Encode Club, is a four-week virtual hackathon inviting developers to push Bitcoin into everyday]]></description><link>https://blog.boar.network/boar-hackathon-starter-pack-for-the-mezo-hackathon-building-bitcoin-future</link><guid isPermaLink="true">https://blog.boar.network/boar-hackathon-starter-pack-for-the-mezo-hackathon-building-bitcoin-future</guid><category><![CDATA[mezo]]></category><category><![CDATA[hackathon]]></category><category><![CDATA[Blockchain]]></category><category><![CDATA[AI]]></category><category><![CDATA[mcp]]></category><category><![CDATA[Bitcoin]]></category><dc:creator><![CDATA[Krzysztof Łuczak]]></dc:creator><pubDate>Thu, 09 Apr 2026 16:10:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/69d65ddb707c1ce7683d066b/5a1edd51-f0d1-4d45-8ef4-58c5c66bdf19.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>🎁 Special gifts for hackathon builders! 🎁</p>
</blockquote>
<p>The <a href="https://www.encodeclub.com/programmes/mezo-hackathon-building-bitcoins-future">Mezo Hackathon: Building Bitcoin’s Future</a>, organized by <a href="https://www.encodeclub.com">Encode Club</a>, is a four-week virtual hackathon inviting developers to push Bitcoin into everyday finance. Builders compete for a <strong>$30,000 worth of veMEZO tokens</strong> by creating apps and services on top of Mezo — the Bitcoin-first, EVM-compatible blockchain.</p>
<p>Whether you are building a DeFi protocol, a payment tool, a yield-generating product, or anything in between — Boar provides the infrastructure to build on top of it. RPC endpoints for Mezo, Ethereum, and Bitcoin, plus AI-ready tooling on top, so you can focus on shipping.</p>
<hr />
<h2><strong>A Starter Pack from Boar</strong></h2>
<p>We work every day to make blockchain infrastructure accessible to developers — with RPCs for Bitcoin, Ethereum, and Mezo, plus tooling for AI-assisted development. For this hackathon, we are putting three things on the table specifically for participating builders.</p>
<hr />
<h3><strong>1. Blockchain MCP — Query Mezo, Ethereum, and Bitcoin with Your AI Agent</strong></h3>
<p>One of the hardest parts of blockchain development is debugging: tracing a failed transaction, decoding a revert reason, tracking balances across chains. Our <a href="https://github.com/boar-network/blockchain-mcp">Blockchain MCP</a> is a free, remote <a href="https://modelcontextprotocol.io/">Model Context Protocol</a> server that gives your AI coding assistant (Claude, Cursor, and others) <strong>direct read access to blockchain data</strong> — no local node, no API key, no setup.</p>
<p><strong>What it supports:</strong></p>
<ul>
<li><p><strong>Bitcoin</strong> — mainnet and testnet (UTXOs, transactions, blocks, address data)</p>
</li>
<li><p><strong>Ethereum</strong> — mainnet (balances, tokens, smart contract calls, ENS resolution)</p>
</li>
<li><p><strong>Mezo</strong> — mainnet (EVM-compatible queries, bridge data, MUSD tracking)</p>
</li>
</ul>
<p><strong>What it can do:</strong></p>
<ul>
<li><p>Look up balances, transactions, and block data across all three chains</p>
</li>
<li><p>Decode smart contract ABI and trace transaction failures with human-readable revert reasons</p>
</li>
<li><p>Batch-read on-chain state via Multicall3</p>
</li>
<li><p>Resolve ENS names and fetch associated metadata</p>
</li>
<li><p>Query ERC-20 token holdings with full metadata</p>
</li>
</ul>
<p>It is completely stateless and requires zero authentication. Just drop the config into Claude, Cursor or your AI agent of choice and start querying.</p>
<p><strong>Claude Code — run in your terminal:</strong></p>
<pre><code class="language-shell">claude mcp add boar-blockchain-mcp-basic --transport http --scope project https://mcp.boar.network/basic
claude mcp add boar-blockchain-mcp-advanced --transport http --scope project https://mcp.boar.network/advanced
</code></pre>
<p>Full setup instructions and a Prompt Cookbook with six ready-to-use agent workflows are available in the <a href="https://github.com/boar-network/blockchain-mcp">GitHub repository</a>.</p>
<hr />
<h3><strong>2. Higher RPC Limits for Mezo and Ethereum</strong></h3>
<p>Boar offers production-grade RPC endpoints for Mezo and Ethereum, free to use within standard rate limits. For hackathon teams building under time pressure, those limits can sometimes get in the way.</p>
<p>If you are participating in the Mezo Hackathon and need higher throughput, <strong>you can request elevated limits at no cost — double the free plan limits</strong>.</p>
<p>RPC endpoints available:</p>
<ul>
<li><p><strong>Mezo Mainnet</strong></p>
</li>
<li><p><strong>Ethereum Mainnet</strong></p>
</li>
</ul>
<p>You can generate your API key and find endpoint URLs at <a href="https://dashboard.boar.network/">dashboard.boar.network</a>.</p>
<hr />
<h3><strong>3. Free Bitcoin RPC — Mainnet and Testnet3</strong></h3>
<p>Bitcoin RPC access is a paid tier on the Boar platform. For the Mezo Hackathon, <strong>we are opening it up for free</strong> to all registered participants — both Bitcoin Mainnet and Testnet3.</p>
<p>If your project involves bridging assets to Mezo, building a Bitcoin-native payment flow, or anything that touches the Bitcoin base layer, you will need reliable RPC access. We are making sure that is not a blocker for you.</p>
<p><strong>To unlock both higher RPC limits and free Bitcoin access in one step</strong>, fill out our <a href="https://forms.gle/uCsrczkQgq1JXXuv8">hackathon request form</a>. After submitting, the Bitcoin Mainnet and Testnet3 endpoint URLs will appear directly on the confirmation screen — no waiting, no back-and-forth. <a href="https://dashboard.boar.network/">Sign in or create your Boar account</a> first to get your API key, then submit the form.</p>
<hr />
<h2><strong>Why Boar</strong></h2>
<p>We are a blockchain infrastructure provider focused on three networks that matter most to the Mezo ecosystem: Bitcoin, Ethereum, and Mezo itself. Our platform offers:</p>
<ul>
<li><p><strong>RPC endpoints</strong> for Bitcoin, Ethereum, and Mezo</p>
</li>
<li><p><strong>High availability</strong> infrastructure with global coverage</p>
</li>
<li><p><strong>Developer-first tooling</strong>, including the Blockchain MCP for AI-assisted workflows</p>
</li>
</ul>
<p>Our goal is simple: developers should spend time building products, not fighting infrastructure. Everything we are giving away for this hackathon reflects that.</p>
<hr />
<h2><strong>How to Get Started</strong></h2>
<p>Here is the short version of what to do right now:</p>
<ol>
<li><p><strong>Register for the hackathon</strong> at <a href="https://www.encodeclub.com/programmes/mezo-hackathon-building-bitcoins-future">encodeclub.com/programmes/mezo-hackathon-building-bitcoins-future</a></p>
</li>
<li><p><strong>Create your Boar account</strong> at <a href="https://dashboard.boar.network/">dashboard.boar.network</a> and generate your API key</p>
</li>
<li><p><strong>Add the Blockchain MCP</strong> to your AI tool of choice using the instructions at <a href="https://github.com/boar-network/blockchain-mcp">github.com/boar-network/blockchain-mcp</a></p>
</li>
<li><p><a href="https://forms.gle/uCsrczkQgq1JXXuv8"><strong>Submit the hackathon form</strong></a> to unlock elevated RPC limits and free Bitcoin RPC access</p>
</li>
</ol>
<p>Good luck, and build something great.</p>
<hr />
<p><em>Have questions about Boar’s infrastructure or the hackathon offer? Reach out to us.</em></p>
]]></content:encoded></item><item><title><![CDATA[So Your AI Coding Assistant Can't See the Blockchain? ]]></title><description><![CDATA[There's a weird gap in how AI coding assistants work today. They can write a Solidity contract from a napkin spec. They can scaffold a frontend that talks to that contract. They'll catch reentrancy bu]]></description><link>https://blog.boar.network/so-your-ai-coding-assistant-can-t-see-the-blockchain</link><guid isPermaLink="true">https://blog.boar.network/so-your-ai-coding-assistant-can-t-see-the-blockchain</guid><category><![CDATA[mcp]]></category><category><![CDATA[mcp server]]></category><category><![CDATA[#ai-tools]]></category><category><![CDATA[Blockchain]]></category><category><![CDATA[Bitcoin]]></category><category><![CDATA[Ethereum]]></category><category><![CDATA[Bitcoin Layer 2 Solutions]]></category><dc:creator><![CDATA[Jagoda | Boar]]></dc:creator><pubDate>Thu, 09 Apr 2026 09:43:24 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/69d65ddb707c1ce7683d066b/45b11c70-4205-4d18-a320-876309f2e1a4.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>There's a weird gap in how AI coding assistants work today. They can write a Solidity contract from a napkin spec. They can scaffold a frontend that talks to that contract. They'll catch reentrancy bugs you missed at 2am. But if you ask one a genuinely simple question - "hey, did my deploy actually go through?" - it can't tell you. It doesn't know. It has no way to look.</p>
<p>Your AI assistant can write code that <em>reads</em> the blockchain, but it can't <em>read the blockchain itself</em>. It's like having a very talented translator who's never allowed to listen to the conversation.</p>
<p>This post is about why that gap exists, what MCP is and how it closes that gap, and some fun things that become possible once your AI can actually see what's happening on-chain.</p>
<h2>Why AI agents are blind to on-chain data</h2>
<p>It helps to understand why this limitation exists in the first place.</p>
<p>Large language models work with text. They're trained on text, they reason in text, they produce text. When you ask Claude or Copilot to write a smart contract, it's doing something it's built for - generating text that happens to be code.</p>
<p>But blockchain state isn't text. It's a live, constantly changing database distributed across thousands of nodes. To check a balance, you need to make an RPC call to an actual node. To decode a failed transaction, you need to fetch the receipt, extract the revert data, and run it through an ABI decoder. These are runtime operations, not text generation tasks.</p>
<p>Without a way to make those calls, the model can only work with what's in the conversation: the code you pasted in, the error message you copied, whatever context you manually provided. It's working with a snapshot you gave it, not with reality.</p>
<h2>MCP: giving AI tools hands (and eyes)</h2>
<p>This is where the <a href="https://modelcontextprotocol.io/">Model Context Protocol</a> comes in. MCP is an open standard - think of it as a plugin system for AI assistants. It lets your AI client (Claude Desktop, Cursor, VS Code, Claude Code, Windsurf, Gemini CLI) connect to external servers that expose tools the model can call during a conversation.</p>
<p>The key insight is that the model doesn't need to know <em>how</em> to make an RPC call. It just needs to know that a tool called <code>eth_get_balance</code> exists, what parameters it takes, and what it returns. The MCP server handles the actual blockchain interaction. The model handles the reasoning.</p>
<p>This is a general pattern - MCP servers exist for databases, APIs, file systems, all sorts of things. But for blockchain developers specifically, it solves a problem that's been surprisingly annoying: your AI assistant can finally look things up instead of asking you to look things up for it.</p>
<h2>What this looks like in practice (with real addresses)</h2>
<p>We built <a href="https://github.com/boar-network/blockchain-mcp">Boar's Blockchain MCP</a> , a remote MCP server with 50 read-only tools covering Bitcoin, Ethereum, and Mezo. No install, no API key, no account. It runs on Cloudflare's edge, uses Streamable HTTP transport with SSE fallback, and every request is stateless.</p>
<p>But enough about the plumbing. Let's talk about what you can actually <em>do</em> with it, because that's where it gets interesting.</p>
<p><strong>Settle a mass between friends</strong></p>
<p>Your coworker insists Vitalik has moved most of his ETH out of his main wallet. You're not so sure. Instead of alt-tabbing to Etherscan:</p>
<blockquote>
<p>"Resolve vitalik.eth and tell me the current ETH balance. Also, has the address received any transactions in the last 10 blocks?"</p>
</blockquote>
<p>The agent resolves the ENS name, fetches the balance, and checks recent activity. Argument settled in 15 seconds without leaving your editor. (Spoiler: he still has quite a bit.)</p>
<p><strong>The "is this contract what I think it is?" moment</strong></p>
<p>You're reading through someone's codebase and they're importing a contract at <code>0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48</code>. No comment explaining what it is. Classic.</p>
<blockquote>
<p>"What is the contract at 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 on Ethereum? Fetch its ABI and give me a summary of what it can do."</p>
</blockquote>
<p>The agent verifies it's a contract (not an EOA), pulls the verified ABI from Sourcify, and tells you it's the USDC proxy - listing <code>transfer</code>, <code>approve</code>, <code>balanceOf</code>, <code>mint</code>, <code>burn</code>, the <code>Transfer</code> event, all of it. You can then ask follow-up questions: "what's the total supply?" and it'll call <code>eth_call</code> to read directly from the contract.</p>
<p>This beats the workflow of: open browser → navigate to Etherscan → paste address → click "Contract" tab → click "Read Contract" → scroll through the list.</p>
<p><strong>Archaeology on the Bitcoin genesis block</strong></p>
<p>This one's just fun:</p>
<blockquote>
<p>"Look up the balance and UTXOs for Satoshi's address: 1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa. How many people have sent BTC to it, and what's the total?"</p>
</blockquote>
<p>The agent calls <code>btc_get_balance</code> and <code>btc_get_utxos</code> and returns years worth of accumulated donations from people sending small amounts to the genesis address. It's a great way to test that your Bitcoin tooling works, and honestly it's just a fun rabbit hole to go down with an AI that can actually read the data.</p>
<p><strong>The 3am "why did this revert?" debugging session</strong></p>
<p>Your transaction failed. The receipt says <code>status: 0x0</code>. Very helpful, Ethereum.</p>
<blockquote>
<p>"Transaction 0x596030fc0f8e2ccf193ce5f2b661446d48645da5621d02938621e2c1b6b93e2a failed on Ethereum. Fetch the receipt, decode the revert reason, and explain what went wrong in plain English."</p>
</blockquote>
<p>The agent pulls the receipt, extracts the revert data, decodes it, and tells you: this was a Uniswap V3 swap that failed because the transaction deadline had passed. In plain English. No hex decoding, no ABI lookup, no opening five different tabs.</p>
<p>This is the kind of thing that takes 30 seconds with the MCP and 10 minutes without it - and at 3am, that difference matters a lot.</p>
<p><strong>Batch-reading contract state without writing a script</strong></p>
<p>You need to check 5 different values from a contract - maybe prices from an oracle, balances from a vault, or parameters from a governance contract.</p>
<blockquote>
<p>"Use Multicall3 to batch-read the following from contract 0x...: totalSupply, paused, owner, decimals, and symbol."</p>
</blockquote>
<p>One call, five results. No for-loop, no script, no waiting for sequential RPC responses. The agent batches everything through <code>eth_multicall</code> and gives you a clean summary.</p>
<h2>The setup - it's embarrassingly simple</h2>
<p>All of this works once you add a URL to a config file. Here's the quick version for the most popular clients.</p>
<p><strong>Claude Desktop</strong> - add to <code>claude_desktop_config.json</code>:</p>
<pre><code class="language-json">{
  "mcpServers": {
    "boar-blockchain-mcp-basic": {
      "type": "streamable-http",
      "url": "https://mcp.boar.network/basic"
    },
    "boar-blockchain-mcp-advanced": {
      "type": "streamable-http",
      "url": "https://mcp.boar.network/advanced"
    }
  }
}
</code></pre>
<p><strong>Claude Code</strong> - run in terminal:</p>
<pre><code class="language-shell">claude mcp add boar-blockchain-mcp-basic --transport http --scope project https://mcp.boar.network/basic
claude mcp add boar-blockchain-mcp-advanced --transport http --scope project https://mcp.boar.network/advanced
</code></pre>
<p><strong>Configuration for Cursor, VS Code, Windsurf, Gemini and others is</strong> <a href="https://github.com/boar-network/blockchain-mcp?tab=readme-ov-file#-quick-start"><strong>here</strong></a><strong>.</strong></p>
<p>That's the <code>/basic</code> endpoint with 37 tools - balances, transactions, blocks, logs, gas, ENS, ABIs. It covers most of what you'll need.</p>
<p>When you want contract calls, revert decoding, calldata encoding, or Multicall3, add the <code>/advanced</code> endpoint as a second server (13 tools, completely separate set, no overlap).</p>
<p>One thing worth knowing: each endpoint adds its tool descriptions to your AI's context window. If you're working on something where context space is tight, start with just <code>/basic</code> and add <code>/advanced</code> only when you need it.</p>
<h2>Why "read-only" is a feature, not a limitation</h2>
<p>Every tool is strictly read-only. No transaction signing, no private key access, no state modification.</p>
<p>A read-only surface area means the security model is simple. There's no risk of your AI agent accidentally sending a transaction, approving a token spend, or interacting with a malicious contract in a way that costs you money. It can look at everything, touch nothing. For a tool that's designed to run inside your AI assistant - where prompts can be ambiguous and the model is making autonomous tool calls - that's exactly the trust model you want.</p>
<h2>Things people don't immediately think of</h2>
<p>Beyond the obvious "look up a balance" use case, a few patterns have surprised us.</p>
<p><strong>Onboarding new team members</strong> gets easier. Someone who's new to blockchain development can ask "what tokens does this address hold?" or "what happened in this transaction?" in plain English. They don't need to learn <code>cast</code>, pick the right block explorer, or parse JSON-RPC responses. The AI translates between "human question" and "blockchain query" automatically.</p>
<p><strong>Exploring unfamiliar chains</strong> becomes frictionless. The interface is the same whether you're looking at Bitcoin UTXOs, Ethereum contract state, or Mezo token balances. You don't need to find the right explorer, learn a new CLI, or figure out which RPC methods a chain supports. You just ask.</p>
<p>And if you're <strong>building AI agents</strong> that need on-chain awareness - monitoring bots, analytics pipelines, research tools - the MCP gives you a clean interface without writing custom RPC code for each chain.</p>
<h2>The repo, the docs, and how to break things</h2>
<p>The whole thing is MIT-licensed and lives on <a href="https://github.com/boar-network/blockchain-mcp">GitHub</a>. The repo includes full tool references for the <a href="https://github.com/boar-network/blockchain-mcp/blob/main/docs/tools-basic.md">basic</a> and <a href="https://github.com/boar-network/blockchain-mcp/blob/main/docs/tools-advanced.md">advanced</a> endpoints, setup guides for every supported client, and a <a href="https://github.com/boar-network/blockchain-mcp/blob/main/docs/prompt-cookbook.md">prompt cookbook</a> with six detailed workflows you can copy-paste right now.</p>
<p>We're adding more chains and more tools based on what people actually use. If you try it and something doesn't work the way you'd expect, or you want support for a chain we don't cover yet, <a href="https://github.com/boar-network/blockchain-mcp/issues">open an issue</a>. The best features we've shipped so far came from exactly that kind of feedback.</p>
<p>So - what's the first thing you'd ask your AI to look up on-chain? We've been having fun with increasingly weird prompts internally, and we're curious what people come up with.</p>
]]></content:encoded></item><item><title><![CDATA[Why Your RPC Provider Matters More Than You Think]]></title><description><![CDATA[If you've built anything on a blockchain, you've dealt with RPC endpoints. They're one of those things that seem trivial until they aren't - and then suddenly a flaky provider is the reason your trans]]></description><link>https://blog.boar.network/why-your-rpc-provider-matters-more-than-you-think</link><guid isPermaLink="true">https://blog.boar.network/why-your-rpc-provider-matters-more-than-you-think</guid><category><![CDATA[RPC]]></category><category><![CDATA[RPC NODE PROVIDER]]></category><category><![CDATA[Blockchain]]></category><category><![CDATA[Ethereum]]></category><category><![CDATA[Bitcoin]]></category><category><![CDATA[Bitcoin Layer 2 Solutions]]></category><dc:creator><![CDATA[Jagoda | Boar]]></dc:creator><pubDate>Wed, 08 Apr 2026 09:34:35 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/69d65ddb707c1ce7683d066b/a29d9b39-baf2-40ad-a94b-71d1cce90ea3.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If you've built anything on a blockchain, you've dealt with RPC endpoints. They're one of those things that seem trivial until they aren't - and then suddenly a flaky provider is the reason your transactions are failing, your frontend is showing stale data, and you're spending an afternoon debugging a problem that isn't even in your code.</p>
<p>We've been there. That's a big part of why we built Boar Network's RPC service.</p>
<h2>The infrastructure you forget about - until it breaks</h2>
<p>Every time your application reads a balance, submits a transaction, or fetches a block, it's making an RPC call. It's the most fundamental piece of infrastructure in any blockchain project - and it's the one most developers spend the least time thinking about.</p>
<p>Here's what typically happens: you grab a free endpoint from whoever shows up first in a Google search, hardcode it into your config, and move on. It works fine for a while. Then your app gets real users, or you start deploying across multiple chains, and things start breaking in ways that are hard to diagnose.</p>
<p>Timeouts that come and go. Rate limits you didn't know existed. Inconsistent responses between providers on different chains. The kind of problems where the error message tells you nothing useful.</p>
<h2>So we built our own - and opened it up</h2>
<p>Boar Network runs RPC infrastructure for Ethereum, Bitcoin, and Mezo, with more chains coming. We're not a reseller - we run our own nodes, contribute to protocol development, and participate in governance on the networks we support. We understand these chains because we help build them.</p>
<p>The service works through a simple model: you sign up at the <a href="https://dashboard.boar.network/login">Boar Dashboard</a>, generate a key, and your endpoints are live. The URL format is straightforward:</p>
<pre><code class="language-markdown">https://&lt;network-name&gt;.boar.network/&lt;your-key&gt;
</code></pre>
<p>One dashboard, one key management system, multiple chains. No juggling separate providers for each network.</p>
<h2>From sign-up to first response</h2>
<p>Once you have your key, you can test it immediately:</p>
<pre><code class="language-shell">curl \
    -X POST \
    https://&lt;network-name&gt;.boar.network/&lt;your-key&gt; \
    -H "Content-Type: application/json" \
    -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
</code></pre>
<p>That's a standard JSON-RPC call asking for the latest block number. You should get a response back in single-digit milliseconds. Nothing special, but that consistency is the point.</p>
<h2>Drop it into whatever you're already using</h2>
<p>The integration looks the same as any other RPC provider, which is intentional. You shouldn't have to refactor anything to switch.</p>
<p><strong>Hardhat</strong> - add your networks to <code>hardhat.config.js</code>:</p>
<pre><code class="language-javascript">module.exports = {
  networks: {
    mezo: {
      url: "https://&lt;network-name&gt;.boar.network/&lt;your-key&gt;",
      accounts: [process.env.PRIVATE_KEY],
    },
    ethereum: {
      url: "https://&lt;network-name&gt;.boar.network/&lt;your-key&gt;",
      accounts: [process.env.PRIVATE_KEY],
    },
  },
};
</code></pre>
<p><strong>viem:</strong></p>
<pre><code class="language-typescript">import { createPublicClient, http } from "viem";

const client = createPublicClient({
  transport: http("https://&lt;network-name&gt;.boar.network/&lt;your-key&gt;"),
});

const blockNumber = await client.getBlockNumber();
console.log("Current block:", blockNumber);
</code></pre>
<p><strong>ethers.js:</strong></p>
<pre><code class="language-typescript">import { JsonRpcProvider } from "ethers";

const provider = new JsonRpcProvider(
  "https://&lt;network-name&gt;.boar.network/&lt;your-key&gt;"
);

const block = await provider.getBlockNumber();
console.log("Current block:", block);
</code></pre>
<p>The point is: it's a standard RPC endpoint. If your tooling speaks JSON-RPC, it works with Boar.</p>
<h2>What about when traffic gets real?</h2>
<p>One thing worth mentioning: switching RPC providers in a running project is one of those tasks that should take five minutes and somehow takes a full day. You're updating configs across environments, touching CI pipelines, and hoping nothing quietly breaks.</p>
<p>We thought about this early on. Contact us to receive a free Enterprise plan for the duration of your migration. No need to switch applications all at once. Migrate without double-spending.</p>
<h2>What are we missing?</h2>
<p>We're actively expanding to more networks and improving the service based on what developers actually need. If there's a chain you want us to support, or something about the developer experience that could be smoother, we'd genuinely like to hear about it.</p>
<p>You can <a href="https://boar.network/">reach us here</a> or find us on the channels listed on our site. We read everything.</p>
]]></content:encoded></item></channel></rss>