DEV Community

Isaac Atunbi
Isaac Atunbi

Posted on

Redbelly Network Troubleshooting Guide: 22 Common Developer Errors and Their Fixes

description: "Fixes for the 22 errors developers hit most often on Redbelly Network — RPC and chain ID conflicts, MetaMask setup, USD-pegged gas, permissioned-network reverts, Hardhat deployment, Routescan verification, and the Eligibility SDK."
tags: blockchain, web3, ethereum, solidity
canonical_url:https://dev.to/isaac_atunbi_c2ed2489e89c/redbelly-network-troubleshooting-guide-22-common-developer-errors-and-their-fixes-1g26/edit
cover_image:

published: true

Redbelly Network Troubleshooting Guide

22 common developer errors, with the exact command that fixes each one.

Redbelly Network is an EVM-compatible L1 built for compliant asset tokenisation. "EVM
compatible" gets you most of the way, but three things about Redbelly are genuinely
different from every other EVM chain, and they account for most of the time developers
lose here:

  1. It is a permissioned network. An address that has not claimed a network access credential cannot write to the chain. Transactions from it fail in a way that looks like an ordinary revert.
  2. Gas is priced in US dollars, not gwei. eth_gasPrice returns roughly 165,000 gwei. That is about four orders of magnitude above Ethereum, it is correct, and it breaks any tool with a hardcoded fee cap or a "that can't be right" sanity check.
  3. The two official documentation sites disagree with each other, including on the mainnet chain ID. One of them is stale.

Every entry below follows the same shape — Symptom → Root Cause → Solution →
Prevention
— and every command is copy-pasteable. Every chain ID, URL, contract address
and package name was read from a live source on the date in the footer, not from memory.

Sources read: 8 August 2026. Every chain ID, URL, contract address and package name
below was read from a live source on that date and is traced in
sources.md.

Verified: 8 August 2026. A verification harness was run against the live network:
30 of 33 checks passed, and the log ships as
verification-log.json. Three checks changed what this
guide says — see Verification and evidence. On-chain
state-changing checks (deploy, nonce reuse, contract verification) have not been run.


How to use this guide

  • You have an error string: jump to the Error message index and search for it literally. Every literal error string in this guide is listed there.
  • You know the area but not the error: use the table of contents below.
  • You are setting up from scratch: read A1, A2, C1 and B1 in that order. Those four cover the configuration that everything else depends on.

Table of contents

A. Network and RPC connection

B. Wallet connection

C. Gas, fees and stuck transactions

D. Network access and permissioning (Redbelly-specific — no equivalent on other EVM chains)

E. Contract deployment and compilation

F. Explorer and contract verification

G. Eligibility SDK integration


Reference card

Copy this. Everything in the guide is consistent with it.

Mainnet Testnet
Network name Redbelly Network Mainnet Redbelly Testnet
Chain ID (decimal) 151 153
Chain ID (hex, for wallets) 0x97 0x99
RPC URL https://governors.mainnet.redbelly.network https://governors.testnet.redbelly.network
Currency symbol RBNT (18 decimals) RBNT (18 decimals)
Block explorer https://redbelly.routescan.io https://redbelly.testnet.routescan.io
Explorer API (Etherscan-compatible) https://api.routescan.io/v2/network/mainnet/evm/151/etherscan https://api.routescan.io/v2/network/testnet/evm/153/etherscan
Network access dApp https://access.redbelly.network https://access.redbelly.network
Faucet https://redbelly.faucetme.pro

Chain 152 (DevNet) is deprecated. Chain 154 is not mainnet — see A1 and A3.

EVM version: prague. Solidity: 0.8.30. Pin both — see E1.


A. Network and RPC connection

A1 — Official docs disagree on the mainnet chain ID: 151 or 154?

Category: Network/RPC · Severity: High — silently connects you to nothing

Symptom

You configure mainnet with chain ID 154 because that is what the technical documentation
says, and one of the following happens:

Error: could not detect network (event="noNetwork", code=NETWORK_ERROR)
Enter fullscreen mode Exit fullscreen mode
Chain ID returned by the custom network does not match the submitted chain ID.
Enter fullscreen mode Exit fullscreen mode
Error HH110: Invalid JSON-RPC response received: <html>...
Enter fullscreen mode Exit fullscreen mode

Or nothing happens at all: your provider connects, but every read returns empty and no
transaction ever confirms.

Root Cause

Redbelly publishes two documentation sites, and they do not agree:

Source Mainnet chain ID RPC URL published?
Vine developer portal — vine.redbelly.network/environments/ 151 Yes
Technical docs — docs.redbelly.network/pages/general/rb-env/ 154 No — "Coming Soon"
chainid.network / ethereum-lists registry 151 Yes

151 is correct. Confirmed directly by the Redbelly team: asked in the Redbelly Telegram
on 8 August 2026 which mainnet chain ID is right, moderator Appie answered "The chain
ID is 151."
Three further independent confirmations:

  1. The Vine portal — the current developer portal — publishes 151 with a working RPC URL.
  2. The chainid.network registry lists chain 151 as Redbelly Network Mainnet, status active, with the same RPC URL and the Routescan explorer.
  3. The technical docs site contradicts itself. Its own Eligibility SDK backend page registers the mainnet DID method with chainId: 151, on the same domain as the page that claims 154.

Chain 154 is a real registration, but it is not mainnet. In the chainid.network
registry, 154 is Redbelly Network TGE — a separate network, with an empty rpc array and
no explorer. Pointing a mainnet deployment at 154 aims it at a network that publishes no
endpoint, which is why the failure is so quiet.

The rb-env page is stale: it also lists both RPC URLs as "Coming Soon", although both have
been live and published on Vine and in the public registry for some time.

Solution

1. Use 151 for mainnet and 153 for testnet. Do not take the chain ID from
docs.redbelly.network/pages/general/rb-env/.

2. Confirm it yourself in one command rather than trusting any document, including this
one:

curl -s -X POST https://governors.mainnet.redbelly.network \
  -H 'Content-Type: application/json' \
  --data '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}'
Enter fullscreen mode Exit fullscreen mode

Expected response — 0x97 is 151:

{"jsonrpc":"2.0","id":1,"result":"0x97"}
Enter fullscreen mode Exit fullscreen mode

And for testnet — 0x99 is 153:

curl -s -X POST https://governors.testnet.redbelly.network \
  -H 'Content-Type: application/json' \
  --data '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}'
Enter fullscreen mode Exit fullscreen mode
{"jsonrpc":"2.0","id":1,"result":"0x99"}
Enter fullscreen mode Exit fullscreen mode

3. Convert the hex yourself if you want a third opinion:

python3 -c "print(int('0x97', 16), int('0x99', 16))"
# 151 153
Enter fullscreen mode Exit fullscreen mode

Prevention

Make the chain ID an assertion rather than a constant. In Hardhat, set chainId explicitly
in the network config — Hardhat compares it to what the node reports and aborts on a
mismatch instead of proceeding against the wrong chain:

networks: {
  redbellyTestnet: {
    type: "http",
    chainType: "l1",
    url: "https://governors.testnet.redbelly.network",
    chainId: 153,          // Hardhat verifies this against eth_chainId
    accounts: [configVariable("REDBELLY_PRIVATE_KEY")],
  },
}
Enter fullscreen mode Exit fullscreen mode

In application code with ethers v6:

const provider = new ethers.JsonRpcProvider(
  "https://governors.testnet.redbelly.network",
  { chainId: 153, name: "redbelly-testnet" },
  { staticNetwork: true }   // fail fast instead of auto-detecting
);
Enter fullscreen mode Exit fullscreen mode

Treat vine.redbelly.network as authoritative for environment values, and
docs.redbelly.network as authoritative for Receptor and Eligibility SDK material. Where
they overlap, Vine is the one that is current.


A2 — could not detect network / 404 from the RPC endpoint

Category: Network/RPC · Severity: High

Symptom

Error: could not detect network (event="noNetwork", code=NETWORK_ERROR, version=6.17.0)
Enter fullscreen mode Exit fullscreen mode
HttpProviderError: Invalid JSON-RPC response received
Enter fullscreen mode Exit fullscreen mode
404 page not found
Enter fullscreen mode Exit fullscreen mode

The endpoint resolves in a browser but every JSON-RPC call fails.

Root Cause

The RPC path is wrong. Redbelly's public endpoints take JSON-RPC POSTs at the domain
root
— there is no /rpc path segment and no port suffix:

https://governors.mainnet.redbelly.network      ✅
https://governors.testnet.redbelly.network      ✅

https://governors.testnet.redbelly.network/rpc  ❌ 404
https://governors.testnet.redbelly.network:8545 ❌ connection refused
Enter fullscreen mode Exit fullscreen mode

The /rpc suffix is a reasonable guess for two reasons, and both appear in Redbelly's own
material. Many EVM chains do use /rpc. And the Eligibility SDK backend documentation
contains a leftover example pointing at an internal staging host in exactly that shape:

// From docs.redbelly.network — a staging host, not a public endpoint. Do not copy this.
"redbelly:mainnet": new resolver.EthStateResolver(
  "https://rbn9bb267fa.staging.redbelly.network/rpc",
  "0xEd5604a53971cB4e7f51A351b5d93Eef50517a7e"
),
Enter fullscreen mode Exit fullscreen mode

That staging hostname is not reachable publicly, and the /rpc shape it uses does not apply
to the governors.* endpoints.

A trailing slash is harmless — https://governors.testnet.redbelly.network/ works, and
Redbelly's own network-fees page uses that form.

Solution

1. Test the bare endpoint directly. A working endpoint answers this in under a second:

curl -s -X POST https://governors.testnet.redbelly.network \
  -H 'Content-Type: application/json' \
  --data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
Enter fullscreen mode Exit fullscreen mode
{"jsonrpc":"2.0","id":1,"result":"0x2f9e23"}
Enter fullscreen mode Exit fullscreen mode

2. If that returns HTML or a 404, print the status code and body to see what you
actually hit:

curl -s -o /dev/null -w 'HTTP %{http_code}  ->  %{url_effective}\n' \
  -X POST https://governors.testnet.redbelly.network \
  -H 'Content-Type: application/json' \
  --data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
Enter fullscreen mode Exit fullscreen mode

HTTP 200 means the endpoint is correct and the problem is elsewhere in your config.
HTTP 404 means the path has an extra segment on it.

3. Strip any path segment from the URL in your .env, hardhat.config.ts, wallet
config and frontend chain definition. The value is the bare hostname with https://.

Prevention

Define the URL once, in one file, and import it everywhere:

// src/chains.js — single source of truth
export const REDBELLY_TESTNET = {
  id: 153,
  rpcUrl: "https://governors.testnet.redbelly.network",
  explorer: "https://redbelly.testnet.routescan.io",
};
Enter fullscreen mode Exit fullscreen mode

Add a startup assertion that fails loudly on a bad endpoint rather than degrading:

const net = await provider.getNetwork();
if (net.chainId !== 153n) {
  throw new Error(`Expected chain 153, got ${net.chainId} from ${rpcUrl}`);
}
Enter fullscreen mode Exit fullscreen mode

A3 — Tutorials point at DevNet (chain 152), which is deprecated

Category: Network/RPC · Severity: Medium — wastes hours before it announces itself

Symptom

You follow a Redbelly tutorial or a community post that references DevNet, and:

  • the DevNet RPC URL does not resolve, or resolves and never answers;
  • the faucet has no DevNet option;
  • the DevNet block explorer link 404s;
  • eth_chainId never returns 0x98.

Root Cause

Redbelly DevNet (chain ID 152) is deprecated. The Vine portal states it directly:

"Redbelly DevNet is now deprecated. Any testing of smart contracts should be done on the
Redbelly Testnet."

Its environment table lists both the RPC URL and the block explorer as Deprecated rather
than giving values. The public chainid.network registry corroborates this: chain 152 is
registered as Redbelly Network Devnet with status incubating and an empty rpc
array
— no endpoint is published for it anywhere.

The reason this keeps catching people is that DevNet references survive in older tutorials,
blog posts and Discord history, and there is no redirect or deprecation banner on the
endpoints themselves. Material written while DevNet was current still reads as current.

Some Redbelly documentation also still refers to DevNet in passing — the testing-coins page,
for instance, describes the faucet as serving "DevNet or Testnet accounts". Testnet is the
live half of that sentence.

Solution

Substitute Testnet for DevNet everywhere. The mapping is one-to-one:

DevNet (dead) Testnet (use this)
Chain ID 152 Chain ID 153
(no published RPC) https://governors.testnet.redbelly.network
(deprecated explorer) https://redbelly.testnet.routescan.io
RedbellyDevNet Redbelly Testnet

Find every DevNet reference in your project in one pass:

grep -rniE 'devnet|\b152\b|0x98' \
  --include='*.js' --include='*.ts' --include='*.json' \
  --include='*.env*' --include='*.sol' --include='*.md' .
Enter fullscreen mode Exit fullscreen mode

Then confirm what you switched to is live:

curl -s -X POST https://governors.testnet.redbelly.network \
  -H 'Content-Type: application/json' \
  --data '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}'
# {"jsonrpc":"2.0","id":1,"result":"0x99"}
Enter fullscreen mode Exit fullscreen mode

Prevention

Date-check any Redbelly tutorial before following it. If it mentions DevNet as a live
target, it predates the deprecation and its other values — endpoints, contract addresses,
package names — are the same age.

Cross-check any environment value you find in a third-party post against
https://vine.redbelly.network/environments/ before you paste it into a config.


A4 — RPC returns HTTP 429 under load

Category: Network/RPC · Severity: Medium

Symptom

Error: server response 429 Too Many Requests
Enter fullscreen mode Exit fullscreen mode
code=SERVER_ERROR, status=429
Enter fullscreen mode Exit fullscreen mode

Typically during an indexer backfill, a test suite that fires many parallel calls, or a
frontend that polls per-component.

Root Cause

The public governors.* endpoints are shared infrastructure with request-rate limits.
Redbelly does not publish a numeric limit, so the practical approach is to treat 429 as
expected under burst load and handle it, rather than to design against a specific published
threshold.

Basis for this entry, measured. A 40-request parallel burst against the testnet
endpoint on 8 August 2026 (check A4.1) returned HTTP 200 on all 40 — no rate limiting
was triggered at that volume. So this entry is a defensive pattern for sustained or
high-volume load, not a report of a threshold you will hit casually.
If you are seeing
429s, the cause is more likely per-component polling or unbatched historical reads than
normal application traffic. The patterns below still apply; treat them as good practice
rather than as a fix for a limit that 40 parallel calls did not reach.

Two client-side patterns trigger it far more often than raw transaction volume:

  • Per-component polling. Each React component creating its own provider and polling independently multiplies request count by component count.
  • Unbatched historical reads. for (let i = from; i <= to; i++) await provider.getBlock(i) issues one HTTP round trip per block.

Solution

1. Retry with exponential backoff and jitter. Drop-in, no dependencies:

async function rpcWithRetry(fn, { retries = 5, baseMs = 250 } = {}) {
  for (let attempt = 0; ; attempt++) {
    try {
      return await fn();
    } catch (err) {
      const status = err?.status ?? err?.info?.responseStatus;
      const is429 = status === 429 || String(err?.message ?? "").includes("429");
      if (!is429 || attempt >= retries) throw err;
      const delay = baseMs * 2 ** attempt + Math.random() * 100;
      await new Promise((r) => setTimeout(r, delay));
    }
  }
}

// usage
const block = await rpcWithRetry(() => provider.getBlock("latest"));
Enter fullscreen mode Exit fullscreen mode

2. Batch reads into a single HTTP request. ethers v6 batches automatically when you
give JsonRpcProvider a batching config:

const provider = new ethers.JsonRpcProvider(
  "https://governors.testnet.redbelly.network",
  { chainId: 153, name: "redbelly-testnet" },
  {
    staticNetwork: true,
    batchMaxCount: 25,   // up to 25 calls per HTTP request
    batchStallTime: 20,  // ms to wait while collecting calls to batch
  }
);
Enter fullscreen mode Exit fullscreen mode

3. Share one provider across the whole app instead of constructing one per component:

// src/provider.js
import { ethers } from "ethers";
let _provider;
export function getProvider() {
  _provider ??= new ethers.JsonRpcProvider(
    "https://governors.testnet.redbelly.network",
    { chainId: 153, name: "redbelly-testnet" },
    { staticNetwork: true, batchMaxCount: 25, batchStallTime: 20 }
  );
  return _provider;
}
Enter fullscreen mode Exit fullscreen mode

4. Cap concurrency on bulk jobs. For a backfill, process in fixed-size chunks rather
than mapping over the whole range at once:

async function inChunks(items, size, worker) {
  const out = [];
  for (let i = 0; i < items.length; i += size) {
    out.push(...(await Promise.all(items.slice(i, i + size).map(worker))));
  }
  return out;
}

const blocks = await inChunks(range, 10, (n) =>
  rpcWithRetry(() => provider.getBlock(n))
);
Enter fullscreen mode Exit fullscreen mode

Prevention

Set staticNetwork: true on every provider. Without it, ethers re-issues eth_chainId
to detect the network on reconnection, which adds request volume precisely when you are
already being throttled.

For production indexing and sustained high-volume reads, use the Routescan API for
historical queries and reserve the RPC for live state — historical range queries are what
the explorer API is built for.


B. Wallet connection

B1 — MetaMask does not list Redbelly Network

Category: Wallet · Severity: High — first thing every new developer hits

Symptom

Redbelly does not appear in MetaMask's network list, and adding it by hand produces a
network that shows a zero balance and cannot send transactions. Or your dApp's "Switch
network" button throws:

Unrecognized chain ID "0x99". Try adding the chain using wallet_addEthereumChain first.
Enter fullscreen mode Exit fullscreen mode

Root Cause

Redbelly is not in MetaMask's built-in network list, so it has to be added manually or
programmatically. The manual route fails most often on the chain ID field, because
MetaMask's "Chain ID" input takes a decimal value while the wallet_addEthereumChain
RPC method takes a hex string — and because the technical docs publish a stale mainnet
chain ID (see A1).

Solution

Option 1 — Add it programmatically. This is the payload; both networks are given in
full, with no placeholders to substitute:

// Redbelly Testnet — chain 153 (0x99)
await window.ethereum.request({
  method: "wallet_addEthereumChain",
  params: [{
    chainId: "0x99",
    chainName: "Redbelly Testnet",
    nativeCurrency: { name: "Redbelly Network Coin", symbol: "RBNT", decimals: 18 },
    rpcUrls: ["https://governors.testnet.redbelly.network"],
    blockExplorerUrls: ["https://redbelly.testnet.routescan.io"],
  }],
});
Enter fullscreen mode Exit fullscreen mode
// Redbelly Network Mainnet — chain 151 (0x97)
await window.ethereum.request({
  method: "wallet_addEthereumChain",
  params: [{
    chainId: "0x97",
    chainName: "Redbelly Network Mainnet",
    nativeCurrency: { name: "Redbelly Network Coin", symbol: "RBNT", decimals: 18 },
    rpcUrls: ["https://governors.mainnet.redbelly.network"],
    blockExplorerUrls: ["https://redbelly.routescan.io"],
  }],
});
Enter fullscreen mode Exit fullscreen mode

Add-then-switch, handling the "already added" case:

const TESTNET = { /* the params object above */ };

async function connectRedbellyTestnet() {
  try {
    await window.ethereum.request({
      method: "wallet_switchEthereumChain",
      params: [{ chainId: "0x99" }],
    });
  } catch (err) {
    if (err.code === 4902) {              // 4902 = chain not added yet
      await window.ethereum.request({
        method: "wallet_addEthereumChain",
        params: [TESTNET],
      });
    } else {
      throw err;
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Option 2 — Add it by hand. MetaMask → Settings → Networks → Add a network manually.
Enter these values exactly, noting that Chain ID is decimal here, not hex:

Field Testnet Mainnet
Network name Redbelly Testnet Redbelly Network Mainnet
New RPC URL https://governors.testnet.redbelly.network https://governors.mainnet.redbelly.network
Chain ID 153 151
Currency symbol RBNT RBNT
Block explorer URL https://redbelly.testnet.routescan.io https://redbelly.routescan.io

A zero balance after adding the network is expected and does not mean the config is
wrong — a new address holds no RBNT. Fund it via the faucet (D2). Separately, an
address needs a network access credential before it can send anything (D1).

Prevention

Ship the network config in your dApp instead of asking users to type it. Every value the
user would otherwise enter by hand is a value they can get wrong.

For wagmi / viem, define the chain once:

import { defineChain } from "viem";

export const redbellyTestnet = defineChain({
  id: 153,
  name: "Redbelly Testnet",
  nativeCurrency: { name: "Redbelly Network Coin", symbol: "RBNT", decimals: 18 },
  rpcUrls: { default: { http: ["https://governors.testnet.redbelly.network"] } },
  blockExplorers: {
    default: { name: "Routescan", url: "https://redbelly.testnet.routescan.io" },
  },
  testnet: true,
});
Enter fullscreen mode Exit fullscreen mode

B2 — Chain ID returned by the custom network does not match the submitted chain ID

Category: Wallet · Severity: High

Symptom

MetaMask refuses to add the network:

Chain ID returned by the custom network does not match the submitted chain ID.
Enter fullscreen mode Exit fullscreen mode

Some wallet versions phrase it as:

Error: Chain ID returned by RPC URL https://... does not match 0x9a
Enter fullscreen mode Exit fullscreen mode

Root Cause

The wallet calls eth_chainId against the RPC URL you supplied and compares the answer to
the chain ID you typed. A mismatch means one of the two is wrong. In practice there are
three causes, in descending order of frequency:

  1. Chain ID 154 entered for mainnet, taken from docs.redbelly.network/pages/general/rb-env/, against the mainnet RPC — which reports 0x97 (151). This is A1 surfacing in the wallet.
  2. Hex and decimal confused. 153 typed into a field expecting hex, or 0x99 typed into MetaMask's decimal Chain ID field.
  3. Testnet and mainnet values crossed — mainnet RPC paired with 153, or testnet RPC paired with 151.

Solution

1. Ask the endpoint what it is, and use that answer rather than any document:

curl -s -X POST https://governors.testnet.redbelly.network \
  -H 'Content-Type: application/json' \
  --data '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}'
# {"jsonrpc":"2.0","id":1,"result":"0x99"}   -> 0x99 = 153
Enter fullscreen mode Exit fullscreen mode

2. Enter the value in the representation the field expects:

Where Testnet Mainnet
MetaMask "Chain ID" field (manual add) 153 151
wallet_addEthereumChain chainId param "0x99" "0x97"
Hardhat chainId 153 151
viem / wagmi defineChain id 153 151

3. Convert with certainty instead of by eye:

printf '0x%x\n' 153     # 0x99
printf '0x%x\n' 151     # 0x97
python3 -c "print(int('0x99',16))"   # 153
Enter fullscreen mode Exit fullscreen mode

Prevention

Never type a chain ID twice. Derive the hex form from the decimal one:

const CHAIN_ID = 153;
const CHAIN_ID_HEX = `0x${CHAIN_ID.toString(16)}`;  // "0x99"
Enter fullscreen mode Exit fullscreen mode

B3 — Wrong block explorer URL in wallet config

Category: Wallet · Severity: Low — cosmetic, but it makes debugging much harder

Symptom

"View on block explorer" from the wallet opens a page reading Transaction not found, or a

  1. The transaction succeeded and is visible if you search the correct explorer by hand.

Root Cause

Redbelly's two Routescan explorers differ by a hostname segment that is easy to drop:

https://redbelly.routescan.io           <- MAINNET  (chain 151)
https://redbelly.testnet.routescan.io   <- TESTNET  (chain 153)
Enter fullscreen mode Exit fullscreen mode

A testnet network configured with the mainnet explorer sends every lookup to a chain that
has never seen the transaction. The explorer answers honestly that it does not exist, which
reads as "my transaction failed" rather than "I am looking in the wrong place".

Solution

1. Pair the explorer with the chain ID:

Chain ID Explorer URL
151 (mainnet) https://redbelly.routescan.io
153 (testnet) https://redbelly.testnet.routescan.io

2. Confirm a transaction hash resolves before blaming the transaction. The
Etherscan-compatible API answers without a browser:

# Testnet
curl -s "https://api.routescan.io/v2/network/testnet/evm/153/etherscan/api\
?module=transaction&action=gettxreceiptstatus&txhash=0xYOUR_TX_HASH"
Enter fullscreen mode Exit fullscreen mode
# Mainnet
curl -s "https://api.routescan.io/v2/network/mainnet/evm/151/etherscan/api\
?module=transaction&action=gettxreceiptstatus&txhash=0xYOUR_TX_HASH"
Enter fullscreen mode Exit fullscreen mode

"status":"1" in result means the transaction executed successfully.

3. Correct the wallet entry: MetaMask → Settings → Networks → select the network →
update "Block explorer URL" → Save.

Prevention

Keep chain ID, RPC URL and explorer URL in one object so they cannot drift apart:

export const CHAINS = {
  151: {
    name: "Redbelly Network Mainnet",
    rpc: "https://governors.mainnet.redbelly.network",
    explorer: "https://redbelly.routescan.io",
    api: "https://api.routescan.io/v2/network/mainnet/evm/151/etherscan",
  },
  153: {
    name: "Redbelly Testnet",
    rpc: "https://governors.testnet.redbelly.network",
    explorer: "https://redbelly.testnet.routescan.io",
    api: "https://api.routescan.io/v2/network/testnet/evm/153/etherscan",
  },
};
Enter fullscreen mode Exit fullscreen mode

C. Gas, fees and stuck transactions

C1 — Gas price looks absurd (~165,000 gwei) and the wallet shows a huge fee

Category: Gas · Severity: High — the most misdiagnosed issue on Redbelly

Symptom

eth_gasPrice returns a number roughly four orders of magnitude larger than you expect:

curl -s -X POST https://governors.mainnet.redbelly.network \
  -H 'Content-Type: application/json' \
  --data '{"jsonrpc":"2.0","method":"eth_gasPrice","params":[],"id":1}'
Enter fullscreen mode Exit fullscreen mode
{"jsonrpc":"2.0","id":1,"result":"0x962477744fe0"}
Enter fullscreen mode Exit fullscreen mode

That is 165,083,367,100,384 wei — about 165,083 gwei. MetaMask displays a fee that
looks like a misconfiguration, and code with a sanity check rejects it:

Error: gas price too high
Enter fullscreen mode Exit fullscreen mode
Error: max fee per gas less than block base fee
Enter fullscreen mode Exit fullscreen mode

Root Cause

This is correct behaviour, not a bug. Redbelly fixes gas prices in US dollar terms
rather than in gwei. From the network fees documentation:

"The gas model ensures that a simple native transaction on Redbelly costs US$0.01. A
simple native transaction on Redbelly costs 21000 gas, thus fixing the unit gas price at
US$0.000000476190476190."

An on-chain price oracle feeds a USD/RBNT rate to the nodes, which deduct the RBNT needed to
meet the gas cost in dollars at execution time. The consequence is the opposite of every
other EVM chain: the dollar cost is constant and the gwei number floats. As the RBNT
price falls, the gwei figure rises to keep the dollar cost pegged.

The measured value above is internally consistent with the documented model:

21,000 gas x 165,083,367,100,384 wei  =  3.4668 RBNT per simple transfer
US$0.01 / 3.4668 RBNT                 =  US$0.00288 per RBNT
Enter fullscreen mode Exit fullscreen mode

So a 165,000 gwei gas price and a one-cent transaction are the same statement.

Redbelly's own documentation flags the tooling consequence directly:

"for most wallets, gas related information may not be handled accurately since they have
not been configured to account for Redbelly's fixed unit gas cost in US dollar terms."

Solution

Do not hardcode a gas price, and do not cap it. Read it from the chain every time:

// ethers v6 — correct on Redbelly
const feeData = await provider.getFeeData();
const tx = await contract.someMethod(args, {
  gasPrice: feeData.gasPrice,   // whatever the oracle currently says
});
Enter fullscreen mode Exit fullscreen mode

Remove any of these patterns from your code:

// ❌ orders of magnitude too low on Redbelly — transaction never mines
{ gasPrice: ethers.parseUnits("20", "gwei") }

// ❌ a cap that Redbelly's real price exceeds by ~4 orders of magnitude
{ maxFeePerGas: ethers.parseUnits("100", "gwei") }

// ❌ sanity check calibrated for Ethereum
if (gasPrice > 500_000_000_000n) throw new Error("gas price too high");
Enter fullscreen mode Exit fullscreen mode

In Hardhat, leave gasPrice unset so it is fetched per transaction. Setting
gasPrice: "auto" or omitting the field both work; a numeric literal does not:

networks: {
  redbellyTestnet: {
    type: "http",
    chainType: "l1",
    url: "https://governors.testnet.redbelly.network",
    chainId: 153,
    accounts: [configVariable("REDBELLY_PRIVATE_KEY")],
    // no gasPrice key — Hardhat calls eth_gasPrice per transaction
  },
}
Enter fullscreen mode Exit fullscreen mode

Budget in dollars, not in gwei. The documented unit price is US$0.000000476190476190
per gas, so:

estimated cost (USD) = gasUsed x 0.000000476190476190
Enter fullscreen mode Exit fullscreen mode

A 1,200,000-gas deployment costs about US$0.57, regardless of what the gwei figure looks
like on the day.

To see the live RBNT price the network is using, read the price feed oracle through the
bootstrap registry — the address is the same on both networks:

Bootstrap registry: 0xDAFEA492D9c6733ae3d56b7Ed1ADB60692c98Bc5
Enter fullscreen mode Exit fullscreen mode

Prevention

Assert on the dollar cost in tests, not the gwei price:

const GAS_UNIT_PRICE_USD = 0.000000476190476190;   // published, fixed
const estimated = await contract.someMethod.estimateGas(args);
const usd = Number(estimated) * GAS_UNIT_PRICE_USD;
if (usd > 1.00) throw new Error(`Call would cost $${usd.toFixed(2)}`);
Enter fullscreen mode Exit fullscreen mode

That assertion stays valid as the RBNT price moves. A gwei-denominated one does not.


C2 — Transaction stuck pending forever / replacement transaction underpriced

Category: Gas · Severity: High

Symptom

A transaction is accepted by the node, returns a hash, and never mines. Later attempts to
replace it fail:

Error: replacement transaction underpriced
Enter fullscreen mode Exit fullscreen mode
Error: transaction underpriced
Enter fullscreen mode Exit fullscreen mode

Every subsequent transaction from the same address also stays pending, because they queue
behind the stuck nonce.

Root Cause

Almost always an underpriced first transaction — a hardcoded gwei gas price left over from
another chain, which on Redbelly is far below what the oracle currently requires
(C1). The node accepts it into the pool but it is never economically viable to
include.

Replacement then fails because a replacement must exceed the original's gas price by a
margin, and a replacement built from the same hardcoded constant does not.

Solution

1. Find the stuck nonce — the gap between latest (mined) and pending (accepted):

ADDR=0xYOUR_ADDRESS
RPC=https://governors.testnet.redbelly.network

echo -n "latest:  "; curl -s -X POST $RPC -H 'Content-Type: application/json' \
  --data "{\"jsonrpc\":\"2.0\",\"method\":\"eth_getTransactionCount\",\"params\":[\"$ADDR\",\"latest\"],\"id\":1}"
echo -n "pending: "; curl -s -X POST $RPC -H 'Content-Type: application/json' \
  --data "{\"jsonrpc\":\"2.0\",\"method\":\"eth_getTransactionCount\",\"params\":[\"$ADDR\",\"pending\"],\"id\":1}"
Enter fullscreen mode Exit fullscreen mode

If pending is higher than latest, the first stuck nonce is the latest value.

2. Replace it with a zero-value self-transfer at a raised gas price. This clears the
nonce without moving funds anywhere:

import { ethers } from "ethers";

const provider = new ethers.JsonRpcProvider(
  "https://governors.testnet.redbelly.network",
  { chainId: 153, name: "redbelly-testnet" },
  { staticNetwork: true }
);
const wallet = new ethers.Wallet(process.env.REDBELLY_PRIVATE_KEY, provider);

const stuckNonce = await provider.getTransactionCount(wallet.address, "latest");
const current = (await provider.getFeeData()).gasPrice;
const bumped = (current * 130n) / 100n;   // +30% over the live oracle price

const tx = await wallet.sendTransaction({
  to: wallet.address,
  value: 0,
  nonce: stuckNonce,
  gasPrice: bumped,
  gasLimit: 21000,
});
console.log("replacement sent:", tx.hash);
await tx.wait();
console.log("nonce cleared");
Enter fullscreen mode Exit fullscreen mode

The bump is applied to the live price from getFeeData(), not to the price of the stuck
transaction — that is the part that fixes replacement transaction underpriced.

3. If several nonces are stuck, repeat from the lowest and work upward. Nonces clear in
order; a gap blocks everything behind it.

Prevention

Remove every hardcoded gasPrice and maxFeePerGas from the codebase (C1) — that
single change prevents the great majority of stuck transactions on Redbelly.

Add a timeout so a stuck transaction announces itself instead of hanging:

const tx = await contract.someMethod(args);
const receipt = await Promise.race([
  tx.wait(),
  new Promise((_, rej) =>
    setTimeout(() => rej(new Error(`Tx ${tx.hash} pending >120s — likely underpriced`)), 120_000)
  ),
]);
Enter fullscreen mode Exit fullscreen mode

C3 — Gas estimation returns null or reverts during estimation

Category: Gas · Severity: High

Symptom

Error: cannot estimate gas; transaction may fail or may require manual gas limit
Enter fullscreen mode Exit fullscreen mode
UNPREDICTABLE_GAS_LIMIT
Enter fullscreen mode Exit fullscreen mode
execution reverted (no reason string)
Enter fullscreen mode Exit fullscreen mode

Or estimateGas resolves to null, and passing that value onward throws a type error.

Root Cause

eth_estimateGas executes the transaction against current state without committing it. A
null or reverting estimate means the call would fail if sent — the estimator is
reporting a real problem, not failing at its job. On Redbelly there are four causes, and the
first is specific to this chain:

  1. The sender has no network access credential. Redbelly is permissioned; a non-enabled address cannot send. Note that this cause does not show up here — measured on 8 August 2026, estimation succeeds normally for un-onboarded addresses, because the permission gate applies at execution rather than estimation. So a clean estimate does not clear the address. See D1, which has a check that works.
  2. The sender cannot cover gas x price + value. At ~165,000 gwei (C1), an RBNT balance that looks generous by Ethereum intuition may not be. See E2.
  3. The contract call genuinely reverts — failed require, wrong arguments, wrong contract address.
  4. from is missing. Conventionally the node then estimates from the zero address and the estimate fails for reasons unrelated to your code. Measured caveat: on Redbelly, a simple self-transfer estimate returned the same 0x5f12 with and without from (check C3.1, 8 August 2026), so this cause is not universal here. Send from anyway — it costs nothing and matters for calls whose logic depends on msg.sender.

Solution

1. Always send from. This alone resolves a large share of null estimates:

const gas = await contract.someMethod.estimateGas(args, { from: wallet.address });
Enter fullscreen mode Exit fullscreen mode

Raw JSON-RPC equivalent:

curl -s -X POST https://governors.testnet.redbelly.network \
  -H 'Content-Type: application/json' \
  --data '{
    "jsonrpc":"2.0","method":"eth_estimateGas",
    "params":[{"from":"0xYOUR_ADDRESS","to":"0xCONTRACT","data":"0xENCODED_CALLDATA"}],
    "id":1
  }'
Enter fullscreen mode Exit fullscreen mode

2. Get the revert reason by replaying the same call with eth_call, which returns the
reason string that eth_estimateGas swallows:

curl -s -X POST https://governors.testnet.redbelly.network \
  -H 'Content-Type: application/json' \
  --data '{
    "jsonrpc":"2.0","method":"eth_call",
    "params":[{"from":"0xYOUR_ADDRESS","to":"0xCONTRACT","data":"0xENCODED_CALLDATA"},"latest"],
    "id":1
  }'
Enter fullscreen mode Exit fullscreen mode

In ethers, the same information arrives as structured fields:

try {
  await contract.someMethod.staticCall(args, { from: wallet.address });
} catch (err) {
  console.log("reason:", err.reason);      // require() message, if any
  console.log("data:  ", err.data);        // raw revert data
}
Enter fullscreen mode Exit fullscreen mode

3. Rule out the three chain-level preconditions in order, before debugging contract
logic:

ADDR=0xYOUR_ADDRESS
RPC=https://governors.testnet.redbelly.network

# a. Does the address hold RBNT?
curl -s -X POST $RPC -H 'Content-Type: application/json' \
  --data "{\"jsonrpc\":\"2.0\",\"method\":\"eth_getBalance\",\"params\":[\"$ADDR\",\"latest\"],\"id\":1}"

# b. Does a plain self-transfer estimate? If this fails, the problem is the
#    address (permission or balance), not the contract.
curl -s -X POST $RPC -H 'Content-Type: application/json' \
  --data "{\"jsonrpc\":\"2.0\",\"method\":\"eth_estimateGas\",\"params\":[{\"from\":\"$ADDR\",\"to\":\"$ADDR\",\"value\":\"0x0\"}],\"id\":1}"

# c. Is there code at the target address? "0x" means nothing is deployed there.
curl -s -X POST $RPC -H 'Content-Type: application/json' \
  --data "{\"jsonrpc\":\"2.0\",\"method\":\"eth_getCode\",\"params\":[\"0xCONTRACT\",\"latest\"],\"id\":1}"
Enter fullscreen mode Exit fullscreen mode

Step (b) is the discriminator. If a zero-value self-transfer will not estimate, no contract
call from that address will either, and the fix is D1 or D2.

4. Set a manual gas limit only after the cause is known. Overriding the limit on a
transaction that genuinely reverts converts a free failed estimate into a paid failed
transaction:

const tx = await contract.someMethod(args, { gasLimit: 500_000 });
Enter fullscreen mode Exit fullscreen mode

Prevention

Keep a preflight that runs before any write path and fails with a specific message:

async function preflight(provider, address) {
  const balance = await provider.getBalance(address);
  if (balance === 0n) throw new Error(`${address} holds no RBNT — fund via the faucet`);

  // Note: estimateGas cannot detect a missing network access credential on Redbelly —
  // it succeeds for un-onboarded addresses. Use useHasChainPermission (see D1) for that.
  try {
    await provider.estimateGas({ from: address, to: address, value: 0n });
  } catch {
    throw new Error(`${address} cannot estimate a bare self-transfer — check balance and RPC`);
  }
}
Enter fullscreen mode Exit fullscreen mode

C4 — nonce too low / nonce has already been used

Category: Gas · Severity: Medium

Symptom

Error: nonce too low
Enter fullscreen mode Exit fullscreen mode
Error: nonce has already been used
Enter fullscreen mode Exit fullscreen mode
ProviderError: invalid nonce; got 7, expected 9
Enter fullscreen mode Exit fullscreen mode

Common when re-running a deployment script, or after a wallet has been used from two tools
at once.

Root Cause

The nonce you sent is at or below the account's confirmed transaction count — that nonce is
already spent. Three usual causes:

  1. A cached nonce. A long-lived signer computed the nonce once and reused it after other transactions confirmed.
  2. latest versus pending confusion. latest counts mined transactions only. With transactions in flight, building from latest reuses a nonce that is already claimed in the pool.
  3. Two clients, one key — MetaMask and a Hardhat script both signing from the same address, each unaware of the other's transactions.

Solution

1. Read both counts and compare:

ADDR=0xYOUR_ADDRESS
RPC=https://governors.testnet.redbelly.network

for TAG in latest pending; do
  echo -n "$TAG: "
  curl -s -X POST $RPC -H 'Content-Type: application/json' \
    --data "{\"jsonrpc\":\"2.0\",\"method\":\"eth_getTransactionCount\",\"params\":[\"$ADDR\",\"$TAG\"],\"id\":1}"
  echo
done
Enter fullscreen mode Exit fullscreen mode
  • Equal → nothing in flight; send with the pending value.
  • pending > latest → transactions are queued; sending at latest triggers this error. Either use pending, or clear the queue first (C2).

2. Let the library manage the nonce. Omitting nonce makes ethers fetch pending
before each send, which is correct for sequential scripts:

// ✅ no explicit nonce
const tx = await wallet.sendTransaction({ to, value });
await tx.wait();
Enter fullscreen mode Exit fullscreen mode

3. For a batch from one address, assign nonces explicitly from a single base so they do
not collide:

let nonce = await provider.getTransactionCount(wallet.address, "pending");
const sent = [];
for (const call of calls) {
  sent.push(await wallet.sendTransaction({ ...call, nonce: nonce++ }));
}
await Promise.all(sent.map((t) => t.wait()));
Enter fullscreen mode Exit fullscreen mode

4. Reset MetaMask's cached nonce if the wallet is the one out of step:
Settings → Advanced → Clear activity tab data. This clears MetaMask's local transaction
history and nonce cache. It does not touch on-chain state, funds or keys.

Prevention

Use a dedicated deployment key that no wallet has imported. Nonce collisions overwhelmingly
come from one key being driven by two tools at once.

In scripts, await tx.wait() before sending the next transaction. Fire-and-forget sends
from a loop race each other for nonces.


D. Network access and permissioning

This category has no equivalent on Ethereum, Polygon, Arbitrum or any other permissionless
EVM chain. If you are debugging a transaction that fails for no visible reason, read
D1 before anything else.

D1 — Transactions revert because the address has no network access credential

Category: Permissioning · Severity: Critical — and almost always diagnosed last

Symptom

Everything looks correct and nothing works. The address holds RBNT. The contract is
deployed. The ABI matches. And yet:

Error: cannot estimate gas; transaction may fail or may require manual gas limit
Enter fullscreen mode Exit fullscreen mode
execution reverted
Enter fullscreen mode Exit fullscreen mode
Error: transaction execution reverted (no reason string)
Enter fullscreen mode Exit fullscreen mode

The tell is that a zero-value transfer to yourself fails when actually sent. That rules
out your contract entirely — the address itself cannot write to the chain.

Important, and measured: the failure appears when the transaction is submitted, not
when it is estimated. A harness run on 8 August 2026 (check D1.1) called
eth_estimateGas for a self-transfer from a freshly generated, never-onboarded address and
received a normal 0x5208 (21,000 gas) — a clean estimate. Permissioning is enforced at
execution, not at estimation, so eth_estimateGas succeeding tells you nothing about
whether an address may send.
See the Solution below for a check that does work.

Root Cause

Redbelly is a permissioned network. Holding RBNT is not sufficient to transact. From the
Vine portal:

"Before being granted access to Redbelly Network, each user must claim their access
credential from a network accredited issuer before they self enable their account with
write access to the network through a specific network smart contract."

Confirmed by the Redbelly team. Asked directly in the Redbelly Telegram on 8 August
2026 whether an address that has not been through access.redbelly.network can send a
testnet transaction, moderator Appie answered: "No, they can't." In a follow-up the
same day, they set out the boundary precisely:

"building is still permissionless, but in order to make a transaction (sending), you will
need to go through KYC/KYB. However, you can receive without the need for KYC/KYB."

So there are three distinct capabilities, not two:

Action Needs KYC/KYB?
Building — writing, compiling, working on contracts No
Receiving — holding RBNT, being sent tokens No
Sending — any transaction that writes to the chain Yes

The middle row is why this is so easy to misdiagnose. An un-onboarded address is not
inert: it holds a balance, it receives transfers, and it looks entirely healthy in a wallet.
That makes "my address has RBNT in it, so permissioning can't be the problem" a false
inference — and it is the single most common wrong turn on this failure. A funded balance
tells you nothing about whether the address can send.

If you have read that Redbelly is permissionless and are now watching your transactions
revert, this is the distinction you are missing. Both statements are true; they describe
different actions.

Write access is gated on-chain. An address that has not completed that flow can read state
all day and cannot send a transaction. Because the gate is enforced at execution, the
failure surfaces as a bare revert with no reason string — indistinguishable, at a glance,
from a failed require in your own contract.

This is confirmed by the Eligibility SDK's public API, which ships a hook for exactly this
question:

import { useHasChainPermission } from "@redbellynetwork/eligibility-sdk";

const { data, error, isLoading } = useHasChainPermission(userAddress);
// data === true  -> address may transact
// data === false -> address is not enabled
Enter fullscreen mode Exit fullscreen mode

A dedicated hook exists because unpermissioned addresses are a routine, expected state on
this network — not an edge case.

Getting the credential requires proving a valid photo identity document, verified by
biometric check, through a network-accredited issuer. It is a real identity process, not a
checkbox, so it cannot be completed inside a script or a CI job.

Solution

1. Do not use eth_estimateGas to test for this. It is the obvious check and it does not
work. Measured on 8 August 2026 against a freshly generated, never-onboarded address:

curl -s -X POST https://governors.testnet.redbelly.network \
  -H 'Content-Type: application/json' \
  --data '{"jsonrpc":"2.0","method":"eth_estimateGas","params":[{"from":"0xRANDOM_UNONBOARDED","to":"0xRANDOM_UNONBOARDED","value":"0x0"}],"id":1}'
# {"jsonrpc":"2.0","id":1,"result":"0x5208"}      <- a clean estimate, from an address that cannot send
Enter fullscreen mode Exit fullscreen mode

Estimation simulates execution without applying the permission gate, so it answers 0x5208
for addresses that will nonetheless fail to transact.

2. Use the SDK's permission hook, which queries the on-chain permission state directly and
is the only read-only check that answers this question:

import { useHasChainPermission } from "@redbellynetwork/eligibility-sdk";

const { data: canSend } = useHasChainPermission(address);
// true  -> address may send
// false -> address must complete access.redbelly.network first
Enter fullscreen mode Exit fullscreen mode

3. Without the SDK, send a probe transaction. A zero-value self-transfer costs one cent
and gives a definitive answer where estimation does not:

try {
  const tx = await wallet.sendTransaction({ to: wallet.address, value: 0n });
  await tx.wait();
  console.log("address can send");
} catch (err) {
  console.log("address cannot send — claim access at https://access.redbelly.network");
  console.log("raw:", err.shortMessage ?? err.message);
}
Enter fullscreen mode Exit fullscreen mode

4. Check your balance for context, not for diagnosis.

curl -s -X POST $RPC -H 'Content-Type: application/json' \
  --data "{\"jsonrpc\":\"2.0\",\"method\":\"eth_getBalance\",\"params\":[\"$ADDR\",\"latest\"],\"id\":1}"
Enter fullscreen mode Exit fullscreen mode

A non-zero result with a failing estimate points at permissioning. Note that a healthy
balance is not evidence against this diagnosis — an un-onboarded address can receive RBNT
without KYC/KYB, so a funded account that cannot send is exactly what this failure looks
like.

3. Claim the credential and enable the account. Go to
https://access.redbelly.network, connect the wallet holding that address, and complete
the prompts. The flow issues the access credential and then submits the on-chain
transaction that enables write access for the address.

4. Re-run step 1. A gas quantity where there was previously an error confirms the
address is enabled.

Prevention

Onboard the address before writing any code against it. Network access is a
prerequisite, in the same category as having a funded account — not something to discover
midway through debugging.

Detect the state in your dApp and tell the user, rather than letting a transaction fail
in front of them:

import { useHasChainPermission } from "@redbellynetwork/eligibility-sdk";

function TransactButton({ address }) {
  const { data: hasPermission, isLoading } = useHasChainPermission(address);

  if (isLoading) return <button disabled>Checking access…</button>;
  if (!hasPermission) {
    return (
      <a href="https://access.redbelly.network" target="_blank" rel="noreferrer">
        Enable your account on Redbelly to continue
      </a>
    );
  }
  return <button onClick={submit}>Submit</button>;
}
Enter fullscreen mode Exit fullscreen mode

Without the SDK, there is no read-only check that works — estimation does not surface the
permission gate (see Solution step 1). The reliable options are a one-cent probe transaction
at startup, or handling the failure when it happens:

async function assertCanTransact(wallet) {
  try {
    const tx = await wallet.sendTransaction({ to: wallet.address, value: 0n });
    await tx.wait();
  } catch (err) {
    throw new Error(
      `${wallet.address} cannot send on Redbelly — claim network access at ` +
      `https://access.redbelly.network, then retry. (${err.shortMessage ?? err.message})`
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

Every address you use needs its own credential — deployer keys, CI keys, relayer keys and
test accounts included. There is no way to delegate write access from one enabled address to
an arbitrary un-enabled one. (Verified businesses are the exception: a Business Identifier
contract can authorise external wallets to act on the company's behalf without each one
completing KYB separately.)


D2 — Testnet faucet does not distribute RBNT

Category: Faucet · Severity: High — blocks all testnet work

Symptom

The faucet does not pay out. Symptoms vary: the request button does nothing, the claim
appears to succeed but the balance stays 0x0, or the site refuses to let you request at
all.

Root Cause

Four distinct causes, and the error surface does not reliably distinguish them:

  1. No Discord account linked. The faucet is FAUCETME, and Redbelly's documentation states you "join with your Discord account". Discord authentication is the gate, not an optional convenience.
  2. Wrong network selected. FAUCETME serves multiple networks. A DevNet selection is dead (A3); the target is Testnet.
  3. Cooldown. Faucets rate-limit per account and per address. A repeat request inside the window is refused, sometimes silently.
  4. Balance checked on the wrong chain. A funded testnet address shows zero when queried against mainnet — a common consequence of B3.

Separately: Redbelly's docs note that a nominal amount of RBNT is granted automatically
when you receive access to testing environments — so completing network access
(D1) may fund the address without the faucet at all.

Solution

1. Request from the faucet:

  • Go to https://redbelly.faucetme.pro
  • Authenticate with Discord when prompted
  • Select Redbelly Testnet — not DevNet
  • Paste the address and submit

2. Confirm the balance against the testnet RPC specifically, rather than trusting a
wallet UI that may be pointed elsewhere:

curl -s -X POST https://governors.testnet.redbelly.network \
  -H 'Content-Type: application/json' \
  --data '{"jsonrpc":"2.0","method":"eth_getBalance","params":["0xYOUR_ADDRESS","latest"],"id":1}'
Enter fullscreen mode Exit fullscreen mode

Anything other than "0x0" means funds arrived. Convert to RBNT:

python3 -c "print(int('0xPASTE_RESULT_HERE', 16) / 1e18, 'RBNT')"
Enter fullscreen mode Exit fullscreen mode

3. If the balance is zero, work through the causes in order:

Check Command or action
Discord linked? Re-authenticate at https://redbelly.faucetme.pro
Right network? Re-select Redbelly Testnet, not DevNet
Cooldown? Wait and retry; faucets rate-limit per account
Address enabled? Complete https://access.redbelly.network — this also grants a nominal starting balance
Right chain queried? The command above targets governors.testnet — confirm you are not querying mainnet

4. Work out how much you actually need. Redbelly's gas is priced in USD
(C1), so budget by the published unit price rather than by RBNT intuition:

cost in USD = gas units x 0.000000476190476190
Enter fullscreen mode Exit fullscreen mode

A simple transfer is US$0.01. A typical contract deployment at ~1.2M gas is about US$0.57.
Faucet-scale amounts go a long way; a zero balance is far more often a delivery problem than
an insufficiency problem.

Prevention

Fund and enable the deployment address once, at project setup, and keep the key stable.
Rotating keys means repeating both the faucet request and the network access flow.

Add a balance assertion to your deploy script so a drained account fails with a clear message
instead of an opaque insufficient funds further in:

const balance = await provider.getBalance(deployer.address);
if (balance === 0n) {
  throw new Error(
    `${deployer.address} has no RBNT. Fund at https://redbelly.faucetme.pro`
  );
}
Enter fullscreen mode Exit fullscreen mode

E. Contract deployment and compilation

E1 — Contract compiles but misbehaves or fails to deploy: EVM version mismatch

Category: Deployment · Severity: Critical · Affects every project started today

Symptom

The contract compiles without complaint locally and passes its tests against the in-process
Hardhat network, then on Redbelly:

  • deployment reverts with no reason;
  • the contract deploys but a function reverts on a code path that works locally;
  • eth_estimateGas fails on a call your tests cover.

Nothing points at the compiler, because the compiler was happy.

Root Cause

Redbelly's EVM implements the prague revision. Modern Solidity does not target prague
by default.

Redbelly's EVM compatibility page pins two values:

Setting Redbelly
EVM version Prague
Solidity version v0.8.30

And Solidity's own default EVM target has moved twice since:

Solidity release Date Default EVM version
0.8.28 Oct 2024 cancun
0.8.30 May 2025 prague ← matches Redbelly
0.8.31 Dec 2025 osaka ← newer than Redbelly
0.8.36 Jul 2026 osaka (adds amsterdam support)

So a project started today, on a current toolchain, compiles for osaka by default,
targeting an EVM revision newer than the chain implements. Bytecode that assumes opcodes or
semantics the network does not provide fails at deployment or at the first call that reaches
the affected code path — and it fails on-chain, not at compile time, which is why it presents
as a contract bug.

There is a second, quieter version of the same problem. npx hardhat --init scaffolds a
project pinned to Solidity 0.8.28, whose default target is cancun. Cancun is older
than Prague, so it runs — but you silently lose access to everything Prague added, and your
build no longer matches the version Redbelly documents.

Neither default is correct for Redbelly. Only an explicit setting is.

Solution

Pin both the compiler version and the EVM version. Never rely on the default.

Hardhat 3:

// hardhat.config.ts
import { defineConfig } from "hardhat/config";

export default defineConfig({
  solidity: {
    profiles: {
      default: {
        version: "0.8.30",
        settings: {
          evmVersion: "prague",           // must match Redbelly's EVM
          optimizer: { enabled: true, runs: 200 },
        },
      },
    },
  },
});
Enter fullscreen mode Exit fullscreen mode

Hardhat 2:

// hardhat.config.js
module.exports = {
  solidity: {
    version: "0.8.30",
    settings: {
      evmVersion: "prague",
      optimizer: { enabled: true, runs: 200 },
    },
  },
};
Enter fullscreen mode Exit fullscreen mode

Foundry:

# foundry.toml
[profile.default]
solc_version = "0.8.30"
evm_version = "prague"
optimizer = true
optimizer_runs = 200
Enter fullscreen mode Exit fullscreen mode

Pin the pragma in the contract too, so the file cannot be built by a different compiler:

// SPDX-License-Identifier: MIT
pragma solidity 0.8.30;   // exact, not ^0.8.30
Enter fullscreen mode Exit fullscreen mode

Confirm what you actually built. The compiler records its settings in the build output:

# Hardhat 3
npx hardhat compile --force
grep -r '"evmVersion"' artifacts/build-info/*.json | head -1
# expect: "evmVersion": "prague"
Enter fullscreen mode Exit fullscreen mode
# Foundry
forge build --force
jq '.metadata.settings.evmVersion' out/YourContract.sol/YourContract.json
# expect: "prague"
Enter fullscreen mode Exit fullscreen mode

If that prints osaka or cancun, the setting has not taken effect — most often because a
second solidity block elsewhere in the config is overriding it.

Prevention

Treat evmVersion as a required field in any Redbelly project, in the same way as the chain
ID. Leaving it unset means the target moves whenever the compiler is upgraded, and it moves
without warning.

Add a build assertion so a toolchain upgrade cannot silently retarget your contracts:

# scripts/assert-evm-version.sh
set -e
FOUND=$(grep -ho '"evmVersion": *"[a-z]*"' artifacts/build-info/*.json | head -1 | grep -o '[a-z]*"$' | tr -d '"')
if [ "$FOUND" != "prague" ]; then
  echo "ERROR: compiled for evmVersion=$FOUND, Redbelly requires prague" >&2
  exit 1
fi
echo "OK: evmVersion=prague"
Enter fullscreen mode Exit fullscreen mode

Re-read https://vine.redbelly.network/consensus/evm-compatibility/ when upgrading the
compiler. Redbelly will move to newer EVM revisions over time, and the correct value is
whatever that page says — not whatever the compiler defaults to.


E2 — Hardhat deployment fails with insufficient funds for gas * price + value

Category: Deployment · Severity: High

Symptom

Error: insufficient funds for intrinsic transaction cost
Enter fullscreen mode Exit fullscreen mode
ProviderError: insufficient funds for gas * price + value
Enter fullscreen mode Exit fullscreen mode

Often surprising, because the balance looks healthy by the standards of other chains.

Root Cause

The account cannot cover gasLimit x gasPrice + value. On Redbelly the gasPrice term is
the one that catches people: eth_gasPrice returns roughly 165,000 gwei (C1),
so an RBNT balance that would fund hundreds of Ethereum transactions may fund very few here
— in RBNT terms. In dollar terms it is cheap; the two intuitions disagree, and RBNT is what
the balance check uses.

Two other causes present identically:

  • Deploying from the wrong account. Hardhat uses the first entry in accounts, which is not necessarily the address you funded.
  • The address was never funded on this network. A balance on testnet is not a balance on mainnet, and vice versa.

Solution

1. Read the actual numbers rather than estimating them:

ADDR=0xYOUR_DEPLOYER
RPC=https://governors.testnet.redbelly.network

echo -n "balance:  "; curl -s -X POST $RPC -H 'Content-Type: application/json' \
  --data "{\"jsonrpc\":\"2.0\",\"method\":\"eth_getBalance\",\"params\":[\"$ADDR\",\"latest\"],\"id\":1}"
echo; echo -n "gasPrice: "; curl -s -X POST $RPC -H 'Content-Type: application/json' \
  --data '{"jsonrpc":"2.0","method":"eth_gasPrice","params":[],"id":1}'
echo
Enter fullscreen mode Exit fullscreen mode

2. Compute what the deployment needs, in RBNT and in USD:

// scripts/estimate-deploy.mjs
import { ethers } from "ethers";

const provider = new ethers.JsonRpcProvider(
  "https://governors.testnet.redbelly.network",
  { chainId: 153, name: "redbelly-testnet" },
  { staticNetwork: true }
);

const address  = process.env.DEPLOYER_ADDRESS;
const gasPrice = (await provider.getFeeData()).gasPrice;
const balance  = await provider.getBalance(address);
const gasLimit = 1_500_000n;                    // replace with your estimate

const needed = gasLimit * gasPrice;
console.log("balance :", ethers.formatEther(balance), "RBNT");
console.log("needed  :", ethers.formatEther(needed), "RBNT");
console.log("in USD  :", (Number(gasLimit) * 0.000000476190476190).toFixed(4));
console.log(needed > balance ? "SHORTFALL" : "sufficient");
Enter fullscreen mode Exit fullscreen mode

3. Confirm Hardhat is using the account you funded:

npx hardhat console --network redbellyTestnet
Enter fullscreen mode Exit fullscreen mode
const [signer] = await ethers.getSigners();
console.log(signer.address);
console.log(ethers.formatEther(await signer.provider.getBalance(signer.address)));
Enter fullscreen mode Exit fullscreen mode

If that address is not the one you funded, the key in REDBELLY_PRIVATE_KEY is not the one
you think it is.

4. Top up at https://redbelly.faucetme.pro (D2).

5. If the balance is genuinely sufficient and it still fails, the error is misleading and
the real cause is permissioning — D1. A zero-value self-transfer distinguishes them in
one call.

Prevention

Fail early, with a message that names the shortfall:

const gasPrice = (await provider.getFeeData()).gasPrice;
const estimated = await deployTx.estimateGas();
const needed = (estimated * gasPrice * 120n) / 100n;   // 20% headroom
const balance = await provider.getBalance(deployer.address);

if (balance < needed) {
  throw new Error(
    `Deployer ${deployer.address} needs ${ethers.formatEther(needed)} RBNT, ` +
    `has ${ethers.formatEther(balance)}. Fund at https://redbelly.faucetme.pro`
  );
}
Enter fullscreen mode Exit fullscreen mode

Budget in dollars. Gas costs the same USD amount regardless of what the RBNT figure does,
which makes the dollar number the stable one to plan against.


E3 — No official Hardhat config exists for Redbelly

Category: Deployment · Severity: Medium — a documentation gap, not a bug

Symptom

Redbelly's developer portal recommends Hardhat, then links to hardhat.org without giving any
Redbelly-specific settings. There is no network block, no chain ID, no RPC URL, and no
verification configuration anywhere in the official documentation. Everyone writes their own
and gets a slightly different one.

The deployment page compounds it by recommending Remix as the primary tool, which is
workable for a single contract and does not scale to a project with tests and CI.

Redbelly's testing page recommends Waffle. ethereum-waffle last published version
4.0.10 on 15 February 2023 — over three years ago. It is not the current recommendation
for new Hardhat projects.

Root Cause

Redbelly's smart-contract documentation is written as a tour of the ecosystem rather than as
a setup guide. Each page names a tool and links to that tool's own site: Hardhat for the
development environment, Waffle for testing, Remix for deployment. None of them carries the
Redbelly-specific values those tools need — network name, chain ID, RPC URL, explorer API —
because those live on a separate page (/environments/) that the tooling pages do not
reproduce.

The result is that every developer assembles the config themselves from two or three pages,
and each one makes an independent decision about the settings the documentation never
mentions at all: evmVersion (E1), the chainDescriptors block needed for
verification (F1), and whether to set a gasPrice (C1). Those three are
exactly the settings that are wrong by default on this chain, so the gap is not merely
inconvenient — it reliably produces broken projects.

The tool recommendations have also aged at different rates. Hardhat has since released a
major version with a different config format, and Waffle has not published since February
2023, so following the pages literally produces a stack that does not match either the
current tooling or the current chain.

Solution

Use this configuration. Every value in it is traced to a live source in
sources.md, and the harness that ships with this wiki re-checks
each one against the chain when run.

Hardhat 3 (npm install --save-dev hardhat gives you 3.x):

// hardhat.config.ts
import hardhatEthers from "@nomicfoundation/hardhat-ethers";
import hardhatVerify from "@nomicfoundation/hardhat-verify";
import { configVariable, defineConfig } from "hardhat/config";

export default defineConfig({
  plugins: [hardhatEthers, hardhatVerify],

  solidity: {
    profiles: {
      default: {
        version: "0.8.30",
        settings: {
          evmVersion: "prague",                 // see E1
          optimizer: { enabled: true, runs: 200 },
        },
      },
    },
  },

  // Hardhat 3 replaced hardhat-verify's `customChains` with `chainDescriptors`. See F1.
  chainDescriptors: {
    151: {
      name: "Redbelly Network Mainnet",
      blockExplorers: {
        etherscan: {
          name: "Routescan",
          url: "https://redbelly.routescan.io",
          apiUrl: "https://api.routescan.io/v2/network/mainnet/evm/151/etherscan",
        },
      },
    },
    153: {
      name: "Redbelly Testnet",
      blockExplorers: {
        etherscan: {
          name: "Routescan",
          url: "https://redbelly.testnet.routescan.io",
          apiUrl: "https://api.routescan.io/v2/network/testnet/evm/153/etherscan",
        },
      },
    },
  },

  verify: {
    etherscan: { apiKey: "routescan" },         // placeholder; Routescan needs no key
  },

  networks: {
    redbellyTestnet: {
      type: "http",
      chainType: "l1",
      url: "https://governors.testnet.redbelly.network",
      chainId: 153,
      accounts: [configVariable("REDBELLY_PRIVATE_KEY")],
      // no gasPrice — Redbelly's is USD-pegged and must be read per transaction. See C1.
    },
    redbellyMainnet: {
      type: "http",
      chainType: "l1",
      url: "https://governors.mainnet.redbelly.network",
      chainId: 151,
      accounts: [configVariable("REDBELLY_PRIVATE_KEY")],
    },
  },
});
Enter fullscreen mode Exit fullscreen mode

Hardhat 2 (npm install --save-dev hardhat@hh2):

// hardhat.config.js
require("@nomicfoundation/hardhat-toolbox");

module.exports = {
  solidity: {
    version: "0.8.30",
    settings: { evmVersion: "prague", optimizer: { enabled: true, runs: 200 } },
  },
  networks: {
    redbellyTestnet: {
      url: "https://governors.testnet.redbelly.network",
      chainId: 153,
      accounts: [process.env.REDBELLY_PRIVATE_KEY],
    },
    redbellyMainnet: {
      url: "https://governors.mainnet.redbelly.network",
      chainId: 151,
      accounts: [process.env.REDBELLY_PRIVATE_KEY],
    },
  },
  etherscan: {
    apiKey: { redbellyTestnet: "routescan", redbellyMainnet: "routescan" },
    customChains: [
      {
        network: "redbellyTestnet",
        chainId: 153,
        urls: {
          apiURL: "https://api.routescan.io/v2/network/testnet/evm/153/etherscan/api",
          browserURL: "https://redbelly.testnet.routescan.io",
        },
      },
      {
        network: "redbellyMainnet",
        chainId: 151,
        urls: {
          apiURL: "https://api.routescan.io/v2/network/mainnet/evm/151/etherscan/api",
          browserURL: "https://redbelly.routescan.io",
        },
      },
    ],
  },
};
Enter fullscreen mode Exit fullscreen mode

Supplying the key. Hardhat 3's configVariable reads from the encrypted keystore or the
environment:

npx hardhat keystore set REDBELLY_PRIVATE_KEY     # stored encrypted, preferred
# or
export REDBELLY_PRIVATE_KEY=0xyourkey             # process env
Enter fullscreen mode Exit fullscreen mode

Deploy and confirm:

npx hardhat compile
npx hardhat run scripts/deploy.ts --network redbellyTestnet
Enter fullscreen mode Exit fullscreen mode

Prevention

Keep chain ID, RPC URL, explorer URL and explorer API URL in the config rather than scattered
across scripts. Redbelly's two documentation sites disagree on some of these values
(A1), so a single definition per project means one place to correct if a value moves.


F. Explorer and contract verification

F1 — The network "..." with chain id "153" is not supported

Category: Verification · Severity: High

Symptom

Error HHE80000: The network "redbellyTestnet" with chain id "153" is not supported.
Enter fullscreen mode Exit fullscreen mode
No Etherscan block explorer is configured for the 153 chain in the chain descriptors.
Enter fullscreen mode Exit fullscreen mode

Hardhat 2 produces the equivalent:

Error HH303: Unrecognized task verify
Enter fullscreen mode Exit fullscreen mode
The chain id 153 is not supported. To see the list of supported chains, run npx hardhat verify --list-networks
Enter fullscreen mode Exit fullscreen mode

Root Cause

hardhat-verify resolves an explorer API URL from Etherscan's multichain chain list, then
falls back to whatever the config supplies. Redbelly is not on Etherscan's list — chains
151 and 153 do not appear in https://api.etherscan.io/v2/chainlist — so unless you supply
the explorer yourself, the plugin has nowhere to send the verification request.

Redbelly's explorer is Routescan, which exposes an Etherscan-compatible API at a
different base URL. Supplying that URL is all that is required; Routescan accepts
verification without an API key.

The second trap is version-specific: Hardhat 3 removed customChains. A config carried
over from Hardhat 2 has an etherscan.customChains array that Hardhat 3 ignores entirely,
so the error persists and appears not to respond to the fix. Hardhat 3's replacement is a
top-level chainDescriptors map.

Solution

Hardhat 3 — add chainDescriptors:

export default defineConfig({
  plugins: [hardhatEthers, hardhatVerify],

  chainDescriptors: {
    153: {
      name: "Redbelly Testnet",
      blockExplorers: {
        etherscan: {
          name: "Routescan",
          url: "https://redbelly.testnet.routescan.io",
          apiUrl: "https://api.routescan.io/v2/network/testnet/evm/153/etherscan",
        },
      },
    },
    151: {
      name: "Redbelly Network Mainnet",
      blockExplorers: {
        etherscan: {
          name: "Routescan",
          url: "https://redbelly.routescan.io",
          apiUrl: "https://api.routescan.io/v2/network/mainnet/evm/151/etherscan",
        },
      },
    },
  },

  verify: { etherscan: { apiKey: "routescan" } },
});
Enter fullscreen mode Exit fullscreen mode

The apiKey looks redundant because Routescan does not require one — but hardhat-verify
raises The Etherscan API key is empty. if the field is missing or blank, so a placeholder
string is required.

Hardhat 2 — use customChains (see the full block in E3). Note that the Hardhat 2
apiURL ends in /api and the Hardhat 3 apiUrl does not — the plugin appends it in v3:

apiURL: "https://api.routescan.io/v2/network/testnet/evm/153/etherscan/api"   // HH2
apiUrl: "https://api.routescan.io/v2/network/testnet/evm/153/etherscan"       // HH3
Enter fullscreen mode Exit fullscreen mode

Then verify:

npx hardhat verify --network redbellyTestnet 0xYOUR_CONTRACT "constructor arg"
Enter fullscreen mode Exit fullscreen mode

Confirm the API endpoint is reachable before blaming your config:

curl -s "https://api.routescan.io/v2/network/testnet/evm/153/etherscan/api?module=contract&action=checkverifystatus&guid=probe"
# {"status":"0","message":"NOTOK","result":"Verification not found"}
Enter fullscreen mode Exit fullscreen mode

That response is the good outcome — an Etherscan-format reply proves the endpoint is live
and speaking the right protocol.

If hardhat verify remains awkward, verify over the API directly. Routescan accepts the
standard Etherscan verifysourcecode call:

curl -X POST "https://api.routescan.io/v2/network/testnet/evm/153/etherscan/api" \
  -d "module=contract" \
  -d "action=verifysourcecode" \
  -d "contractaddress=0xYOUR_CONTRACT" \
  -d "sourceCode=$(cat artifacts/build-info/*.json | jq -c '.input')" \
  -d "codeformat=solidity-standard-json-input" \
  -d "contractname=contracts/YourContract.sol:YourContract" \
  -d "compilerversion=v0.8.30+commit.73712a01" \
  -d "constructorArguements=YOUR_ABI_ENCODED_ARGS"
Enter fullscreen mode Exit fullscreen mode

Poll the returned GUID:

curl -s "https://api.routescan.io/v2/network/testnet/evm/153/etherscan/api?module=contract&action=checkverifystatus&guid=YOUR_GUID"
Enter fullscreen mode Exit fullscreen mode

Prevention

Put both chain descriptors in the config at project setup, before the first deployment.
Verification is far more painful to retrofit once you have contracts deployed and have moved
on from the exact compiler settings that produced them.


F2 — Verification submitted but the contract shows no source

Category: Verification · Severity: Medium

Symptom

Fail - Unable to verify
Enter fullscreen mode Exit fullscreen mode
Error! Unable to locate ContractCode at 0x...
Enter fullscreen mode Exit fullscreen mode

Or verification reports success and the explorer's Contract tab still shows only bytecode.

Root Cause

Verification recompiles your source and compares the result byte-for-byte with what is
deployed. Any difference in compiler input produces different bytecode and the match fails.
Four causes account for nearly all of it:

  1. Compiler settings drift. Compiler version, evmVersion, optimizer enabled/disabled, or optimizer runs differing from the deployment build. On Redbelly, evmVersion is the likeliest offender because it is the setting people do not realise they had to set (E1).
  2. Constructor arguments wrong or missing. They are part of the deployment payload and must be supplied exactly.
  3. Verifying too early. The explorer has not indexed the deployment yet, and reports the address as having no bytecode:
   The request to ... failed because the address "0x..." does not have bytecode.
Enter fullscreen mode Exit fullscreen mode
  1. Wrong chain. A testnet contract submitted to the mainnet API — see B3.

Solution

1. Wait for indexing before verifying. Five confirmations is a reliable threshold:

const contract = await Factory.deploy(...args);
await contract.deploymentTransaction().wait(5);   // then verify
Enter fullscreen mode Exit fullscreen mode

Or from the shell, confirm the explorer can see the bytecode:

curl -s "https://api.routescan.io/v2/network/testnet/evm/153/etherscan/api\
?module=proxy&action=eth_getCode&address=0xYOUR_CONTRACT&tag=latest"
Enter fullscreen mode Exit fullscreen mode

A result longer than "0x" means it is indexed.

2. Read the settings that were actually used — not the ones you believe you configured:

jq '.input.settings | {optimizer, evmVersion}' artifacts/build-info/*.json
Enter fullscreen mode Exit fullscreen mode
{
  "optimizer": { "enabled": true, "runs": 200 },
  "evmVersion": "prague"
}
Enter fullscreen mode Exit fullscreen mode

3. Pass constructor arguments exactly as deployed:

npx hardhat verify --network redbellyTestnet 0xYOUR_CONTRACT "MyToken" "MTK" 18
Enter fullscreen mode Exit fullscreen mode

For complex or nested arguments, use a file so quoting cannot corrupt them:

// arguments.js
module.exports = ["MyToken", "MTK", 18, ["0xabc...", "0xdef..."]];
Enter fullscreen mode Exit fullscreen mode
npx hardhat verify --network redbellyTestnet --constructor-args arguments.js 0xYOUR_CONTRACT
Enter fullscreen mode Exit fullscreen mode

4. Confirm the result rather than trusting the CLI message:

curl -s "https://api.routescan.io/v2/network/testnet/evm/153/etherscan/api\
?module=contract&action=getsourcecode&address=0xYOUR_CONTRACT" | jq '.result[0] | {ContractName, CompilerVersion, EVMVersion, OptimizationUsed}'
Enter fullscreen mode Exit fullscreen mode

A populated ContractName means the source is published. An EVMVersion that reads
Default rather than prague indicates the settings did not travel with the submission.

Prevention

Deploy and verify from the same command, so the settings cannot drift between them:

{
  "scripts": {
    "deploy:testnet": "hardhat run scripts/deploy.ts --network redbellyTestnet && npm run verify:testnet",
    "verify:testnet": "hardhat verify --network redbellyTestnet --constructor-args arguments.js $CONTRACT_ADDRESS"
  }
}
Enter fullscreen mode Exit fullscreen mode

Commit hardhat.config.ts and the lockfile together. Verification failures months later are
almost always a compiler version that moved underneath an unpinned dependency range.


G. Eligibility SDK integration

G1 — npm install @redbellynetwork/eligibility-sdk fails with 404

Category: SDK · Severity: Critical — blocks the first step of SDK integration

Symptom

npm error code E404
npm error 404 Not Found - GET https://registry.npmjs.org/@redbellynetwork%2feligibility-sdk
npm error 404
npm error 404  '@redbellynetwork/eligibility-sdk@*' is not in this registry.
Enter fullscreen mode Exit fullscreen mode

Root Cause

The package is not published on the public npm registry. It is published to GitHub
Packages
, which is a separate registry that npm does not consult unless told to, and which
requires authentication even for reads.

Confirmed directly:

  • https://registry.npmjs.org/@redbellynetwork%2Feligibility-sdkHTTP 404
  • A registry search for redbellynetwork returns zero packages
  • https://npm.pkg.github.com/@redbellynetwork%2Feligibility-sdkHTTP 401 {"error":"authentication token not provided"}

So the default npm install fails not because the name is wrong but because npm is asking
the wrong registry, and the right one requires a token.

Solution

1. Create a GitHub personal access token (classic) with the read:packages scope, at
https://github.com/settings/tokens. Read access is sufficient — no write scopes are needed.

2. Create .npmrc in your project root:

@redbellynetwork:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}
always-auth=true
Enter fullscreen mode Exit fullscreen mode

The leading // on the second line is required. Redbelly's installation page publishes
that line without it, which fails in a way that looks like a bad token — see G2.

3. Export the token and install:

export GITHUB_TOKEN=ghp_your_token_here
npm install @redbellynetwork/eligibility-sdk
Enter fullscreen mode Exit fullscreen mode

4. Install the peer dependencies, which are not pulled in automatically:

npm install @reown/appkit @reown/appkit-adapter-wagmi wagmi viem @tanstack/react-query
Enter fullscreen mode Exit fullscreen mode

Required versions, per the SDK documentation:

Package Version
react / react-dom 18.x or higher
viem 2.x or higher
wagmi 2.x or higher
@tanstack/react-query 5.x or higher

5. Confirm the registry is answering you before debugging your app:

curl -s -H "Authorization: Bearer $GITHUB_TOKEN" \
  https://npm.pkg.github.com/@redbellynetwork%2Feligibility-sdk | head -c 200
Enter fullscreen mode Exit fullscreen mode

JSON containing dist-tags means authentication works. {"error":"authentication token not provided"} means the token is not reaching the registry.

Prevention

Never commit the token. Keep .npmrc referencing ${GITHUB_TOKEN} and set the variable in
your shell, your .env, or your CI secrets:

# GitHub Actions
- run: npm ci
  env:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Enter fullscreen mode Exit fullscreen mode

Add .npmrc to .gitignore if you ever inline a literal token into it. A leaked
read:packages token is low-severity but trivially avoidable.

Document the token requirement in your project README. This is the single most common place
for a new contributor's npm install to fail, and the error message says nothing about
GitHub Packages.


G2 — .npmrc configured but install still fails with 401

Category: SDK · Severity: High · Caused by an error in the official docs

Symptom

You followed the installation instructions, created .npmrc, supplied a valid token, and:

npm error code E401
npm error 401 Unauthorized - GET https://npm.pkg.github.com/@redbellynetwork%2feligibility-sdk
npm error Incorrect or missing password.
Enter fullscreen mode Exit fullscreen mode

Regenerating the token changes nothing, because the token was never the problem.

Root Cause

The .npmrc auth line in Redbelly's installation documentation is missing its leading
//.
The page publishes:

@redbellynetwork:registry=https://npm.pkg.github.com
npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}      # ← missing //
always-auth=true
Enter fullscreen mode Exit fullscreen mode

npm's config format keys credentials by a URI-like path that must begin with //.
Without it, npm treats npm.pkg.github.com/:_authToken as an unrelated config key, silently
ignores it, and sends the request unauthenticated. GitHub Packages returns 401 for
unauthenticated reads, so the symptom is identical to a wrong token — which is why people
regenerate tokens repeatedly instead of looking at the file.

Redbelly's getting-started page contains the same line with the slashes, so the two
pages disagree. The getting-started page is the correct one.

Solution

1. Fix the line. Correct form:

@redbellynetwork:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}
always-auth=true
Enter fullscreen mode Exit fullscreen mode

2. Confirm npm has parsed the credential — this prints the resolved config and is the
fastest way to tell:

npm config get //npm.pkg.github.com/:_authToken
Enter fullscreen mode Exit fullscreen mode

Your token (or the literal ${GITHUB_TOKEN}) means the key parsed. undefined means the
line is still malformed.

3. Clear the cached failure, which otherwise persists across a corrected config:

npm cache clean --force
rm -rf node_modules package-lock.json
npm install
Enter fullscreen mode Exit fullscreen mode

4. If it still 401s, work through the remaining causes in order:

Cause Check
Token lacks read:packages Regenerate at https://github.com/settings/tokens with that scope ticked
GITHUB_TOKEN not exported echo $GITHUB_TOKEN — empty means npm substitutes nothing
Token expired Classic tokens expire; the page shows the expiry date
SSO not authorised If your account is in an SSO-enabled org, click "Configure SSO" on the token and authorise it

5. Test the token independently of npm, which separates a token problem from a config
problem:

curl -s -o /dev/null -w '%{http_code}\n' \
  -H "Authorization: Bearer $GITHUB_TOKEN" \
  https://npm.pkg.github.com/@redbellynetwork%2Feligibility-sdk
Enter fullscreen mode Exit fullscreen mode

200 means the token is fine and the fault is in .npmrc. 401 means the token is the
problem.

Prevention

Copy the .npmrc block from this wiki or from Redbelly's getting-started page, not from the
installation page, until the typo is corrected upstream.

Keep a one-line check in your project's setup script so the failure names itself:

npm config get //npm.pkg.github.com/:_authToken >/dev/null 2>&1 \
  || echo "WARNING: .npmrc auth line missing or malformed — check the leading //"
Enter fullscreen mode Exit fullscreen mode

G3 — Quickstart git clone fails: the starter repo is private

Category: SDK · Severity: High — the documented quickstart cannot be completed

Symptom

Step 1 of the "Quickstart: Your First Eligibility Check" guide is:

git clone https://github.com/redbellynetwork/eligibility-sdk-quickstart.git
Enter fullscreen mode Exit fullscreen mode

which produces:

Cloning into 'eligibility-sdk-quickstart'...
remote: Repository not found.
fatal: repository 'https://github.com/redbellynetwork/eligibility-sdk-quickstart.git/' not found
Enter fullscreen mode Exit fullscreen mode

Or a credential prompt that no valid credentials satisfy.

Root Cause

The repository is not publicly accessible. Verified:

  • https://api.github.com/repos/redbellynetwork/eligibility-sdk-quickstartHTTP 404
  • https://github.com/redbellynetwork/eligibility-sdk-quickstartHTTP 404 logged out
  • The git endpoint returns HTTP 401, which indicates the repository exists but requires authorisation — a private repo, rather than a deleted one

GitHub returns 404 rather than 403 for private repositories you cannot see, so "Repository
not found" here means "not visible to you", not "does not exist".

The same quickstart page has a second dead step: Step 6 directs you to a "Demo Credential
Faucet" for a test KYC credential, marked (under development). Without it there is no
documented way to obtain the test credential the final verification step needs.

So the documented 15-minute quickstart cannot currently be completed end to end by an
external developer.

Solution

Build the integration directly from the API documentation rather than from the starter
kit. The pieces are all documented; only the assembled repository is missing.

1. Scaffold a Next.js app and install the SDK (G1):

npx create-next-app@latest my-eligibility-app --typescript --app
cd my-eligibility-app

cat > .npmrc <<'EOF'
@redbellynetwork:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}
always-auth=true
EOF

export GITHUB_TOKEN=ghp_your_token
npm install @redbellynetwork/eligibility-sdk
npm install @reown/appkit @reown/appkit-adapter-wagmi wagmi viem @tanstack/react-query
Enter fullscreen mode Exit fullscreen mode

2. Wrap the root component, which is the documented requirement for the SDK's hooks and
widgets to initialise at all (G4):

// app/providers.tsx
"use client";

import { WagmiProvider } from "wagmi";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { WagmiAdapter } from "@reown/appkit-adapter-wagmi";
import { createAppKit } from "@reown/appkit/react";
import { defineChain } from "@reown/appkit/networks";
import { EligibilitySDKProvider } from "@redbellynetwork/eligibility-sdk";

const redbellyTestnet = defineChain({
  id: 153,
  caipNetworkId: "eip155:153",
  chainNamespace: "eip155",
  name: "Redbelly Testnet",
  nativeCurrency: { decimals: 18, name: "Redbelly Network Coin", symbol: "RBNT" },
  rpcUrls: { default: { http: ["https://governors.testnet.redbelly.network"] } },
});

const networks = [redbellyTestnet] as const;
const projectId = process.env.NEXT_PUBLIC_REOWN_PROJECT_ID!;   // from https://cloud.reown.com
const wagmiAdapter = new WagmiAdapter({ projectId, networks: [...networks] });
const queryClient = new QueryClient();

createAppKit({
  adapters: [wagmiAdapter],
  projectId,
  networks: [...networks],
  metadata: {
    name: "My Eligibility App",
    description: "Eligibility checks on Redbelly Network",
    url: "http://localhost:3000",
    icons: [],
  },
});

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <WagmiProvider config={wagmiAdapter.wagmiConfig}>
      <QueryClientProvider client={queryClient}>
        <EligibilitySDKProvider
          config={{
            network: "testnet",                      // "mainnet" | "testnet" | "staging"
            apiKey: process.env.NEXT_PUBLIC_REDBELLY_API_KEY!,
          }}
        >
          {children}
        </EligibilitySDKProvider>
      </QueryClientProvider>
    </WagmiProvider>
  );
}
Enter fullscreen mode Exit fullscreen mode

3. Implement the three backend routes. The SDK requires these exact paths, and the
documentation is explicit that they "must match exactly or the SDK's will not function
properly":

Route Method Purpose
/auth-request POST Starts a proof session, returns { request, sessionId }
/callback POST Receives the ZK proof token from the wallet
/status/:sessionId GET Returns idle / verifying / failed / success

Register the receptor DID method before starting the server, with the correct chain IDs —
151 for mainnet, 153 for testnet:

const { core } = require("@iden3/js-iden3-auth");

core.registerDidMethodNetwork({
  method: "receptor", methodByte: 0b10000011,
  blockchain: "redbelly", network: "testnet",
  networkFlag: 0b10000011, chainId: 153,
});
core.registerDidMethodNetwork({
  method: "receptor", methodByte: 0b01010111,
  blockchain: "redbelly", network: "mainnet",
  networkFlag: 0b01010111, chainId: 151,
});
Enter fullscreen mode Exit fullscreen mode

State resolvers for proof verification:

const resolvers = {
  "redbelly:mainnet": new resolver.EthStateResolver(
    "https://governors.mainnet.redbelly.network",
    "0x1cc7261e1777D69505Cb6413a91bb27ca9eb1456"
  ),
  "redbelly:testnet": new resolver.EthStateResolver(
    "https://governors.testnet.redbelly.network",
    "0x69376715FB5E2B924a33e9C27302F52DEa178CDC"
  ),
};
Enter fullscreen mode Exit fullscreen mode

4. Download the Iden3 circuit verification keys into a keys/ directory in your backend
root. They come from the Polygon ID trusted setup:
https://github.com/0xPolygonID/phase2ceremony

5. Request an API key. EligibilitySDKProvider requires one, and the documentation
directs you to Averer customer support to obtain it. There is no self-service route.

6. For the test credential that the quickstart's demo faucet was meant to supply, use a
Privado-compatible identity wallet (Privado Web or Privado Mobile) and request a credential
from an accredited issuer. The authorised issuer DIDs are:

Network Issuer DID
Mainnet did:receptor:redbelly:mainnet:31AAH8sSaGd6fpnG1TcB6yQ4UZnNeyHzTk5aM2P7rjv
Testnet did:receptor:redbelly:testnet:31K82iKCtE6ciDc7oAr3T5EpjZb4S1EFM7c4xJaWkM2

Prevention

Before following any quickstart that begins with git clone, check the repository resolves:

curl -s -o /dev/null -w '%{http_code}\n' \
  https://api.github.com/repos/redbellynetwork/eligibility-sdk-quickstart
# 404 = not publicly available
Enter fullscreen mode Exit fullscreen mode

Ten seconds spent there saves the confusion of a clone failure that looks like a git
credential problem.


G4 — SDK components render nothing / window is not defined

Category: SDK · Severity: High

Symptom

ReferenceError: window is not defined
Enter fullscreen mode Exit fullscreen mode
Error: useConfig must be used within WagmiProvider
Enter fullscreen mode Exit fullscreen mode
Error: No QueryClient set, use QueryClientProvider to set one
Enter fullscreen mode Exit fullscreen mode

Or the widget mounts and renders nothing at all, with no error.

Root Cause

Three causes, in the order they usually appear:

1. The root component is not wrapped in EligibilitySDKProvider. The documentation
states that to use the SDK you "need to wrap your root component with EligibilitySDKProvider
and initialize wallet support via AppKit's Wagmi adapter". The SDK's hooks and widgets read
from that context; without it they have nothing to read and render nothing.

The provider chain has a required order — Wagmi outermost, then React Query, then the SDK:

WagmiProvider > QueryClientProvider > EligibilitySDKProvider > your components
Enter fullscreen mode Exit fullscreen mode

2. Server-side rendering. window is not defined means SDK code ran during SSR. The SDK
renders a QR modal and talks to browser wallet APIs, so it is client-only. In Next.js App
Router, a component tree without "use client" renders on the server first.

3. A missing API key. EligibilitySDKProvider takes an apiKey, and onboarding and
issuer-claim flows are gated behind it. An absent or invalid key produces a widget that
mounts and then does nothing.

On the "SDK iframe cross-origin" issue. This is worth stating precisely, because it is
commonly described as an iframe problem and the SDK does not use one. The SDK ships React
components — <IndividualOnboarding />, <BusinessOnboardingSdk /> — rendered directly into
your tree. The genuine cross-origin problem is different: the user's wallet calls your
backend from the public internet
, so localhost is unreachable to it. That is what breaks,
and the fix is a public HTTPS tunnel, covered below.

Solution

1. Mark the provider tree as client-only. Next.js App Router:

// app/providers.tsx
"use client";                      // ← required
// ... full provider setup as in G3, step 2
Enter fullscreen mode Exit fullscreen mode
// app/layout.tsx
import { Providers } from "./providers";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body><Providers>{children}</Providers></body>
    </html>
  );
}
Enter fullscreen mode Exit fullscreen mode

2. Import SDK components dynamically with SSR disabled, for any page that renders them:

import dynamic from "next/dynamic";

const IndividualOnboarding = dynamic(
  () => import("@redbellynetwork/eligibility-sdk").then((m) => m.IndividualOnboarding),
  { ssr: false, loading: () => <p>Loading onboarding…</p> }
);

export default function OnboardingPage() {
  return <IndividualOnboarding />;
}
Enter fullscreen mode Exit fullscreen mode

3. Render only after the component has mounted, which removes any remaining hydration
mismatch:

"use client";
import { useEffect, useState } from "react";

export function ClientOnly({ children }: { children: React.ReactNode }) {
  const [mounted, setMounted] = useState(false);
  useEffect(() => setMounted(true), []);
  return mounted ? <>{children}</> : null;
}
Enter fullscreen mode Exit fullscreen mode

4. Expose your backend over a public HTTPS URL. The wallet posts the proof to your
/callback endpoint from the internet, so http://localhost:3000 cannot receive it:

ngrok http 3000
# Forwarding  https://randomstring.ngrok.io -> http://localhost:3000
Enter fullscreen mode Exit fullscreen mode

Use the https:// URL as HOST_URL in your backend and as the base for the callback URI. A
QR code encoding a localhost callback will scan successfully and then never complete.

5. Allow the wallet's origin on your backend routes, since those requests are
cross-origin:

const cors = require("cors");
app.use(cors());                      // tighten for production
app.use(express.json());
Enter fullscreen mode Exit fullscreen mode

6. Keep the proof-request payload small. The SDK's documentation is explicit that QR
codes encode roughly 3 KB reliably, and that large or multiple queries can exceed the limit
and cause QR rendering to fail. A widget that renders a blank box where the QR should be is
usually an oversized scope array — reduce it to the minimum set of credential checks.

7. Confirm the provider chain is actually mounted:

import { useHasChainPermission } from "@redbellynetwork/eligibility-sdk";

function SdkHealthCheck({ address }: { address: string }) {
  const { data, error, isLoading } = useHasChainPermission(address);
  return <pre>{JSON.stringify({ data, error: error?.message, isLoading }, null, 2)}</pre>;
}
Enter fullscreen mode Exit fullscreen mode

If this throws "must be used within", the provider tree is wrong or not client-side. If it
returns a boolean, the SDK is wired correctly and the problem is elsewhere.

Prevention

Set the provider tree up once, in a dedicated providers.tsx, and never render SDK
components outside it.

Keep the verification scope on the server. The SDK documentation is explicit that scope
controls which credential proofs a user must satisfy and must not be read from req.body,
query parameters, or any other client-controlled input. Accept a stable flow ID from the
client and map it to a server-defined scope:

const FLOWS = {
  "aml-check": ELIGIBILITY_SCOPE,     // defined server-side
};

app.post("/auth-request", (req, res) => {
  const scope = FLOWS[req.body.flowId];
  if (!scope) return res.status(400).send("Unknown flow");
  // ... build the authorization request with the server-owned scope
});
Enter fullscreen mode Exit fullscreen mode

Keep revocation checks enabled. skipClaimRevocationCheck: true disables revocation
enforcement and belongs only in clearly marked non-production flows.


Error message index

Search this page for the literal string your tool printed. Every error string quoted anywhere
in this guide appears below, mapped to the entry that fixes it.

Error message (literal) Entry
404 page not found A2
cannot estimate gas; transaction may fail or may require manual gas limit C3, D1
Chain ID returned by the custom network does not match the submitted chain ID. B2, A1
Chain ID returned by RPC URL https://... does not match B2
code=SERVER_ERROR, status=429 A4
could not detect network (event="noNetwork", code=NETWORK_ERROR) A1, A2
Error! Unable to locate ContractCode at 0x... F2
Error HH110: Invalid JSON-RPC response received A1
Error HH303: Unrecognized task verify F1
Error HHE80000: The network "..." with chain id "153" is not supported. F1
execution reverted D1, C3
execution reverted (no reason string) C3, D1
Fail - Unable to verify F2
gas price too high C1
HttpProviderError: Invalid JSON-RPC response received A2
Incorrect or missing password. G2
insufficient funds for gas * price + value E2
insufficient funds for intrinsic transaction cost E2
Invalid JSON-RPC response received: <html> A1
max fee per gas less than block base fee C1
No Etherscan block explorer is configured for the 153 chain in the chain descriptors. F1
No QueryClient set, use QueryClientProvider to set one G4
nonce has already been used C4
nonce too low C4
npm error 404 Not Found - GET https://registry.npmjs.org/@redbellynetwork%2feligibility-sdk G1
npm error code E401 G2
npm error code E404 G1
ProviderError: invalid nonce; got 7, expected 9 C4
ReferenceError: window is not defined G4
remote: Repository not found. G3
replacement transaction underpriced C2
server response 429 Too Many Requests A4
The chain id 153 is not supported. F1
The contract "..." at address "0x..." is already verified. F2
The Etherscan API key is empty. F1
The request to ... failed because the address "0x..." does not have bytecode. F2
transaction execution reverted (no reason string) D1
transaction underpriced C2
Transaction not found (in explorer) B3
UNPREDICTABLE_GAS_LIMIT C3
Unrecognized chain ID "0x99". Try adding the chain using wallet_addEthereumChain first. B1
useConfig must be used within WagmiProvider G4
{"error":"authentication token not provided"} G1, G2

Keyword index

Keyword Entries
151 / mainnet chain ID A1, B1, B2
152 / DevNet A3
153 / testnet chain ID A1, B1, B2, F1
154 / TGE A1, B2
429 / rate limit A4
access credential D1, D2, C3
chainDescriptors F1, E3
customChains F1, E3
deployment E1, E2, E3
Discord D2
EligibilitySDKProvider G4, G3
eth_chainId A1, B2
eth_estimateGas C3, D1
eth_gasPrice C1, E2
EVM version / prague / osaka E1, F2, E3
explorer / Routescan B3, F1, F2
faucet / FAUCETME D2, E2
gas / fees / USD-pegged C1, C2, C3, E2
GitHub Packages G1, G2
Hardhat config E3, E1, F1
MetaMask B1, B2, B3, C4
Next.js / SSR G4, G3
ngrok / cross-origin G4
nonce C4, C2
.npmrc G1, G2
permissioned network D1, C3, E2
quickstart repo G3
RPC URL format A2, A1
Solidity 0.8.30 E1, E3
stuck / pending transaction C2, C1
useHasChainPermission D1, G4
wallet_addEthereumChain B1, B2
Waffle E3
contract verification F1, F2

Coverage and methodology

This section states exactly what this guide's issue list was derived from, so its coverage
can be judged rather than taken on trust.

Sources used to select the 22 entries:

  1. Redbelly's own documentation, read in full — the Vine developer portal (vine.redbelly.network) and the technical documentation site (docs.redbelly.network), including the Eligibility SDK reference, Receptor documentation, environments, network fees and EVM compatibility pages. Four entries (A1, G2, G3, and part of A2) document defects found in that documentation itself.
  2. Live probing of the published infrastructure — the mainnet and testnet JSON-RPC endpoints, the Routescan explorer APIs, the public chainid.network registry, the npm and GitHub Packages registries, and the GitHub API.
  3. Toolchain source — the shipped source of Hardhat 3.12.0, @nomicfoundation/hardhat-verify 3.0.22 and @nomicfoundation/hardhat-errors 3.0.18 were read directly to obtain the literal error strings and the exact configuration shapes quoted in this guide, rather than paraphrasing documentation.
  4. Solidity release history — the compiler's own release notes, to establish the default EVM version at each version (E1).
  5. The failure modes common to all EVM chains — nonce handling, gas estimation, rate limiting — adapted to Redbelly's specifics where they differ.
  6. A capture of the Redbelly Discord, analysed below.

Discord coverage: measured, and the result was not what we expected

No percentage of "Discord support questions" is claimed by this guide, because the channel
analysis found that there are none to measure against.

A capture of the Redbelly Network Discord's 💬︱general channel covering 23 July – 8 August
2026
yielded 21 distinct community questions. Of those:

Category Count
Ecosystem & Tokeniser 6
Roadmap & timing 4
Token & staking 3
Market positioning 2
Builder / partnership enquiries (non-technical) 2
DAO & task board 1
Support process 1
Legal / regional restrictions 1
Moderation 1
Developer technical support 0

Not one question in the sample concerned an RPC endpoint, a chain ID, wallet configuration,
gas, a nonce, contract deployment, contract verification, the faucet, or the Eligibility SDK.

The reason is structural: the Redbelly Discord has no developer support surface. The
captured channel list contains no #dev, #developers, #dev-support or #builders
channel — #technical-analysis sits under Price & Market and is chart analysis. Redbelly
team members in the capture confirm the routing:

"Please is there a ticket here?" — community member
"No, how can I help?" — Daniel Bressoud, Redbelly team, 5 Aug 2026

"We love builders. If you want to build on the Redbelly Network, the best would be to reach
out to our team info@redbelly.network"
— Daniel Bressoud, 5 Aug 2026

"You should join our Telegram and check Alan's update on this topic." — Daniel Bressoud,
5 Aug 2026

A developer who hits one of the errors in this guide, joins the official Discord, and looks
for somewhere to ask will find a community server with no developer channel and no ticket
system, and will be pointed at an email address.

That is a sharper version of the problem this guide exists to solve, and it is why the
entries here are sourced from documentation and live infrastructure testing rather than from
a question tally. Full analysis, transcript and screenshots:
evidence/channel-analysis.md.

Limits of the sample, stated plainly: one channel (#general), 16 days, Discord only.
#averer-kyc-support and the Telegram group (t.me/redbellychat) — where technical questions
are actually routed — were not sampled. A developer channel gated behind a role would not have
appeared. If a further capture changes this picture, this section and the analysis file get
updated together.

Recommendation to the Redbelly DAO: create a #dev-support channel. The server has 40,719
members and nowhere for a developer to ask why their transaction reverted.


Verification and evidence

Every technical claim in this guide is traced to the source it came from in
sources.md, and every fix is designed to be checked mechanically
rather than taken on trust. The harness in harness/ is what does the
checking.

Run on 8 August 2026, Node v20.20.2, 23 seconds. Result: 30 PASS, 2 FAIL, 1 SKIP of 33.
The full log, with raw responses kept so every conclusion can be re-derived, is
verification-log.json.

Three findings changed this guide, and they are the reason the harness exists:

  1. eth_estimateGas does not detect the permission gate (D1.1). A freshly generated, never-onboarded address received a clean 0x5208 estimate for a self-transfer. The guide previously recommended exactly that call as the diagnostic for D1. That advice was wrong and has been replaced — permissioning is enforced at execution, not estimation.
  2. from made no difference to a simple estimate (C3.1): 0x5f12 with and without. C3's fourth root cause has been qualified accordingly.
  3. No rate limiting at 40 parallel requests (A4.1): 40/40 returned HTTP 200. A4 is now explicitly a defensive pattern for sustained load rather than a threshold you will hit casually.

The two FAILs are broken checks, not wiki errors, and are recorded here rather than
quietly fixed:

  • A1.4 reports that the stale mainnet chain ID has been corrected upstream. It has not. The scraper found neither 154 nor 153 on the page, including the testnet value that is demonstrably present, so it is not reading the rendered content. A1 stands on A1.1A1.3, which all passed against live endpoints and the public registry.
  • G2.2 returned {"hasBrokenForm": false, "hasCorrectForm": false} — neither form of the .npmrc line was found, so the check located nothing and proves nothing either way. G2 rests on the primary observation of the page rather than on this check.

Both checks need their detection logic rewritten. Recording that is more useful than deleting
them.

Not yet run: the --deep stage (contract deployment, nonce reuse, underpriced-transaction
reproduction, Routescan verification). Those entries rest on documented behaviour and source
tracing rather than on execution, and this section will say so until that changes.

What the harness does:

  • Queries both Redbelly RPC endpoints and asserts the chain IDs, endpoint shapes and gas economics this guide states.
  • Re-checks the documentation defects in A1, G2 and G3 against the live pages, so a corrected upstream page is detected rather than silently contradicting us.
  • Compiles the reference contract under both the correct (prague) and the default-modern (osaka) profiles.
  • Deploys to Redbelly Testnet, then reproduces the insufficient-funds, nonce-reuse and underpriced-transaction failures and confirms the documented recovery for each.
  • Verifies the deployed contract through Routescan using the chainDescriptors block in F1.

Reproducing it:

git clone <this repository>
cd harness
./run.sh                # read-only checks; no key or funds needed
./run.sh --deep         # + on-chain checks (needs a funded, onboarded testnet key)
Enter fullscreen mode Exit fullscreen mode

The standard that applies: any entry whose fix does not reproduce when the harness runs
gets cut, not published with a caveat. That rule is stated in advance so it can be checked
against what this guide contains after the run.

Checks marked observational in the log — the rate-limit burst, the from-less gas estimate,
and the un-onboarded address probe — record what the network actually does so that the
wording here matches observed behaviour rather than assumption.


Where to get help when this guide does not cover it

Finding the right place to ask is itself a source of friction on Redbelly, so here is the
map. Every route below is evidenced by the channel capture in
evidence/channel-analysis.md.

You need Go to Notes
A technical answer from the team Telegram — https://t.me/redbellychat Where the team routes technical depth. Redbelly staff direct people here explicitly.
To start building / partnership info@redbelly.network The team's stated route for builders.
KYC, onboarding or credential issues #averer-kyc-support in the Discord The one engineering-adjacent Discord channel.
Network access for your address https://access.redbelly.network Required before any address can write. See D1.
Testnet RBNT https://redbelly.faucetme.pro Discord account required. See D2.
Node operator help Node Operator Support Portal (linked from Vine support) Separate from developer support.
General community Discord — https://discord.com/invite/sxwBgwmdq6 40,719 members. Community and token holders.

What does not exist: a Discord developer-support channel, and a support ticket system.
Asked directly whether there was a ticket system, a Redbelly team member answered "No, how
can I help?"
(5 Aug 2026). Budget your expectations accordingly — for anything in this guide,
the fastest route is the guide itself, then Telegram.


Contributing

This guide is maintained, not published-and-abandoned.

Found something wrong? Open an issue with the error string, the command that produced it,
and the chain ID you were on. Wrong information here is worse than missing information, and
corrections are prioritised over additions.

Have an error that is not covered? The most useful report includes:

  • the literal error message, copied verbatim
  • what you ran to produce it
  • chain ID (151 or 153), and the tool and version
  • what fixed it, if you found out

Adding an entry. Entries follow a fixed shape — Symptom, Root Cause, Solution,
Prevention — and two rules:

  1. No vague instructions. "Run this command" rather than "check your configuration". Every solution step is something the reader can execute.
  2. Every claim is verifiable. New entries come with a check added to harness/verify.mjs or harness/scripts/deep-checks.ts, so the fix is proven rather than asserted.

Re-verification. Chain IDs, endpoints, package versions and compiler defaults all move.
Re-run ./run.sh before relying on this guide after the "sources read" date, and open an
issue for anything that has drifted.


Sources

Every technical value in this guide traces to one of these, all read on 8 August 2026:

Redbelly — Vine developer portal

Redbelly — technical documentation

Independent registries and services

Toolchain


Sources read and verified: 8 August 2026 · 30 of 33 harness checks passed —
log · Corrections welcome

Top comments (0)