Ajo PiggyBank is a decentralized savings application that allows users to deposit ETH into an on-chain piggy bank, lock it for a defined period, and withdraw only after the lock has expired. This repository currently hosts the React + Vite frontend, which is designed to connect to a PiggyBank smart contract (typically maintained in a separate Foundry repository) deployed on the Base network.
💡 Key Features:
- Time-locked savings with enforced lock periods
- Wallet integration via REOWN AppKit and WalletConnect v2
- Real-time balance tracking with countdown timers
- Multi-network support (Local, Base Sepolia, Base Mainnet)
- Comprehensive diagnostics for development and troubleshooting
- Overview
- Tech Stack
- Project Structure
- Frontend – Getting Started
- Contract Configuration Guide
- Smart Contract (Conceptual Overview)
- Testing and Quality
- Development Diagnostics
- Deployment
- CI/CD
- Security and Disclaimer
- License
The Ajo PiggyBank dApp is built to encourage disciplined savings by enforcing time-locked deposits on-chain.
Key capabilities:
- Connect an Ethereum wallet using REOWN AppKit with WalletConnect v2
- Deposit ETH into a personal piggy bank contract
- Configure time-locked savings with enforced lock periods
- View real-time balance, lock status, and countdown timer
- Withdraw funds only after the configured unlock time has passed
Scope of this repository: only the frontend lives here (in
frontend/). ThePiggyBanksmart contract itself should be managed in a dedicated Solidity/Foundry repository and deployed separately to Base testnet or mainnet.
| Layer | Technology |
|---|---|
| Frontend | React 19, Vite 7, TypeScript |
| State / Data | TanStack Query |
| Wallet Integration | REOWN AppKit, WalletConnect v2 |
| Ethereum Toolkit | Wagmi, Viem |
| Blockchain Network | Base (Testnet / Mainnet ready) |
| Smart Contracts | Solidity (via Foundry – external) |
| Tooling & Quality | ESLint, TypeScript, Prettier |
.
├── frontend/ # React + Vite dApp (UI and wallet integration)
└── README.md # Project-level documentation (this file)
- Frontend details (components, hooks, config, scripts) are documented in
frontend/README.md. - The
PiggyBanksmart contract code is expected to live in a separate Solidity/Foundry repository (for example,piggybank-contracts) and is not part of this repo.
Use this if you want to run both the contracts (in your own Foundry repo) and the frontend locally:
-
Contracts (in your contracts repo):
forge install forge build forge test -vvv # deploy and copy the deployed PiggyBank address
-
Frontend (in this repo):
cd frontend npm install cp .env.example .env # set VITE_REOWN_PROJECT_ID and VITE_PIGGYBANK_ADDRESS npm run dev
🔧 For Complete Local Development Setup:
See the comprehensive Local Development Setup Guide in CONTRACTS.md for detailed step-by-step instructions on:
- Setting up local blockchain with Anvil
- Deploying contracts locally
- Configuring frontend for local testing
- Environment switching between local/testnet/mainnet
- Troubleshooting common issues
Additional details on the expected contract interface live in CONTRACTS.md. UX copy and demo scripts are in docs/.
Add a link here to your contracts repository once it is available, for example:
Contracts repository: https://github.com/<org-or-user>/piggybank-contracts
- Node.js v18+
- npm (or another Node.js package manager)
- A REOWN Project ID (from https://cloud.reown.com/)
- A WalletConnect-compatible Ethereum wallet (e.g. MetaMask)
cd frontend
npm installCopy the example file and set the required values:
cd frontend
cp .env.example .envEdit .env and provide your configuration:
VITE_REOWN_PROJECT_ID=your_reown_project_id_here
VITE_PIGGYBANK_ADDRESS=your_deployed_piggybank_contract_address_hereRequired Configuration:
VITE_REOWN_PROJECT_ID– Project ID from REOWN CloudVITE_PIGGYBANK_ADDRESS– Address of your deployedPiggyBanksmart contract
🌐 Setting VITE_PIGGYBANK_ADDRESS by Environment:
For Local Development:
-
Local Network (Hardhat/Foundry/Anvil):
# Deploy your contract to local network forge create PiggyBank --rpc-url http://localhost:8545 # Copy the deployed address from output VITE_PIGGYBANK_ADDRESS=0x1234567890123456789012345678901234567890
-
Using Existing Testnet Contract:
# Use a shared testnet contract from your team VITE_PIGGYBANK_ADDRESS=0xabcdefabcdefabcdefabcdefabcdefabcdefabcd
For Base Sepolia Testnet:
- Deploy your PiggyBank contract to Base Sepolia:
# Deploy to Base Sepolia testnet forge create PiggyBank --rpc-url $BASE_SEPOLIA_RPC --private-key $PRIVATE_KEY
- Find your contract address:
- Check deployment script output
- Visit Base Sepolia Explorer
- Search by transaction hash
- Set the address:
VITE_PIGGYBANK_ADDRESS=0x1234567890123456789012345678901234567890
For Base Mainnet (Production):
- Deploy your PiggyBank contract to Base mainnet
- Verify the contract on BaseScan
- Set the verified mainnet address:
VITE_PIGGYBANK_ADDRESS=0xabcdefabcdefabcdefabcdefabcdefabcdefabcd
🔗 Finding Contract Addresses:
- Base Sepolia: https://sepolia.basescan.org/
- Base Mainnet: https://basescan.org/
- Local: Check your deployment script terminal output
- Testnet and mainnet addresses are completely different and NOT interchangeable
- The frontend will fail to load if this address is incorrect or missing
- Always use the correct address for your target network
- Contract verification on blockchain explorers is recommended for transparency
- Share testnet addresses with your development team for consistent testing
cd frontend
npm run devBy default, Vite serves the application at http://localhost:3000 (or another available port as indicated in the terminal).
From the frontend/ directory:
-
Build for production:
npm run build
-
Preview the built app locally:
npm run preview
-
Lint the codebase:
npm run lint
-
Type-check the project:
npm run type-check
Step-by-step guide to configure the frontend with a PiggyBank contract address for testing.
This section provides focused instructions for setting up the VITE_PIGGYBANK_ADDRESS environment variable to connect your frontend to the correct contract on the right network.
Use this for local testing with Anvil/Hardhat blockchain:
-
Run the automated setup:
cd frontend ./scripts/switch-env.sh local
-
Start the local blockchain:
anvil
-
Deploy contract (if not already deployed):
forge create PiggyBank --rpc-url http://localhost:8545 --constructor-args 3600
-
Update contract address in
.envif needed:VITE_PIGGYBANK_ADDRESS=0x_your_deployed_local_contract_address
-
Start frontend:
npm run dev
✅ Result: Frontend running at http://localhost:3000 with local contract!
Use this for testing with Base Sepolia testnet:
-
Deploy your contract to testnet:
export RPC_URL="https://sepolia.base.org" export PRIVATE_KEY="your_private_key_here" forge create PiggyBank --rpc-url $RPC_URL --private-key $PRIVATE_KEY --constructor-args 3600
-
Copy the deployed contract address from the deployment output.
-
Configure environment:
cd frontend cp .env.example .env # Edit .env and set: VITE_PIGGYBANK_ADDRESS=0x_your_testnet_contract_address_here
-
Start frontend:
npm run dev
🔗 Find your contract:
- Base Sepolia Explorer: https://sepolia.basescan.org/
- Search by transaction hash or address
✅ Result: Frontend connected to Base Sepolia testnet!
Use this for production with real ETH on Base mainnet:
-
Deploy your contract to mainnet:
export RPC_URL="https://mainnet.base.org" export PRIVATE_KEY="your_mainnet_private_key" forge create PiggyBank --rpc-url $RPC_URL --private-key $PRIVATE_KEY --constructor-args 31536000
-
Copy the deployed contract address.
-
Configure environment:
cd frontend cp .env.example .env # Edit .env and set: VITE_PIGGYBANK_ADDRESS=0x_your_mainnet_contract_address_here
-
Start frontend:
npm run dev
🔗 Find your contract:
- BaseScan: https://basescan.org/
✅ Result: Frontend connected to Base mainnet!
Switch between networks instantly:
cd frontend
# Switch to local development
./scripts/switch-env.sh local
# Switch to Base Sepolia testnet
./scripts/switch-env.sh sepolia
# Switch to Base mainnet
./scripts/switch-env.sh mainnetWindows users:
.\scripts\switch-env.ps1 -Environment local
.\scripts\switch-env.ps1 -Environment sepolia
.\scripts\switch-env.ps1 -Environment mainnetIf you prefer to configure manually:
-
Edit the
.envfile:cd frontend cp .env.example .env nano .env # or use your preferred editor
-
Set the required variables:
VITE_REOWN_PROJECT_ID=your_project_id_from_cloud_reown VITE_PIGGYBANK_ADDRESS=0x_your_contract_address_here
-
Verify your setup:
- Network: Ensure you're connected to the correct network in MetaMask
- Contract: Verify the address matches your deployed contract
- Frontend: Check that the app loads without errors
| Network | How to Get Contract Address |
|---|---|
| Local | Check deployment script output or use Anvil's default |
| Base Sepolia | Base Sepolia Explorer |
| Base Mainnet | BaseScan |
- Network-specific: Contract addresses are network-specific and not interchangeable
- Required for functionality:
VITE_PIGGYBANK_ADDRESSmust be set correctly - Frontend will fail to load if this address is incorrect or missing
- Testnet vs Mainnet: Never use testnet addresses on mainnet or vice versa
Common issues:
- "Contract not deployed" error: Check contract address and network
- "Function not found" error: Verify ABI matches deployed contract
- Transaction fails: Check account balance and network connection
For detailed troubleshooting: See CONTRACTS.md
After configuring the contract address:
- Start the development server:
npm run dev - Connect your wallet to the appropriate network
- Test the functionality with deposits and withdrawals
- Verify contract interactions work as expected
For complete local development setup: See CONTRACTS.md#-local-development-setup
The actual PiggyBank implementation should live in your contracts repository (for example, a Foundry project) and be deployed to Base Sepolia or Base mainnet. The frontend in this repo only consumes that contract via its address and ABI.
A typical PiggyBank contract used with this dApp exposes the following behaviour:
- Stores the owner address.
- Stores a timestamp representing when funds can be withdrawn.
- Accepts ETH deposits while locked.
- Allows the owner to withdraw only after the unlock time.
An illustrative example contract might look as follows (for reference only):
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract PiggyBank {
address public owner;
uint256 public unlockTime;
constructor(uint256 _unlockTime) payable {
owner = msg.sender;
unlockTime = _unlockTime;
}
function deposit() external payable {}
function withdraw() external {
require(block.timestamp >= unlockTime, "PiggyBank: Locked");
require(msg.sender == owner, "PiggyBank: Not owner");
payable(owner).transfer(address(this).balance);
}
function getBalance() external view returns (uint256) {
return address(this).balance;
}
}The canonical source of truth for the contract (files, tests, deployment scripts) should be your contracts repository. Use this section to link to it and briefly describe how the frontend and contracts are wired together (e.g. how the ABI and address are kept in sync).
From the frontend/ directory:
-
Run linting:
npm run lint
-
Run TypeScript type checks:
npm run type-check
If you manage the PiggyBank smart contract with Foundry, a typical workflow might look like:
forge install
forge build
forge test -vvvThe application includes a comprehensive developer-only diagnostics system for troubleshooting blockchain connectivity, contract interactions, and environment configuration issues.
Development Mode Only: The diagnostics feature is only available when NODE_ENV=development.
-
Start the development server:
cd frontend npm run dev -
Navigate to the Debug page:
- Look for the "🔧 Debug" button in the navigation menu
- Only visible in development mode
- Click to access the comprehensive diagnostics dashboard
- RPC Connectivity: Test if the RPC endpoint is reachable
- Network Details: Chain ID, network name, and type (mainnet/testnet)
- Block Information: Latest block number and timestamp
- Connection Status: Real-time connection health with error details
- Contract Validation: Verify contract address format and validity
- Deployment Status: Check if contract is deployed at specified address
- ABI Validation: Confirm ABI structure and available functions
- Function Analysis: List all available contract functions with state mutability
- Event Analysis: Display contract events and their parameters
- Network Matching: Ensure contract network matches frontend configuration
- Transaction History: Display recent wallet transactions
- Status Tracking: Show pending, success, or error states
- Hash & Details: Copy transaction hashes and view block numbers
- Error Reporting: Display transaction errors when available
- Environment Flags: Node environment (development/production)
- Configuration Status: Verify required environment variables
- Build Information: App version and build timestamp
- Feature Flags: Check enabled diagnostics and debug features
- Wallet Connection: Current wallet address, connector, and network details
- Start with the header overview - Check overall connection status
- Expand sections as needed by clicking section headers
- Use refresh button to reload diagnostic data
- Copy values by clicking on copyable fields (RPC URLs, addresses, hashes)
🔴 Blockchain Connection Issues:
- "Disconnected" status: Check network configuration and RPC endpoint
- RPC errors: Verify network connectivity and RPC URL validity
- Network mismatch: Ensure frontend chain ID matches expected network
🔴 Contract Issues:
- "Invalid Address": Check
VITE_PIGGYBANK_ADDRESSenvironment variable - "Not Deployed": Verify contract is deployed on the correct network
- "ABI Error": Ensure contract ABI matches deployed contract version
- "Network Mismatch": Check that contract is deployed on the expected network
🔴 Transaction Issues:
- "Pending" status: Wait for transaction confirmation
- "Error" status: Check transaction details for specific error messages
- No transactions: Normal for new deployments or cleared data
🔴 Environment Issues:
- "Missing" REOWN Project: Set
VITE_REOWN_PROJECT_IDin.env - "Not Configured" Contract: Set
VITE_PIGGYBANK_ADDRESSin.env - Production Mode: Debug page only available in development
The diagnostics feature includes comprehensive unit tests:
cd frontend
# Run all tests
npm run test
# Run diagnostics-specific tests
npm run test diagnostics.test.ts
# Run tests with UI
npm run test:ui
# Run tests with coverage
npm run test:coverage- Development Only: Diagnostics page is automatically hidden in production
- No Sensitive Data: Diagnostics don't expose private keys or sensitive information
- Local Storage: Transaction data stored only in browser localStorage
- Read-Only: Diagnostics provides read-only access to blockchain data
-
Build the frontend:
cd frontend npm run build -
Deploy the contents of
frontend/distto your preferred static hosting platform (e.g. Vercel, Netlify, Cloudflare Pages, or an S3 + CDN setup). -
Ensure the deployed environment has the correct
VITE_REOWN_PROJECT_IDandVITE_PIGGYBANK_ADDRESSvalues configured.
This repository includes a GitHub Actions workflow at .github/workflows/ci.yml that:
- Runs on pull requests and pushes to
main. - Installs frontend dependencies, lints, type-checks, and builds the app.
- Optionally runs Foundry tests if
foundry.tomlexists in the repository. - Provides an optional, manual
deployjob via the "Run workflow" button.
pull_request: Lint, type-check, and build run automatically for PRs.pushtomain: Same checks run on main.workflow_dispatch: Manual run; set inputdeploy: trueto run optional deploy job.
Configure these repository secrets if you enable the deploy job (usually in your contracts repository):
PRIVATE_KEY: Deployer private key (use a test key; never commit)RPC_URL: Network RPC endpoint (e.g., Base Sepolia)EXPLORER_API_KEY: Block explorer API key (optional, for verification if added)
Note: This repo hosts the frontend. Contract deployment/verification typically lives in the contracts repository where your Foundry project resides.
- Always audit and thoroughly test smart contracts before deploying to mainnet.
- Do not deposit more funds than you are willing to lose while the system is under active development.
- This project is provided for educational and experimental purposes and comes with no guarantees.
MIT