Skip to content

Production-ready multi-chain batch sender: Base Account + EIP-5792, testable core modules, security & UI polish - #1

Open
arena-ai-coding-agent[bot] wants to merge 2 commits into
mainfrom
arena/01a019cb-farsend
Open

arena-ai-coding-agent[bot] wants to merge 2 commits into
mainfrom
arena/01a019cb-farsend

Conversation

@arena-ai-coding-agent

@arena-ai-coding-agent arena-ai-coding-agent Bot commented Sep 20, 2026

Copy link
Copy Markdown

Overview

Turns FarSend into a production-grade, multi-chain batch sender that works with regular EOAs, Farcaster Mini App context, and Base Account (the passkey ERC-4337 smart wallet powering the Base App) — all through the same deployed, immutable BatchSender contracts, so no existing contracts need redeploying.

Note: this branch is based on the rewritten main (4966cdb); it rebases the work onto it cleanly (verified MERGEABLE / CLEAN).

Commits

  1. Add BatchSender contract, multi-chain config, testable core modules, and docs
  2. Rewrite app around core modules: Base Account + EIP-5792 dispatch, lazy Reown, UI polish

Key changes

Base Account / Base App smart wallet support

  • "Sign in with Base Account" connect button; Base Account featured first in the Reown wallet modal (official wallet ID from Base docs)
  • EIP-5792 dispatch: when the connected wallet advertises wallet_sendCalls (checked via wallet_getCapabilities), the batch is submitted atomically to the smart wallet — which can bundle and sponsor gas — and tracked via wallet_getCallsStatus to a terminal state
  • Seamless fallback to the standard gas-estimated signer path for EOAs; user rejections (4001 / ACTION_REJECTED) are always surfaced, never swallowed
  • ERC20 dispersements send value=0x0 in the smart-wallet batch (non-payable disperseToken); only ETH batches attach the ETH value

Architecture (pure core modules, all unit-tested)

  • src/core/: parse, validate (burn/dead-address detection), distribute, debounce, errors (revert decoding), rpc (fallback read providers), sendCalls (EIP-5792)
  • 62 Vitest unit tests; drift guard keeps public/chains.json in sync with the AppKit network list (npm run check:chains)
  • Debounce + generation-token on the async summary/allowance path (fixes stale results when switching chains/tokens)
  • Centralized error handling with ABI revert-reason decoding; read-only RPC fallback for balance/allowance reads (signing always uses the connected wallet)
  • Reown AppKit lazy-loaded after first paint (requestIdleCallback) — main bundle ~36 kB raw / ~11 kB gzip; the ~400 kB-gzip Reown chunk deferred to idle time

Security & safety

  • Reviewed BatchSender.sol (stateless: no owner, no upgrade path, no withdraw) — same code as the contract uploaded to main, with NatSpec documenting its safety properties; ABI mirrored in public/chains.json
  • Burn/dead-address (zero + ...dead) confirmation gate: dispatch disabled until explicitly acknowledged
  • Gas-estimation failure is now fatal — no more broadcasting with a manual gas guess that guarantees a revert
  • MAX_RECIPIENTS = 500 safety cap
  • Full review captured in AUDIT.md (active doc) + ARCHITECTURE.md design notes

Multi-chain config

  • 8 chains: Base, Ethereum, Optimism, Arbitrum, BSC, Avalanche, Polygon PoS, LitVM LiteForge
  • Per-chain fallback RPC URLs for read paths
  • Adding a new chain = deploy the same BatchSender.sol + one entry in chains.json; old deployments untouched

UI/UX & infra polish

  • Emoji replaced with a consistent SVG icon system (notifications, section headers, stepper checks); ARIA live region for notifications
  • Fixed vercel.json duplicate redirects key — the Farcaster /.well-known/farcaster.json manifest redirect was being silently dropped
  • package-lock.json re-synced: npm ci now verified clean on a fresh checkout

Verification

  • node --check clean on all modules
  • npm test → 62/62 passing
  • npm run check:chains → OK (8 chains, ABI present)
  • npm run build → clean (only the expected large-chunk warning for the lazy-loaded Reown bundle)
  • npm ci verified clean on a fresh checkout
  • Live preview verified: app serves, Base Account button present, core modules load

Notes / follow-ups (intentionally out of scope)

  • .github/workflows/ci.yml is ready but held locally: the GitHub App used for this session lacks the workflows permission, so pushing it is rejected. Once granted it can be added (also add node --check for debounce.js, errors.js, rpc.js, sendCalls.js).
  • Guaranteed gas sponsorship on Base Account requires app registration + paymaster config; the current path lets the smart wallet apply its own gas policies (capabilities.paymasterService can be passed to wallet_sendCalls later).
  • Dead webhookUrl in the Farcaster manifest left as-is per request.
  • End-to-end wallet/passkey testing needs to happen outside this sandbox (no real wallet connection available).

@vercel

vercel Bot commented Sep 20, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
farsend Ready Ready Preview Sep 20, 2026 6:42pm UTC

CryptoExplor and others added 2 commits September 20, 2026 18:40
…and docs

- Stateless BatchSender.sol (disperseEther/disperseToken) — reviewed version
  of the contract uploaded to main: identical code, plus NatSpec documenting
  that it has no owner, no upgrade path, and no withdraw function
- Multi-chain registry in public/chains.json (Base, Ethereum, Optimism,
  Arbitrum, BSC, Avalanche, Polygon PoS, LitVM LiteForge) with per-chain
  fallback RPC URLs; ABI sanity-checked against the contract
- Extract pure logic from main.js into src/core/ (parse, validate,
  distribute, debounce, errors, rpc, sendCalls) with 62 Vitest unit tests
- npm run check:chains drift guard keeps chains.json in sync with the
  AppKit network list
- AUDIT.md (security review) and ARCHITECTURE.md (design notes)
- Rename gitignore -> .gitignore; drop dead public/indexold.html
- Vite code-splitting + host allowlist for the preview proxy;
  package-lock.json re-synced so npm ci works (it failed against the
  previously committed lock file)

Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
…zy Reown, UI polish

Base Account (Base App passkey smart wallet) support:
- Dedicated 'Sign in with Base Account' connect button; Base Account
  featured first in the Reown wallet modal (official wallet ID from
  docs.base.org)
- EIP-5792 dispatch: when the connected wallet advertises wallet_sendCalls
  (detected via wallet_getCapabilities), the batch is submitted atomically
  to the smart wallet and tracked via wallet_getCallsStatus to a terminal
  state; standard gas-estimated signer path remains as fallback
- ERC20 dispersements send value=0x0 in the smart-wallet batch
  (disperseToken is non-payable); only ETH batches attach the ETH value

Architecture & robustness:
- Reown AppKit lazy-loaded after first paint (requestIdleCallback) — main
  bundle ~36 kB raw / ~11 kB gzip; the ~400 kB-gzip Reown chunk is deferred
- Centralized error handling with ABI revert-reason decoding
  (src/core/errors.js); read-only RPC fallback provider for
  balance/allowance reads — signing always uses the connected wallet
- Debounce + generation-token on the async summary/allowance path so
  switching chains/tokens can no longer apply stale results
- Gas-estimation failure is fatal (no more manual-gas broadcast that would
  guarantee a revert); MAX_RECIPIENTS=500 safety cap
- Burn/dead-address (zero + ...dead) confirmation gate: dispatch stays
  disabled until the user explicitly acknowledges

UI/UX:
- Consistent SVG icon system for notifications, section headers, and
  stepper checks (emojis removed); ARIA live region for notifications
- vercel.json: single redirects array — the Farcaster
  /.well-known/farcaster.json manifest redirect was silently dropped by a
  duplicate JSON key

Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant