ADD LIQUIDITY I.Luồng thực hiện function AddLiquidity Các bước để add liquidity : Approve số lượng token tự tạo cho phép router sử dụng . Có thể approve trên testnet.bscscan hoặc remix. Các coin như ETH,BTC,DAI… thì không cần approve. Approve trên testnet.bscscan thì cần verify contract trước.
Gọi hàm addLiquidity
1._addLiquidity: tính lượng token được add vào liquidity 2.safeTransferFrom: chuyển token an toàn vào địa chỉ to 3.pairFor: lấy địa chỉ LP token 4.mint: trả về địa chỉ to lượng LP token mint ra sau khi add liquidity với cặp token
STT Input Nội dung Input 1 address tokenA Địa chỉ token A 2 address tokenB Địa chỉ token B 3 uint amountADesired Số lượng token A muốn add liquidity 4 uint amountBDesired Số lượng token B muốn add liquidity 5 uint amountAMin Số lượng token A tối thiểu được add vào liquidity 6 uint amountBMin Số lượng token B tối thiểu được add vào liquidity 7 address to Địa chỉ nhận liquidity token trả về 8 uint deadline Thời gian tối đa thực hiện function STT Output Nội dung Output 1 uint amountA Số lượng token A được add vào liquidity 2 uint amountB Số lượng token B được add vào liquidity 3 uint liquidity Số lượng LP token được trả về ví
II.Nội dung function AddLiquidity: (amountA, amountB) = _addLiquidity(tokenA, tokenB, amountADesired, amountBDesired, amountAMin, amountBMin);
Gọi hàm _addLiquidity trả về kết quả là số lượng token A và token B trong pool vừa tạo gán trong 2 biến amountA và amount.(*) address pair = PancakeLibrary.pairFor(factory, tokenA, tokenB);
Địa chỉ LP token trả về gán vào biến pair TransferHelper.safeTransferFrom(tokenA, msg.sender, pair, amountA); TransferHelper.safeTransferFrom(tokenB, msg.sender, pair, amountB); Chuyển số lượng token A là amountA, token B với số lượng amountB từ địa chỉ đang gọi hàm đến địa chỉ LP token vừa tạo ra ở trên liquidity = IPancakePair(pair).mint(to); Gọi hàm mint trong PancakePair để tạo LP token với số lượng lưu trong biến liquidity và chuyển tới địa chỉ ví là to
III.Nội dung chi tiết các hàm liên quan: library PancakeLibrary
sortTokens : Sắp xếp token STT Input Nội dung Input 1 address tokenA Địa chỉ token A 2 address tokenB Địa chỉ token B STT Output Nội dung Output 1 address token0 Địa chỉ thứ 1 sau khi sắp xếp 2 address token1 Địa chỉ thứ 2 sau khi sắp xếp
Nôi dung function: require(tokenA != tokenB, 'PancakeLibrary: IDENTICAL_ADDRESSES'); kiểm tra yêu cầu 2 token phải khác nhau nếu không báo lỗi (token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA); So sánh thứ tự địa chỉ token theo a-z,a-9 rồi sắp xếp và gán vào biến token 0 và token1 require(token0 != address(0), 'PancakeLibrary: ZERO_ADDRESS'); kiểm tra yêu cầu địa chỉ thứ 1 sau sắp xếp có tồn tại hại không, nếu không báo lỗi
pairFor: lấy địa chỉ LP token STT Input Nội dung Input 1 address factory Địa chỉ contract factory 2 address tokenA Địa chỉ token A 3 address tokenB Địa chỉ token B STT Output Nội dung Output 1 address pair Địa chỉ của LP token
Nôi dung function: Tính toán ra địa chỉ của LP token mà không phải gọi hàm từ ngoài vào giúp tiếp kiệm gas
getReserve: lấy số dư Token STT Input Nội dung Input 1 address factory Địa chỉ contract factory 2 address tokenA Địa chỉ token A 3 address tokenB Địa chỉ token B STT Output Nội dung Output 1 uint reserveA Số dư token A 2 uint reserveB Số dư token B
Nôi dung function: (address token0,) = sortTokens(tokenA, tokenB); Sắp xếp 2 token pairFor(factory, tokenA, tokenSăpB); Tính địa chỉ LP token (uint reserve0, uint reserve1,) = IPancakePair(pairFor(factory, tokenA, tokenB)).getReserves(); Lấy số dư của 2 token (reserveA, reserveB) = tokenA == token0 ? (reserve0, reserve1) : (reserve1, reserve0); Sắp xếp thứ tự 2 số dư này tương ứng với thứ tự token đã sắp xếp
quote STT Input Nội dung Input 1 uint amountA Số lượng tokenA cần qui đổi 2 uint reserveA Số dư token A 3 uint reserveB Số dư token B STT Output Nội dung Output 1 uint amountB Số dư token B qui đổi ra
Nôi dung function: require(amountA > 0, 'PancakeLibrary: INSUFFICIENT_AMOUNT'); require(reserveA > 0 && reserveB > 0, 'PancakeLibrary: INSUFFICIENT_LIQUIDITY'); Điều kiện yêu cầu số lượng token A nhập vào >0, số dư 2 token trong ví>0 amountB = amountA.mul(reserveB) / reserveA; Tính ra số lượng token B theo công thức PancakeRouter _addLiquidity: tính lượng token được add vào liquidity STT Input Nội dung Input 1 address tokenA Địa chỉ token A 2 address tokenB Địa chỉ token B 3 uint amountADesired Số lượng token A muốn add liquidity 4 uint amountBDesired Số lượng token B muốn add liquidity 5 uint amountAMin Số lượng token A tối thiểu được add vào liquidity 6 uint amountBMin Số lượng token B tối thiểu được add vào liquidity STT Output Nội dung Output 1 uint amountA Số lượng token A được add vào liquidity 2 uint amountB Số lượng token B được add vào liquidity
Nội dung function:
if (IPancakeFactory(factory).getPair(tokenA, tokenB) == address(0)) { IPancakeFactory(factory).createPair(tokenA, tokenB);
Kiểm tra xem token A và token B đã được tạo pair hay chưa nếu chưa sẽ gọi hàm createPair ở contract factory để tạo pair (uint reserveA, uint reserveB) = PancakeLibrary.getReserves(factory, tokenA, tokenB); Lấy số dư của 2 token if (reserveA == 0 && reserveB == 0) { (amountA, amountB) = (amountADesired, amountBDesired); Nếu cả 2 token có số dư =0 thì hàm trả ra số lượng token được add liquidity bằng số lượng input đã nhập } else { uint amountBOptimal = PancakeLibrary.quote(amountADesired, reserveA, reserveB); if (amountBOptimal <= amountBDesired) { require(amountBOptimal >= amountBMin, 'PancakeRouter: INSUFFICIENT_B_AMOUNT'); (amountA, amountB) = (amountADesired, amountBOptimal); Nếu ít nhất 1 token có số dư khác 0 thì tính khối lượng qui đổi số lượng tokenB tối ưu từ số lượng tokenA mong muốn. Nếu kết quả nhỏ hơn số lượng tokenB mong muốn và không nhỏ hơn số lượng tokenB tối thiểu thì số lượng tokenB tối ưu này được add liquidity cùng với số lượng tokenA đã nhập vào } else { uint amountAOptimal = PancakeLibrary.quote(amountBDesired, reserveB, reserveA); assert(amountAOptimal <= amountADesired); require(amountAOptimal >= amountAMin, 'PancakeRouter: INSUFFICIENT_A_AMOUNT'); (amountA, amountB) = (amountAOptimal, amountBDesired); } Nếu không thì tính tiếp số lượng tokenA tối ưu qui đổi từ số lượng tokenB mong muốn. Kiểm tra xem kết quả trả ra phải nhỏ hơn số lượng token Among muốn nếu không sẽ revert Kiểm tra tiếp yêu cầu kết quả trả ra phải lớn hơn số lượng tokenA tối thiểu thì revert và báo lỗi. Sau đó thì số tokenA tối ưu này sẽ được add liquidity cùng với số lượng tokenB đã nhập vào library TransferHelper safeTransferFrom: chuyển token an toàn STT Input Nội dung Input 1 address token Địa chỉ token 2 address from Địa chỉ chuyển token 3 address to Địa chỉ nhận token 4 uint256 value Số lượng token chuyển STT Output Nội dung Output 1 uint amountA Số lượng token A được add vào liquidity 2 uint amountB Số lượng token B được add vào liquidity
Nội dung function:
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value)); require( success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper::transferFrom: transferFrom failed' );
Gọi hàm chuyển token transfer qua giao thức call địa chị token . Hàm trả ra 2 kết quả bao gồm success là true nếu gọi hàm call thành công, false nếu không gọi được hàm và data là dữ liệu dạng bytes Kiểm tra yêu cầu phải gọi hàm thành công và không có dữ liệu data trả về hoặc decode data trả về true , nếu không sẽ báo lỗi và revert
PancakePair 4.1 mint : trả về địa chỉ to lượng LP token mint ra sau khi add liquidity với cặp token STT Input Nội dung Input 1 address to Địa chỉ nhận LP token STT Output Nội dung Output 1 uint liquidity Số lượng LP token được mint ra
Nội dung function:
(uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings uint balance0 = IERC20(token0).balanceOf(address(this)); uint balance1 = IERC20(token1).balanceOf(address(this)); uint amount0 = balance0.sub(_reserve0); uint amount1 = balance1.sub(_reserve1); bool feeOn = _mintFee(_reserve0, _reserve1); uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee
Lấy số dư trước khi add liquidity của 2 token trong pool Lấy số dư sau khi add liquidity của 2 token trong pool Gọi hàm _mintFee để mint ra lượng LP token tương đương lượng phí 0,03% chuyển vào vào địa chỉ treasury của Pancakeswap chính là địa chỉ feeTo đã set trước đó (Phí pancakeswap đang tính là 0.25% khối lượng giao dịch trong đó 0,17% phân phối cho các liquidity provider, 0,05% pancakeswap sẽ mua lại và burn,0,03% sẽ đi vào quỹ treasury để phát triển nền tảng). Để tiết kiệm gas thì phí này chỉ tính khi liquidity provider add hoặc remove liquidity if (_totalSupply == 0) { liquidity = Math.sqrt(amount0.mul(amount1)).sub(MINIMUM_LIQUIDITY); _mint(address(0), MINIMUM_LIQUIDITY); // permanently lock the first MINIMUM_LIQUIDITY tokens } else { liquidity = Math.min(amount0.mul(_totalSupply) / _reserve0, amount1.mul(_totalSupply) / _reserve1); } Tính lượng LP token: Nếu là lần đầu add liquidity của cặp token thì tính số lượng LP token như công thức Lượng LP token tối thiểu được mint rồi khóa vĩnh viễn ở địa chỉ address(0) để tránh phép chia cho 0 Nếu cặp token đã được add liquidity trước đó thì số lượng LP token sẽ lấy theo số nhỏ hơn như là sự trừng phạt dành cho liquidity provider nếu cố tình add liquidity theo tỷ lệ không giống như tỷ lệ sẵn có trong pool require(liquidity > 0, 'Pancake: INSUFFICIENT_LIQUIDITY_MINTED'); _mint(to, liquidity);
_update(balance0, balance1, _reserve0, _reserve1); if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date
if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date Kiểm tra điều kiện LP token được tính ra ra phải >0 nếu không báo lỗi mint không thành công Gọi hàm _mint để mint LP token theo số lượng đã tính ở trên và chuyển về địa chỉ ví to Update số dư của 2 token trong pool Update k
REMOVE LIQUIDITY
I.Luồng thực hiện function RemoveLiquidity
Các bước để remove liquidity : Approve số lượng token tự tạo và LP token cho phép router sử dụng . Có thể approve trên testnet.bscscan hoặc remix. Các coin như ETH,BTC,DAI… thì không cần approve. Approve trên testnet.bscscan thì cần verify contract trước.
Gọi hàm RemoveLiquidity
1.pairFor: lấy địa chỉ LP token của cặp token 2.transferFrom:chuyển từ địa chỉ ví của người gọi hàm lượng LP token tới địa chỉ LP token vừa tìm được ở trên 3.burn: đốt lượng LP token và nhận về lượng 2 token tương ứng của cặp token 4.sortTokens:sắp xếp token
STT Input Nội dung Input 1 address tokenA Địa chỉ token A 2 address tokenB Địa chỉ token B 3 uint liquidity Số lượng LP token A muốn burn 4 uint amountAMin Số lượng token A tối thiểu được trả về ví 5 uint amountBMin Số lượng token B tối thiểu được trả về ví 6 address to Địa chỉ nhận liquidity token trả về 7 uint deadline Thời gian tối đa thực hiện function STT Output Nội dung Output 1 uint amountA Số lượng token A trả về ví 2 uint amountB Số lượng token B trả về ví
, II.Nội dung function RemoveLiquidity: Nội dung function: address pair = PancakeLibrary.pairFor(factory, tokenA, tokenB); IPancakePair(pair).transferFrom(msg.sender, pair, liquidity); // send liquidity to pair (uint amount0, uint amount1) = IPancakePair(pair).burn(to); (address token0,) = PancakeLibrary.sortTokens(tokenA, tokenB); (amountA, amountB) = tokenA == token0 ? (amount0, amount1) : (amount1, amount0); require(amountA >= amountAMin, 'PancakeRouter: INSUFFICIENT_A_AMOUNT'); require(amountB >= amountBMin, 'PancakeRouter: INSUFFICIENT_B_AMOUNT');
Lấy địa chỉ LP token của cặp token Chuyển từ địa chỉ ví của người gọi hàm lượng LP token tới địa chỉ LP token vừa tìm được ở trên Đốt lượng LP token và nhận về lượng 2 token tương ứng của cặp token Sắp xếp 2 token Sắp xếp thứ tự của số lượng token trả về tương ứng với thứ tự token sau khi sắp xếp Kiểm tra yêu cầu cả 2 lượng token trả về phải lớn hơn số tối thiểu đã nhập vào nếu không sẽ revert và báo lỗi
III.Nội dung chi tiết các hàm liên quan: PancakeLibrary pairFor: ở trên
PancakePair TransferFrom: chuyển token STT Input Nội dung Input 1 address from Địa chỉ chuyển token 2 address to Địa chỉ nhận token 3 uint256 value Số lượng token chuyển STT Output Nội dung Output 1 bool trả về giá trị true nếu chuyển thành công
Nội dung function: function transferFrom(address from, address to, uint value) external returns (bool) { if (allowance[from][msg.sender] != uint(-1)) { allowance[from][msg.sender] = allowance[from][msg.sender].sub(value); } _transfer(from, to, value); return true; -kiểm tra nếu allowance(số token được sử dụng tối đa) chưa đạt giá trị tối đa thì update lại allowance sau khi trừ đi số token muốn chuyển -chuyển token trả về giá trị true nếu chuyển thành công Burn: trả về địa chỉ to 2 token sau khi burn LP token STT Input Nội dung Input 1 address to Địa chỉ nhận token trả về STT Output Nội dung Output 1 uint amountA Số lượng token A trả về ví 2 uint amountB Số lượng token B trả về ví
Nội dung function: (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings address _token0 = token0; // gas savings address _token1 = token1; // gas savings uint balance0 = IERC20(_token0).balanceOf(address(this)); uint balance1 = IERC20(_token1).balanceOf(address(this)); uint liquidity = balanceOf[address(this)];
bool feeOn = _mintFee(_reserve0, _reserve1);
Lấy số dư trước khi remove liquidity của 2 token trong pool Lấy số dư sau khi remove liquidity của 2 token trong pool Lấu số lượng LP token trong contract Gọi hàm _mintFee để mint ra lượng LP token tương đương lượng phí 0,03% chuyển vào vào địa chỉ treasury của Pancakeswap chính là địa chỉ feeTo đã set trước đó uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee amount0 = liquidity.mul(balance0) / _totalSupply; // using balances ensures pro-rata distribution amount1 = liquidity.mul(balance1) / _totalSupply; // using balances ensures pro-rata distribution require(amount0 > 0 && amount1 > 0, 'Pancake: INSUFFICIENT_LIQUIDITY_BURNED'); Tính số lượng 2 token sẽ trả về ví Kiểm tra yêu cầu 2 số tính ra phải >0 nếu không báo lỗi burn không thành công _burn(address(this), liquidity); _safeTransfer(_token0, to, amount0); _safeTransfer(_token1, to, amount1); Gọi hàm _burn để update số dư và tổng cung LP token trong contract sau khi trừ đi lượng LP token cần burn Chuyển 2 token về địa chỉ ví to theo số lượng đã tính ở trên balance0 = IERC20(_token0).balanceOf(address(this)); balance1 = IERC20(_token1).balanceOf(address(this));
_update(balance0, balance1, _reserve0, _reserve1); if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date
update lại số dư 2 token trong contract, update k PancakeLibrary 3.1 sortTokens: ở trên