본문 바로가기

:D/block chain

UNISWAP 3 : 유니스왑 V2 백서

 

 

 

 

유니스왑 V2
기존 V1의 단점들을 보완해서 2020년 5월에 새로 개발한 컨트랙트가 유니스왑 V2 (유니스왑 v2 core 백서)

 

 

원래 유니스왑 V1에서는 토큰 pair를 ERC-20토큰과 이더리움을 서로 pair로 만들어서 사용했음
UNISWAP 2 에서 봤던 Token Exchange 컨트랙트가 전부 다 ERC-20과 이더리움 pair로 이루어져 있음
이더리움을 bridge currency(기축통화)로 사용함


이렇게 되면 어떤 문제가 발생하냐면

어떤 사람이 스테이블코인(stable coin) DAI와 USDT를 가지고 pair를 만들고 싶은데

유니스왑에서는 반드시 이더리움을 bridge로 써야 함

그래서 그 pair를 만들기 위해서는 DAI-ETH pair 와 USDT-ETH pair를 만들어야 했음


그러면 또 어떤 문제가 발생하냐
이더리움이 반드시 필요함 (이더리움을 무조건 가지고 있어야 함)

그리고 만약 이더리움 가격이 하락했을 경우 이 사람은 stable coin만 pool을 제공하고 싶었는데

어쩔 수 없이 이더리움을 제공함으로 인해서 이더리움 가격이 떨어졌을 때 impermanent loss(일시적인 손해) 입게 됨

 

Q1. 만약 ETH/DAI 각각 비율이 1:100 인 풀에 1ETH 와 100 DAI를 Deposit 을 하고 약 100 LP토큰을 받게 되었고 그때의 100 LP토큰 지분율이 전체 LP토큰의 10% 라고 가정 했을 때, 그 후에 계속 유동성제공자들이 생겨서 LP토큰이 유동성 제공자들에게 제공이 됨. 그렇게 한달동안 LP토큰이 분배가 되어 가지고 있는 LP토큰의 지분율이 전체 대비 1%로 줄어들게 되었다면 Withdraw(출금) 했을 때 애초에 넣었던 토큰의 1ETH, 100DAI 를 받게 되는건지? 아니면 그것보다 적게 받게 되는건지? 그리고 만약 교환비율이 1:200 이 된다고 했을 때 얼마의 ETH와 DAI를 받게 되는지?

A1. 우선, ETH<>DAI의 교환비가 변하지 않았다고 가정할 때, 지분이 1%로 줄어들어도 withdraw할 때는 같은 1ETH + 100DAI + @(수수료 ETH, DAI)를 받게 됨. 그런데 이더리움의 가격이 두 배가 되어 교환비가 1:200으로 급격하게 변하게 되면 Impermanent Loss(IL)라는 것이 발생함. 계산을 해보면 그냥 홀드를 하는 것에 비해 약 5.72%만큼의 손해를 보게되고, 1.41ETH + 282.84DAI를 받게 됨. (https://impermanent-loss-calculator.netlify.app/). 계산하는 사이트

그러므로 IL을 최소화하기 위해서는 가격이 커플링이 되는 코인들을 페어로 묶는 것이 좋음.

 

 

그래서  V2에서는 ERC-20과 ERC-20 
영어로 하면 arbitrary 라고 하는데 무작위의 ERC-20 토큰을 가지고 pair를 만들 수 있게 만들었음
=> DAI-USDT pair를 만들 수 있게 됨
그러면 이더리움 가격이 하락함으로 인해서 발생할 수 있는 손해를 막을 수 있음

그런데 이렇게 되면 이더리움과 ERC-20 간의 pair는 따로 처리를 해줘야 함
같은 로직에 대해서 경우의 수가 두 개가 발생하게 됨 (ERC-20과 ERC-20 / ERC-20과 이더리움)
그러면 같은 코드를 두번 짜야하고 예외 케이스가 생기고 코드 양이 배로 늘어나게 됨


이런 불상사를 막기 위해서 V2에서는 이더리움을 쓰지 않고

이더리움을 ERC-20 토큰 형태로 wrapping  Wrapped ETH(WETH)  라고 하는 것을 사용하게 됨

그래서 V2 에서는 무조건 ERC-20 표준을 가지는 두 토큰을 pair로 만들어서 사용
이렇게 되면 코드도 간단해지고 토큰이 이더리움인지 ERC-20인지 판단하는 로직이 사라지기 때문에 수수료도 적게 듦

 

Q2. 유니스왑 v2는 ERC-20과 ERC-20간의 direct 스왑이 가능해짐. 그럼 ERC-20끼리 스왑할 때 ERC-20-> ETH -> ERC-20 이런식으로 스왑할 필요가 없어짐. 근데 스왑경로가 ERC-20-> ETH -> ERC-20 나올 때가 있음. WHY?

A2. 유니스왑은 스왑을 하고자 하는 풀의 유동성이 적어서 슬리피지(DEX에서는 price impact(*가격 영향))가 크다고 판단이 될 때 라우팅을 통해서 다른 경로로 스왑을 수행함. ERC-20-> ETH -> ERC-20로 route가 뜬 것은 ERC-20 -> ERC-20 풀의 유동성이 적기 때문에 유니스왑에서 최적 경로를 라우팅 해준 것.

* price impact(가격 영향) : 시장가와 예상가 간의 차이(처음에 의도했던 가격과 실제 거래되는 가격 사이에 차이가 발생하는 것)로 토큰을 교환할 때 손해를 보게되는데 풀의 유동성이 적을 수록, 거래량이 많을 수록 슬리피지(가격영향)이 커지게 됨. 슬리피지가 커질 수록 결과적으로 거래자는 더 비싼 가격에 토큰 거래를 하게 됨. 

참고 : https://hyun-jeong.medium.com/uniswap-series-2-cpmm-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-4a82de8aba9 

 



또한 이전에는 매 거래마다 0.3%의 거래 수수료를 받았음

그래서 그 0.3%를 LP들이 갖고 있는 토큰의 지분만큼 분배해줌

하지만 V2에서는 수수료를 다 안 주고 0.25%만 주고 0.05%는 프로토콜 fee라고 해서 따로 걷으려고 하는 로직을 넣음

이 fee는 이 유니스왑 사람들에게 가는 건지 아니면 유니스왑 토큰(UNI) 홀더들에게 가는 건지는 모르겠음그런데 아마 유니스왑 토큰 홀더들에게 갈 것으로 예상

왜냐면 유니스왑 홀더들이 계속해서 유니스왑 거버넌스라고 하는 유니스왑에 어떤 제안들을 하고 투표를 할 수 있는 사이트가 있는데 유니 토큰이 발생됨으로 인해서 유니 홀더들이 그 수수료를 달라고 계속 주장을 하고 있음


그리고 또 하나 추가된 게 price oracle 이라고 해서 유니스왑에서 거래되는 토큰의 교환비가 시중에서 거래되는 토큰의 교환비와 매우 유사하다 거의 일치한다고 하는 논문이 발표가 됐음
그래서 그 논문을 근거로 유니스왑에서 거래되는 토큰 거래 비율을 가격 정보를 제공하는 어떤 정보로 쓰기 위한 로직을 넣어놨음

그런데 이 유니스왑을 price oracle로 쓰게 되면 공격자가 나올 수 있음
이 유니스왑 풀에서 다량의 토큰을 교환한다거나 아니면 토큰 풀을 집어넣는다거나 해 가지고 가격을 왜곡시킬 수 있음
그러면 이 유니스왑을 price oracle로 쓰는 다른 컨트랙트들이 피해를 보게 됨

그래서 유니스왑이 가격 정보를 그냥 반영하지 않고

과거부터 현재까지 쭉 이어지는 가격의 어떤 변화 그 가격 변화들을 그 가격이 유지된 시간만큼

(최소 15초? 왜냐면 이더리움의 블록 인터벌이 15초니까)

전체 시간에서 각 가격별로 그 시간을 곱한 만큼 더해가지고 가격을 결정하게 됨

그렇게 되면 가격이 급격하게 변동하지 않게 됨

그리고 유니스왑에서 두 토큰의 교환 비율을 정할 때 이게 정수로 딱 떨어지지 않음 보통 소수점으로 떨어짐
그런데 solidity는 부동 소수점. 소수를 표현할 수 있는 데이터 타입을 지원하지 않음
그래서 이 소수를 표현할 수 있는 새로운 데이터 타입을 정의해서 씀

 

256-bit 자료형을 쓰는데 그 자료형에서 처음 112-bit 는 정수형을 사용
그리고 그 뒤에 있는 112-bit는 소수점 자리수를 위해서 사용

나머지 32비트는 그 가격이 유지되는 시간을 표현하기 위해서 사용함

자세한 내용은 백서에 나와있음 (아래 더보기 참고 또는 유니스왑 v2 core 백서 참고)
그리고 두 ERC-20 토큰을 pair로 만들어서 풀을 만들고 거래할 수 있는 그 컨트랙트의 주소를 바꿀 수 있게 만들었음
원래라면 토큰 pair에 따라서 주소가 결정됨 그런데 그 주소가 마음에 안들 때 혹은 주소에다가 특정한 의미를 가진 string을 넣고 싶다. 그럴 때 주소를 조작하기 위해서 사용함 딱히 중요한 의미가 있지는 않음 ㅎ

 

유니스왑 V2 백서내용

 




LP 들이 받게되는 LP 토큰의 수량

 

 

Initialization of liquidity token supply의 12번째, 13번째 equation 참고

S_minted (새로 생성되는 LP 토큰)은 X_starting 분의 X_deposited임
어떤 사람이 X 토큰을 풀에 넣었을 때 생성되는 게 LP 토큰
X_starting은 X 토큰을 넣기 전의 수량

X_deposited는 넣은 후의 수량 곱하기 S_starting(그 사람이 X 토큰을 풀에 넣기 전에 원래 있던 LP 토큰의 양)

 

새로 생성되는 LP 토큰의 양은 어떤 특정한 ERC-20 토큰의 (이 사람이 넣은 수량 / 전체 수량) 만큼 생성
근데 왜 (X,Y pair 중) 하나냐 저 X가 사실 Y가 될 수도 있음
근데 X, Y 중에서 저 S_minted의 값이 더 작은 값 이 되도록 하는 S_minted를 찾아서 생성
원래 LP 토큰이 있을 때는 저런 공식에 의해서 토큰이 만들어지는구나~~~


그렇다면 만약에 토큰 pair를 처음 만들었을 때 아무것도 없는 상태에서는 어떻게 저 수량을 결정 하나?
왜냐면 X_starting이 0이기 때문에 S_minted 값이 무한대가 되어버리기 때문에 그렇게 되면 안 되니까

처음에는 이 공식을 따름 equation 13번을 보면 S_minted가 루트(X_deposited * Y_deposited)임
즉, X와 Y를 집어넣게 되면 두 토큰 수량의 곱의 루트 값 다시 말해서 기하 평균값으로 정해짐
저기서부터 시작해서 새론 누군가가 토큰을 넣게 되면 저 위의 공식(12)을 따라서 토큰이 생성되게 됨

예를 들어서

ABC 토큰과 XYZ 토큰이 있을 경우 이때 교환비율을 1:100이라고 하고 initial deposit이 2 ABC랑 200 XYZ라고 함
교환비율은 1:100으로 맞고 이때 depositor가 받는 토큰의 양은 루트의 2 * 200 이니까 20에 해당하는 LP 토큰을 받게 됨 그런데 여기서 조금 특이한 점이 있음
맨 처음에 페어를 만들어서 LP 토큰을 만드는 사람은 그 토큰 share를 온전히 가질 수 없음 일부 수수료를 냄
그리고 그 수수료는 0번 주소로 전송이 돼서 영원히 묶이게 됨 즉, burn이 되는 효과가 있음
왜 이렇게 되냐 하면 공격이 있을 수 있음

LP share(토큰)는 최소 단위가 10^-18 임 (10의 -18승)
보통 ERC-20 토큰의 (최소) 단위랑 같음
즉, 다음 사람들이 LP share를 받으려면 최소 단위가 10^-18인 거임
근데 그 사람이 넣는 토큰 portion(지분)의 비중이 그것(10^-18) 보다 작다라고 하면 LP share를 못 받음
그런 경우를 대비하는 것
그래서 그 사람이 넣은 LP share의 10^-15 만큼(10^-18의 1000배) 즉, 하나의 LP share의 가치에 1000배에 해당하는 LP token을 태워야 함

예를 들면, 어떤 사람이 LP share 하나의 가격을 $100으로 맞추고 싶을 경우
그것의 1000배인 $10만에 해당하는 토큰이 없어지게 됨
이렇게 burn을 시키게 되면 손해가 매우 큼
사람들이 초기에 돈을 넣을 때 너무 많이 집어넣지 않는다고 함
(돈을 너무 많이 넣으면 다른 LP들이 못 들어옴

사실 못 들어온다기보다 LP share의 지분율이 큰 다른 누군가가 있다면 새로운 유동성 공급자가 수수료 이득을 보기 힘들기 때문에 들어올 유인이 없어진다고 보는 게 맞음)
왜냐면 본인이 나중에 수수료를 받아서 얻을 수 있는 이득보다 지금 태워지는 양이 더 많을 수 있기 때문에 그런 짓을 안 함

 




Protocol fee

 

 

이 프로토콜 fee가 이전에는 없었는데 새로 도입된 것
그리고 지금 켜져 있는지 꺼져 있는지는 모르겠지만 이게 만약 켜진다면 프로토콜이 fee를 일부 갖고 가게 됨
그 fee를 어떻게 가져가게 되는지에 대한 공식이 2.4 Protocol fee에 나와 있음

 

 2.4 Protocol fee

fee라고 하는 것은 전체 LP 토큰 풀의 인플레이션에 비례해서 부과가 됨
그래서 그 인플레이션을 보면 이 K 값이 두 토큰(X, Y) 수량의 곱인데


사람들이 돈을 집어넣게 되면 그 곱이 늘어나게 됨. 그래서 이 K_2가 늘어난 LP 토큰의 양 (루트 X*Y)이고
K_1이 그 전의 LP 토큰의 양. 토큰이 늘어났다고 생각하면 K_2가 K_1보다 큼. 

1 - 루트 K1 / 루트 K2를 하게 되면 그 값이 이 풀의 인풀레이션이 됨

그 인플레이션에서 이 사람들이 0.05%를 가지고 가는 것 

저 수수료는 LP 토큰이 생성되거나 burn 될 때만 call(호출)이 되어서 사용이(부과가) 됨

그래서 이 사람들이 수수료를 계산하는 방법을 대략적으로 이렇게 만들었음 

즉, 인플레이션의 6분의 1만큼의 fee를 갖고 가겠다고 한 것.

그 공식이 5번 공식

S_1이 기존에 있는 LP 토큰의 양이고 S_m이 프로토콜 fee로 가져가는 LP 토큰의 양 

S_m / ( S_m + S_1 ) 이 Φ (파이) 이게 수수료 비율인데 Φ * f_1,2 가 되도록 가져가겠다는 것

이때 Φ를 1/6로 잡았음 f_1,2는 인플레이션 비율 

equation 4번과 5번을 합치면 6번을 유도할 수 있음

 

 

이때 Φ에 1/6을 넣게 되면 루트 K_2 앞에 곱해지는 값이 5가 됨 

이 상태에서 이 공식을 따라서 이 프로토콜이 정한 fee를 가져가는 사람에게 수여되는 LP 토큰의 양이 정해짐 

 

예를 들면, 100 DAI와 1 ETH를 가지고 있는 토큰 pair 가 있다고 할 때,

100 DAI와 1 ETH니까 루트 100*1 하면 처음에 LP 토큰의 양이 10

그 상태에서 인플레이션이 됐음 96 DAI와 1.5 ETH가 됨

그렇게 되면 8번 공식 내용 

거기에 기존의 LP share의 양만큼, 10을 곱해주는 것

그렇게 되면 대략적으로 0.0286 만큼이 나옴 

저만큼을 프로토콜 fee로 가져가는 것 

 

 

 

UNISWAP 4 : 유니스왑 V2 코드 분석(1) 보러가기