본문 바로가기
DeFi 뿌수기

유니스왑 V2 해체하기 Factory Contract

by gun_poo 2022. 4. 14.

한 토큰 페어 당 유일한 하나의 컨트렉트를 만드는 역할을 한다.

pool을 만드는 컨트렉트이다

 

우선 UniswapV2Factory가 상속하고 있는 인터페이스인 IUniswapV2Factory 인터페이스에 대해 알아보자

 

import './interfaces/IUniswapV2Factory.sol';

 

pragma solidity >=0.5.0;

interface IUniswapV2Factory {
    event PairCreated(address indexed token0, address indexed token1, address pair, uint);
	
    function feeTo() external view returns (address);
    //pool을 사용하는 유저가 수수료를 지불하는 주소
    function feeToSetter() external view returns (address);
	//feeTo를 설정할 수 있는 주소
    
    function getPair(address tokenA, address tokenB) external view returns (address pair);
    //getPiar 페어로 설정 된 두개의 토큰 컨트렉트 주소
    
    function allPairs(uint) external view returns (address pair);
    //파라미터로 들어온 토큰 주소 반환
    
    function allPairsLength() external view returns (uint);
    //전체 풀의 갯수

    function createPair(address tokenA, address tokenB) external returns (address pair);
	// 파라미터로 이뤄진 Poll을 만든다
    
    function setFeeTo(address) external;
    function setFeeToSetter(address) external;
}

 UniswapV2Factory.sol 뜯어보기

pragma solidity =0.5.16;

import './interfaces/IUniswapV2Factory.sol';
import './UniswapV2Pair.sol';

contract UniswapV2Factory is IUniswapV2Factory {
    address public feeTo;
    address public feeToSetter;
    //feeTo를 설정하는 관리자

    mapping(address => mapping(address => address)) public getPair;
    address[] public allPairs;
    //주소의 배열: 모든 pair contract 주소
	
    event PairCreated(address indexed token0, address indexed token1, address pair, uint);
	//event로 PairCreated를 기록한다
    
    constructor(address _feeToSetter) public {
        feeToSetter = _feeToSetter;
    }

    function allPairsLength() external view returns (uint) {
        return allPairs.length;
    }
    // allPairsLength: view 함수로 보아 모든 페어의 갯수를 호출하여 확인 가능하다.

    function createPair(address tokenA, address tokenB) external returns (address pair) {
       
       // createPair : 새로운 페어를 생성하는 함수, tokenA 주소와, tokenB의 함수를 받는다
       
       require(tokenA != tokenB, 'UniswapV2: IDENTICAL_ADDRESSES');
       
       //require로 한번 걸러준다, tokenA, tokenB의 주소가 다른지 확인하고
        
        (address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
        
        //주소 형태의 token0, token1을 생성하고 tokenA,B를 작은것 부터 대입한다.
        //ex) tokenA Address = 0, tokenB Address = 1 => token0 = tokenA, token1 = tokenB
        
        require(token0 != address(0), 'UniswapV2: ZERO_ADDRESS');
        
        // token0 의 주소 값이 zero address 인지 체크한다
        
        require(getPair[token0][token1] == address(0), 'UniswapV2: PAIR_EXISTS'); // single check is sufficient
        
        // IUniswapV2Factory의 getPair 함수에 token0, token1 값을 넣고 value가 존재하면 스왑에 존재하는 토큰 페어로 인색하여 실패한다. 
        // 처음으로 만들어지는 페어여야만 한다.
        
        bytes memory bytecode = type(UniswapV2Pair).creationCode;
        //import 한 UniswapV2pair의 byte code를 가져온다.
        
        bytes32 salt = keccak256(abi.encodePacked(token0, token1));
        assembly {
            pair := create2(0, add(bytecode, 32), mload(bytecode), salt)
        }
        
        // EIP 1014는 새로운 CREATE2 opcode를 추가하였고 CREATE2는 기존의 CREATE와 다른 방식으로 컨트렉트 주소를만든다
        //CREATE는 msg.sender의 address와 msg.sender의 nonce를 이용해서 컨트렉트 주소를 만들었다
        // keccak256(rlp([sender, nonce]))[12:]
        
        //CREATE2는 컨트렉트 주소를 만들때 4개의 값 (0xff, address, salt, init code)를 이용한다
        //keccak256(0xff ++ address ++ salt ++ keccak256(init_code))[12: ]
        
        //Create와 Create2의 차이점은 Create는 nonce를 사용하지만 CREATE2는 nonce를 사용하지 않는 다는 점이다
        //Create로 같은 주소의 컨트랙트를 새로 만들 수 없지만 Create2를 이용하면 같은 주소의 컨트랙트를 새로 만들 수 있다.
        //Create는 nonce를 이용하여 주소를 만드는데 메세지 콜을 하거나 컨트랙트를 생성 할 때 마다 논스가 1 증가 하기 때문에 Create로 컨트렉 주소를 만들면 항상 다른 주소를 생성한다.
        //Creat2는 nonce를 사용하지않고 0xff, address, salt, init code를 이용해 컨트렉 주소를 만드는데
        // 0xff, address, salt, init code을 미리 알고 있다면 같은 주소의 컨트렉트를 재배포 할 수 있다.
        
        // 0xff : CREATE 함수 충돌 방지 상수
        // address: senderAddress 보낸 사람 자신의 주소
        // salt : 발신자가 제공한 임의의 값
  		// bytecode : 배포할 계약의 바이트 코드
        
        // 오? 컨트렉 수정이 가능한건가??
        //아니다 Create or Create2로 생성된 컨트랙은 nonce가 1로 set 된다. 
        // 컨트렉을 생성할 때 해당 주소의 논스가 0인지 검사하고 0이 아니면 revert한다. 그렇기에 한번 배포된 컨트렉은 
        // 같은 주소로 재배포 될 수 없다.
        // 해당 컨트렉트가 selfdestrurct 함수 호출로 블록체인에서 삭제된 경우 create2를 이용해 
        // 삭제된 컨트렉트와 같은 주소로 재배포가 가능하다!!
        // 장점은 Create2의 주소를 오프체인에서 미리 계산할 수 있고 계약이 온체인에 배포되기 전에
        // 미리 계산된 계약 주소로 ETH를 보낼 수 있다 라는 점이다
        
        IUniswapV2Pair(pair).initialize(token0, token1);
        getPair[token0][token1] = pair;
        getPair[token1][token0] = pair; // populate mapping in the reverse direction
        //뭘 먼저 대입하여도 같은 값이 나오도록 설정해준다.
        
        allPairs.push(pair);
        
        //allPairs 배열에 새로 만들어진 주소를 push 한다.
        

        emit PairCreated(token0, token1, pair, allPairs.length);
        // PairCreated 이벤트를 emit
    }

    function setFeeTo(address _feeTo) external {
        require(msg.sender == feeToSetter, 'UniswapV2: FORBIDDEN');
        feeTo = _feeTo;
    }

    function setFeeToSetter(address _feeToSetter) external {
        require(msg.sender == feeToSetter, 'UniswapV2: FORBIDDEN');
        feeToSetter = _feeToSetter;
    }
}

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

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

댓글