[DeFi] UniswapV3 — Part2. Oracle & Fee
블록체인 학회 Catena Lab에서 ‘DeFi’를 주제로 그룹별 발표 진행한 내용을 담았습니다. 이전 글에서는 유니스왑V3에서 변경된 주요 개념들에 대해서 살펴보았습니다. 이번 글에서는 이번 유니스왑V3를 조사하면서 파악한 Oracle의 작동방식, Governance에 대해 살펴봤습니다.
Author
writter : 서학용 (robert seo), 김용현 (kimyonghyun), 권상윤 (elllo_ollle)
Published by : Catena Lab (Catena LAB)
Reviewed by :
Oracle이 뭐야? RDBMS 아니야??
블록체인 업계에서는 종종 “Oracle”에 대한 얘기가 나온다. 그럴 때는 DB에 대해 조금 경험이 있는 엔지니어들은 RDBMS인 Oracle이 블록체인에서도 뭘 하나? 라는 생각을 하기도 한다. 그러나 결론부터 말하면 RDBMS인 Oracle과 블록체인에서 말하는 Oracle은 개념이 다르다.
블록체인에서 Oracle이란 블록체인의 스마트 컨트랙트와 오프체인 데이터 공급자를 연결하는 통로 역할을 말한다. 오프체인이란 블록체인에 있지 않은 데이터를 말하는데 예를 들면 “항공”, “물류”, “의료”등 블록체인에 없는 데이터를 말한다. 이 데이터와 블록체인의 스마트 컨트랙트를 중간에서 연결하는 역할을 한다.
그렇다면 유니스왑V3에서 Price Oracle은 어떤식으로 동작하나요?
모든 유니스왑V3 Pool은 자체적으로 Price Oracle 역할을 할 수 있습니다. 이게 무슨 뜻이냐면 유니스왑V3 Pool이 신뢰할 수 있는 가격정보를 제공해줄 수 있습니다. 어떻게 그렇게 할 수 있을까요?? 바로 차익거래자와 CEX 때문입니다. 유니스왑V3에서는 높은 유동성과 차익거래자들로 인해 중앙화된 거래소와 가격이 비슷합니다. 따라서 중앙화된 거래소를 자산 가격의 신뢰할 수 있는 출처로 사용하는 대신 유니스왑V3를 신뢰할 수 있는 출처로 사용할 수 있으며 온체인 데이터 전송과 관련된 문제를 해결할 필요가 없습니다.(잘못된 데이터가 유입되는 리스크를 걱정할 필요가 없습니다.)
잠깐! TWAP 계산 전에 유니스왑V2와 유니스왑V3의 TWAP 계산 방식은 다음과 같은 개념을 사용했습니다.
유니스왑V2 TWAP : 산술평균
유니스왑V3 TWAP : 기하평균
유니스왑V3 Price Oracle 작동방식
유니스왑V2에서는 풀 계약의 히스토리에서 매 초 가격의 합계인 누적가격을 추적한다.
이 접근 방식을 사용하면 t1, t2 두 시점 사이의 시간 가중 평균 가격(Time Weighted Average Price, TWAP)을 찾을 수 있습니다. 다음과 같이 a[t2] — at[1]에서 누적된 가격을 가져온 뒤 두 지점 사이의 시간(초)로 나누면 다음과 같은 공식이 나온다.
위와 같은 방식이 유니스왑V2에서 TWAP을 계산할 때 쓰는 방법인 “산술기하평균” 방식이다.
다음은 유니스왑V3에서 계산하는 방식이다.
밑이 같은 로그의 덧셈은 곱셈으로 변하기 때문에 a의 값이 커질수록 모두 곱해집니다.
그리고 가격의 평균을 구하는 대신 다음과 같이 기하평균을 구합니다.
5.4에서 1.0001^x와 같이 밑으로 하면 5.5와 같이 변합니다.
유니스왑V3는 위 그림과 같이 TWAP을 계산할 때 “기하평균” 방식으로 계산한다. 그리고 유니스왑V2는 과거 누적 가격을 저장하지 않기 때문에 평균 가격을 계산할 때 과거가격을 찾기 위해 타사 블록체인의 데이터 인덱싱 서비스를 참조해야했다. 그러나 유니스왑V3는 최대 65,535개의 과거 누적가격을 배열로 저장함으로서 과거의 TWAP 가격을 훨씬 쉽게 계산할 수 있다. 이 배열로 저장하는 방식도 순환버퍼와 같이 오래된 값은 새로운 갚으로 덮어씌워지는 형태로 저장된다.
다음 사진을 보면 좀 더 명확히 유니스왑V2에서 유니스왑V3로 TWAP Oracle이
어떻게 변화했는지 알 수 있다.
위 그림을 살펴보면 유니스왑V2는 priceCumulative 변수에 13:00, 14:00에 체크포인트를 쿼리로 확인한 뒤 누적해서 TWAP을 계산하는 방식으로 동작한다. 반면에 유니스왑V3는 그냥 한번의 온체인 호출로 최근 9일간의TWAP을 계산한다.
Oracle Observations
유니스왑V3에서는 Oracle Observations를 통해 가격 정보의 정확성을 높이고 가격 누적값을 계산하는데 필요한 정보를 제공한다. 이를 통해 유동성 공급자들은 더욱 정확하고 신뢰 높은 가격정보를 제공받게 됩니다.
이 기능을 수행하는 함수들은 Oracle 라이브러리에 포함되어 있으며 가격 정보를 업데이트하고 계산하는 데 사용됩니다. 이런 함수들은 Oracle 라이브러리를 사용하는 다른 유니스왑V3 핵심 컨트랙트에서 호출된다.
동작하는 코드는 다음과 같다.
- initialize 함수
function initialize(
struct Oracle.Observation[65535] self,
uint32 time
) internal returns (uint16 cardinality, uint16 cardinalityNext)
첫 번째 슬롯을 작성하여 오라클 배열을 초기화합니다. 관찰 배열의 수명 주기 동안 한 번 호출됩니다.
2. Write 함수
function write(
struct Oracle.Observation[65535] self,
uint16 index,
uint32 blockTimestamp,
int24 tick,
uint128 liquidity,
uint16 cardinality,
uint16 cardinalityNext
) internal returns (uint16 indexUpdated, uint16 cardinalityUpdated)
배열에 오라클에서 Observation한 내역을 업데이트합니다.
블록당 최대 한 번만 쓸 수 있습니다. 인덱스는 가장 최근에 쓰여진 요소를 나타냅니다. 카디널리티와 인덱스는 외부에서 추적해야 합니다.
인덱스가 (카디널리티에 따라) 허용되는 배열 길이의 끝에 있고 다음 카디널리티가 현재 카디널리티보다 큰 경우 카디널리티가 증가될 수 있습니다. 이 제한은 순서를 유지하기 위해 만들어졌습니다.
3. grow함수
function grow(
struct Oracle.Observation[65535] self,
uint16 current,
uint16 next
) internal returns (uint16)
다음 관측값까지 저장할 오라클 배열을 준비하는 함수입니다.
4. Observe 함수
function observe(
struct Oracle.Observation[65535] self,
uint32 time,
uint32[] secondsAgos,
int24 tick,
uint16 index,
uint128 liquidity,
uint16 cardinality
) internal view returns (int56[] tickCumulatives, uint160[] liquidityCumulatives)
주어진 시간으로부터 몇 초 전의 각 시간 기준 누산기 값을 secondsAgos 배열로 반환합니다.
Liquidity Oracle
유니스왑 V3의 Liquidity accumulator은 시간에 따른 유동성 변화를 추적하고 기록하는 기능을 합니다. 이를 통해 Uniswap V3는 각 풀의 가격 및 가용성을 더욱 정확하게 예측할 수 있습니다. Liquidity accumulator의 구체적인 특징은 다음과 같습니다.
- Liquidity accumulator는 관측 시점의 초/범위 내 유동성 값(1/L)을 저장합니다.
- 가장 신뢰할 수 있는 TWAP pool에 대한 정보 제공
- 모든 활성화된 유동성에 대한 보상을 균등하게 분배할 수 있습니다.
우리는 지금까지 유니스왑V3의 Price Oracle, Liquidity Oracle에 대해 배웠습니다. 그렇다면 이제 저희는 유니스왑V2에서 유니스왑V3로 변하면서 fee에 대한 것은 어떻게 변했는지 알아볼까요??
유니스왑V2와 유니스왑V3의 Fee 비교 표
1. UniswapV2의 fee는 0.3%로 고정되어 있습니다. 반면에 UniswapV3의 수수료는 0.05%, 0.3%, 1%로 나뉘어져있습니다. 또 거버넌스에 의해 추가로 수수료를 만들수도 있습니다.
2. 프로토콜 수수료는 유니스왑v2는 1/6, 유니스왑V3는 1/4~1/10까지 다양합니다.
3. pool에 재투자를 하는 부분입니다. 유니스왑 V2는 토큰이 ERC20 즉 대체가 가능한 토큰이기 때문에 pool에 수수료를 자동 재투자했습니다. 반면에 유니스왑V3는 토큰이 ERC721이고 대체불가능한 토큰이기 때문에 재투자를 하지 못합니다.
4. 유동성의 활성화 범위도 유니스왑V2는 0부터 무한대지만, 유니스왑V3는 특정 범위에서 활성화됩니다.
5. 틱 스페이싱에 대해서도 유니스왑V2는 틱 스페이싱에 대한 개념이 없고 유니스왑 V3는 표와 같이 수수료에 따라 틱 스페이싱이 0.05%(0.10%), 0.3%(0.30%), 1%(2.02%)로 설정되어 있습니다.
위의 표에서 수수료를 Governance에서 투표를 통해 추가할 수 있다고 했는데요. 그렇다면 Governance에 대해 간략하게 알아보겠습니다.
위의 첫 번째 그림과 같이 유니스왑 거버넌스는 동작합니다. 250만 개 이상의 UNI(총 공급량의 0.25%)토큰을 위임받은 주소는 완성된 실행 코드가 포함된 거버넌스 작업을 제안할 수 있습니다. 제안이 생성되면 커뮤니티는 7일간의 투표 기간 동안 투표를 진행할 수 있습니다.
‘찬성’ 투표가 ‘반대’ 투표보다 많고(즉, 단순 과반수) ‘찬성’ 투표 수가 4백만 표를 초과하면(정족수 충족) 타임록에 대기되며, 최소 2일 후에 실행될 수 있습니다.
참고 :
유니스왑V3 공식 홈 : https://blog.uniswap.org/uniswap-v3
유니스왑V3 core 오라클 라이브러리: https://docs.uniswap.org/contracts/v3/reference/core/libraries/Oracle
유니스왑V3 periphery Oracle 라이브러리:https://github.com/Uniswap/v3-periphery/blob/main/contracts/libraries/OracleLibrary.sol
유니스왑V3 TWAP Oracle : https://blog.uniswap.org/uniswap-v3-oracles
이더리움 문서 Oracle : https://ethereum.org/en/developers/docs/oracles/
유니스왑V3 book : https://uniswapv3book.com/docs/milestone_5/price-oracle/