A Solidity-based crowdfunding smart contract built with Foundry that allows users to fund a project with ETH. The contract uses Chainlink Price Feeds to ensure a minimum funding amount in USD, regardless of ETH price fluctuations.
FundMe is a smart contract that enables decentralized crowdfunding with the following features:
- Minimum USD Contribution: Enforces a minimum contribution of $5 USD worth of ETH using Chainlink price feeds
- Owner-Only Withdrawals: Only the contract owner can withdraw accumulated funds
- Gas-Optimized: Includes optimized withdrawal functions to reduce gas costs
- Multi-Network Support: Configured for deployment on Sepolia testnet and local Anvil network
- Comprehensive Testing: Includes unit and integration tests with gas snapshots
FundMe.sol: Main crowdfunding contract with funding and withdrawal logicPriceConverter.sol: Library for converting ETH amounts to USD using Chainlink price feeds
- Price Feed Integration: Uses Chainlink's
AggregatorV3Interfaceto get real-time ETH/USD prices - Funder Tracking: Maintains a mapping of addresses to funding amounts and an array of funders
- Dual Withdrawal Methods:
withdraw(): Standard withdrawal functioncheaperWithdraw(): Gas-optimized version using memory variables
- Fallback Funding: Implements
receive()andfallback()functions to accept direct ETH transfers
- Solidity: ^0.8.18
- Foundry: Development framework
- Forge: Testing framework
- Cast: Command-line tool for interacting with contracts
- Anvil: Local Ethereum node
- Chainlink: Price feed oracles
- OpenZeppelin: Smart contract libraries (via dependencies)
FundMe/
βββ src/
β βββ FundMe.sol # Main crowdfunding contract
β βββ PriceConverter.sol # Price conversion library
βββ script/
β βββ DeployFundMe.s.sol # Deployment script
β βββ HelperConfig.s.sol # Network configuration helper
β βββ Interaction.s.sol # Fund and withdraw scripts
βββ test/
β βββ FundMeTest.t.sol # Unit tests
β βββ integration/ # Integration tests
β βββ mocks/ # Mock contracts for testing
βββ foundry.toml # Foundry configuration
βββ Makefile # Build and deployment commands
βββ .env # Environment variables
- Foundry installed
- An Ethereum wallet with testnet ETH (for Sepolia deployment)
- Etherscan API key (for contract verification)
- Clone the repository:
git clone <repository-url>
cd FundMe- Install dependencies:
forge install- Create a
.envfile with the following variables:
SEPOLIA_RPC_URL=<your-sepolia-rpc-url>
PRIVATE_KEY=<your-private-key>
ETHERSCAN_API_KEY=<your-etherscan-api-key>
ANVIL_RPC_URL=http://127.0.0.1:8545
ANVIL_PRIVATE_KEY=<anvil-default-private-key>Compile the smart contracts:
forge buildOr using the Makefile:
make buildforge testforge test -vvvforge snapshotforge coveragemake deploy-sepoliaThis will:
- Deploy the FundMe contract to Sepolia
- Verify the contract on Etherscan
- Use the Chainlink Sepolia ETH/USD price feed
- Start Anvil in a separate terminal:
anvil- Deploy the contract:
make deploy-anvilUsing the Makefile (Sepolia):
make fundUsing Forge directly:
forge script script/Interaction.s.sol:FundFundMe --rpc-url <RPC_URL> --private-key <PRIVATE_KEY> --broadcastUsing the Makefile (Sepolia):
make withdrawUsing Forge directly:
forge script script/Interaction.s.sol:WithdrawFundMe --rpc-url <RPC_URL> --private-key <PRIVATE_KEY> --broadcastCheck contract balance:
cast balance <CONTRACT_ADDRESS> --rpc-url <RPC_URL>Call a view function:
cast call <CONTRACT_ADDRESS> "getOwner()" --rpc-url <RPC_URL>Send a transaction:
cast send <CONTRACT_ADDRESS> "fund()" --value 0.1ether --private-key <PRIVATE_KEY> --rpc-url <RPC_URL>fund(): Fund the contract with ETH (minimum $5 USD equivalent)withdraw(): Withdraw all funds (owner only)cheaperWithdraw(): Gas-optimized withdrawal (owner only)getVersion(): Get the Chainlink price feed version
getAddressToAmountFunded(address): Get the amount funded by a specific addressgetFunder(uint256): Get the funder address at a specific indexgetOwner(): Get the contract owner address
MINIMUM_USD: Minimum funding amount (5 USD in wei)
- Source Directory:
src/ - Output Directory:
out/ - Libraries:
lib/ - Remappings: Configured for Chainlink and Forge Standard Library
- FFI: Enabled for advanced scripting
The HelperConfig.s.sol script automatically configures the correct Chainlink price feed address based on the network:
- Sepolia: Uses Chainlink's Sepolia ETH/USD price feed
- Anvil: Deploys a mock price feed for local testing
The contract includes two withdrawal methods:
withdraw(): Standard implementationcheaperWithdraw(): Optimized version that:- Caches array length in memory
- Reduces storage reads
- Saves approximately 20-30% gas
Gas snapshots are tracked in .gas-snapshot for monitoring optimization improvements.
- Owner-only withdrawals: Uses custom error
FundMe__NotOwner()for gas-efficient access control - Minimum funding requirement: Prevents spam transactions
- Safe type casting: Includes safety checks when converting Chainlink price data
- Reentrancy protection: Uses checks-effects-interactions pattern
MIT
Contributions are welcome! Please feel free to submit a Pull Request.
This project is for educational purposes. Always audit smart contracts before deploying to mainnet with real funds.