본문 바로가기
DeFi 뿌수기

유니스왑 V2 해체하기 ERC-20 Contract

by gun_poo 2022. 4. 11.

UniswapV2ERC20.sol

pragma solidity =0.5.16;

import './interfaces/IUniswapV2ERC20.sol';
import './libraries/SafeMath.sol';
//유니스왑은 인터페이스를 자체 제작하여 ERC-20 인터페이스를 끌고오고있으며
// safemath 를 사용하여 언더플로우 오버플로우를 예방하고 있다

contract UniswapV2ERC20 is IUniswapV2ERC20 {
    using SafeMath for uint;
	// uint 자료형 오버플로우 방지
    
    
    string public constant name = 'Uniswap V2';
    string public constant symbol = 'UNI-V2';
    uint8 public constant decimals = 18;
    uint  public totalSupply;
    mapping(address => uint) public balanceOf;
    //주소와 총 잔액을 매핑하여 balanceOf 확인
    
    mapping(address => mapping(address => uint)) public allowance;
	// allowance는  입력한 두 개의 주소 값에 대한 _allowance 값 다시말해 owner가 spender에게 토큰을 등록한 양을 반환하는 함수이고 
    //여기서는 매핑 과정이다.
    
    bytes32 public DOMAIN_SEPARATOR;
    // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
    bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
    mapping(address => uint) public nonces;

    event Approval(address indexed owner, address indexed spender, uint value);
    event Transfer(address indexed from, address indexed to, uint value);

    constructor() public {
        uint chainId;
        //이더리움 네트워크 체인이 여러가지 존재하기에
        assembly {
            chainId := chainid
        }
        // assembly를 이용해 가스 비 절약, := 연산자로 초기화
        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, uint value) internal {
        totalSupply = totalSupply.add(value);
        balanceOf[to] = balanceOf[to].add(value);
        emit Transfer(address(0), to, value);
    }
    //토큰발행함수
    //민트 함수는 받을 사람의 주소와, 받을 토큰의 양을 받는다
    //총 공급량에 받을 토큰의 양을 더해주고
    //모든 잔액을 가진 배열이 balanceof 인데 받을 주소에 추가 공급량을 추가해준다
    // emit => event에 기록하는 용도

    function _burn(address from, uint value) internal {
        balanceOf[from] = balanceOf[from].sub(value);
        totalSupply = totalSupply.sub(value);
        emit Transfer(from, address(0), value);
    }
    //토큰소각함수
    //소각하는 주소와, 토큰의 양을 함수가 받고
    //민트 함수와 반대의 개념으로 이해하면 된다.
	
    function approve(address spender, uint value) external returns (bool) {
        _approve(msg.sender, spender, value);
        return true;
    }
    function _approve(address owner, address spender, uint value) private {
        allowance[owner][spender] = value;
        emit Approval(owner, spender, value);
    }
    //spender 에게 value 만큼의 토큰을 인출할 권리를 부여. 
    //이 함수를 이용할 때는 반드시 Approval 이벤트 함수를 호출해야 한다.
    //approve 는 spender 가 당신의 계정으로부터 amount 한도 하에서 여러 번 출금하는 것을 허용.
    //이 함수를 여러번 호출하면, 단순히 허용량을 amount 으로 재설정한다

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

    function transfer(address to, uint value) external returns (bool) {
        _transfer(msg.sender, to, value);
        return true;
    }
    // 전송함수
    // 호출이 정상적으로 완료 되었을 경우 Transfer event 발생
    //실행한 사람(sender)이 가진 토큰의 지갑에서 토큰을 개수만큼 빼고, 
    //받을 사람(recipient)의 토큰 지갑에 개수만큼 더해준다
    

    function transferFrom(address from, address to, uint value) external returns (bool) {
        if (allowance[from][msg.sender] != uint(-1)) {
        
         // uint256(-1)는 uint256의 max 값
         
            allowance[from][msg.sender] = allowance[from][msg.sender].sub(value);
        }
        _transfer(from, to, value);
        return true;
    }
    
    //transferFrom은 양도를 수행하는 거래 대행자(msg.sender)가 
    //from이 허락해준 값만큼 상대방(to)에게 토큰을 이동
    

    function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external {
        require(deadline >= block.timestamp, 'UniswapV2: EXPIRED');
        
        //require 문으로 한번 걸러주는데 
        //deadline을 블록타임스탬프로 체크한다. 블록타임 스탬프 시간이 데드라인을 초과 할 경우 UniswapV2: EXPIRED 출력된다.
        //UniswapV2: EXPIRED 는 메인넷 브로드캐스트 하는데 걸리는 시간이 오래걸리게 되면 나오는 결과
        
        bytes32 digest = keccak256(
            abi.encodePacked(
            //encode 방식 보다 간편하게 인코딩 하기위해 abi.encodePaced 함수를 사용
            //인수의 압축 인코딩 수행
                '\x19\x01',
                DOMAIN_SEPARATOR,
                keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))
            )
        );
        address recoveredAddress = ecrecover(digest, v, r, s);
        // 서명에서 주소를 복구하는 ecrecover 함수 사용
        // 서명 값인 v, r, s 값과 데이터 hash값을 ecrecover에 넣고 실행하면 데이터를 서명한 사용자의 주소 출력
        
        require(recoveredAddress != address(0) && recoveredAddress == owner, 'UniswapV2: INVALID_SIGNATURE');
        _approve(owner, spender, value);
    }
}

 

'DeFi 뿌수기' 카테고리의 다른 글

유니스왑 v2 백서 분석  (0) 2023.02.28
유니스왑 V2 해체하기 Factory Contract  (0) 2022.04.14
DeFi - UniSwap 리서치  (0) 2022.04.11

댓글