Skip to content

osr21/arcpay

Repository files navigation

ArcPay

Cross-border stablecoin invoice and payment platform built on Arc Network.

Businesses create invoices denominated in USDC or EURC and share a payment link. Payers settle on-chain in three steps — connect wallet, approve tokens, fund escrow — with no intermediaries and sub-second finality.

Live app: arc-network-research.replit.app


Features

  • Invoice creation — title, amount, currency (USDC/EURC), recipient address, due date, optional privacy mode
  • Shareable pay links/pay/:id checkout page with guided 3-step on-chain flow and live transaction links
  • Escrow settlementapprove → fundEscrow → releaseEscrow with configurable dispute window
  • Invoice NFT — each paid invoice mints an ERC-721 receipt to the payer's wallet
  • Privacy mode — amount hidden on the public pay page; stored off-chain only
  • PDF export — download any invoice as a formatted PDF, in-browser
  • Analytics dashboard — volume charts, currency split, payment status breakdown (Recharts)
  • Protocol revenue tracker — live on-chain FeeCollected event feed from ArcScan
  • CCTP cross-chain payments — payers on Ethereum, Avalanche, or Base can pay via Circle's Cross-Chain Transfer Protocol
  • Mainnet readiness checklist — interactive pre-flight page at /mainnet with live RPC check and copyable deploy commands

Smart Contracts

Written in Solidity 0.8.20, deployed with Hardhat. No external oracle or bridge dependency for the core payment flow.

Architecture

Payer
  │
  ├─ ERC-20.approve(PaymentEscrow, amount)
  ├─ PaymentEscrow.fundEscrow(invoiceId)   ← pulls tokens, holds in escrow
  └─ PaymentEscrow.releaseEscrow(id)       ← sends net to payee, fee to treasury, calls markPaid
                                                │
                                         InvoiceRegistry.markPaid(id)
                                         InvoiceNFT.mint(payer)
  • InvoiceRegistry — stores invoice metadata, enforces state machine (Pending → Paid/Cancelled)
  • PaymentEscrow — holds funds, deducts 30 bps protocol fee on release, emits FeeCollected
  • InvoiceNFT — ERC-721 receipt minted to payer on settlement
  • Protocol fee — 30 bps (0.30%), configurable by owner via setFee(feeTo, feeBps), hard-capped at 500 bps

Deployed Contracts

Arc Network Testnet (chain ID 5042002)

Contract Address
InvoiceRegistry 0x64D160b7E91e78e52dFc0e8829640E32A919164C
PaymentEscrow v3 0xAc6F84B255910D95efC4E90D6f1dd5A3e7FCe7e2
InvoiceNFT 0x6F80056564491425273A6a481EcC5DAea9D57f23
USDC 0x3600000000000000000000000000000000000000 (native system contract)
EURC 0x89B50855Aa3bE2F677cD6303Cec089B5F319D72a

Explorer: testnet.arcscan.app
Faucet: faucet.circle.com — select Arc Testnet (20 USDC / 2 hrs)

Ethereum Sepolia Testnet (chain ID 11155111)

Contract Address
InvoiceRegistry 0xa77710C5d06A829809b8149C602C71654a9F40bD
PaymentEscrow 0xC45A6938Fd656Ed48021A6C611396BF18b3C8EaA
InvoiceNFT 0x1e7e5166029e287ca6264456b88d200d1118289D
USDC 0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238 (Circle)
EURC 0x08210F9170F89Ab7658F0B5E3fF39b0E03C594D4 (Circle)

Arc Network Mainnet (chain ID 4564) — pending Arc launch

Contracts will be deployed when Arc Mainnet is live. See the Mainnet Readiness page in the app for the pre-flight checklist.


Stack

Layer Technology
Frontend React 19, Vite 7, Tailwind CSS v4, Wouter, TanStack Query
On-chain viem 2.x, MetaMask / EIP-1193
API Express 5, Node.js 24
Database PostgreSQL, Drizzle ORM
Validation Zod v4, drizzle-zod
Contracts Hardhat 2.28, Solidity 0.8.20, ethers v6
Charts Recharts
PDF jsPDF
Monorepo pnpm workspaces, TypeScript 5.9
API codegen Orval (OpenAPI → React Query hooks + Zod schemas)

Project Structure

├── artifacts/
│   ├── arcpay/          # React + Vite frontend
│   └── api-server/      # Express API
├── contracts/
│   ├── src/             # Solidity contracts
│   │   ├── InvoiceRegistry.sol
│   │   ├── PaymentEscrow.sol
│   │   └── InvoiceNFT.sol
│   ├── scripts/
│   │   └── deploy.ts    # Hardhat deploy script
│   └── deployments/     # Generated address files per network
├── lib/
│   ├── contract-abis/   # TypeScript ABI constants (@workspace/contract-abis)
│   ├── db/              # Drizzle schema + migrations
│   └── api-spec/        # OpenAPI spec + codegen
└── DEPLOYMENT.md        # Full deploy guide

Local Development

Requirements

  • Node.js 24+
  • pnpm 9+
  • PostgreSQL (or use the Replit-managed database)
  • MetaMask browser extension

Setup

# Clone and install
git clone <repo-url>
cd arcpay
pnpm install

# Set environment variables (copy and fill in)
cp artifacts/arcpay/.env.example artifacts/arcpay/.env.local
# Required: DATABASE_URL, VITE_CHAIN_ID, contract addresses

# Push database schema
pnpm --filter @workspace/db run push

# Start the API server (port 8080)
pnpm --filter @workspace/api-server run dev

# Start the frontend (port from $PORT)
pnpm --filter @workspace/arcpay run dev

Compile and deploy contracts locally

cd contracts
pnpm install

# Compile
pnpm run compile

# Start a local Hardhat node
pnpm run node

# Deploy to local node (new terminal)
pnpm run deploy:local

# Copy addresses to frontend
cp deployments/localhost.env ../artifacts/arcpay/.env.local

Deploy to Arc Testnet

cd contracts

# Set your deployer key in contracts/.env
echo "DEPLOYER_PRIVATE_KEY=0x..." > .env

pnpm run deploy:arc
cp deployments/arc.env ../artifacts/arcpay/.env.local

See DEPLOYMENT.md for the complete guide including mainnet deployment, contract verification on ArcScan, and fee management.


Environment Variables

Frontend (artifacts/arcpay/.env.local):

VITE_CHAIN_ID=5042002
VITE_INVOICE_REGISTRY_ADDRESS=0x...
VITE_PAYMENT_ESCROW_ADDRESS=0x...
VITE_INVOICE_NFT_ADDRESS=0x...
VITE_USDC_ADDRESS=0x...
VITE_EURC_ADDRESS=0x...

API server:

DATABASE_URL=postgresql://...
SESSION_SECRET=...

Contracts (contracts/.env):

DEPLOYER_PRIVATE_KEY=0x...
# For mainnet only:
ARC_MAINNET_USDC_ADDRESS=0x...
ARC_MAINNET_EURC_ADDRESS=0x...

Contract Ownership & Revenue

  • Owner — the wallet that ran the deploy script. Can call setFee() and transferOwnership() on PaymentEscrow, and setAuthorizedEscrow() on InvoiceRegistry.
  • Protocol fee — 30 bps (0.30%) of each payment, sent to feeTo on releaseEscrow. Defaults to the deployer wallet; update with setFee(newTreasury, 30) to point to a multisig.
  • Fee cap — hard-coded at 500 bps (5%) in the contract; cannot be exceeded regardless of what the owner sets.

Payment Flow (on-chain)

  1. Payer visits /pay/:invoiceId — fetches invoice metadata from API
  2. Connect wallet — MetaMask prompt, switches to Arc Network automatically
  3. ApproveUSDC.approve(PaymentEscrow, amount) — one MetaMask tx
  4. Fund escrowPaymentEscrow.fundEscrow(invoiceId) — pulls USDC into contract
  5. ReleasePaymentEscrow.releaseEscrow(escrowId) — sends net to payee, fee to treasury, mints NFT receipt, marks invoice paid

Steps 4 and 5 can be merged into instant settlement by setting releaseDelay = 0 (current default).


Links

Resource URL
Live app https://arc-network-research.replit.app
Arc Testnet Explorer https://testnet.arcscan.app
Arc Mainnet Explorer https://arcscan.app
Arc Documentation https://docs.arc.network
Circle USDC Faucet https://faucet.circle.com
Circle CCTP Docs https://developers.circle.com/stablecoins/docs/cctp-getting-started

License

MIT

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors