본문 바로가기

:D/block chain

UNISWAP 4 : 유니스왑 V2 코드 분석(1)

V2-core github
 

GitHub - Uniswap/v2-core: 🎛 Core smart contracts of Uniswap V2

🎛 Core smart contracts of Uniswap V2. Contribute to Uniswap/v2-core development by creating an account on GitHub.

github.com


Uniswap/v2-core 의 contracts 디렉토리
(interfaces, libraries, test 볼 필요가 없고 UniswapV2ERC20.sol UNI 토큰에 대한 내용이라서 볼 필요 없음)



UniswapV2Factory.sol

로운 pair를 거래하는 컨트랙트를 생성해내고 관리하는 게이트웨이 같은 것
저 V2 Pair가 실제로 두 토큰을 거래하는 풀이자 거래소가 됨

 

이 createPair는 새로운 token exchange contract. 즉, pair 컨트랙트를 만들어내는 함수임

처음 argument로 들어오는게 tokenAtokenB의 주소

당연히 두 토큰의 주소는 같으면 안되고 두 토큰의 주소를 서로 비교함
토큰 A가 큰지 B가 큰지 B가 더 크면 A-B pair를 만들고 A가 더 크면 B-A pair를 만드는데 왜 이렇게 하느냐?
A-B pair 와 B-A pair는 같음 이 둘을 구별하기 위해서 그 주소의 크기를 비교한 것
Uniswap info를 보면 가장 liquidity(유동성)가 많은 토큰 pair가 WBTC-ETH임
근데 두 번째가 ETH-USDT임 왜 ETH가 첫 번째에는 뒤에오고 두 번째에는 앞에 올까?
바로 createPair 함수 때문임 저 주소로 토큰 pair의 순서를 정함
WBTC의 주소가 0x2260으로 시작 첫자리가 2 / WETH는 첫자리가 C
16진수에서 A가 10이고 B가 11이고 C가 12임 2보다 12가 더 크기 때문에 WETH가 뒤에옴
반대로 USDT 컨트랙트의 주소는 첫자리가 d임. d는 10진수로 13임 C보다 큼
그래서 ETH-USDT pair가 되는 거임

그 다음은 token0이 0이 아닌지를 검사함
0이라는 주소를 가지는 컨트랙트는 없으니까.
token0는 token1 보다 주소가 작기때문에 token1은 무조건 0보다 큼
그렇기 때문에 token0만 검사하면 됨

그리고 마지막으로 검사하는게 두 토큰 pair에 해당하는 컨트랙트가 이미 있는지를 검사
Factory가 토큰 pair를 관리하는 자료구조를 담고 있는 변수 이름이 getPair 인데
이 getPair에 token0와 token1을 넣어보는 것
그랬을 때, 값이 있으면 zero address(없는 주소)가 아니면 이미 있는거고
만약 있다면 revert(실행 취소) 시킴 수행하지 않음 또 만들면 안되니까
없으면 다음으로 내려감

UniswapV2Pair 아까 github의 contracts 디렉토리 안에 있던 tokenPair 컨트랙트의 코드를 가지고 옴
그 코드를 바이트 코드(bytecode)로 가져옴 그리고나서 salt를 하나 만듦
만드는 방식을 보니까 token0와 token1을 가지고 그 abi를 가지고 keccak256이라고 하는 해시 함수를 돌려서 만들어 냄
그리고 나서 create2 함수를 가지고 pair 컨트랙트를 만듦 이건 어셈블리(assembly)어로 선언이 되어있음
이제 그 토큰 pair에 token0와 token1을 initialize(초기화) 해주는 거임

그러면 그 컨트랙트는 이제부터 token0와 token1을 교환할 수 있는 그리고 그 풀을 형성하는 컨트랙트가 되는 것
아까 getPair가 두 토큰을 교환하는 token exchange contract의 주소를 담고 있는 자료구조라고 했음
여기다가 token0와 token1 쌍에 해당하는 element(요소)에다가 저 pair의 주소를 넣어주는 것
지금 보면 token0, token1 / token1, token0 모든 조합에 대해서 다 넣어줌
안정성을 위해서 하는 것으로 보임..
그리고 allPairs에다가도 push를 해줌
그 다음에 emit 해서 새로 pair를 만들었다는 로그(log)를 남김

 

V2에서 새로 생성된 부분이 fee 중의 일부, 0.05%를 가져온다고 했었는데
그것을 위해서 feeTo라고 하는 address를 넣어놨음
이게 만약 쓰이게 된다면 이 feeTo에게 이제부터는 토큰 컨트랙트에서 거래되는 수수료의 일부가 가게됨



UniswapV2Pair.sol

 

먼저 이 constructor에 factory 주소를 넣어줌
V2Pair는 Factory가 생성함 그렇기 때문에 이 컨트랙트에서 factory하는 변수에다가 이 컨트랙트를 create한 Factory의 주소를 넣어주는데 그것을 받아올 수 있는게 * msg.sender 임
왜냐하면 이 컨트랙트를 배포한 컨트랙트가 Factory 컨트랙트이니까

*msg.sender는 트랜잭션을 보낸 address를 나타냄

 

그리고 initialize 함수가 있는데 아까 Factory에서 call(호출)을 했었음
그래서 token0에는 token0 argument token1에는 token1 argument를 assign(대입) 해줌


이 컨트랙트는 계속해서 자기가 얼마를 들고 있는지를 추적함 그런데 그 추적이 항상 맞진 않음
왜냐하면 실제 balance(잔고)는 token0와 token1 컨트랙트에 있고
그리고 그 잔고에 대한 정보는 컨트랙트를 가야지 볼 수 있음
내가 지금 기록하고 있는 토큰의 수량보다 그 컨트랙트로 가서 관찰했을 때 거기는 잔고가 더 많다면
내가 돈을 받았다는 것을 알 수 있음 이 토큰 풀에 토큰이 들어왔다(입금)는 것을 알 수 있음
그 정보를 보고 업데이트를 해 주는게 _update 함수

uint32 blockTimestamp, uint32 timeElapsed, price0CumulativeLast, price1CumulativeLast 라고 하는 변수들을 모종의 로직을 따라서 바꿔 주고 있음 이게 바로 유니스왑을 price oracle로 쓰기위한 로직

각각의 price 들이 유지된 시간의 가중치 만큼 더해서 평균을 내서 현재 가격을 결정한다고 했음
price0(1)CumulativeLast라고 하는 변수가 public으로 선언되어있음
그렇기 때문에 다른 컨트랙트에서 이 유니스왑을 price oracle로 쓰려면 저 price0CumulativeLast 변수를 보면 됨
그러면 시간에 따라서 달라지는 가격을 우리가 조금 완충해서 볼 수가 있기 때문에
다른 컨트랙트에서 가격 정보를 받아올 수 있음

그리고 _mintFee 라는 함수가 있음 이 함수는 프로토콜 fee 를 계산하는 공식임

 

 

 

 

 

이미지가 더이상 추가가 안되는 관계로 2탄에서 이어집니다