Split bills and pay instantly with crypto on Base blockchain.
- 🔐 Smart Wallet Integration – Coinbase Smart Wallet and Base Account connector via OnchainKit
- 💰 Automatic Calculations – Split items, tax, and tips proportionally
- 🧾 Identity-Aware Participants – Resolve Basenames, ENS, and raw addresses with duplicate protection
- 📱 Responsive Retro UI – Windows 95-inspired design that works on mobile and desktop
- 🔗 Easy Sharing – Share bills via link or QR code
- 📈 Live ETH Pricing – Real-time oracle for accurate payment amounts
- ⚡ Instant Payments – Pay your share onchain in seconds
- 📦 Permanent Bill Storage – Store bill data permanently on blockchain
- 🔗 Auto URL Shortening – Long share links automatically shorten after publishing on-chain
- 📊 User Statistics – Track total bills created and volume
- 🏷️ Tag-Based Search – Find bills by tags (restaurant, cafe, groceries, etc.)
- 🔒 Privacy Controls – Make bills private or public
- ⭐ Bill Ratings – Rate and review bills
- 👥 Access Management – Grant/revoke access to private bills
- 📋 My Bills List – View all your published bills in one place
- 🔒 Smart Contract Escrow – Funds held securely until all participants pay
- 📊 Real-Time Status Tracking – Live payment progress with auto-updates every 5 seconds
- 💸 Flexible Refund System – Cancel bills and refund participants anytime
- ⏰ Automatic Deadlines – 7-day default deadline with auto-refund if not all paid
- 🎯 Partial Settlement – Settle with only those who paid
- 🔄 Status Indicators – Clear visual feedback (PAID, UNPAID, REFUND, REFUNDED, CANCELLED)
- 🎨 Dynamic UI – Payment buttons hide when bill is cancelled/complete
- 🔔 Smart Notifications – Toast alerts for all payment events
- 🔄 Backward Compatible – Existing bills continue to work without changes
- 🚀 Performance Optimized – Lazy loading, smart polling, 70-80% fewer RPC calls
- 📦 Bundle Optimized – 10-15% smaller main bundle with code splitting
- 🧪 Fully Tested – Comprehensive test suite for all features
- 📊 Analytics Ready – Built-in event tracking for user insights
- Frontend: Next.js 15, TypeScript, React 19
- Styling: Custom Retro CSS (Windows 95-inspired)
- Blockchain: Base Sepolia (testnet), Solidity 0.8.20
- Wallet: OnchainKit, Wagmi v2, Viem
- State Management: React hooks, TanStack Query
- Smart Contracts: Hardhat, Ethers.js
- Testing: Jest, React Testing Library
- SplitBillEscrow.sol - Trustless bill settlement with escrow protection
- BillMetadataRegistry.sol - On-chain bill storage and social features
- useBillMetadata.ts - Hook for metadata operations (publish, rate, search)
- PublishBillButton.tsx - Publish bills to blockchain
- UserBillsList.tsx - Display user's published bills
- BillsByTagSearch.tsx - Search bills by tags
- AccessControlPanel.tsx - Privacy controls and access management
- useEscrow.ts - Hook for escrow operations (create, pay, cancel, refund)
- useEscrowBillData.ts - Hook for reading contract state with auto-refresh
- EscrowPaymentProgress.tsx - Real-time payment tracking component
- ParticipantPaymentStatus.tsx - Individual participant status display
- useEscrowBillInfo() - Reads bill status (cancelled, settled, paidCount)
- useCanRefund() - Checks if participant can claim refund
- useEscrowPaymentStatus() - Checks if participant has paid
- Auto-refresh every 5 seconds for real-time updates
- EscrowManagementPanel - Creator controls (cancel, partial settle)
- RefundClaimButton - Participant refund claiming
- EscrowDeadlineDisplay - Countdown timer with auto-refund trigger
- GasEstimateDisplay - Real-time gas cost preview
- TransactionPending - Transaction status with slow network warnings
- Node.js 18+
- npm or yarn
- Clone the repository
- Install dependencies:
npm install-
Get your OnchainKit API key from Coinbase Developer Platform
-
Create
.env.localand add your configuration:
# Required: OnchainKit API key
NEXT_PUBLIC_ONCHAINKIT_API_KEY=your_api_key_here
# Optional: Escrow contract address (leave empty to disable escrow features)
NEXT_PUBLIC_ESCROW_CONTRACT_ADDRESS=
# Optional: Bill metadata registry for onchain share links
NEXT_PUBLIC_BILL_METADATA_CONTRACT_ADDRESS=- Run the development server:
npm run dev- Connect Wallet - Click "Connect Wallet" and choose Base Account or Coinbase Smart Wallet
- Create Bill - Enter a title and create a new bill
- Choose Payment Mode - Toggle escrow protection on/off (optional)
- Add Items - Add items with descriptions and amounts
- Add Participants - Invite friends by adding wallet addresses, Basenames, or ENS names
- Set Tip & Tax - Add tip and tax amounts (percentage or fixed amount)
- Activate Escrow - If using escrow, activate it to lock in participants and amounts
- Share - Copy the link or QR code; recipients load the full bill instantly
- Pay - Each participant pays their calculated share onchain
- Track Status - Watch real-time payment progress with auto-updating status indicators
When using escrow protection, creators have several management options:
- Cancel & Refund All - Cancel the bill and allow all participants who paid to claim refunds
- Partial Settlement - If some participants paid but not all, settle with those who paid
- Auto-Refund - If the 7-day deadline expires, anyone can trigger auto-refund for all participants
The app shows clear visual feedback for payment status:
- Active Bill: Blue progress bar showing "X / Y paid"
- Complete Bill: Green progress bar with "✓ Complete"
- Cancelled Bill: Red progress bar with "✗ Cancelled - Refunds Available"
Participant statuses:
- ✓ PAID (green) - Payment received
- ✗ UNPAID (red) - Waiting for payment
- ↩ REFUND (orange) - Refund available to claim
- ✓ REFUNDED (gray) - Refund successfully claimed
- ✗ CANCELLED (gray) - Bill cancelled, no payment made
After creating a bill, you can publish it to the blockchain for permanent storage:
- Publish Button - Click "Publish to Blockchain" on any bill
- Add Metadata - Optionally add:
- Tags (restaurant, cafe, groceries, etc.)
- Privacy setting (public/private)
- Beneficiary address (for charity/group bills)
- Confirm Transaction - Pay gas fee to store bill on-chain
- Auto URL Shortening - Long share links automatically become short after publishing
- View Published Bills - Access "My Bills" to see all your published bills
Benefits of Publishing:
- Permanent storage on blockchain
- Shorter, cleaner share links
- Searchable by tags
- User statistics tracking
- Privacy controls
- Bill ratings and reviews
- Instant peer-to-peer payments
- No smart contract required
- Lower gas costs
- Best for trusted groups
- Funds held in smart contract until all participants pay
- Automatic settlement when complete
- Real-time payment status tracking
- Flexible refund options:
- Cancel & Refund All - Creator can cancel and refund everyone
- Auto-Refund - Automatic refund if deadline expires
- Partial Settlement - Settle with only those who paid
- Clear status indicators for each participant:
- ✓ PAID (green) - Participant has paid
- ✗ UNPAID (red) - Participant hasn't paid yet
- ↩ REFUND (orange) - Refund available to claim
- ✓ REFUNDED (gray) - Refund claimed
- ✗ CANCELLED (gray) - Bill cancelled, participant didn't pay
- Dynamic UI that adapts to bill status:
- Payment buttons hide when bill is cancelled or complete
- "Back to Home" button appears for finished bills
- Progress bar changes color based on status (blue → green/red)
- Best for larger groups or unfamiliar participants
Note: Existing bills created before escrow feature continue to work as direct payment bills.
splitbill/
├── src/
│ ├── app/ # Next.js App Router
│ │ ├── page.tsx # Home page (create bill)
│ │ ├── layout.tsx # Root layout with providers
│ │ ├── retro.css # Retro Windows 95 styling
│ │ └── bill/[id]/page.tsx # Bill detail page
│ │
│ ├── components/ # Reusable UI components
│ │ ├── WalletConnect.tsx # Wallet connection UI
│ │ ├── ActivateEscrowButton.tsx # Escrow activation
│ │ ├── GasEstimateDisplay.tsx # Gas cost preview
│ │ ├── TransactionPending.tsx # Transaction status
│ │ ├── PublishBillButton.tsx # Publish to blockchain
│ │ ├── UserBillsList.tsx # User's published bills
│ │ ├── BillsByTagSearch.tsx # Search bills by tag
│ │ └── AccessControlPanel.tsx # Privacy & access management
│ │
│ ├── features/ # Feature modules (isolated)
│ │ ├── bill/ # Bill management feature
│ │ │ ├── components/ # Bill-specific UI
│ │ │ │ ├── CreateBillFormRetro.tsx
│ │ │ │ ├── EscrowPaymentProgress.tsx # Real-time progress
│ │ │ │ ├── ParticipantPaymentStatus.tsx # Status indicators
│ │ │ │ ├── EscrowManagementPanel.tsx # Creator controls
│ │ │ │ └── EscrowDeadlineDisplay.tsx # Deadline countdown
│ │ │ └── hooks/ # Bill logic
│ │ │ ├── useBill.ts # Bill state management
│ │ │ └── useBillMetadata.ts # Metadata operations
│ │ │
│ │ └── payment/ # Payment feature
│ │ ├── components/ # Payment UI
│ │ │ ├── EscrowPaymentButton.tsx # Escrow payment
│ │ │ └── RefundClaimButton.tsx # Refund claiming
│ │ └── hooks/ # Payment logic
│ │ ├── usePayment.ts # Direct payments
│ │ ├── useEscrow.ts # Escrow operations
│ │ └── useEscrowBillData.ts # Contract data reading
│ │
│ └── lib/ # Core utilities
│ ├── config/ # Configuration
│ │ ├── chains.ts # Blockchain config
│ │ ├── wagmi.ts # Wallet config
│ │ ├── escrow.ts # Escrow contract config
│ │ └── metadata.ts # Metadata contract config
│ ├── providers/ # React providers
│ │ ├── OnchainProviders.tsx
│ │ └── ToastProvider.tsx
│ ├── types/ # TypeScript interfaces
│ │ └── bill.ts # Bill data types
│ └── utils/ # Helper functions
│ ├── calculations.ts # Bill calculations
│ ├── escrow.ts # Escrow helpers
│ ├── escrowErrors.ts # Error handling
│ ├── storage.ts # LocalStorage helpers
│ └── share.ts # URL shortening & sharing
│
├── contracts/ # Smart contracts
│ ├── SplitBillEscrow.sol # Escrow contract (deployed)
│ ├── BillMetadataRegistry.sol # Metadata registry (deployed)
│ ├── deploy.sh # Escrow deployment script
│ ├── deploy-metadata-registry.sh # Metadata deployment script
│ ├── deploy.js # Deployment logic
│ └── README.md # Contract docs
│
├── tests/ # Test suite
│ ├── integration/ # Integration tests
│ └── unit/ # Unit tests
│
├── docs/ # Documentation
│ └── ESCROW_OPTIMIZATION_GUIDE.md
│
└── dev-notes/ # Development notes (gitignored)
└── *.md # Temporary docs and guides
NEXT_PUBLIC_ONCHAINKIT_API_KEY- Your OnchainKit API key from Coinbase Developer Platform
-
NEXT_PUBLIC_ESCROW_CONTRACT_ADDRESS- Smart contract address for escrow functionality- Leave empty or set to
0x0000000000000000000000000000000000000000to disable escrow features - The app will automatically fall back to direct payment mode when not configured
- After deploying the escrow contract, add the address here to enable escrow protection
- Leave empty or set to
-
NEXT_PUBLIC_BILL_METADATA_CONTRACT_ADDRESS- Metadata registry for onchain share links (optional)
Example .env.local:
NEXT_PUBLIC_ONCHAINKIT_API_KEY=your_api_key_here
NEXT_PUBLIC_ESCROW_CONTRACT_ADDRESS=0x1234567890abcdef1234567890abcdef12345678
NEXT_PUBLIC_BILL_METADATA_CONTRACT_ADDRESS=0xabcdef1234567890abcdef1234567890abcdef12import { useBillMetadata } from '@/features/bill/hooks/useBillMetadata';
function BillComponent({ billId }) {
const {
publishBill,
updateBillPrivacy,
rateBill,
grantAccess,
revokeAccess,
getUserStats,
searchByTag,
getUserBills,
isPublishing,
error
} = useBillMetadata();
// Publish bill to blockchain
const handlePublish = async () => {
await publishBill(
billId,
['restaurant', 'dinner'], // tags
false, // isPrivate
'0x...' // beneficiary (optional)
);
};
// Rate a bill
const handleRate = async () => {
await rateBill(billId, 5); // 1-5 stars
};
// Get user statistics
const stats = await getUserStats('0x...');
console.log(`Total bills: ${stats.totalBills}, Volume: ${stats.totalVolume}`);
return (
<button onClick={handlePublish} disabled={isPublishing}>
Publish to Blockchain
</button>
);
}import { useEscrow } from '@/features/payment/hooks/useEscrow';
import { useEscrowBillInfo } from '@/features/payment/hooks/useEscrowBillData';
function BillComponent({ escrowBillId }) {
// Get bill status (auto-refreshes every 5 seconds)
const { cancelled, settled, paidCount, participantCount } = useEscrowBillInfo(escrowBillId);
// Escrow operations
const { cancelAndRefund, partialSettle, isPending } = useEscrow();
// Cancel bill and refund all participants
const handleCancel = async () => {
await cancelAndRefund(escrowBillId);
};
return (
<div>
<p>Status: {cancelled ? 'Cancelled' : settled ? 'Complete' : 'Active'}</p>
<p>Progress: {paidCount} / {participantCount} paid</p>
<button onClick={handleCancel} disabled={isPending}>
Cancel & Refund All
</button>
</div>
);
}import { useEscrowPaymentStatus, useCanRefund } from '@/features/payment/hooks/useEscrowBillData';
function ParticipantStatus({ escrowBillId, participantAddress }) {
// Check if participant has paid (auto-refreshes)
const { hasPaid } = useEscrowPaymentStatus(escrowBillId, participantAddress);
// Check if refund is available
const { canRefund } = useCanRefund(escrowBillId, participantAddress);
return (
<div>
{hasPaid && !canRefund && <span>✓ PAID</span>}
{!hasPaid && <span>✗ UNPAID</span>}
{canRefund && <span>↩ REFUND AVAILABLE</span>}
</div>
);
}Deploy to Vercel, Netlify, or any Next.js hosting platform:
npm run build
npm startEnvironment Variables for Production:
- Set
NEXT_PUBLIC_ONCHAINKIT_API_KEYin your hosting platform's environment settings - Optionally set
NEXT_PUBLIC_ESCROW_CONTRACT_ADDRESSif using escrow features
The escrow smart contract is optional but provides trustless bill settlement.
Quick Deploy:
# 1. Get testnet ETH from faucet
# https://www.coinbase.com/faucets/base-ethereum-sepolia-faucet
# 2. Set your private key
export PRIVATE_KEY=your_private_key_here
# 3. Run deployment script
./contracts/deploy.shThe script will:
- Deploy SplitBillEscrow to Base Sepolia
- Update
.env.localwith contract address - Save deployment info to
contracts/deployment.json
For detailed instructions, see:
contracts/DEPLOYMENT_GUIDE.md- Quick referencecontracts/README.md- Full documentation
- Get testnet ETH from Base Sepolia Faucet
- Deploy smart contract (optional)
- Test your transactions
- Get professional security audit for smart contract
- Test extensively on testnet
- Update
src/lib/config/chains.tsto use Base mainnet - Never deploy unaudited contracts to mainnet
This project maintains full backward compatibility with bills created before the escrow feature:
- Old bills (without
escrowEnabledfield) work as direct payment bills - New bills can choose between direct payment or escrow protection
- Mixed history displays both types correctly
- No migration required - all existing data works without changes
npm testnode test-backward-compatibility.jsThis validates:
- Old bills load and function correctly
- New bills work with both payment modes
- Type safety for optional fields
- Mixed data handling
- Status updates and UI changes
The escrow feature is highly optimized for production use:
- Auto-refresh every 5 seconds: Payment status, bill status, and refund availability
- Adaptive intervals: Stops polling when bill is settled or page is hidden
- 70-80% fewer RPC calls for completed bills
- Zero polling when tab is inactive (saves battery and bandwidth)
- Instant UI updates: Status changes appear automatically without page refresh
- Lazy loading: Escrow components load on-demand
- 10-15% smaller main bundle for faster initial page load
- Code splitting: Separate chunks for escrow features
- Gas-efficient refunds: Individual participant refund claims (no gas limit issues)
- Batch operations: Create bills with multiple participants in one transaction
- Optimized storage: Minimal on-chain data storage
- Comprehensive tracking: 12 event types for user insights
- Lightweight storage: Last 100 events in localStorage
- Production-ready: Easy integration with Google Analytics, Mixpanel, etc.
- Proper cleanup: Event listeners cleaned up on unmount
- No memory leaks: Verified with browser profiler
- Efficient state: Minimal re-renders with optimized hooks
For detailed information, see:
- ✅ Permanent Bill Storage - Store bills on blockchain via BillMetadataRegistry
- ✅ Auto URL Shortening - Long share links automatically shorten after publishing
- ✅ User Statistics - Track total bills created and volume per user
- ✅ Tag-Based Search - Find bills by tags (restaurant, cafe, groceries, etc.)
- ✅ Privacy Controls - Make bills private or public
- ✅ Bill Ratings - Rate and review bills (1-5 stars)
- ✅ Access Management - Grant/revoke access to private bills
- ✅ My Bills List - View all your published bills in one place
- ✅ Beneficiary Support - Optional beneficiary field for charity/group bills
- ✅ Real-time status updates for cancelled/refunded bills
- ✅ Dynamic payment button visibility (hides when bill is cancelled/complete)
- ✅ Enhanced status indicators with 5 states (PAID, UNPAID, REFUND, REFUNDED, CANCELLED)
- ✅ Color-coded progress bar (blue/green/red based on bill status)
- ✅ Auto-updating UI every 5 seconds
- ✅ "Back to Home" button for completed/cancelled bills
- ✅ Smart contract escrow with automatic settlement
- ✅ Flexible refund system (cancel, auto-refund, partial settlement)
- ✅ Real-time payment tracking
- ✅ 7-day deadline with auto-refund
- ✅ Performance optimizations (70-80% fewer RPC calls)
- ✅ Bundle optimization (10-15% smaller main bundle)
- ✅ Comprehensive analytics tracking
- Optimistic UI updates with payment state sync
- Multi-currency settlement with stablecoins (USDC, DAI)
- Receipt upload and OCR
- Group history and analytics
- Push notifications
- Mobile app (React Native)
- Check that
NEXT_PUBLIC_ESCROW_CONTRACT_ADDRESSis set in.env.local - Verify the contract is deployed to Base Sepolia
- Ensure you have testnet ETH for gas fees
- Check browser console for error messages
- Status updates automatically every 5 seconds
- If updates stop, check your internet connection
- Try refreshing the page
- Verify the contract address is correct
- Check Base Sepolia block explorer for transaction status
- If pending for >2 minutes, try increasing gas price
- Network congestion may cause delays
- Use the "slow network" warning as a guide
- Verify the bill is cancelled (check contract status)
- Ensure you paid before cancellation
- Check that you haven't already claimed the refund
- Wait 5-10 seconds for status to update
Contributions are welcome! Please feel free to submit a Pull Request.
MIT
This project was built for the Base Batches buildathon to bring more users onchain with practical, everyday use cases.
- ✅ Practical everyday use case (bill splitting)
- ✅ Onchain payments with smart wallet integration
- ✅ Trustless escrow system with smart contracts
- ✅ Real-time status tracking and updates
- ✅ User-friendly retro UI design
- ✅ Comprehensive error handling and gas transparency
- ✅ Production-ready with optimizations and testing
-
SplitBillEscrow:
0x5FbDB2315678afecb367f032d93F642f64180aa3- Trustless bill settlement with escrow protection
- Flexible refund system
- 7-day deadline with auto-refund
-
BillMetadataRegistry:
0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512- Permanent on-chain bill storage
- User statistics and analytics
- Tag-based search and discovery
- Privacy controls and access management
- Bill ratings and reviews
- Live Demo: [Coming Soon]
- Documentation: See
docs/folder - Development Notes: See
dev-notes/folder (local only)