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
Nguồn: viblo.asia