Fund-Me Smart Contract: This is a minimal project allowing user to fund the contract owner with donations. The smart contract accepts ETH as donations, dominated in USD. Donations has minimum USD value , otherwise they are rejected. The value is priced using a Chainlink Price Feed , and the smart contract keeps track of the doners in case they are to be rewarded in the future.
The FundMe Project smart contract enables transparent, "decentralized crowdfunding", acting like a programmable Kickstarter on the blockchain. Its core purpose is to "securely accept native cryptocurrency donations (fund)", "track contributor data", "enforce a minimum USD value using Chainlink oracles", and allow "authorized withdrawals (withdraw)".
The FundMe contract enables decentralized fundraising with the following key features:
- Minimum funding requirement of $5 USD (converted from ETH)
- Real-time ETH/USD price conversion using Chainlink oracles
- Owner-only can withdraw functionality
- Automatic fund() execution via fallback and receive functions
It demonstrates:
- Price feed integration via AggregatorV3Interface
- Mocking Chainlink feeds for local testing
- Foundry-style unit, integration tests
Built this project to understand in-dept about:
- Oracle integration (Chainlink)
- Access control with onlyOwner
- Safe ETH withdrawal patterns.
- Gas optimizations with constant, immutable, and custom errors.
FundMe.sol: Core contract implementing funding and withdrawal functions. It verifies the minimum funding threshold based on real-time ETH/USD prices fetched from Chainlink's price feed.FundMe.s.sol: Deployment script for the FundMe contract, utilizing HelperConfig to select the appropriate price feed address.HelperConfig.sol: Configures data feeds for different networks and deploys a mock aggregator for local testing.FundMe.t.sol: Test suite containing unit tests to validate contract functionalities.
- Minimum Contribution: $5 USD equivalent in ETH
- Price Oracle Integration: Uses Chainlink ETH/USD price feeds (Sepolia testnet).
- Access Control: Only contract deployer can withdraw funds
- Fallback Support: Automatically processes direct ETH transfers as donations.
- Funder Tracking: Records who sent ETH , how much and their contributed amounts.
- Auto ETH Handling: receive() and fallback() support direct ETH
- Chainlink Integration: The
PriceConverterlibraryuses Chainlink'sAggregatorV3Interfaceto fetch the latest ETH/USD price, enabling the contract to handle price volatility. - Customizable threshold: Minimum USD value can be adjusted.
- Multi-Network Support: Deployable on Sepolia, Mainnet, and local Anvil.
- Efficient Design: Uses libraries (PriceConverter) and custom errors (NotOwner) for gas optimization.
Think of this contract like a donation box:
- People (like e.g "Nathaniel" π§) can donate ETH
- But it only accepts donations worth $5 or more in real-time USD value
- You (the contract creator) are the only one allowed to withdraw the funds.
- The contract safely tracks all donors and protects against misuse.
- msg.sender, msg.value, address(this).balance
- Chainlink Integration: Working with external price feed oracles , using Chainlink AggregatorV3Interface.
- Price conversion using custom library (PriceConverter)
- onlyOwner modifier using immutable i_owner
- transfer, send, and call ETH withdrawal methods
- Gas-optimized NotOwner() custom error.
- receive() and fallback() to catch direct ETH transactions.
- Makefiles: Workflow automation for common tasks
- Helper Configs: For Multi-network deployment configurations
- fund() - Send ETH to the contract (minimum 5 USD equivalent required)
- withdraw() - Owner can withdraw all funds from the contract
- cheaperWithdraw() - Gas-optimized withdrawal function
- Modifiers:
onlyOwner: Restricts access to owner-only functionality.
- Chainlink Price Oracle integration for accurate ETH/USD conversion
- Gas optimizations using:
constantfor unchanging valuesimmutablefor one-time initialization values- Custom errors instead of revert strings
- Memory optimization in withdrawal function
- ETH handling through:
receive()function for direct ETH transfersfallback()function for unexpected interactions
- Solidity - The programming language for writing the Smart contracts.
- Foundry(forge, cast, anvil) - Development framework and testing suite
- Chainlink - For Price/Data Feeds (real + mock)
- MetaMask - Ethereum wallet for transactions.
- Solidity Compiler, Version ^0.8.18 or higher.
- Foundry development framework for compiling and testing.
- Install Git. You'll know you did it right if you can run
git --versionand you see a response likegit version x.x.x. - Chainlink Oracles for price feed integration.
- An Ethereum wallet (Metamask).
- Sepolia ETH (for testnet deployment).
- Etherscan API key for contract verification.
-
Clone the repository:
git clone https://github.com/legendarycode3/fund-me-smart-contract
cd fund-me-smart-contract -
Install dependencies:
forge install smartcontractkit/chainlink-brownie-contracts@0.6.1
make install # Or forge install -
Build the project:
make build
A Makefile is included to streamline commands for cleaning, building, testing, updating, formatting, deployment, and more. You can use it to execute tasks without needing to remember specific commands. Just run the command you need like this:
make <command>The Makefile defines the following commands for quick project management:
clean: Cleans up build artifacts.remove: Clears Git submodules and libraries, then commits the changes.all: Runs clean, remove, install, update, and build in sequence.install: Installs required packages without committing.update: Updates dependencies.build: Compiles the contracts.test-anvil: Runs tests in a local Anvil environment.test-sepolia: Runs tests on Sepolia fork using SEPOLIA_RPC_URL.snapshot: Generates a snapshot of contract states.format: Formats code according to standards.coverage: Runs code coverage.deploy-anvil: Deploys contract locally using Anvil.deploy-sepolia: Deploys contract to Sepolia network with verification on Etherscan.test: Run tests.
Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.
Foundry consists of:
- Forge: Ethereum testing framework (like Truffle, Hardhat and DappTools).
- Cast: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data.
- Anvil: Local Ethereum node, akin to Ganache, Hardhat Network.
- Chisel: Fast, utilitarian, and verbose solidity REPL.
- Setup environment variables:
You'll want to set your SEPOLIA_RPC_URL and PRIVATE_KEY as environment variables. You can add them to a .env file
- PRIVATE_KEY: The private key of your account (like from metamask. NOTE: FOR DEVELOPMENT, PLEASE USE A KEY THAT DOESN'T HAVE ANY REAL FUNDS ASSOCIATED WITH IT.
- SEPOLIA_RPC_URL: This is url of the sepolia testnet node you're working with. You can get setup with one for free from alchemy Optionally, add your ETHERSCAN_API_KEY if you want to verify your contract on Etherscan
- Get testnet ETH:
Head over to cloud.google.com or faucets.chain.link and get some testnet Sepolia ETH. You should see the Sepolia ETH show up in your metamask. - Deploy(Using Script):
forge script script/DeployFundMe.s.sol --rpc-url $SEPOLIA_RPC_URL --private-key $PRIVATE_KEY --broadcast --verify --etherscan-api-key $ETHERSCAN_API_KEY- Start a local Anvil node on one terminal:
anvil- Deploy the contract:
forge script script/DeployFundMe.s.sol:DeployFundMe --rpc-url http://127.0.0.1:8545 --private-key <ANVIL_PRIVATE_KEY> --broadcastAfter deploying the contract to the testnet or to the local net, you can either directly interact with the contract running the scripts.
- Fund the FundMe contract
- Direct interaction using terminal:
cast send <FUNDME_CONTRACT_ADDRESS> "fund()" --value 0.1ether --rpc-url $SEPOLIA_RPC_URL --private-key <PRIVATE_KEY>
- By running the script in the terminal:
forge script script/Interactions.s.sol:FundFundMe --rpc-url $SEPOLIA_RPC_URL --private-key $PRIVATE_KEY --broadcast
- Withdraw from the FundMe contract
- Direct interaction using terminal:
cast send <FUNDME_CONTRACT_ADDRESS> "withdraw()" --rpc-url $SEPOLIA_RPC_URL --private-key <PRIVATE_KEY>
- By running the script in the terminal:
forge script script/Interactions.s.sol:WithdrawFundMe --rpc-url $SEPOLIA_RPC_URL --private-key $PRIVATE_KEY --broadcast
Compile the smart contracts:
forge build
# or
make buildRun all tests:
forge testOr
make testRun tests with verbosity:
make test -vvvRun specific test:
forge test --mt testFunctionNameTest coverage:
make coverageExample test scenarios:
- Funding below minimum threshold (should revert)
- Successful funding above minimum
- Non-owner withdrawal attempt (should revert)
- Owner withdrawal functionality
- Price conversion accuracy.
forge fmtYou can estimate how much gas things cost by running:
forge snapshot anvil forge script script/DeployFundMe.s.sol:DeployFundMe --rpc-url <your_rpc_url> --private-key <your_private_key>Deploy to Sepolia testnet:
make deploy-sepolia
$ cast <subcommand>$ forge --help
$ anvil --help
$ cast --helpSepolia Testnet
- Chain ID: 11155111
- Price Feed:
0x694AA1769357215DE4FAC081bf1f309aDC325306 - Currency Pair: ETH/USD
- Faucet: Sepolia Faucet
Mainnet
- Chain ID: 1
- Price Feed:
0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419 - Currency Pair: ETH/USD
Local Development (Anvil)
- Chain ID: 31337
- Price Feed: MockV3Aggregator (deployed automatically)
- Simulated Price: $2000 ETH/USD
- fund(): Allows users to contribute funds to the campaign. Requires the sent amount to meet the minimum USD requirement.
- getPrice(): Retrieves the latest ETH to USD conversion rate using Chainlink's AggregatorV3Interface.
- getVersion(): Retrieves the version of the Chainlink AggregatorV3Interface being used.
- getConversionRate(uint256 ethAmount): Calculates the equivalent USD amount for the provided ETH amount.
- withdraw(): Enables the contract owner to be able to pull all funds (Ether) out of the contract to their own wallet.
- receive(): Receives Ether and calls
fund(). - fallback(): Fallback function that also calls
fund().
- MINIMUM_USD: The minimum amount of USD (in Wei) required for funding (set to 5 USD in the contract).
- i_owner: Immutable variable storing the ownerβs address.
- funders: Array of addresses that have funded the contract.
- addressesToFunded: Mapping of funder addresses to the amount they have funded.
DeployFundme.s.sol: Main deployment scriptHelpingConfig.s.sol: Network-specific configurationsMockV3Aggregator.sol: Local testing price feed mock
Create a .env file with the following variables:
SEPOLIA_RPC_URL=your_sepolia_rpc_url
ETHERSCAN_API_KEY=your_etherscan_api_key
SEPOLIA_PRIVATE_KEY=your_sepolia_private_key- Private Keys: Never commit private keys to version control.
- Environment Variables: Keep your .env file secure and never share it.
- Testnet Only: This setup is configured for testnet deployment.
- Audit: Have your contracts audited before mainnet deployment.
- Reentrancy Protection: Uses checks-effects-interactions pattern.
- Chainlink pricefeed: Validate Chainlink price feed addresses for the target network.
βββscript
β βββ DeployFundMe.s.sol
β βββ HelperConfig.s.sol
β βββ Interactions.s.sol
βββ src
βββ PriceConverterLibrary.sol
β βββ FundMe.sol
βββ test
βββ integration
β βββ FundMeTestIntegrationOrIntetractionTest.t.sol
βββ mocks
β βββ MockV3Aggregator.sol
βββ unit
βββ FundMeTest.t.sol
βββ foundry.toml
βββ README.md
The contract implements several gas optimization techniques:
- Immutable owner variable
- Constant minimum USD value
- Efficient array resetting in withdrawal
- Gas Optimization: Fund during low network congestion periods
- Minimum Amount: Always check current ETH price to meet $5 minimum
- Transaction Monitoring: Use Etherscan (blockchain explorer) to track your funding transactions
- Testing Strategy: Always test on testnets before mainnet deployment
- Price Feed Updates: Monitor Chainlink price feed deprecation notices
- Security: Use hardware wallets for mainnet deployments
- Gas Estimation: Use
forge snapshotto track gas usage changes
- Foundry Book
- Solidity Official Documentation
By completing this project, you should be comfortable with:
- Writing Solidity contracts with external dependencies
- Using libraries and immutable variables
- Writing Foundry tests with mocks and cheats
- Debugging EVM and Foundry errors
- Understanding environment-specific behavior (local vs testnet vs fork)
Some users were having a confusion that whether Chainlink-brownie-contracts is an official Chainlink repository or not. Here is the info. Chainlink-brownie-contracts is an official repo. The repository is owned and maintained by the chainlink team for this very purpose, and gets releases from the proper chainlink release process. You can see it's still the smartcontractkit org as well. smartcontractkit
LegendaryCode
- LinkedIn: @legendarycode3
- Twitter: @legendary_code_
- Github: @legendarycode3