Skip to content

Repository files navigation

ARCtestnetUI

CI License: MIT

A modular, embeddable dApp that lets anyone receive Arc Testnet USDC in a single click — drop it into any project (PARSEC, a MetaMask dApp, plain HTML) with one tag.

It plugs into any EVM browser wallet that exposes an EIP-1193 provider — MetaMask, Coinbase Wallet, Rabby, Brave — and automates the whole flow from request to receipt:

pick a recipient  ->  (switch to Arc Testnet)  ->  prove/skip  ->  faucet sends USDC  ->  explorer link

Grounded in the lablab.ai tutorial Getting Started with Arc Testnet: Send USDC with ethers.js — USDC is dispensed via the ERC-20 transfer() interface on Arc's USDC system contract.

Three ways to receive

The <arc-faucet> widget offers three recipient modes:

  1. Connect wallet — connect an existing EIP-1193 wallet; sign a free message to prove address control. Verified claim.
  2. Generate a wallet — have a wallet created in your browser for you; the private key is shown locally (copy it / download an encrypted keystore) and used to sign. The key never leaves your browser. Verified claim.
  3. Enter an address — paste any 0x… address. Unverified claim — rate-limited only.

Why can't I just click a button and get Arc testnet tokens?

As an end user of this app, you can — that is exactly what the widget delivers. A wallet signature costs no gas and is proof of ownership, not a credential.

The friction is supply-side: a faucet pays out of a pre-funded treasury wallet an operator funds first; that funding comes from Circle's faucet, which caps ~20 USDC per address per 2h and is captcha-gated (its API needs a paid mainnet account). And every public faucet must rate-limit to survive bots. See POLICY.md.

This project ships a committed demo treasury (treasury.demo.json) so the app runs with zero setup — once that wallet is funded (one human captcha click), every end user afterwards claims with no captcha.


Embed it in your project

The widget is a framework-agnostic custom element. It works in plain HTML, React, Vue, PARSEC, or any MetaMask-style dApp.

Plain HTML / any project — single file, no build:

<script type="module" src="/path/to/arc-faucet.iife.js"></script>
<arc-faucet faucet-url="https://your-faucet.example"></arc-faucet>

ESM / bundled projects:

import '@arc-faucet/widget';   // registers the <arc-faucet> element
<arc-faucet faucet-url="https://your-faucet.example"></arc-faucet>

The element emits an arc-faucet:claim DOM event on success (detail = the confirmed claim). For a custom UI, use the headless @arc-faucet/wallet-core module directly — see "Using the wallet module" below.


Credentials & configuration

End user (claiming tokens) — no credentials

Just a browser. No sign-up, no API key, no gas. Either connect a wallet, let the app generate one, or paste an address.

Operator (running the faucet) — one optional secret

The faucet dispenses from a treasury wallet. Two ways to provide it:

  • Zero-config demo: the committed packages/faucet-server/treasury.demo.json — a deliberately-public throwaway wallet (testnet only, no value). Used automatically when no env key is set. Anyone can fund or drain it; never send real funds to it.
  • Production: set TREASURY_PRIVATE_KEY in packages/faucet-server/.env (git-ignored) — this overrides the demo wallet and is the only true secret.
Variable Secret? Purpose Default
TREASURY_PRIVATE_KEY secret (optional) Treasury wallet key. If unset, the committed demo wallet is used. (demo wallet)
ARC_TESTNET_RPC_URL no Arc Testnet JSON-RPC endpoint. https://rpc.testnet.arc.network
USDC_ADDRESS no USDC system contract on Arc. 0x3600…0000
FAUCET_AMOUNT_USDC no USDC sent per successful claim. 0.5
ALLOW_UNVERIFIED_CLAIMS no Allow the enter-an-address (unsigned) mode. true
CLAIM_COOLDOWN_MS no Per-address cooldown between claims. 86400000 (24h)
NONCE_TTL_MS no Challenge validity window. 300000 (5m)
CORS_ORIGIN no Allowed browser origin. http://localhost:5173
EXPLORER_BASE_URL no Explorer base URL for tx links. https://testnet.arcscan.app
IP_RATE_LIMIT_WINDOW_MS / IP_RATE_LIMIT_MAX no Per-IP rate limiting. 900000 / 20

No Circle API key is required. See packages/faucet-server/.env.example.


Architecture

An npm-workspaces monorepo; ethers v6 throughout, viem for wallet generation.

Package Role
packages/shared Shared faucet API types.
packages/wallet-core Framework-agnostic EIP-1193 / EIP-6963 wallet module + viem wallet generation.
packages/widget The embeddable <arc-faucet> web component.
packages/faucet-server Automated ethers v6 faucet API.
apps/demo Demo page that embeds <arc-faucet>.

Arc Testnet reference

Field Value
Chain ID 5042002 (0x4cef52)
RPC https://rpc.testnet.arc.network
Explorer https://testnet.arcscan.app
USDC contract 0x3600000000000000000000000000000000000000 (6 decimals)
Native gas token USDC (18 decimals)
Circle faucet https://faucet.circle.com

Quick start

Prerequisites: Node.js ≥ 18.

git clone https://github.com/parsec-wallet/ARCtestnetUI.git
cd ARCtestnetUI
npm install
npm run dev            # faucet API + demo, using the committed demo treasury

Open the demo (e.g. http://localhost:5173) and use any of the three recipient modes.

To actually dispense tokens, the treasury must hold USDC. Fund the demo treasury's address (printed at server startup) once at faucet.circle.com — or run your own:

npm run generate:wallet            # viem — prints a fresh address + key
# fund the address at faucet.circle.com, then put the key in
# packages/faucet-server/.env as TREASURY_PRIVATE_KEY
npm run typecheck && npm run build

Using the wallet module

@arc-faucet/wallet-core is framework-agnostic and works against any EIP-1193 provider. The whole claim is one call, with a Recipient for each mode:

import { runClaimFlow, discoverWallets, generateWallet } from '@arc-faucet/wallet-core';

// connect an existing wallet
const [wallet] = await discoverWallets();
await runClaimFlow({ recipient: { kind: 'connected', provider: wallet.provider },
                     faucetBaseUrl: 'http://localhost:8787' });

// generate a wallet in-browser (key stays local)
const fresh = generateWallet();   // { address, privateKey }
await runClaimFlow({ recipient: { kind: 'generated', privateKey: fresh.privateKey },
                     faucetBaseUrl: 'http://localhost:8787' });

// claim to a plain address (unverified)
await runClaimFlow({ recipient: { kind: 'address', address: '0x…' },
                     faucetBaseUrl: 'http://localhost:8787' });

runClaimFlow emits a ClaimStateEvent for every transition via onState.


Faucet API reference

Base URL defaults to http://localhost:8787.

POST /api/faucet/challenge

Body { "address": "0x…" }ChallengeResponse { nonce, message, issuedAt, expiresAt }.

POST /api/faucet/claim

Body { "address", "nonce", "signature?" }. With a signature, the server recovers the signer and requires it to equal address (verified: true). Without one, the claim is accepted only if ALLOW_UNVERIFIED_CLAIMS is set (verified: false). Returns ClaimResponse { status, txHash, explorerUrl, amount, recipient, verified }.

GET /health

Treasury address + USDC balance — an operator readiness check.

Error codes

{ error, message } with one of: BAD_REQUEST, INVALID_SIGNATURE, ADDRESS_MISMATCH, NONCE_UNKNOWN, NONCE_EXPIRED, NONCE_USED, RATE_LIMITED, TREASURY_INSUFFICIENT, RPC_ERROR.


Security

  • Non-custodial. A participant's private key is never compromised: a generated key is created only in the browser, never transmitted, logged, or persisted. The faucet server only ever sees addresses and signatures. See SECURITY.md.
  • The only private key in the repo is treasury.demo.json — a deliberate, labelled public throwaway. A production TREASURY_PRIVATE_KEY lives only in .env (git-ignored).
  • Challenge nonces are single-use with a TTL (replay protection); per-address cooldown + per-IP rate limiting guard the treasury; the treasury balance is checked before broadcast.

Contributing

See CONTRIBUTING.md. Faucet policy and data handling: POLICY.md.

License

MIT © Parsec Wallet

About

Agnostic EVM wallet module + automated faucet for one-click Arc Testnet USDC receipt

Resources

Contributing

Security policy

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages