SaharRamezani/Crypto-Donation-Platform
A decentralized application (dApp) for donating Sepolia ETH to charitable organizations.
Crypto Donation Platform
A decentralized application (dApp) for donating Sepolia ETH to charitable organizations.
Features
- Donate ETH - Send Sepolia ETH directly to verified charities
- Pre-loaded Charities - 1 initial charity ready for donations
- Propose Charities - Anyone can propose new charities for approval
- Multi-admin Governance - Multiple administrators can manage proposals and contract settings
- Upgradeable Contract - UUPS proxy-based architecture allowing for safe contract upgrades
- Version Tracking - V2 contract includes version tracking (
getVersion()) - Leaderboard - Track top donors on the platform
- Transaction History - View all donations via events
- Security - Reentrancy protection, AccessControl (Roles), and UUPS Proxy
Screenshots
Main Dashboard (light mode)
Main Dashboard (dark mode)
Leaderboard & Transactions
Propose a Charity
Admin Governance
Project Structure
Crypto-Donation-Platform/
├── contracts/
│ ├── CharityDonation.sol # V1 implementation (base contract)
│ └── CharityDonationV2.sol # V2 implementation (adds version tracking)
├── frontend/
│ ├── index.html # Web interface
│ ├── styles.css # Theme styling
│ ├── app.js # ethers.js integration
│ └── contract-abi.json # Contract ABI (auto-generated)
├── scripts/
│ ├── deploy_proxy.js # Deploys the initial V1 proxy
│ └── upgrade_contract.js # Upgrades proxy to V2
├── deployments/ # Deployment records and proxy addresses
├── test/
│ ├── CharityDonation.test.js # V1 contract tests
│ └── CharityDonationV2.test.js # V2 upgrade tests
├── docker/
│ └── nginx.conf # Nginx configuration
├── Dockerfile # Multi-stage Docker build
├── docker-compose.yml # Docker Compose configuration
├── hardhat.config.js # Hardhat configuration
├── package.json # Node.js dependencies
└── .env.example # Environment template
Local Development with Hardhat
If you want to test transactions locally without deploying to Sepolia:
Step 1: Force Local Hardhat Mode
By default, Docker uses your Sepolia deployment if it exists. To force local mode:
# Start Docker daemon first (may need sudo)
sudo systemctl start docker
# Temporarily rename the Sepolia config
mv frontend/contract-abi.sepolia.json frontend/contract-abi.sepolia.json.bak
# Start Docker (will deploy to local Hardhat)
docker compose up --buildThis starts:
- Hardhat Node on
http://localhost:8545 - Frontend on
http://localhost:3000
Open http://localhost:3000 in your browser to use the dApp!
To stop:
docker compose downNote: When you move from Hardhat to Sepolia (and vice versa), you need to modify the .env file and change the private key, Metasmask network, clear the cache, and refresh the page.
Step 2: Add Hardhat Network to MetaMask
- Open MetaMask → Click network dropdown → Add Network
- Enter these settings:
| Field | Value |
|---|---|
| Network Name | Hardhat Local |
| RPC URL | http://localhost:8545 |
| Chain ID | 31337 |
| Currency Symbol | ETH |
Step 3: Import a Test Account
Hardhat provides pre-funded test accounts. Import one into MetaMask:
-
When Docker starts, you'll see accounts in the terminal:
Account #0: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 (10000 ETH) Private Key: 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 -
In MetaMask → Click account → Import Account
-
Paste the private key
-
Click Import
Note: These are test accounts with fake ETH. Never use these private keys on mainnet!
Step 4: Restore Sepolia Mode
When done testing locally:
mv frontend/contract-abi.sepolia.json.bak frontend/contract-abi.sepolia.json
docker compose down && docker compose upDeploy to Sepolia (Public Testnet)
This section explains how to deploy your contract to the real Sepolia testnet so anyone in the world can interact with it.
Prerequisites
- MetaMask browser extension
- Node.js and npm installed
- Some Sepolia testnet ETH (free, see below)
Step 1: Get Sepolia ETH (Free)
You need testnet ETH to pay for gas fees. Get it from a faucet:
| Faucet | URL | Notes |
|---|---|---|
| Google Cloud | https://cloud.google.com/application/web3/faucet/ethereum/sepolia | Requires Google account |
| Alchemy | https://sepoliafaucet.com | Requires Alchemy account |
| Infura | https://www.infura.io/faucet/sepolia | Requires Infura account |
Tip: Copy your MetaMask wallet address and paste it into the faucet. You'll receive 0.1-0.5 Sepolia ETH, which is more than enough for deployment.
Step 2: Get an RPC URL
You need an RPC endpoint to connect to Sepolia. Choose a provider:
Alchemy (Recommended):
- Go to https://alchemy.com and create a free account
- Create a new app → Select "Ethereum" → "Sepolia"
- Copy the HTTPS URL (looks like
https://eth-sepolia.g.alchemy.com/v2/YOUR_API_KEY)
Infura (Alternative):
- Go to https://infura.io and create a free account
- Create a new project → Select "Web3 API"
- Copy the Sepolia endpoint
Step 3: Export Your Private Key from MetaMask
- Open MetaMask in your browser
- Click the three dots (⋮) next to your account name
- Click "Account details"
- Click "Show private key"
- Enter your MetaMask password
- Copy the private key (without the
0xprefix)
Step 4: Configure Your .env File
# Copy the example environment file
cp .env.example .envEdit .env with your real values:
# Your RPC URL from Alchemy or Infura
SEPOLIA_RPC_URL=https://eth-sepolia.g.alchemy.com/v2/YOUR_API_KEY
# Your wallet private key (WITHOUT 0x prefix)
PRIVATE_KEY=your_private_key_here
# Optional: For contract verification on Etherscan
ETHERSCAN_API_KEY=your_etherscan_api_keyStep 5: Deploy the V1 Proxy
# Install dependencies
npm install
# Compile contracts
npm run compile
# Deploy to Sepolia
npm run deploy:sepoliaStep 6: Verify on Etherscan (Optional)
Verification makes your source code public and allows users to interact with your contract directly on Etherscan.
- Get an Etherscan API key from https://etherscan.io/myapikey
- Add it to your
.envfile - Run verification:
npx hardhat verify --network sepolia <IMPLEMENTATION_ADDRESS>Note
If you see an error saying "Failed to verify ERC1967Proxy contract... Already Verified", this is expected! It means the proxy structure itself is already known by Etherscan. The important part is that your implementation contract is verified and linked.
Once verified, view your contract at:
https://sepolia.etherscan.io/address/YOUR_PROXY_ADDRESS
Step 7: Use the Frontend with Sepolia
After deployment, the contract-abi.sepolia.json file is automatically created in your frontend/ folder.
- Open the frontend at
http://localhost:3000(or you can run the docker container as it is already configured to run the frontend with Sepolia) - Switch MetaMask to Sepolia Test Network
- Connect your wallet
- Start donating with real Sepolia ETH!
Upgrading to V2
The contract uses the UUPS (Universal Upgradeable Proxy Standard) pattern. This allows you to upgrade the contract logic without changing the proxy address or losing any data.
How to Upgrade
To upgrade an existing V1 deployment to V2:
# Local Hardhat
npm run upgrade:local
# Inside the docker container
docker compose run --rm deployer npm run upgrade:docker
# Sepolia Testnet
npm run upgrade:sepoliaWhat the Upgrade Script Does
- Loads the existing proxy address from
deployments/ - Deploys the new V2 implementation contract
- Calls
upgradeProxy()to swap the logic - Verifies by calling
getVersion()- returns "v2.0.0" - Updates the frontend ABI with V2 functions
V2 Features
| Function | Description |
|---|---|
getVersion() |
Returns the hardcoded version string "v2.0.0" |
contractVersion() |
State variable for custom version string |
setVersion(string) |
Admin-only: set a custom version |
Reset Project (Start Fresh)
If you ever see errors like "Deployment not registered" or "doesn't look like an ERC 1967 proxy", it usually means your local files are out of sync with a fresh blockchain. You can start completely fresh with:
npm run reset:dockerNote: This wipes all local donations and charity proposals.
Security Features
- Reentrancy Guard - Prevents reentrancy attacks on
donate()andwithdrawFunds() - Checks-Effects-Interactions - State updates before external calls
- Access Control - Role-based permissions for admin functions
- UUPS Proxy - Upgrades require admin authorization via
_authorizeUpgrade()
Pull over Push Pattern
Instead of automatically sending donations to charities, funds accumulate in the contract. Charities must manually withdraw their funds:
- Connect MetaMask with the charity's registered wallet address
- Click "Withdraw Funds" on your charity card
- Confirm the transaction
This prevents reentrancy attacks and ensures reliable donation processing.
NPM Scripts
| Command | Description |
|---|---|
npm run compile |
Compile all Solidity contracts |
npm run test |
Run all tests (V1 + V2) |
npm run node |
Start a local Hardhat node |
npm run deploy:local |
Deploy V1 proxy to localhost |
npm run deploy:docker |
Deploy V1 proxy inside Docker |
npm run deploy:sepolia |
Deploy V1 proxy to Sepolia |
npm run upgrade:local |
Upgrade to V2 on localhost |
npm run upgrade:docker |
Upgrade to V2 inside Docker |
npm run upgrade:sepolia |
Upgrade to V2 on Sepolia |
npm run clean:docker |
Remove project Docker containers, images, and volumes |
npm run reset:docker |
Fully reset the Docker environment and redeploy |
Troubleshooting
MetaMask Nonce Issues
If you restart Hardhat node, MetaMask may have stale transaction data:
- Open MetaMask → Settings → Advanced
- Click "Clear activity tab data"
Wrong Network Error
If you see "CALL_EXCEPTION" or "Connection Failed":
- Switch MetaMask to the correct network (Sepolia or Hardhat Local)
- Ensure the RPC URL is accessible
"GoChain Testnet" Warning
When adding Chain ID 31337, MetaMask may suggest "GoChain Testnet". This is a known ID collision with Hardhat. Simply rename it to "Hardhat Local".
Share Remotely with Cloudflare Tunnel
To demo the project to remote stakeholders using your laptop as a server:
Step 1: Install Cloudflared
wget -q https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
sudo dpkg -i cloudflared-linux-amd64.debStep 2: Start Your Project
docker compose upStep 3: Create a Public Tunnel
In a new terminal:
cloudflared tunnel --url http://localhost:3000You'll see output like:
| Your quick Tunnel has been created! Visit it at: |
| https://random-words-here.trycloudflare.com |
Share this URL with stakeholders!
Important Notes
- Keep both terminals running (docker compose + cloudflared)
- Stakeholders need MetaMask connected to Sepolia to interact with the real contract
- URL changes each time you restart cloudflared (unless you create a named tunnel with a Cloudflare account)




