DeFi交易龙头之Uniswap:V2(核心合约-ERC20合约)

在uniV2中,关于erc20的合约其实有两部分,一个是erc20合约的接口部分,一个是接口合约对应的实现。、

IUniswapV2 ERC20解析

接口合约规定了需要实现的所有erc20标准方法,以下主要是接口合约的代码

post image

UniswapV2 ERC20解析

其实现代码如下:

pragma solidity =0.5.16;

import "./interfaces/IUniswapV2ERC20.sol";
import "./libraries/SafeMath.sol";

contract UniswapV2ERC20 is IUniswapV2ERC20 {
    using SafeMath for uint256;
    //token名称
    string public constant name = "Uniswap V2";
    //token缩写
    string public constant symbol = "UNI-V2";
    //token精度
    uint8 public constant decimals = 18;
    //累计流动性LP总量
    uint256 public totalSupply;
    //余额映射
    mapping(address => uint256) public balanceOf;
    //批准映射
    mapping(address => mapping(address => uint256)) public allowance;

    //域分割
    bytes32 public DOMAIN_SEPARATOR;
    // keccak256('Permit(address owner,address spender,uint value,uint nonce,uint deadline)');
    bytes32
        public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
    //nonces映射
    mapping(address => uint256) public nonces;

    //批准事件
    event Approval(
        address indexed owner,
        address indexed spender,
        uint256 value
    );
    //发送事件
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev 构造函数
     */
    constructor() public {
        uint256 chainId;
        // solium-disable-next-line
        assembly {
            chainId := chainid
        }
        //EIP712Domain
        DOMAIN_SEPARATOR = keccak256(
            abi.encode(
                keccak256(
                    "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
                ),
                keccak256(bytes(name)),
                keccak256(bytes("1")),
                chainId,
                address(this)
            )
        );
    }

    function _mint(address to, uint256 value) internal {
        totalSupply = totalSupply.add(value);
        balanceOf[to] = balanceOf[to].add(value);
        emit Transfer(address(0), to, value);
    }

    function _burn(address from, uint256 value) internal {
        balanceOf[from] = balanceOf[from].sub(value);
        totalSupply = totalSupply.sub(value);
        emit Transfer(from, address(0), value);
    }

    function _approve(
        address owner,
        address spender,
        uint256 value
    ) private {
        allowance[owner][spender] = value;
        emit Approval(owner, spender, value);
    }

    function _transfer(
        address from,
        address to,
        uint256 value
    ) private {
        balanceOf[from] = balanceOf[from].sub(value);
        balanceOf[to] = balanceOf[to].add(value);
        emit Transfer(from, to, value);
    }

    function approve(address spender, uint256 value) external returns (bool) {
        _approve(msg.sender, spender, value);
        return true;
    }

    function transfer(address to, uint256 value) external returns (bool) {
        _transfer(msg.sender, to, value);
        return true;
    }

    function transferFrom(
        address from,
        address to,
        uint256 value
    ) external returns (bool) {
        if (allowance[from][msg.sender] != uint256(-1)) {
            allowance[from][msg.sender] = allowance[from][msg.sender].sub(
                value
            );
        }
        _transfer(from, to, value);
        return true;
    }

    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external {
        // solium-disable-next-line security/no-block-members
        require(deadline >= block.timestamp, "UniswapV2: EXPIRED");
        bytes32 digest = keccak256(
            abi.encodePacked(
                "\x19\x01",
                DOMAIN_SEPARATOR,
                keccak256(
                    abi.encode(
                        PERMIT_TYPEHASH,
                        owner,
                        spender,
                        value,
                        nonces[owner]++,
                        deadline
                    )
                )
            )
        );
        address recoveredAddress = ecrecover(digest, v, r, s);
        require(
            recoveredAddress != address(0) && recoveredAddress == owner,
            "UniswapV2: INVALID_SIGNATURE"
        );
        _approve(owner, spender, value);
    }
}

以上我只调选核心的函数进行说明

铸币方法
铸币方法

铸币方法:主要是向某个地址发送一定数量的token

销毁方法和授权私有方法
销毁方法和授权私有方法

销毁方法: 目的是销毁某个地址所持有的token

授权私有方法:修改allowance对应的映射并发出event

_approve
_approve

私有 转账方法:其逻辑就是将from对应的balanceO减去value,to对应的balanceOf加上value,最后触发Transfer事件。

以上就是erc20的主要常用的方法,实际上UniswapV2 中并没有直接使用上述erc20合约,在智能合约中部署的是配对合约,而配对合约是继承自上述合约。

常见问题解答:

  1. 为什么在一些合约中要用call方法,而不是把合约的接口引入进去直接用该方法?

post image

主要原因是想获得某些函数的返回直接结果 来判断该方法执行是否执行成功!

而一些函数的实现并没有按照标准的erc20的要求返回一个是否成功的标识状态。

2. 有小伙伴问在工厂合约中uniswapV2Factory继承了IUniswapV2Factory接口,而IUniswapV2Factory中有八个未实现方法,而uniswapV2Factory中只实现四种方法,为什么没有全部实现接口方法呢?

post image
post image

是这样,在uniswapV2Factory中状态变量的类型是public类型,而public类型是自动会生成对应名称的getter函数。具体可参考这篇文章。

https://learnblockchain.cn/docs/solidity/contracts.html#getter-functions