A collateralized stablecoin system using ERC4626 vault shares as collateral. Users can deposit vault shares and automatically receive stablecoin based on a configurable collateralization ratio.
- Stablecoin: ERC20 governance token with voting capabilities
- CollateralManager: Manages collateral deposits and stablecoin minting with configurable fees
- Governor: Governance contract for protocol parameter changes
- Treasury: Holds protocol fees collected from minting operations
All contracts use the UUPS (Universal Upgradeable Proxy Standard) pattern for upgradeability.
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.
npm run build
# or
forge buildnpm run test
# or
forge testnpm run test:fuzz
# or
forge test --fuzz-runs 1000npm run format
# or
forge fmtnpm run format:check
# or
forge fmt --checkforge snapshotnpm run anvil
# or
anvilDeploy all contracts (Stablecoin, Treasury, CollateralManager, Governor) using UUPS proxy pattern:
npm run deploy:local
# or
forge script script/Deploy.s.sol:DeployScript --rpc-url http://localhost:8545 --broadcast --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80For other networks:
forge script script/Deploy.s.sol:DeployScript --rpc-url <your_rpc_url> --broadcast --private-key <your_private_key>The script will:
- Deploy implementation contracts
- Deploy ERC1967Proxy for each contract
- Initialize each proxy
- Set CollateralManager as minter for Stablecoin
- Transfer ownership to Governor
- Output all contract addresses
Important: Use the proxy addresses (not implementation addresses) for interactions.
Add an ERC4626 vault to the CollateralManager:
export COLLATERAL_MANAGER_ADDRESS=0x...
export VAULT_ADDRESS=0x...
forge script script/AddVault.s.sol:AddVaultScript --rpc-url <your_rpc_url> --broadcast --private-key <your_private_key>cast <subcommand>forge --help
anvil --help
cast --helpThis repository uses Husky to run pre-commit hooks that:
- Format Solidity contracts with
forge fmt
The hooks run automatically on git commit. To manually run:
npm run formatThis repository includes a demonstration of the ERC-4626 inflation attack vulnerability, as described in the MixBytes article.
The inflation attack exploits rounding issues in ERC-4626 vaults. Attackers can manipulate the share calculation formula to make victims receive zero or minimal shares, allowing them to steal deposits.
Attack Formula:
shares = totalSupply × assets / totalAssets
- Start Anvil (local blockchain):
npm run anvil- Deploy the demo contracts:
npm run deploy:attack-demo- Set environment variables in
ui/.env.local:
NEXT_PUBLIC_ASSET_ADDRESS=0x...
NEXT_PUBLIC_VAULT_ADDRESS=0x...
NEXT_PUBLIC_ATTACKER_ADDRESS=0x...
NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID=your_project_id- Start the UI:
cd ui
npm install
npm run dev- Run the attack tests:
forge test --match-contract InflationAttackTest -vv- Mint Initial Share: Attacker deposits 1 wei to mint the first share
- Inflate Denominator: Attacker transfers assets directly to vault, manipulating the formula
- Victim Deposits: Victim receives zero or minimal shares due to rounding
- Attacker Steals: Attacker redeems their share and receives almost all assets
src/VulnerableVault.sol- Vulnerable ERC-4626 implementationsrc/Attacker.sol- Attack contract demonstrating the exploittest/InflationAttack.t.sol- Test cases showing the attackui/- React UI for visualizing the attack
This repository also includes a demonstration of the reentrancy attack that led to the Ethereum hard fork in 2016, as described in the Chainlink article.
The reentrancy attack exploits the order of operations in smart contracts. When a contract sends ETH before updating state, an attacker can re-enter the function and drain funds.
The Vulnerability:
function withdraw() public {
uint256 bal = balances[msg.sender];
(bool sent, ) = msg.sender.call{value: bal}(""); // Send ETH first
balances[msg.sender] = 0; // Update balance after - TOO LATE!
}- Start Anvil (local blockchain):
npm run anvil- Deploy the demo contracts:
npm run deploy:reentrancy-demo- Set environment variables in
ui/.env.local:
NEXT_PUBLIC_VULNERABLE_DAO_ADDRESS=0x...
NEXT_PUBLIC_SECURE_DAO_ADDRESS=0x...
NEXT_PUBLIC_ATTACKER_ADDRESS=0x...
NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID=your_project_id- Start the UI:
cd ui
npm run dev- Run the attack tests:
forge test --match-contract ReentrancyAttackTest -vv- Attacker Deposits: Attacker deposits 1 ETH to the vulnerable DAO
- Attacker Withdraws: Calls
withdraw()function - DAO Sends ETH: Vulnerable DAO sends ETH to attacker before updating balance
- Reentrancy: Attacker's
receive()function callswithdraw()again - Repeat: Process repeats until DAO is drained
- Balance Updated: Only after all reentrancy calls complete does the balance get set to 0
src/VulnerableDAO.sol- Vulnerable contract with reentrancy flawsrc/SecureDAO.sol- Fixed version with reentrancy guardsrc/ReentrancyAttacker.sol- Attack contract demonstrating the exploittest/ReentrancyAttack.t.sol- Test cases showing the attackui/app/components/ReentrancyVisualization.tsx- React UI for visualizing the attack
The DAO hack in 2016:
- Drained $150M worth of ETH from The DAO
- Led to a hard fork of Ethereum
- Created Ethereum (current chain) and Ethereum Classic (original chain)
- 85% of the community voted for the fork to recover funds
MIT