# DeFi 課程 (Week2) ## Agenda 1. DeFi 簡介 2. Orderbook & AMM 3. Uniswap 介紹,解析 Uniswap 合約 4. 實作 AMM 5. Aave (Lending) 簡介 6. 補充 - 其他 AMM 公式 - Uniswap X - Solidty Example 7. 作業說明 ## DeFi 簡介 Lido, MakerDAO, Uniswap, Curve, Aave, Compound, Balancer... [DefiLlama](https://defillama.com/) ## Orderbook & AMM Orderbook 訂單簿交易 優點:效率高、無滑點 缺點:需造市商、操作較複雜 AMM 自動做市商 優點:操作容易、馬上成交 缺點:流動性低、高滑點 ### Uniswap (X * Y = K) ``` example: x: ETH y: USDT pool 有 10 個 ETH and 20,000 個 USDT, k = 200,000 -> 1 ETH = 2000 USDT swap 1 ETH 換 USDT (without fee) -> total ETH -> 1 + 10 = 11 -> 200,000 / 11 = 18,181.818... -> 可獲得 20,000 - 18,181.818... ~= 1818 USDT swap 1 ETH 換 USDT (with fee) -> total ETH -> 1 * 0.997 + 10 = 10.997 -> 200,000 / 10.997 = 18,186.778... -> 可獲得 20,000 - 18,186.778... ~= 1813 USDT ``` [Uniswap](https://app.uniswap.org/#/swap) [Uniswap v2 core github](https://github.com/Uniswap/v2-core) [Uniswap v2 periphery github](https://github.com/Uniswap/v2-periphery) Core * UniswapV2Factory * UniswapV2Pair * swap * mint * burn Periphery * UniswapV2Router * addLiquidity * removeLiquidity * swap...For... ### 實作AMM ```solidity= // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; contract AMM { IERC20 public immutable token0; IERC20 public immutable token1; uint256 public reserve0; uint256 public reserve1; uint256 public totalSupply; mapping(address => uint256) public balanceOf; constructor(address _token0, address _token1) { token0 = IERC20(_token0); token1 = IERC20(_token1); } function _mint(address _to, uint256 _amount) private { balanceOf[_to] += _amount; totalSupply += _amount; } function _burn(address _from, uint256 _amount) private { balanceOf[_from] -= _amount; totalSupply -= _amount; } function _update(uint256 _reserve0, uint256 _reserve1) private { reserve0 = _reserve0; reserve1 = _reserve1; } function swap(address _tokenIn, uint256 _amountIn) external returns (uint256 amountOut) { require( _tokenIn == address(token0) || _tokenIn == address(token1), "invalid token" ); require(_amountIn > 0, "amount in = 0"); // 判斷 token0, token1 順序 bool isToken0 = _tokenIn == address(token0); (IERC20 tokenIn, IERC20 tokenOut, uint256 reserveIn, uint256 reserveOut) = isToken0 ? (token0, token1, reserve0, reserve1) : (token1, token0, reserve1, reserve0); tokenIn.transferFrom(msg.sender, address(this), _amountIn); // 0.3% fee uint256 amountInWithFee = (_amountIn * 997) / 1000; // 透過 x * y = k 計算該換多少 amountOut = (reserveOut * amountInWithFee) / (reserveIn + amountInWithFee); tokenOut.transfer(msg.sender, amountOut); _update(token0.balanceOf(address(this)), token1.balanceOf(address(this))); } function addLiquidity(uint256 _amount0, uint256 _amount1) external returns (uint256 shares) { token0.transferFrom(msg.sender, address(this), _amount0); token1.transferFrom(msg.sender, address(this), _amount1); // 需添加等值流動性 if (reserve0 > 0 || reserve1 > 0) { require(reserve0 * _amount1 == reserve1 * _amount0, "x / y != dx / dy"); } // 第一次添加 if (totalSupply == 0) { // 流動性 token 數量計算 = √ (amount0 * amount1) shares = _sqrt(_amount0 * _amount1); } else { // 計算流動性 token 比例,精度問題取小的 shares = _min( (_amount0 * totalSupply) / reserve0, (_amount1 * totalSupply) / reserve1 ); } require(shares > 0, "shares = 0"); _mint(msg.sender, shares); _update(token0.balanceOf(address(this)), token1.balanceOf(address(this))); } function removeLiquidity( uint256 _shares ) external returns (uint256 amount0, uint256 amount1) { uint256 bal0 = token0.balanceOf(address(this)); uint256 bal1 = token1.balanceOf(address(this)); // 計算該取回的 token0 及 token1 數量 amount0 = (_shares * bal0) / totalSupply; amount1 = (_shares * bal1) / totalSupply; require(amount0 > 0 && amount1 > 0, "amount0 or amount1 = 0"); _burn(msg.sender, _shares); _update(bal0 - amount0, bal1 - amount1); token0.transfer(msg.sender, amount0); token1.transfer(msg.sender, amount1); } function _sqrt(uint256 y) private pure returns (uint256 z) { if (y > 3) { z = y; uint256 x = y / 2 + 1; while (x < z) { z = x; x = (y / x + x) / 2; } } else if (y != 0) { z = 1; } } function _min(uint256 x, uint256 y) private pure returns (uint256) { return x <= y ? x : y; } } interface IERC20 { function totalSupply() external view returns (uint256); function balanceOf(address account) external view returns (uint256); function transfer(address recipient, uint256 amount) external returns (bool); function allowance(address owner, address spender) external view returns (uint256); function approve(address spender, uint256 amount) external returns (bool); function transferFrom( address sender, address recipient, uint256 amount ) external returns (bool); event Transfer(address indexed from, address indexed to, uint256 amount); event Approval(address indexed owner, address indexed spender, uint256 amount); } ``` ## Lending ### AAVE https://app.aave.com/ - Over Collateral - Loan to Value (LTV) - Liquidation Threshold ![](https://i.imgur.com/x1aAznW.png =500x) - Utilization Rate ![](https://i.imgur.com/i8uWLzw.png =500x) - Health Factor ![](https://i.imgur.com/CeAVuPp.png =500x) Risk Parameters: https://docs.aave.com/risk/v/aave-v2/asset-risk/risk-parameters ## 補充資料 ### 不同的 AMM 公式 #### Curve: stableswap ![](https://i.imgur.com/Ykj7s0T.png =300x) ![](https://i.imgur.com/naBjEmc.png =500x) #### Balancer V: 常數、t:token種數、W:權重 ![](https://i.imgur.com/L2Xwl6W.png =500x) ex: t = 3 ![](https://i.imgur.com/BoFUK1V.png =500x) 假設 x = ETH, y = USDT, z = BTC 用 ETH 換 BTC ![](https://i.imgur.com/my8eepk.jpg =500x) Link:https://miguelmota.com/blog/understanding-stableswap-curve/ ### AAVE V3 & Layer2 https://github.com/aave/aave-v3-core/blob/master/contracts/protocol/pool/L2Pool.sol ### Permit - ERC20 permit https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/extensions/ERC20Permit.sol ### Uniswap X ![UniswapX-60eee10e73f923436d8858b6fdded078 (1)](https://hackmd.io/_uploads/rJGyNprDp.png =500x) ### Examples https://solidity-by-example.org/ ## 作業 ### 基本題 >用 HackMD 繳交作業。繳交作業時,請提供 HackMD 網址並貼到 [學員社群](https://courses.kryptocamp.dev/communities/groups/membership-program/) 的作業討論區交流 1. 部署自己的 AMM 合約 * 開源並提交合約地址 * 實作 swap, addLiquidity, removeLiquidity 提交截圖並說明各操作 2. 親身體驗 DeFi,選擇兩個前 20 大的 DeFi 協議操作,並詳細說明該協議的功能及特色 * 提交交易的 Tx hash 以及協議地址 * 寫下詳細的操作過程,例如:使用 Uniswap 將 0.1 ETH 換成 ... DAI * 說明該操作使用協議中智能合約的哪些 function 並說明其中程式碼共做了哪些鏈上狀態改變 ### 進階題 1. 實作一個整合 DeFi 協議的智能合約。例如串接 Uniswap 的 Router 將 tokenA 換成 tokenB 或串接 Aave 抵押 tokenA 借出 tokenB ...等等 * 描述該合約的功能以及串接哪個 DeFi 協議 * 使用 foundry or hardhat 完成測試確認成功串接合約