iT邦幫忙

2023 iThome 鐵人賽

DAY 19
0
Web 3

Web3新手初探筆記系列 第 19

實作簡易DEX ep.2

  • 分享至 

  • xImage
  •  

準備階段(池子內代幣應傳入ERC20地址,目前以數字簡易代表)

設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);
    }
}

上一篇
實作簡易DEX ep.1
下一篇
實作簡易DEX ep.3
系列文
Web3新手初探筆記32
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言