A minimal, secure, and well-documented implementation of a collateralized lending protocol built with Solidity and Foundry.
This protocol allows users to:
- Deposit ETH as collateral
- Borrow MockDAI (stablecoin) up to 50% Loan-to-Value (LTV)
- Have positions liquidated if collateral value falls below 55% threshold
- Repay debt to reclaim collateral
- Single Asset Collateral: ETH only
- Single Debt Asset: MockDAI (mock stablecoin)
- LTV Cap: 50% (5000 basis points)
- Liquidation Threshold: 55% (5500 basis points)
- Liquidation Bonus: 5% for liquidators
- No Interest: Simple lending without interest accrual
- No Protocol Fees: Zero fees for basic operations
- ReentrancyGuard: All state-changing functions protected against reentrancy attacks
- CEI Pattern: Checks-Effects-Interactions pattern followed throughout
- Input Validation: All amounts validated for non-zero values
- Access Control: Owner-only functions for admin operations
- Safe Math: Solidity 0.8.x built-in overflow protection
- Health Factor Monitoring: Positions checked before withdrawals
- Liquidation Mechanism: Automatic liquidation of underwater positions
- Price Feed Integration: Configurable price feeds for collateral valuation
- LTV Limits: Strict borrowing limits enforced
- Mock Price Feed: Uses configurable mock price (replace with Chainlink in production)
- Single Collateral: Only ETH supported (extensible design)
- No Interest: Simple lending without interest accrual
- No Protocol Fees: Zero fees (can be added later)
- No Upgradeability: Immutable contracts (consider proxy pattern for production)
lending-task/
├── src/
│ ├── CollateralLending.sol # Main lending contract
│ ├── MockDAI.sol # Mock stablecoin
│ └── mocks/
│ └── MockPriceFeed.sol # Mock price oracle
├── test/
│ ├── CollateralLending.t.sol # Main contract tests
│ └── MockDAI.t.sol # Token tests
├── script/
│ └── Deploy.s.sol # Deployment script
├── foundry.toml # Foundry configuration
└── README.md # This file
Main lending protocol contract with the following key functions:
deposit()- Deposit ETH as collateral (~45k gas)withdraw(uint256)- Withdraw ETH collateral (~35k gas)borrow(uint256)- Borrow MockDAI against collateral (~80k gas)repay(uint256)- Repay MockDAI debt (~60k gas)liquidate(address, uint256)- Liquidate underwater position (~90k gas)
Mock stablecoin with mint/burn restricted to lending contract:
- ERC20 compliant with 18 decimals
- Mint/burn functions for lending contract only
- Initial supply: 1,000,000 DAI
Simple price oracle for ETH/USD:
- Configurable price with 8 decimals
- Owner can update price (for testing)
- Returns ETH price in USD
- Clone and setup:
git clone <repository-url>
cd lending-task
forge install- Install dependencies:
forge install OpenZeppelin/openzeppelin-contracts- Compile contracts:
forge buildforge testforge test -vvforge test --gas-reportforge test --match-contract CollateralLendingTestforge test --match-test testFuzzKey function gas costs (approximate):
deposit(): ~45,000 gaswithdraw(): ~35,000 gasborrow(): ~80,000 gasrepay(): ~60,000 gasliquidate(): ~90,000 gas
- Install Slither:
pipx install slither-analyzer- Run analysis:
slither .- Run with specific detectors:
slither . --detect reentrancy,uninitialized-state,uninitialized-storage- Reentrancy protection on all state-changing functions
- Input validation for all parameters
- Access control on admin functions
- Safe math operations (Solidity 0.8.x)
- Health factor checks before withdrawals
- Liquidation mechanism for underwater positions
- Comprehensive test coverage
- Fuzz testing for edge cases
# Start local node
anvil
# Deploy (in another terminal)
forge script script/Deploy.s.sol --rpc-url http://localhost:8545 --broadcast# Set private key
export PRIVATE_KEY=your_private_key_here
# Deploy to testnet
forge script script/Deploy.s.sol --rpc-url <RPC_URL> --broadcast --verify- Deposit ETH as collateral:
lending.deposit{value: 10 ether}();- Borrow MockDAI:
uint256 maxBorrow = lending.maxBorrowableDai(user);
lending.borrow(maxBorrow);- Check position health:
uint256 healthFactor = lending.healthFactor(user);
bool isLiquidatable = lending.isLiquidatable(user);- Repay debt:
lending.repay(repayAmount);- Withdraw collateral:
lending.withdraw(withdrawAmount);// Liquidate underwater position
lending.liquidate(user, debtAmount);- Interest Accrual: Add interest calculation and accrual mechanism
- Protocol Fees: Implement fee collection system
- Multiple Collaterals: Extend to support other assets
- Real Oracle: Replace MockPriceFeed with Chainlink
- Upgradeability: Add proxy pattern for upgradeable contracts
- Unit Tests: Test individual functions
- Integration Tests: Test complete workflows
- Fuzz Tests: Test with random inputs
- Invariant Tests: Test system invariants
- Gas Tests: Monitor gas usage
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass
- Run security analysis
- Submit a pull request
MIT License - see LICENSE file for details.
This is a prototype implementation for educational purposes. Not audited for production use. Use at your own risk.