Skip to content

Run a faucet ​

bkc-faucet is the server behind faucet.bkc.network. It is plain Node.js with one dependency (the SDK), logs claims in Node's built-in SQLite, and protects itself with a browser proof-of-work or Cloudflare Turnstile.

Design ​

  • The faucet account is not the treasury. It holds only what you top it up with; the worst case is losing that.
  • Both spellings of an address are one account. bkc1… and 0x… are the same 20 bytes; the cooldown is keyed on the bytes.
  • Two cooldowns. One claim per address and per IP per window, so one machine cannot work through a list of addresses and one address cannot be passed around a room.
  • Check and payout are one step. Claims are serialised and the cooldown is re-read inside the queue — two simultaneous requests cannot both pass. (It also has to: two payouts in flight would sign with the same account sequence.)
  • Payouts go over the EVM. A Cosmos MsgSend moves the same coin, but the explorer indexes the EVM side only; an EVM transfer gets its own transaction page and an up-to-date balance.
  • The client IP is read from the right of X-Forwarded-For, past exactly the proxies you declare.

It does not stop someone with a pool of IP addresses: the limits raise the cost, the small balance caps the loss.

Run it ​

Requires Node.js 22.5+ (for node:sqlite).

bash
git clone https://github.com/bkc-network/bkc-faucet && cd bkc-faucet
npm install
cp .env.example .env      # set FAUCET_MNEMONIC
npm start                 # http://localhost:3005

Create and fund the faucet wallet ​

bash
node --experimental-require-module -e '
  import("@bkc-network-lib/sdk").then(async ({ DirectMldsa65HdWallet }) => {
    const w = await DirectMldsa65HdWallet.generate(24, { prefix: "bkc" });
    const [a] = await w.getAccounts();
    console.log(a.address);
    console.log(w.mnemonic);
  })'

Send it what you want the faucet to give away, and top it up as it drains. GET /api/status reports the balance and how many claims are left.

Settings ​

VariableDefaultMeaning
FAUCET_MNEMONIC—the faucet wallet — the only secret
FAUCET_AMOUNT1BKC per claim
FAUCET_COOLDOWN_HOURS24per address and per IP
FAUCET_POW_BITS18proof-of-work difficulty (≈1 s in a browser)
TURNSTILE_SITE_KEY / TURNSTILE_SECRET_KEY—both set → Turnstile replaces proof-of-work
PORT3005
TRUST_PROXY_HOPS1proxies in front; must match the deployment
BKC_EVM_RPChttps://rpc.bkc.network
BKC_COMETBFT_RPChttps://comet-rpc.bkc.network
BKC_GAS_PRICE5000000bkc
BKC_EXPLORER_URL—links in the UI and API responses
BKC_CHAIN_PREFIX, BKC_DENOM, BKC_DECIMALSbkc, bkc, 18
BKC_NETWORK_NAMEderived from the chain ide.g. "BKC Testnet"

.env is parsed by the server, not sourced by a shell, so the mnemonic needs no quoting.

Behind nginx ​

nginx
location / {
    proxy_pass http://127.0.0.1:3005;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

One proxy → TRUST_PROXY_HOPS=1. Add one per extra proxy (a CDN in front makes it 2). Setting it too high lets a visitor forge the IP the cooldown is keyed on.

A systemd unit (bkc-faucet.service) ships in the repository.

API ​

See Get test BKC.

BKC testnet is unaudited. Do not hold value on it.