智能合约开发DeFi 协议开发
进阶3-4周
DeFi 协议开发
实现去中心化金融核心协议,包括 DEX、借贷、质押等
🎯 学习要点
理解 AMM x*y=k 恒定乘积公式
掌握流动性池与 LP Token 机制
实现抵押借贷与清算逻辑
处理收益计算与分配算法
📚 课程章节
AMM 自动做市商原理3小时
Uniswap V2 简化实现4小时
借贷协议(Compound 风格)5小时
质押与收益分配3小时
代码示例
简化版 Uniswap AMM
实现了 Uniswap 风格的 AMM,包含流动性管理和代币交换
solidity
1// SPDX-License-Identifier: MIT2pragma solidity ^0.8.20;3 4import "@openzeppelin/contracts/token/ERC20/IERC20.sol";5import "@openzeppelin/contracts/token/ERC20/ERC20.sol";6 7/**8 * @title SimpleAMM9 * @dev 实现基于 x*y=k 的自动做市商10 */11contract SimpleAMM is ERC20 {12 IERC20 public immutable token0;13 IERC20 public immutable token1;14 15 uint256 public reserve0;16 uint256 public reserve1;17 18 event Mint(address indexed sender, uint256 amount0, uint256 amount1);19 event Burn(address indexed sender, uint256 amount0, uint256 amount1);20 event Swap(21 address indexed sender,22 uint256 amount0In,23 uint256 amount1In,24 uint256 amount0Out,25 uint256 amount1Out26 );27 28 constructor(address _token0, address _token1) ERC20("LP Token", "LP") {29 token0 = IERC20(_token0);30 token1 = IERC20(_token1);31 }32 33 /**34 * @dev 添加流动性35 */36 function addLiquidity(uint256 amount0, uint256 amount1) external returns (uint256 liquidity) {37 token0.transferFrom(msg.sender, address(this), amount0);38 token1.transferFrom(msg.sender, address(this), amount1);39 40 uint256 _totalSupply = totalSupply();41 42 if (_totalSupply == 0) {43 // 初始流动性44 liquidity = sqrt(amount0 * amount1);45 } else {46 // 按比例铸造47 liquidity = min(48 (amount0 * _totalSupply) / reserve0,49 (amount1 * _totalSupply) / reserve150 );51 }52 53 require(liquidity > 0, "Insufficient liquidity minted");54 _mint(msg.sender, liquidity);55 56 _update(57 token0.balanceOf(address(this)),58 token1.balanceOf(address(this))59 );60 61 emit Mint(msg.sender, amount0, amount1);62 }63 64 /**65 * @dev 移除流动性66 */67 function removeLiquidity(uint256 liquidity) external returns (uint256 amount0, uint256 amount1) {68 uint256 balance0 = token0.balanceOf(address(this));69 uint256 balance1 = token1.balanceOf(address(this));70 uint256 _totalSupply = totalSupply();71 72 amount0 = (liquidity * balance0) / _totalSupply;73 amount1 = (liquidity * balance1) / _totalSupply;74 75 require(amount0 > 0 && amount1 > 0, "Insufficient liquidity burned");76 77 _burn(msg.sender, liquidity);78 token0.transfer(msg.sender, amount0);79 token1.transfer(msg.sender, amount1);80 81 _update(82 token0.balanceOf(address(this)),83 token1.balanceOf(address(this))84 );85 86 emit Burn(msg.sender, amount0, amount1);87 }88 89 /**90 * @dev 交换代币91 * @param amount0Out 要换出的 token0 数量92 * @param amount1Out 要换出的 token1 数量93 */94 function swap(uint256 amount0Out, uint256 amount1Out, address to) external {95 require(amount0Out > 0 || amount1Out > 0, "Insufficient output amount");96 require(amount0Out < reserve0 && amount1Out < reserve1, "Insufficient liquidity");97 98 if (amount0Out > 0) token0.transfer(to, amount0Out);99 if (amount1Out > 0) token1.transfer(to, amount1Out);100 101 uint256 balance0 = token0.balanceOf(address(this));102 uint256 balance1 = token1.balanceOf(address(this));103 104 uint256 amount0In = balance0 > reserve0 - amount0Out 105 ? balance0 - (reserve0 - amount0Out) 106 : 0;107 uint256 amount1In = balance1 > reserve1 - amount1Out 108 ? balance1 - (reserve1 - amount1Out) 109 : 0;110 111 require(amount0In > 0 || amount1In > 0, "Insufficient input amount");112 113 // 恒定乘积检查(扣除 0.3% 手续费)114 uint256 balance0Adjusted = (balance0 * 1000) - (amount0In * 3);115 uint256 balance1Adjusted = (balance1 * 1000) - (amount1In * 3);116 117 require(118 balance0Adjusted * balance1Adjusted >= reserve0 * reserve1 * (1000**2),119 "K value decreased"120 );121 122 _update(balance0, balance1);123 emit Swap(msg.sender, amount0In, amount1In, amount0Out, amount1Out);124 }125 126 function _update(uint256 balance0, uint256 balance1) private {127 reserve0 = balance0;128 reserve1 = balance1;129 }130 131 function sqrt(uint256 y) internal pure returns (uint256 z) {132 if (y > 3) {133 z = y;134 uint256 x = y / 2 + 1;135 while (x < z) {136 z = x;137 x = (y / x + x) / 2;138 }139 } else if (y != 0) {140 z = 1;141 }142 }143 144 function min(uint256 x, uint256 y) internal pure returns (uint256) {145 return x < y ? x : y;146 }147}借贷协议核心逻辑
实现了借贷协议核心功能:抵押、借款、还款、清算和利息计算
solidity
1// SPDX-License-Identifier: MIT2pragma solidity ^0.8.20;3 4import "@openzeppelin/contracts/token/ERC20/IERC20.sol";5import "@openzeppelin/contracts/access/Ownable.sol";6 7/**8 * @title SimpleLending9 * @dev 实现基础借贷协议,支持抵押借款和清算10 */11contract SimpleLending is Ownable {12 IERC20 public collateralToken; // 抵押品13 IERC20 public borrowToken; // 借款代币14 15 uint256 public constant COLLATERAL_RATIO = 150; // 150% 抵押率16 uint256 public constant LIQUIDATION_THRESHOLD = 120; // 120% 清算阈值17 uint256 public constant INTEREST_RATE = 5; // 5% 年利率18 19 struct Position {20 uint256 collateralAmount;21 uint256 borrowedAmount;22 uint256 lastUpdateTime;23 }24 25 mapping(address => Position) public positions;26 uint256 public totalBorrowed;27 28 event Deposit(address indexed user, uint256 amount);29 event Borrow(address indexed user, uint256 amount);30 event Repay(address indexed user, uint256 amount);31 event Withdraw(address indexed user, uint256 amount);32 event Liquidate(address indexed liquidator, address indexed borrower, uint256 amount);33 34 constructor(address _collateral, address _borrow) Ownable(msg.sender) {35 collateralToken = IERC20(_collateral);36 borrowToken = IERC20(_borrow);37 }38 39 /**40 * @dev 存入抵押品41 */42 function deposit(uint256 amount) external {43 collateralToken.transferFrom(msg.sender, address(this), amount);44 positions[msg.sender].collateralAmount += amount;45 emit Deposit(msg.sender, amount);46 }47 48 /**49 * @dev 借款50 */51 function borrow(uint256 amount) external {52 Position storage pos = positions[msg.sender];53 _accrueInterest(msg.sender);54 55 uint256 maxBorrow = (pos.collateralAmount * 100) / COLLATERAL_RATIO;56 require(pos.borrowedAmount + amount <= maxBorrow, "Exceeds borrow limit");57 58 pos.borrowedAmount += amount;59 totalBorrowed += amount;60 61 borrowToken.transfer(msg.sender, amount);62 emit Borrow(msg.sender, amount);63 }64 65 /**66 * @dev 还款67 */68 function repay(uint256 amount) external {69 Position storage pos = positions[msg.sender];70 _accrueInterest(msg.sender);71 72 uint256 repayAmount = amount > pos.borrowedAmount ? pos.borrowedAmount : amount;73 74 borrowToken.transferFrom(msg.sender, address(this), repayAmount);75 pos.borrowedAmount -= repayAmount;76 totalBorrowed -= repayAmount;77 78 emit Repay(msg.sender, repayAmount);79 }80 81 /**82 * @dev 提取抵押品83 */84 function withdraw(uint256 amount) external {85 Position storage pos = positions[msg.sender];86 _accrueInterest(msg.sender);87 88 require(amount <= pos.collateralAmount, "Insufficient collateral");89 90 // 检查健康因子91 uint256 newCollateral = pos.collateralAmount - amount;92 uint256 maxBorrow = (newCollateral * 100) / COLLATERAL_RATIO;93 require(pos.borrowedAmount <= maxBorrow, "Would be undercollateralized");94 95 pos.collateralAmount = newCollateral;96 collateralToken.transfer(msg.sender, amount);97 98 emit Withdraw(msg.sender, amount);99 }100 101 /**102 * @dev 清算不健康头寸103 */104 function liquidate(address borrower) external {105 Position storage pos = positions[borrower];106 _accrueInterest(borrower);107 108 uint256 healthFactor = (pos.collateralAmount * 100) / pos.borrowedAmount;109 require(healthFactor < LIQUIDATION_THRESHOLD, "Position is healthy");110 111 // 清算人偿还债务112 uint256 debtToRepay = pos.borrowedAmount;113 borrowToken.transferFrom(msg.sender, address(this), debtToRepay);114 115 // 清算人获得抵押品(带有清算奖励)116 uint256 liquidationBonus = pos.collateralAmount / 10; // 10% 奖励117 uint256 totalReward = pos.collateralAmount + liquidationBonus;118 119 collateralToken.transfer(msg.sender, totalReward);120 121 totalBorrowed -= debtToRepay;122 delete positions[borrower];123 124 emit Liquidate(msg.sender, borrower, debtToRepay);125 }126 127 /**128 * @dev 计算利息129 */130 function _accrueInterest(address user) internal {131 Position storage pos = positions[user];132 if (pos.borrowedAmount == 0) return;133 134 uint256 timeElapsed = block.timestamp - pos.lastUpdateTime;135 uint256 interest = (pos.borrowedAmount * INTEREST_RATE * timeElapsed) / (365 days * 100);136 137 pos.borrowedAmount += interest;138 pos.lastUpdateTime = block.timestamp;139 }140 141 /**142 * @dev 查询健康因子143 */144 function getHealthFactor(address user) external view returns (uint256) {145 Position memory pos = positions[user];146 if (pos.borrowedAmount == 0) return type(uint256).max;147 return (pos.collateralAmount * 100) / pos.borrowedAmount;148 }149}