設reserve0和reserve1是兩個池子的代幣量
uint public reserve0;
uint public reserve1;
LP token的ERC20實作,為簡略直接引用openzeppelin
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract LPtoken is ERC20{
address public owner;
modifier onlyOwner(){
require(msg.sender == owner, "Only owner can use");
_;
}
constructor() ERC20("lptoken", "lpt"){
owner = msg.sender;
}
function mint(address account,uint amount) public onlyOwner{
_mint(account,amount);
}
function burn(address account,uint amount) public onlyOwner{
_burn(account,amount);
}
}
在部署合約時生成一個ERC20
constructor(){
lpToken = new LPtoken();
}
簡易開根號函式(準確度可能不夠)
function _sqrt(uint y) private pure returns (uint z) {
if (y > 3) {
z = y;
uint x = y / 2 + 1;
while (x < z) {
z = x;
x = (y / x + x) / 2;
}
} else if (y != 0) {
z = 1;
}
}
//兌換
function swap0To1 (uint _amount0) external returns (uint _amount1){
_amount1 = _amount0 * reserve1 / (_amount0 + reserve0);
reserve0 += _amount0;
reserve1 -= _amount1;
}
function swap1To0 (uint _amount1) external returns (uint _amount0){
_amount0 = _amount1 * reserve0 / (_amount1 + reserve1);
reserve0 -= _amount0;
reserve1 += _amount1;
}
先限制投入比例需和池子內相同,如為空則不限制
接著根據投入狀態決定發行LP代幣的公式
//投入流動性
function addLiquidity (uint _amount0, uint _amount1) external returns (uint shares){
if(reserve0 > 0 || reserve1 >0){
require(_amount1 * reserve0 == _amount0 *reserve1,"Investment is not proportional");
}
uint totalSupply = lpToken.totalSupply();
if(totalSupply == 0){
shares = _sqrt(_amount0 * _amount1);
}else{
shares = (_amount0 * totalSupply)/reserve0;
}
reserve0 += _amount0;
reserve1 += _amount1;
lpToken.mint(msg.sender,shares);
return shares;
}
//減少流動性
function minusLiquidity (uint _amountLp) external returns (uint _amount0, uint _amount1){
uint totalSupply = lpToken.totalSupply();
lpToken.burn(msg.sender,_amountLp);
_amount0 = (_amountLp * reserve0) / totalSupply;
_amount1 = (_amountLp * reserve1) / totalSupply;
reserve0 -= _amount0;
reserve1 -= _amount1;
}
地址生成過程
//示範如何計算地址
function createLptokenAddress(bytes32 _salt) internal view returns(address) {
bytes memory bytecode = getBytecode();
bytes32 hash = keccak256(
abi.encodePacked(
bytes1(0xff), address(this), _salt, keccak256(bytecode)
)
);
return address(uint160(uint(hash)));
}
function getBytecode() internal pure returns(bytes memory){
bytes memory bytecode = type(LPtoken).creationCode;
return bytecode;
}
每建立一個新的池子對就生成一個新的ERC20。
contract LptokenFactory{
mapping (address => mapping(address => address)) public LptoakePair;
address[] public LptokenAddressList;
LPtoken public lpToken;
address public LPtokenAddress;
address public funcLPtokenAddress;
function createLptokenPair(address token0, address token1) public {
bytes32 _salt = keccak256(
abi.encodePacked(
token0, token1
)
);
lpToken = new LPtoken{
salt:bytes32(_salt)
}();
//兩者地址相同會相等
LPtokenAddress = address(lpToken);
funcLPtokenAddress = createLptokenAddress(_salt);
//添加地址
LptoakePair[token0][token1] = LPtokenAddress;
LptoakePair[token1][token0] = LPtokenAddress;
LptokenAddressList.push(LPtokenAddress);
}
}