A flexible and secure smart contract system for handling both ETH and ERC20 token donations with support for single and batch transactions.
The DonationHandler is an upgradeable smart contract that facilitates donations in both ETH and ERC20 tokens. It supports single donations as well as batch donations to multiple recipients, with optional data attachments for each transaction.
- Multiple Asset Support: Handle both ETH and ERC20 token donations
- Batch Processing: Efficiently process multiple donations in a single transaction
- Upgradeable: Uses OpenZeppelin's upgradeable contract pattern
- Security Features:
- Reentrancy protection
- Input validation
- Ownership controls
- Custom error handling
-
ETH Donations
- Single ETH donations via
donateETH()
- Batch ETH donations via
donateManyETH()
- Direct ETH transfers are prevented
- Single ETH donations via
-
ERC20 Donations
- Single token donations via
donateERC20()
- Batch token donations via
donateManyERC20()
- Automatic allowance checking
- Single token donations via
- ReentrancyGuard implementation
- Input validation for array lengths
- Zero address checks
- Amount validation
- ERC20 allowance verification
- Custom error handling for better gas efficiency
The contract includes comprehensive test coverage for:
- Single ETH donations
- Multiple ETH donations
- Single ERC20 token donations
- Multiple ERC20 token donations
- Error cases and edge conditions
- Direct ETH transfer prevention
event DonationMade(
address indexed recipientAddress,
uint256 amount,
address indexed tokenAddress,
bytes data
);
error InvalidInput();
error InsufficientAllowance();
// Donate 1 ETH to a recipient
donationHandler.donateETH{value: 1 ether}(
recipientAddress,
1 ether,
"0x" // Optional data
);
// Batch donate tokens to multiple recipients
donationHandler.donateManyERC20(
tokenAddress,
totalAmount,
recipientAddresses,
amounts,
data
);
- Solidity ^0.8.0
- OpenZeppelin Contracts (Upgradeable)
- Foundry for testing and deployment
- Sample contracts
- Basic Greeter contract with an external interface.
- Foundry setup
- Foundry configuration with multiple custom profiles and remappings.
- Deployment scripts
- Sample scripts to deploy contracts on both mainnet and testnet.
- Sample Integration, Unit, Property-based fuzzed and symbolic tests
- Example tests showcasing mocking, assertions and configuration for mainnet forking. As well it includes everything needed in order to check code coverage.
- Unit tests are built based on the Branched-Tree Technique, using Bulloak.
- Formal verification and property-based fuzzing are achieved with Halmos and Medusa (resp.).
- Linter
- Simple and fast solidity linting thanks to forge fmt.
- Find missing natspec automatically.
- Github workflows CI
- Run all tests and see the coverage as you push your changes.
- Export your Solidity interfaces and contracts as packages, and publish them to NPM.
- Install Foundry by following the instructions from their repository.
- Copy the
.env.example
file to.env
and fill in the variables. - Install the dependencies by running:
yarn install
. In case there is an error with the commands, runfoundryup
and try them again.
The default way to build the code is suboptimal but fast, you can run it via:
yarn build
In order to build a more optimized code (via IR), run:
yarn build:optimized
Unit tests should be isolated from any externalities, while Integration usually run in a fork of the blockchain. In this boilerplate you will find example of both.
In order to run both unit and integration tests, run:
yarn test
In order to just run unit tests, run:
yarn test:unit
In order to run unit tests and run way more fuzzing than usual (5x), run:
yarn test:unit:deep
In order to just run integration tests, run:
yarn test:integration
In order to start the Medusa fuzzing campaign (requires Medusa installed), run:
yarn test:fuzz
In order to just run the symbolic execution tests (requires Halmos installed), run:
yarn test:symbolic
In order to check your current code coverage, run:
yarn coverage
Configure the .env
variables and source them:
source .env
Import your private keys into Foundry's encrypted keystore:
cast wallet import $MAINNET_DEPLOYER_NAME --interactive
cast wallet import $SEPOLIA_DEPLOYER_NAME --interactive
yarn deploy:sepolia
yarn deploy:mainnet
The deployments are stored in ./broadcast
See the Foundry Book for available options.
Export TypeScript interfaces from Solidity contracts and interfaces providing compatibility with TypeChain. Publish the exported packages to NPM.
To enable this feature, make sure you've set the NPM_TOKEN
on your org's secrets. Then set the job's conditional to true
:
jobs:
export:
name: Generate Interfaces And Contracts
# Remove the following line if you wish to export your Solidity contracts and interfaces and publish them to NPM
if: true
...
Also, remember to update the package_name
param to your package name:
- name: Export Solidity - ${{ matrix.export_type }}
uses: defi-wonderland/solidity-exporter-action@1dbf5371c260add4a354e7a8d3467e5d3b9580b8
with:
# Update package_name with your package name
package_name: "my-cool-project"
...
- name: Publish to NPM - ${{ matrix.export_type }}
# Update `my-cool-project` with your package name
run: cd export/my-cool-project-${{ matrix.export_type }} && npm publish --access public
...
You can take a look at our solidity-exporter-action repository for more information and usage examples.
The primary license for the boilerplate is MIT, see LICENSE