[BokLee series] 2. Yield Protocol 분석

Harry Kim
Decipher Media |디사이퍼 미디어
17 min readMar 4, 2022

서울대학교 블록체인 학회 디사이퍼(Decipher) BokLee팀 에서 Fixed Rate Lending 프로토콜에 대한 글을 시리즈로 연재합니다. 본 시리즈는 Yield Protocol 및 자동 청산의 작동 원리를 자세하게 설명한 글이며, 따라서 해당 주제를 처음 접하시는 분의 경우 이해가 어려울 수 있습니다. 이해가 어려울 것 같은 분들은 본 글을 읽기 전에 관련 Youtube 영상을 먼저 시청하시는 것을 추천합니다. 해당 동영상은 BokLee 팀이 2022년 DE-FERENCE 에서 진행한 본 글 시리즈에 대한 쉬운 설명입니다.

[BokLee: Road to an Ultimate Lending Protocol]

  1. 랜딩 프로토콜에 필요한 두가지 요소
  2. Yield Protocol 분석
  3. Yield Space 분석
  4. 랜딩 프로토콜 개선 — Partial Liquidation

Authors

김한 of Decipher DeFi Research Team BokLee
홍성수 of Decipher DeFi Research Team BokLee
Seoul Nat’l Univ. Blockchain Academy Decipher(@decipher-media)
Reviewed by: 이재복

들어가며

이전 글 ‘[BokLee series] 1. 랜딩 프로토콜에 필요한 두가지 요소’에서 우리는 고정금리 대출의 필요성에 대해서 설명하였습니다. 본 게시글에서는 만기가 정해진 고정이율 대출, 예치에 대한 디파이 서비스인 Yield Protocol 이 어떻게 고정이율을 유지할 수 있는지 정리하였으며, 이를 solidity 코드로 어떻게 구현했는지 분석합니다.

fyToken

작년 6월의 디사이퍼에서 분석한 컴파운드 시리즈에서는 담보로 예치한 암호화폐에 대하여 cToken으로 담보에 대한 증명을 수행하였으며 이를 활용하여 여러 기능을 수행하였습니다. 이와 유사하게 Yield Protocol에서는 fyToken(fixed yield token)을 활용하여 고정 이자의 대출, 상환, 예금의 기능을 수행합니다. fyToken은 할인채(무이표채)로 특정 만기 이후에 기초자산과 1:1로 교환할 수 있습니다. 할인채란 발행 시 미리 액면가격에 대하여 이자율만큼 할인한 채권을 의미하며, Yield Protocol 에서는 이러한 할인채 방식을 통해서 특정한 토큰(ex. ETH, WBTC 등)을 담보로 fyDAI와 fyUSDC 를 발행합니다. 할인채의 개념에 대한 자세한 설명을 위해 Yield Protocol 문서에 존재하는 예시를 들어보겠습니다.

사용자가 1년의 만기가 남은 1fyDAI를 할인된 0.95 DAI에 구매하였다고 가정해봅시다. 그렇다면 사용자는 0.95DAI를 투자하고 1년이라는 만기가 지나면 구매했던 1fyDAI를 1DAI로 전환할 수 있습니다. 할인채의 가격을 P라고 하였을 때 이는 M이라는 만기 시 가격, n이라는 기간과 r이라는 이자율에 대하여 다음과 같이 작성할 수 있습니다.

위의 상황에 따라, 사용자가 0.95DAI를 투자하고(P=0.95), 만기시 가격을 1달러(M=1), 만기까지 1기간(n=1)이라고 가정해서 공식에 대입하여 아래와 같이 5.26%의 이자율을 계산할 수 있습니다.

그렇다면 Yield Protocol에서는 대출, 상환, 예금, 청산의 로직이 어떻게 동작하는지 알아보도록 하겠습니다.

코드 구성

Yield Protocol v2 에서는 대출, 상환, 예금의 기능을 수행하는 컨트랙트로 Cauldron, Join, Ladle, Witch 가 존재하며 다음과 같이 설명할 수 있습니다.

1. Cauldron : Vault를 관리(생성, 수정, 삭제 등 상태의 변화)
2. Join : 프로토콜에서 담보로 보유하고 있는 자산의 입금 및 출금
3. Ladle : Yield Protocol v2 에서 호출하는 모든 기능의 구현
4. Witch : 프로토콜에서 청산 수행

따라서 사용자가 Yield Protocol 서비스에서 대출, 상환, 예금 등의 기능을 수행하기 위해서는 가장 먼저 Ladle이라는 컨트랙트를 호출하고 이후에 다른 컨트랙트의 함수를 호출합니다. 또한 하나의 기능에서 발생한 여러 트랜잭션을 하나로 묶어서 batch 트랜잭션을 수행합니다. 따라서 본 글에서는 두 가지 이상의 컨트랙트를 함께 볼 예정입니다.

대출

요약 : 대출자는 담보를 예치하고 fyToken을 대출할 수 있습니다. 이 때 fyToken은 Yield Space의 AMM 알고리즘에 의해 기초자산으로 교환되어 받습니다.

대출
대출 트랜잭션(etherscan)

유저가 최초로 대출을 하는 경우, batch 로 발생하는 트랜잭션은 총 3가지입니다.

1. (담보물이 Ether인 경우) WETH로 변환 = Ladle.joinEther
2. Vault 생성 = Ladle.bulid
3. WETH 예치 및 FYToken 대출자에게 전송 = Ladle.serve

따라서 최종적으로 유저가 예치한 담보물인 WETH는 Join 컨트랙트로 전송되며, 유저는 FYToken을 발행합니다.

1. WETH 전환 (Ladle.joinEther)

담보가 이더리움인 경우 아래와 같이 대출자가 담보로 맡긴 이더리움을 내부적으로 WETH로 변환한 후 담보를 예치합니다.

Ladle.sol(joinEther)

2. Vault 생성(Ladle.build)

Yield Protocol 에서는 Vault를 통해 대출자의 정보를 관리합니다. Vault는 대출자의 담보대출에 대한 정보(대출자 주소, fyToken 상품 ID, 담보 ID)를 관리하는 대출 계약서로 대출자가 예치한 담보의 양과 대출한 자산의 양이 저장되어 있습니다. 따라서 대출자는 하나의 address 로 여러 개의 Vault 를 관리할 수 있습니다

Ladle.sol(_build)
Cauldron.sol(build)

Ladle의 build 함수는 대출자가 대출을 수행할 때 가장 먼저 호출되는 함수로 keccak256 함수와 salt를 이용하여 랜덤하게 생성된 bytes12 타입의 vaultID와 여기에 Vault Struct를 맵핑하여 Vault를 생성합니다. 이후 Cauldron의 build 함수를 실행하면 대출자의 주소, fyToken 상품 ID, 담보의 ID 정보가 Vault 구조체의 Vault ID 위치에 저장됩니다. 이렇게 저장된 Vault는 이후에 진행되는 대출에 활용됩니다.

3. 담보 예치 및 대출

1) 대출 수수료 계산

Ladle.sol(_pour)
pour : Vault 업데이트
ink : 담보의 입금량
art : fyToken의 대출량
fee : 수수료

담보 대출에 대한 정보가 생성되었다면 이번 단계에서는 담보의 예치와 fyToken의 대출 과정이 진행됩니다. 먼저 Ladle의 pour 함수에서 Vault ID, Vault 구조체를 가져와 _pour함수를 실행하여 수수료를 대출량에 추가합니다. 이 때 수수료는 만기까지 남은 시간(series.marity — block.timestamp)과 대출량, 수수료율을 곱하여 계산합니다.

2) 담보/대출에 잔고 업데이트

Cauldron.sol(pour)

cauldron.pour에서는 대출의 정보를 업데이트합니다.

Cauldron.sol(_pour)

_pour함수에서는 추가 설정된 담보금액을 업데이트한 뒤, 현재 담보금액을 확인합니다. 대출을 수행하기 위해 대출의 최소량과 최대량에 대한 유효성 검사를 수행하고 잔액을 반환합니다.

3) 담보의 유효성 검사

Cauldron.sol(_level)

대출 실행 시 오라클로 담보의 시장 가격 및 이자율을 확인하여 적절한 담보가치를 설정합니다. 이 때 만약 만기가 지난 경우 accural 함수를 통해 이자비용이 청구됩니다. 이자비용이란 만기 이후에도 대출을 갚지 않았을 때 대출자에게 청구되는 비용으로 _accrual 함수를 통해 계산되는데, _accrual 함수는 타 lending protocol 의 이자율을 기반으로 이자비용을 계산합니다. 이후 기존의 담보의 가치에서 이자비용만큼 제거한 뒤 담보의 가치를 산출하게 됩니다.

Cauldron.sol(_accrual)

이자비용은 만기가 된 이후부터 누적되며, 대출된 asset이 다른 lending protocol에 예치되었다고 가정할 때 지급되는 이자를 이자비용을 청구합니다. 현재 yield에서 참조하는 lending protocol 은 compound가 있습니다.

4) 담보와 대출 토큰의 관리

Ladle.sol(_pour)

대출의 경우 담보가 추가된 것으로 Join 컨트랙트의 join함수를 통해서 담보 변수를 증가시키고, fyToken을 민팅을 수행하게 됩니다.

3. fyToken발행 및 기초자산 교환

대출 트랜잭션 로그(etherscan)

fyToken은 할인채로 발행된 Token이고 바로 사용할 수 있는 자산이 아니기 때문에 Yield Protocol은 대출한 fyToken을 Yield Space의 pool의 AMM알고리즘을 통해서 기초자산으로 거래를 수행한 후 대출자에게 전송해줍니다. 위의 예시는 만기가 2022년 5월인 122,279 fyUSDC를 pool에서 122,000 USDC로 전환하는 이벤트로 이 결과 대출자는 fyUSDC이 아닌 USDC로 받게 됩니다.

상환

요약 : 대출자는 기초자산으로 대출 토큰을 상환할 수 있으며 담보를 출금할 수 있습니다. 이 때 기초자산은 Yield Space의 AMM 알고리즘에 의해 fyToken으로 교환되어 상환됩니다.

1. 만기 이전의 상환

만기 이전의 상환
상환 트랜잭션(etherscan)

1) 기초자산의 교환

Ladle.sol(repayVault)

대출자가 상환을 하는 경우 해당 대출의 시리즈(만기가 정해진 상품) 정보와 함께 해당 시리즈 풀(pool) 정보를 호출합니다. 이후 fyToken 상환이 발생하여 유저의 잔고를 업데이트 해주고 retrieveBase 함수를 통해 기초자산 교환 후 남은 토큰을 돌려줍니다.

상환 트랜잭션 자금 전송 로그(etherscan)
상환 트랜잭션 상환 로그(etherscan)
상환 트랜잭션 잔여토큰 반환 로그(etherscan)

예시로 확인한 트랜잭션의 개별 로그를 확인하면 사용자는 처음에 10,166.88USDC를 전송하였으며 10,166.51 USDC로 10,166.88 fyUSDC를 상환하고 남은 0.37 USDC를 반환된 것을 확인할 수 있습니다.

2) 담보 및 대출 잔고 업데이트

Cauldron.sol(_pour)

기초자산의 교환이 발생한 후 대출할 때의 사용되었던 Cauldron 컨트랙트의 pour 함수를 호출하여 잔고 정보 업데이트를 수행합니다.

3) 담보와 대출 토큰의 관리

Ladle.sol(_pour)

상환의 경우 담보가 감소하는 것으로 Join 컨트랙트의 exit 함수를 통해 담보 변수를 감소시키고 fyToken을 소각(burn)합니다.

4) 잔여 토큰 반환

Pool.sol(retrieveBase)

repayValult 함수의 마지막에서는 Yield Space의 pool 컨트랙트에 존재하는 retrieveBase를 수행합니다. 만약 기초자산의 잔고와 pool에서 저장하고 있는 값에서 차이가 발생하는 경우 상환을 수행하여 잔여 토큰을 사용자에게 반환해줍니다.

2. 만기 이후의 상환

만기 이후의 상환

만기 이후에 상환하는 경우 Yield Space에서는 더 이상 거래가 가능한 Pool이 존재하지 않고 fyToken 또한 기초자산의 토큰으로 전환되어 직접적으로 대출 자산에 대한 상환을 수행합니다.

Ladle.sol(close)
Cauldron.sol(debtToBase)

만기가 지난 시리즈에서 더 이상 추가 대출을 일으킬 수 없고 오직 상환만이 가능하기 때문에 close 함수를 통해서 상환이 이루어지며 Cauldron 컨트랙트의 debtToBase 함수를 통해 상환해야 할 토큰의 수량을 계산합니다.

예금

요약 : 예금자는 미래에 기초자산으로 교환할 수 있는 fyToken을 할인하여 구매할 수 있습니다. 즉, 고정된 이율로 기초자산을 예치할 수 있으며 만기 이후에 다시 기초자산으로 청구할 수 있습니다.

1. 기초자산 예금

예금
예금 트랜잭션(etherscan)

Yield Protocol에서 예금이란 fyToken과 Base Token의 pool을 활용하여 fyToken을 할인하여 구매하는 것을 의미합니다. 위의 예시는 200개의 USDC를 예치하고 만기가 2022년 3월인 fyUSDC를 약 201.15 개를 받아간 트랜잭션을 의미하며, 만기 이후에 예치자는 1.15 USDC 만큼의 이익을 이자로 얻을 수 있습니다. 해당 트랜잭션의 Event Log를 확인해보면 마지막에 Trade Event가 발생한 것을 확인할 수 있으며 Yield Space의 AMM 알고리즘과 거래에 대한 내용은 다음 글에서 알아보도록 하겠습니다.

2. fyToken 반환

fyToken 반환
Ladle.sol(redeem)
FYToken.sol(redeem)

만기 이후에 예금자는 fyToken를 Yield Protocol에 반환하고 Base Token을 받을 수 있습니다. Ladle 컨트랙트의 redeem함수가 실행되면 대상이 되는 fyToken의 redeem 함수가 실행되어 먼저 예금인이 반환한 수량만큼 fyToken의 소각이 발생합니다. 이후 전환되는 수량은 기존 수량보다 증가하게 되는데 이는 만기 이후에 요청한 기간만큼의 이자율이 합산되기 때문입니다.

FYToken.sol(_accrual)

이 때의 이자율 계산은 fyToken의 oracle 데이터에서 가져오게 되며 가장 가까운 만기인 3월의 fyTokenCompound의 오라클 데이터를 참고합니다. 이자율이 포함된 수량이 결정되면 기초자산을 보관하고 있던 Join 컨트랙트에서 예금자에게 전송해줍니다.

청산

요약 : 담보의 가치가 낮아지게 되면 Vault의 소유권이 컨트랙트로 이전되어 Auction 대상으로 올라가며 청산자들은 대출을 대신 갚아주고 담보를 가져갑니다.

청산

1. 청산 대상으로 분류

청산 트랜잭 대상 분류 로그(etherscan)
Witch.sol(auction)
Cauldron(_give)

특정 vault가 담보가 부족해지는 경우 Yield Protocol에서는 Witch 컨트랙트의 auction 함수를 실행시킵니다. 이 단계에서는 limit은 동시에 진행 중인 옥션에 대하여 담보 자산에 대한 제약조건을 의미하고 현재의 옥션에 들어가는 vault의 담보의 정보를 추가합니다. 그리고 auctions 배열에 기존의 vault owner와 블록의 시간을 저장하고 Cauldron에 give함수를 실행시켜 vault의 소유권을 Witch 컨트랙트로 변경시킵니다.

2. 청산 수행

청산 수행 트랜잭션(etherscan)

청산 대상으로 분류가 되면 청산자는 빚을 갚아주고 담보를 가져가는 활동을 수행합니다. 위의 예시는 플래시론을 통해서 Uniswap V3 에 DAI를 대출하여 청산을 수행하는 트랜잭션이며 Yield Protocol의 어떠한 함수를 호출하여 이루어지는지 살펴보도록 하겠습니다.

Witch.sol(buy)
base : 대신 갚는 대출량
min : 담보로 가져가는 최소량

auction이 끝나면 청산자는 Cauldron의 debtFromBase를 통해 대신 갚아주는 대출량을 계산하고 담보의 가격을 계산하여 청산자가 요구하는 최소 담보량에 대한 유효성 검사를 합니다.

Cauldron.sol(slurp)

Cauldron의 slurp함수는 청산 대상의 Vault의 담보와 빚의 상태를 감소시키는 기능을 수행합니다. 비슷한 기능을 수행하는 _pour 함수와 다른 점은 담보에 대한 유효성 검사를 하지 않는다는 점입니다. 이는 청산의 경우 담보의 유효성 검사가 필요하지 않기 때문에 제외한 것으로 볼 수 있습니다.

Witch.sol(settle)

이후 Witch컨트랙트의 settle 함수를 호출하여 실제 자산을 관리하는 Join 컨트랙트에 갚아주는 빚을 입금하고 담보를 출금합니다.

마지막으로 담보의 자산이 남아있지 않다면 Cauldron의 give 함수를 호출하여 Vault의 소유권을 다시 원래의 담보 대출자에게 돌려주고 청산 대상에서 해당 Vault를 제외하고 청산의 과정을 종료합니다.

맺으며

본 게시글에서는 Yield Protocol의 작동원리에 대해서 알아보았습니다. fyToken의 개념과 대출, 상환, 예금, 청산 과정에서 대출자, 예금자, 청산자와 같은 시장참여자들이 어떤 역할을 수행하는지를 알아보았습니다. 더 나아가 코드 레벨의 분석을 통해 Cauldron, Join, Ladle, Witch 등의 컨트랙트를 분석함으로써 어떻게 고정금리 대출, 예치를 구현하였는지 알 수 있었습니다.
하지만 할인채의 개념을 활용하는 Yield Protocol의 기능이 원활하게 수행되기 위해서는 즉, 기초자산과 fyToken을 합리적으로 교환하기 위해서는 새로운 AMM 모델이 필요합니다. fyToken은 시간이 만기에 가까워짐에 따라 가격이 기초자산에 가까워지기 때문에 유니스왑, 커브와 같은 기존 AMM 모델로 설명이 불가능합니다. 이에 Yield Protocol은 Yield Space를 통해 이것을 해결했으며 다음 글에서는 Yield Space 논문 분석을 통한 fyToken의 AMM의 도출과정에 대해 설명하겠습니다.

References

디사이퍼 — Bok Lee팀 소개

김한 — Decipher Junior Researcher, Decipher Media Lead
이병헌 — Decipher alumni
이우진 — Decipher Senior Researcher
이재복 — Decipher Junior Researcher
이재승 — Decipher alumni
최유진
홍성수 — Decipher Junior Researcher

--

--