상세 컨텐츠

본문 제목

UniswapV3 Oracle

Blockchain/DeFi

by Yongari 2023. 4. 29. 21:53

본문

 

모든 유니스왑 v3 풀은 오라클 역할을 할 수 있으며, 과거 가격 및 유동성 데이터에 대한 액세스를 제공합니다. 이 기능은 다양한 온체인 사용 사례를 열어줍니다.

과거 데이터는 관측값 배열로 저장됩니다. 처음에는 각 풀이 하나의 관측값만 추적하고 블록이 경과함에 따라 덮어씁니다. 따라서 과거 사용자가 데이터에 접근할 수 있는 범위가 제한됩니다. 그러나 거래 수수료를 지불할 의향이 있는 당사자는 추적 관찰 횟수를 늘려(최대 65535개까지) 데이터 가용 기간을 최대 9일 이상으로 확장할 수 있습니다.

풀 컨트랙트에 가격 및 유동성 이력을 직접 저장하면 호출 컨트랙트에서 논리적 오류가 발생할 가능성이 크게 줄어들고, 과거 값을 저장할 필요가 없어 통합 비용이 절감됩니다. 또한 v3 오라클의 최대 길이가 상당히 길기 때문에 콜링 컨트랙트가 오라클 배열의 길이 내(또는 완전히 포괄하는) 임의의 범위에서 시간 가중 평균을 저렴하게 구성할 수 있으므로 오라클 가격 조작을 훨씬 더 어렵게 만듭니다.

 

Observations

Observations take the following form:

 

struct Observation {
    // the block timestamp of the observation
    uint32 blockTimestamp;
    // the tick accumulator, i.e. tick * time elapsed since the pool was first initialized
    int56 tickCumulative;
    // the seconds per liquidity, i.e. seconds elapsed / max(1, liquidity) since the pool was first initialized
    uint160 secondsPerLiquidityCumulativeX128;
    // whether or not the observation is initialized
    bool initialized;
}

관측값은 v3 풀의 관측 메서드를 통해 검색할 수 있습니다. 그러나 이 방법은 오라클 데이터를 사용하는 데 권장되는 방법은 아닙니다. 대신 관찰을 선호합니다:

 

function observe(uint32[] calldata secondsAgos)
    external
    view
    returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s);

관찰을 호출할 때마다 호출자는 관찰을 반환할 시간을 나타내는 임의의 시간(초)이 포함된 배열을 지정해야 합니다. 지정된 각 시간은 저장된 가장 오래된 관측값보다 더 최근(또는 그만큼 오래된)이어야 합니다. 참고: 시간이 관측값이 기록된 블록과 정확히 일치하지 않으면 사실과 다른 관측값이 생성되므로 호출자가 수동으로 보간할 필요가 없습니다. 이것이 관찰을 통해 관찰을 사용하는 주된 이유 중 하나입니다.

오라클은 매 블록마다 최대 한 번만 업데이트되므로, secondsAgo 값이 0인 observe를 호출하면 가장 최근에 작성된 관측값이 반환되며, 이 관측값은 현재 블록의 시작(또는 그 이전)까지만 최근일 수 있다는 점에 유의하세요.

틱 누적기
눈금 누산기는 관측 시점에 활성 눈금의 누적 합계를 저장합니다. 눈금 누산기 값은 단조롭게 증가하며 현재 눈금 값만큼 초당 증가합니다.

간격에 대한 산술 평균 틱을 구하려면 호출자는 두 개의 관측값을 차례로 검색하고 두 값의 델타를 구한 다음 그 사이의 경과 시간으로 나누어야 합니다. 틱 누산기로부터 TWAP을 계산하는 방법도 백서에 나와 있습니다. 산술 평균 틱을 사용하여 가격을 도출하는 것은 기하 평균 가격에 해당한다는 점에 유의하세요.

틱 누산기 사용 방법의 예는 오라클 라이브러리를 참조하세요.

 






Liquidity accumulator


유동성 어큐뮬레이터는 관측 시점의 초/범위 내 유동성 값을 저장합니다. 유동성 누적기 값은 단조롭게 증가하며 초당 초/범위 내 유동성 값만큼 증가합니다.

구간별 고조파 평균 유동성을 도출하려면 호출자는 두 개의 관측값을 차례로 검색하여 두 값의 델타를 구한 다음 경과 시간을 이 값으로 나누어야 합니다. TWAL 계산은 백서에서 더 자세히 설명합니다.

 

 

범위 내 유동성 어큐뮬레이터는 신중하게 사용해야 합니다. 현재 틱과 현재 범위 내 유동성은 전혀 상관관계가 없을 수 있으므로 풀에서 같은 간격의 산술 평균 틱과 조화 평균 유동성을 취하면 다른 풀과 비교하여 이 풀의 특성이 부정확해질 수 있는 시나리오가 있습니다. 예를 들어 풀 A의 현재 틱이 5초 동안 0이고 5초 동안 100인 경우 틱 누산기는 50이 됩니다. 같은 간격 동안 범위 내 유동성이 5000과 50이라면 고조파 평균 유동성은 ~99가 됩니다. 이를 10초 동안 틱이 50이고 범위 내 유동성이 ~99인 풀 B(동일한 자산으로 구성)와 비교합니다. 누산기 값은 동일하지만 기본 동작은 물론 상당히 다릅니다.

 

 

 

Deriving Price From A Tick

풀의 현재 틱을 지칭할 때 "활성 틱" 또는 다른 용어를 사용할 때는 현재 가격에 가장 가까운 하한 틱 경계를 의미합니다.

풀이 생성되면 각 토큰은 쌍에 있는 토큰의 컨트랙트 주소에 따라 토큰0 또는 토큰1에 할당됩니다. 토큰이 토큰0인지 토큰1인지는 의미가 없으며, 풀 컨트랙트에서 상대적 가치 평가와 일반적인 논리를 위해 고정된 할당을 유지하는 데만 사용됩니다.

현재 틱에서 자산 가격을 도출하는 것은 토큰1에 대한 토큰0의 풀 컨트랙트 전체에 걸쳐 고정된 표현식 덕분에 가능합니다.


WETH/USDC 풀에서 WETH의 가격을 구하는 예시입니다. 여기서 WETH는 토큰0이고 USDC는 토큰1입니다:

tickCumulative의 반환값이 [ 70_000, 1_070_000 ]이고, 관측 사이의 경과 시간이 10초인 오라클 판독값이 있습니다.

누적 값의 차이(1_070_000 - 70_000 = 1_000_000)를 경과 시간(1_000_000 / 10 = 100_000)으로 나누면 이 간격에 대한 평균 틱을 구할 수 있습니다.

틱 판독값이 100_000인 경우, p(i) = 1.0001**i 공식에서 현재 틱을 i로 사용하여 토큰0(WETH)의 관점에서 토큰1(USDC)의 가치를 찾을 수 있습니다(백서 6.1 참조).

1.0001**100_000 ≅ 22015.5 USDC/WETH

틱은 부호 있는 정수이며 음수로 표현할 수 있으므로 token0이 token1보다 낮은 값인 모든 상황에서 tickCumulative는 음수 틱 값을 반환하고 token1에 대해 token0을 계산하여 0 미만의 상대 값을 반환합니다.

 

 

 

레이어 2 롤업에 대한 오라클 통합



옵티미즘
옵티미즘에서는 모든 트랜잭션이 개별 블록으로 확인됩니다. 그러나 이러한 블록의 블록 타임스탬프는 시퀀서가 수집한 마지막 L1 블록의 블록 타임스탬프를 반영합니다. 이러한 이유로 옵티미즘의 유니스왑 풀은 지연 시간이 긴 블록 타임스탬프 업데이트 프로세스로 인해 오라클을 조작하는 데 훨씬 적은 비용이 들기 때문에 오라클 가격을 제공하는 데 적합하지 않습니다. 향후에는 옵티미즘 블록 타임스탬프의 세분성이 훨씬 더 높아지거나(시퀀서에서 작은 신뢰 가정 하에), 강제 포함 트랜잭션이 오라클 보안을 개선할 가능성이 있습니다. 이러한 잠재적인 변경 사항에 대한 자세한 내용은 옵티미즘 스펙 리포지토리를 참조하시기 바랍니다. 당분간 옵티미즘에서 오라클 기능의 사용은 피해야 합니다.

 

출처: https://docs.uniswap.org/concepts/protocol/oracle

 

Oracle | Uniswap

Unfamiliar with the concept of an oracle? Check out the Ethereum Foundation's oracle overview first.

docs.uniswap.org

 

관련글 더보기