한 토큰 페어 당 유일한 하나의 컨트렉트를 만드는 역할을 한다.
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 |
댓글