V2 백서 분석
유니스왑 V2란?
유니스왑이 투자를 받고나서 2020년 5월에 새로 컨트랙트를 개발했다. 그렇게 배포한 것이 유니스왑 V2이다. 기존의 단점을 해결했다.
V1 pair
V1에서는 토큰 pair를 만들 때 ERC-20 토큰과 이더리움을 서로 pair로 만들어서 사용했다.
Token exchange contract 가 전부 ERC-20과 이더리움 pair로 이루어져 있었다. 이더리움을 bridge currency로 사용했다.
어떤 사람이 두 스테이블 코인 DAI와 USDT를 pair로 만들고 싶은데 유니스왑에서는 반드시 이더리움을 bridge로 써야 한다.
DAI-ETH pair와 USDT-ETH pair를 만들어야 한다.
이더리움을 무조건 들고 있어야 한다.
이 사람은 스테이블 코인만을 pool로 제공하고 싶었는데 어쩔 수 없이 이더리움을 제공함으로 인해서 이더리움 가격이 떨어졌을 때 impermanent loss가 발생한다.
V2 pair
ERC-20 토큰과 ERC-20토큰을 가지고 pair를 만들 수 있게 변경했다.
DAI-USDT pair를 생성 가능
이더리움을 보유하지 않아도 되기 때문에 impermanent loss가 방지된다.
하지만 ERC-20과 이더리움 간의 pair도 생성을 해줘야 한다.
그렇기 때문에 예외 케이스가 생기고 코드 양이 배로 늘어난다.
이를 막기 위해서 V2에서는 이더리움을 쓰지 않고 Wrapped ETH라고 하는 이더리움을 ERC-20 토큰 형태로 wrapping한 WETH를 사용한다.
V2에서는 무조건 ERC-20 표준을 가지는 두 토큰을 pair로 만들어서 사용한다.
→ 코드가 간단해지고 수수료가 적게 든다. (이 토큰이 이더리움인지 ERC-20인지 판단하는 로직이 사라지기 때문에)
거래 수수료 변경
V1에서는 거래 수수료 0.3%를 LP들이 가지고 있는 토큰의 지분만큼 분배해줬다.
V2에서는 0.25%만을 지급하고 0.05%는 프로토콜 fee로 따로 걷는다.
0.05%의 프로토콜 수수료는 유니스왑 토큰인 UNI 홀더들에게 제공된다.
Price oracle
유니스왑에서 거래되는 토큰의 교환비가 시중에서 거래되는 토큰의 교환비와 매우 유사하다, 거의 일치한다라는 논문이 발표가 되었다. 이 논문을 근거로 유니스왑에서 거래되는 토큰 비율을 가격 정보로 사용하는 로직을 V2에 넣어놨다.
유니스왑을 price oracle로 사용하게 되면 공격자가 나타날 수 있다. 다량의 토큰을 교환하거나 pool에 집어넣는다거나 해서 정보를 왜곡시킬 수 있다. 그러면 다른 컨트랙트들이 피해를 볼 수 있기 때문에 가격 정보를 그냥 반영하지 않고 과거부터 현재까지 쭉 이어지는 가격 변화들을 가격이 유지된 시간만큼 곱해서 가격을 결정한다. 이렇게 하면 가격이 급격하게 변동하지 않는다.
유니스왑에서 두 토큰의 교환 비율을 정할 때 정수로 딱 떨어지지 않는다. 그런데 Solidity는 소수를 표현할 수 있는 데이터 타입을 지원하지 않는다. 그래서 소수를 표현할 수 있는 새로운 데이터 타입을 정의해서 사용한다. 256-bit 자료형을 사용한다. 첫 112-bit는 정수형을 사용, 그 뒤 112-bit는 소수점 자리수를 위해 사용, 나머지 32-bit는 그 가격이 유지되는 시간을 표현하기 위해서 사용한다.
주소 변경
V2에서는 두 토큰 pair에 대한 주소를 변경할 수 있는 로직을 넣어놨다.
Initialization of liquidity token supply
S_minted : 새로 생성되는 LP 토큰
X_starting : X 토큰을 풀에 넣기 전의 수량
X_deposited : X 토큰을 풀에 넣은 수량
S_starting : X 토큰을 풀에 넣기 전에 원래 있던 LP 토큰의 양
새로 생성되는 LP 토큰의 양은 어떤 특정한 ERC-20 토큰의 (이 사람이 넣은 수량 / 전체 수량) 만큼 생성된다.
X-Y 간의 교환인데 왜 X만으로 계산하는가?
두 토큰 중에 S_minted의 값이 더 작은 값이 되도록 하는 S_minted를 찾아서 생성을 해낸다.
X가 Y가 될 수도 있다.
토큰 pair를 처음 만들었을 경우 LP 토큰의 수량을 어떻게 결정하는가?
처음 생성할 경우 X_starting이 0이기 때문에 다른 공식을 따른다.
집어넣는 X, Y 토큰 수량의 곱의 루트 값이 새로 생기는 LP 토큰의 양이다. 기하평균값.
예시
ABC 토큰과 XYZ 토큰이 있다.
ABC-XYZ 교환 비율이 1:100이다.
initial deposit이 2ABC, 200XYZ 이다.
이때 depositor가 받는 LP 토큰의 양은 (2*200)^(1/2) = 20 이다.
처음 토큰 pair를 생성하는 사람은 그 LP 토큰을 온전히 갖지 못하고 수수료를 낸다.
그 수수료는 0번 주소로 전송되어서 영원히 묶이게 된다. (burn)
이렇게 하는 이유는 어떤 공격이 있을 수 있기 때문이다.
LP share는 최소 단위가 10^-18이다. 보통 ERC-20 토큰의 최소 단위랑 같다.
그런데 그 사람이 넣는 토큰 지분의 비중이 10^-18보다 작으면 LP share를 못 받는다. 이런 경우를 대비하기 위해 그 사람이 넣은 LP share의 10^-15만큼(10^-18의 1000배)의 LP token을 태워야 한다.
예를 들어 어떤 사람이 LP share하나의 가격을 100$로 맞추고 싶으면 그것의 1000배인 10만$에 해당하는 토큰이 없어진다.
그렇기 때문에 사람들이 초기에 돈을 넣을 때 너무 많이 집어넣지 않는다. 본인이 나중에 수수료를 받아서 얻을 수 있는 이득보다 태워지는 양이 더 많을 수 있기 때문이다.
프로토콜 fee
fee라는 것은 전체 LP 토큰 풀의 인플레이션에 비례해서 부과된다.
k : 두 토큰 수량의 곱
k_2 : 늘어난 LP 토큰의 양 (루트 X*Y)
k_1 : 늘어나기 전 LP 토큰의 양
사람들이 풀에 토큰을 더 많이 넣었다고 하면 k_2의 양이 k_1보다 크다.
이 인플레이션에서 프로토콜 fee로 0.05%를 가지고 간다.
이 수수료는 LP 토큰이 생성되거나 burn 될 때만 호출되어서 부과 된다.
s_1 : 기존에 있던 LP token 의 양
s_m : 프로토콜 fee로 가져가는 LP token의 양
ϕ : 수수료 비율
f_1,2 : 인플레이션 비율
이때 ϕ를 1/6로 잡았다.
위의 두 식을 합치면 다음 식을 유도할 수 있다.
ϕ에 1/6을 대입한 공식을 따라서 프로토콜이 정한 fee를 가져가는 사람에게 수여되는 LP 토큰의 양이 정해진다.
예시
100 DAI와 1 ETH가 존재하면 처음 LP token은 10개가 있다.
k_1 : 1* 100 = 100
s_1 : 10
이 상태에서 인플레이션이 돼서 96DAI와 1.5ETH가 됐다.
k_2 : 96 * 1.5
이 값들을 대입하면
s_m : 0.0286
0.0286 만큼을 프로토콜 fee로 가져간다.
Reference
Last updated