[Balancer] 2. Balancer의 Pools

Jungwoo Pyo
BerryFi
Published in
16 min readJun 12, 2022

Balancer는 EVM-compatible한 시스템에서 DEX를 통한 자동화된 포트폴리오 매니지먼트를 제공하는 디파이 프로토콜이다. 이전 아티클에서는 Balancer의 주요 프로덕트 중 하나인 Vault 시스템과 Balancer에서 수수료를 줄이기 위해 사용되는 batchSwap에 대해 컨트랙트 코드 분석과 함께 설명했고, Balancer 프로토콜에 대한 전반적인 설명을 포함하였다. 이번 아티클에서는 Balancer에서 사용하는 Pool의 종류 및 특징과 스왑에 사용되는 수식, 각 pool들의 use cases에 대해 다뤄보고자 한다.

(source)

Author: 표정우

Audited By: 이태헌, 이동헌, 김홍욱

Intro

대부분의 AMM은 CPMM invariant을 활용한 pool로 구성되어 있고, 페어 자산의 비율은 동일하게 설정되어 있다. 자산의 성질이 어떤지에 관계없이 동일한 math를 사용하여 페어 자산 간 교환비를 제공하고 있는 경우가 대다수이다. 하지만 여러 자산들 중에는 두 자산 간 상관관계를 가지고 있는 경우가 있다. 이러한 경우 두 자산으로 CPMM invariant를 이용하는 pair를 구성하게 되면 자산 간 correlation이 전혀 반영되지 않아 시장가와 괴리가 생기게 된다. 이는 신뢰성 있는 DEX 구축을 방해한다.

Balancer에서는 pool을 구성하는 여러 자산들의 상관관계를 고려할 수 있도록 다양한 타입의 pool과 그에 맞는 invariant math들을 제공하며, customized pool을 제공하여 다양한 자산을 하나의 pool에서 관리할 수 있으며 pool 관련 파라미터를 조절 가능하도록 한다. 이번 아티클에서는 각 pool들에 적용되는 invariant math를 포함한 pool들의 특징 및 설명과 그 활용 예시에 대해 살펴보겠다.

Weighted Pool

보편적인 AMM은 두 자산의 비율을 50/50으로 설정하는 경우가 많다. Balancer의 Weighted Pool은 풀 생성자가 두 자산의 비율을 다르게 설정하여 만들 수 있도록 한다. 보통 80/20이나 60/20/20 풀을 많이 이용한다. 각 자산의 weighting이 다르고 pool에서 지원하는 자산 또한 3개 이상 가능하기 때문에, 스왑을 위한 invariant로 기존 AMM과는 조금 다른 weighted math를 사용한다.

Exposure control

DEX를 이용하는 사용자라면 누구나 비영구적 손실에 대해 고민하게 된다. 비영구적 손실은 pool에 초기 유동성을 공급했을 당시의 자산의 가격비가 변동되었을 때 발생한다. Pool에 유동성을 공급함으로써 얻는 governance yield 등을 제외하고 생각해본다면, 자산의 가격이 오를 때 디파이 프로토콜에 유동성 공급 후 얻을 수 있는 시세차익은 두 자산을 독립적으로 보유하는 것보다 적을 가능성이 높다.

특히, 특정 자산의 변동성이 매우 높다고 가정하면, 해당 자산을 포함한 pool이 안정적으로 유동성을 공급해주지 못할 가능성이 높다. 이러한 경우에 Weighted Pool을 사용하면 변동성 높은 자산의 weight를 줄임으로써 해당 자산의 가격이 pool에 미치는 영향을 기존의 방식보다 줄일 수 있다. 다음은 상대적인 가격 변동에 따른 Weighted Pool의 비영구적 손실에 대한 그래프이다.

(source) 그림 1. Weighted Pool DEX 자산의 상대적 가격 변동에 따른 비영구적 손실

Weighted Math

Weighted Math는 가격 상관관계가 없거나 크지 않은 자산 간의 스왑을 위해 설계되었다. 타 AMM과 마찬가지로, 각 자산의 가격에 영향을 주는 요소들은 pool balance, pool weights, 스왑하려는 토큰의 양 등이 있다. 또한 3가지 이상의 자산이 pool에 포함되는 경우를 고려하여야 한다.

Invariant of Weighted Math

Weighted Math의 invariant는 다음과 같이 정의된다.

  • t는 token index에 해당
  • B_t는 t번째 토큰의 balance에 해당
  • W_t는 t번째 토큰이 pool에서 차지하는 normalized weight

invariant이므로 특정 pool의 V는 항상 동일한 값으로 유지되어야 한다. 실제 두 자산의 trade는 다음과 같은 수식에 의해 이루어진다.

outGivenIn, inGivenOut

토큰 i를 통해 토큰 o를 구매하려고 한다. pool에서 차지하는 토큰의 weighting을 각각 W_i, W_o, vault에 저장된 토큰의 balance가 각각 B_i, B_o, 지불하려는 토큰 i의 양을 A_i 라고 할 때, 스왑을 통해 수령할 수 있는 토큰 o의 양은 A_o이며, 다음과 같은 수식에 의해 계산된다.

outGivenIn

마찬가지로, 스왑을 통해 받고싶은 토큰의 양(A_o)이 정해진 경우에는 다음 inGivenOut 수식을 통해 지불해야 하는 토큰 i의 양(A_i)을 계산할 수 있다.

inGivenOut

Example

(자체 제작) 그림 2. Weighted Pool에서의 swap 예시

위 그림에서처럼 두 가지 경우를 예시를 통해 살펴보겠다. weighted pool에 공급된 토큰들의 수량이 다음과 같다고 가정하자.

  • Token A: 100개
  • Token B: 300개
  • Token C: 200개

Case 1: A 50개를 B로 스왑하려고 할 때, 받을 수 있는 B의 양

사용자가 투입할 토큰의 양이 정해져 있는 경우이므로, outGivenIn 수식을 활용하여 사용자가 얻게 되는 B의 양을 계산하면 된다. pool에 포함된 A와 B의 balance는 각각 100개, 300개이며, weighted pool에서 차지하는 A와 B의 weight는 각각 0.3, 0.3으로 동일하다. 위 그림처럼 각각의 값을 식에 대입하면 받을 수 있는 B의 수는 100개이다.

Case 2: C 10개를 받기 위해서 지불해야 하는 A의 양

사용자가 받게 될 토큰의 양이 정해져 있는 경우에는 inGivenOut 수식을 활용하여 사용자가 pool에 지불할 토큰의 수를 계산할 수 있다. pool에 포함된 A와 C의 balance는 각각 100개, 200개이고, weight는 각각 0.3, 0.4이다. 이 값들을 대입하여 inGivenOut의 값을 계산해보면, 지불해야 하는 A의 양은 대략적으로 7개 정도가 된다.

Stable Pools

Stable pool은 각 자산 간의 가격변동이 거의 없는 스테이블 코인 및 합성자산들로 이루어진 pool을 구성할 때 사용한다. stable pool에 사용되는 invariant 역시 Curve protocol에서 제시한 StableSwap을 사용하며, pool의 비율이 크게 깨지지 않는 선에서는 거의 일정한 교환비를 제공한다. 이전에 BerryFi의 Curve 아티클에서 자세한 사항을 다루었기에, 본 아티클에서 Stable pool에 대한 자세한 설명은 생략하겠다.

MetaStable Pools

MetaStable pool은 Stable pool의 extension으로, stable pool이 일정한 가격을 유지하는 자산들의 AMM을 구성할 때 사용되었다면 metastable pool은 자산 간의 교환비가 일정한 AMM을 구성할 때 사용한다. 즉 자산 간의 상관관계가 매우 높지만 일정 가격에 peg되어 있지 않은 경우에 사용된다. MetaStable pool에 사용되는 math invariant 역시 StableSwap에 RateProviders를 추가하여 자산 간 교환비를 업데이트를 하는 방식으로 사용하게 된다.

Price Rate Cache를 통한 MetaStable Pool의 교환비 조정

MetaStable Pool은 RateProviders 컨트랙트를 통해 자산간의 교환비를 불러온다. 불러온 교환비에는 만료 시간이 있는데, 주기적으로 RateProviders 내에 있는 updatePriceRateCache()를 통해 pool 관리자가 수동으로 교환비를 업데이트할 수 있다. 따로 RateProviders의 컨트랙트 주소를 설정하지 않는 경우에는 자산의 교환비가 기본값인 1로 설정되어 StableSwap과 동일하게 동작한다.

Use case — wstETH/WETH 유동성 풀

MetaStable Pool을 사용한 대표적인 케이스가 바로 wstETH/WETH 풀이다. stETH는 Lido Finance에서 제공하는 Ethereum 2.0 staked Ether로써, 비콘체인의 검증인으로 참여하기 위해 스테이킹한 ETH를 유동화한 토큰이라고 생각하면 된다. Lido Finance에 대한 자세한 설명은 해당 아티클을 참고하자.

중요한 것은 stETH와 ETH는 가격적인 측면에서 강한 상관관계가 있다는 것이다. 이더리움 체인과 비콘체인이 merge되는 시점에서 stETH를 redeem함으로써 ETH를 받을 수 있기 때문에, 두 자산의 가격은 동일하지는 않지만 변동폭은 동일하거나 비슷하게 움직인다(적어도 Lido finance의 컨트랙트 결함 등의 이슈가 발생하지 않는 이상 서로 반대 방향으로 가격이 움직이는 경우는 거의 없다).

이러한 특성을 가진 자산의 교환에서 price impact를 많이 줄일 수 있는 방법은 Balancer에서 제공하는 MetaStable Pool을 사용하는 것이다.

Liquidity Bootstraping Pools(LBP)

기본적으로 Weighted Pool의 특성을 가지고 있는데, weighting이 time-dependent하게 동적으로 변하는 굉장히 특이한 pool이다. pool owner에게는 시작/끝의 weighting 값 설정 및 변화하는 시간의 설정, 스왑을 잠시 멈출 수 있는 권한 등이 주어진다. 이렇게 time-dependent weighting의 특성을 가진 pool을 활용하였을 때의 장점을 살펴보자.

Sell pressure를 방지하고 Fair Market을 형성하는 데 도움이 된다

토큰 IDO 런칭 초기에는 해당 토큰이 포함된 pool에서 수요로 인한 거래량이 급증하며, 이에 따라 시세차익을 얻기 위한 트레이딩 봇들이 많이 돌게 된다. 프로젝트의 미래를 보고 투자하려는 투자자는 단순 투기 목적으로 토큰을 구매하는 이들의 거래에 의해 합리적이지 않은 가격에 토큰을 구매하는 일이 많이 생긴다. LBP의 특성을 활용하면 투기 목적의 거래로 인해 토큰의 시세가 망가지며 프로젝트 투자자들이 손해를 보는 상황을 어느 정도 예방할 수 있다.

LBP를 활용해 초기의 프로젝트 토큰과 fiat 의 weighting을 90/10, 99/1 과 같이 프로젝트 토큰에 높은 weighting을 할당한 후, 시간이 지나면서 weighting을 50/50으로 맞추는 방식을 보통 많이 사용한다. 초기에 프로젝트 토큰의 weighting이 높으면, 해당 토큰의 시장가는 목표했던 가격에 비해 높게 형성된다. 따라서 투기 목적의 투자자는 초기에 많은 수의 토큰을 구매하기 망설여지게 되고, 시간이 지남에 따라 weighting이 조절되면서 토큰의 시장가는 점점 내려오게 된다.

프로젝트 토큰의 가격이 구매해도 좋다고 생각되는 적절한 시점이 되면, 추가적인 weighting 조정이 예고되어 있음에도 불구하고 시장 참여자들은 매수를 하게 되고, 결국 해당 토큰의 적절한 시장가가 결정되게 되는 것이다. 이렇듯이 LBP를 활용한 토큰 세일 방식은 AMM 런칭 초기의 투기성 매수를 방지하고 결국 토큰이 적절한 시장가로 유지되도록 도움을 주는 역할을 한다.

초기 자본이 적은 프로젝트에서 사용하기 적합하다

대부분의 프로젝트는 초기 자본이 적은 경우가 다수이다. 이러한 경우 pool을 만들기 위한 페어 자산을 많이 공급하는 것이 부담스러울 수가 있다(보통의 경우 페어 자산으로 스테이블코인이나 해당 블록체인의 native asset으로 구성한다). 그렇다고 적은 자산을 투입하여 유동성이 원활하지 않은 pool을 제공할 경우 스왑으로 인한 price impact가 매우 크기 때문에 장기적으로 프로젝트에 악영향을 끼칠 수 있다. 초기 풀에서 프로젝트 토큰의 weighting이 높으면, 일반적인 50/50 weighting pool에 비해 상대적으로 적은 페어 자산을 투입하여 유동성 공급이 가능하다.

Use cases — Copper

Copper라는 fair launch auction platform이 있는데, LBP를 사용하여 새로운 토큰의 initial offering을 제공한다. 펀딩의 참여자가 적을수록 해당 코인을 비싸게 사는 형태이고 세일 후반부에 살 수록 토큰을 싸게 사는 더치 옥션의 형태를 띄고 있다. 투자자는 세일에 늦게 참여하면 상대적으로 저렴한 가격에 토큰을 구매할 수 있으나, 세일 물량이 소진될경우 토큰을 구매하지 못하는 리스크가 있어 신중하게 토큰 세일에 참여하게 된다.

Additional Pools

Balancer의 pool을 이루는 base component는 위에서 소개한 weighted pool과 stable pool, time-dependent weighted와 관련된 LBP 정도이다. 이후에는 이 base component들을 기반으로 사용자의 목적에 맞게 조금씩 변형된 variant pool들을 소개해 보겠다.

Managed Pools

Managed Pools은 parameter의 제한을 많이 풀어서 자유도를 최상으로 끌어올린 형태이다. 기본적으로 Weighted Math를 사용하고, 해당 pool에 50개의 자산까지 추가할 수 있는 형태로 구성된다. 또한 LBP와 유사하게 time-based weight shifting을 지원하고, dynamic하게 weight를 조정할 수 있는 기능까지 포함한다. 이는 Managed Pool로 하여금 DEX 프레임워크의 역할을 수행할 수 있게 한다.

Feature Rich

Managed Pool은 아래와 같은 다양한 특징을 포함한다.

  • Pool Manager(s)
  • Management Fees (optional)
  • Liquidity Provider Allowlists
  • Up to 50 tokens
  • Active Token Management — Add, Remove, Change token weights
  • Circuit Breakers to protect from malicious/compromised tokens

Boosted Pools

Boosted Pool은 높은 자본 효용을 위해 사용자들에게 유휴 토큰들에 대한 거래 유동성을 외부의 프로토콜로 제공할 수 있게 해준다. 특히 스테이블코인에 대한 유동성을 매우 효과적으로 레버리징할 수 있다. Boosted pool에 대한 원리를 이해하기 위해서는 이를 구성하는 기본 컴포넌트인 linear pool과 phantom BPT에 대한 이해가 선행되어야 한다.

linear pool은 특정 자산을 목적에 맞게 변환한 토큰과 해당 자산이 페어로 이루어진 풀이다. Aave의 token-aToken pool이 바로 linear pool의 대표적인 예시이다. 기본적으로 linear pool의 토큰 쌍의 가격은 1대 1로 페깅되어 있다.

phantom BPT는 기존의 DEX에서 LP 유동성을 추가하고 제거하는 join/exit 기능을 전부 swap을 통해 수행하는 메커니즘을 뜻한다. LP에 대한 유동성을 공급하거나 제거할 때, 기존 방식은 LP token을 mint하거나 burn함으로써 유동성을 조절하였다. 하지만 phantom BPT는 LP가 생성될 당시에 모든 LP token을 발행하며, LP token을 pool 자체가 보유하고 있는 방식을 사용한다. 따라서 사용자는 LP 유동성을 추가하거나 제거하고 싶을때 swap을 통해 token과 LP token을 교환할 수 있게 된다.

(source) 그림 3. Minted-as-Needed BPT(기존 LP 구성 방식) vs phantom BPT

Boosted Pool은 여러 linear pool들을 포함하는 nested한 구조로 되어 있고, Boosted Pool은 기존의 토큰을 직접 관리하지 않고 linear pool의 LP 토큰만을 관리하는 stable pool이다. 아래의 사진을 보면 조금 이해하기 쉽다.

(source) 그림 4. Boosted Pool의 예시(여러 Linear Pool을 포함하고, 각 pool의 bb-a-Token(LP token)만이 Stable USD Pool에서 관리된다.

그림 4는 Boosted Pool을 활용하여 Stable USD Pool을 구성한 것이다. 해당 풀에는 3개의 Linear Pool(DAI, USDC, USDT)이 포함되어 있고, 각각의 linear pool은 해당 token-aToken의 페어로 구성된 별도의 pool이 존재하며, linear pool마다 bb-a-token(LP token)이 존재한다(phantom BPT 방식). 결국 Stable USD Pool이 직접 관리하는 토큰은 각 linear pool의 LP token(bb-a-DAI, bb-a-USDC, bb-a-USDT) 3종류이며, 사용자는 결국 각각의 linear pool을 통해 그들의 스테이블코인과 bb-a-token을 스왑하여 사용하게 된다. 사용자들이 해당 pool을 활용하여 스테이블코인을 스왑하는 과정은 다음과 같다.

case: USDC를 DAI로 바꾸고 싶은 경우

  1. Linear USDC Pool에서 USDC → bb-a-USDC로 스왑(phantom BPT이기 때문에 token-LP token 간 스왑이 가능)
  2. Stable USD Pool에서 bb-a-USDC → bb-a-DAI로 스왑
  3. Linear DAI Pool에서 bb-a-DAI → DAI로 스왑

Conclusion

이번 아티클에서는 weighted pool과 stable pool, MetaStable Pool, time-dependent한 LBP, managed pool, boosted pool 등 Balancer에서 기본적으로 제공하는 pool에 대한 설명과 그 예시를 살펴보았다. Balancer에 있는 pool을 잘 활용한다면 온체인 상에서 우리가 생각할 수 있는 대부분의 자산 포트폴리오를 구성할 수 있으며, 다양한 자산에 대한 pool을 사용자에게 제공함으로써 디파이 서비스에 대한 기획을 할 때 거래에 대한 제약을 크게 신경쓰지 않고 조금 더 자유롭고 폭넓은 사고가 가능할 것으로 생각된다.

--

--

Jungwoo Pyo
BerryFi

Crypto Researcher & Software Engineer | Common Computer | Decipher