이 글을 이해하기 위해서 컴퓨터 공학 지식이 필요하지 않습니다.
Decentralized Finance
DeFi 라고 불리우는 Decentralized Finance 서비스는 Smart Contract 를 통해 구현됩니다.
Decentralized Finance 는 Exchange, Staking, Liquid Swap, Lending 서비스 등을 포괄합니다.
Automated Market Maker (AMM)
DeFi 서비스를 위해서는 Crypto Currency 의 가치를 산정할 방법이 필요합니다.
여기서는 AMM 중 CPMM (Constant Product Market Maker) 방식에 대해서만 간략히 설명합니다.
Constant Product Market Maker
Crypto Currency 를 Pair 로 묶은 유동성 풀을 통해 상대적 가격을 산출합니다.
- 이 항목은 실제 구현에 대한 설명이 아닌, 이해를 위해 간소화 된 설명입니다.
- 정확한 내용은 Formal Specification of Constant Product (x × y = k) Market Maker Model and Implementation 를 참조해주세요.
Token X 와 Token Y 를 Pair 로 묶어 유동성 풀을 구성했다고 가정합시다.
X : 10
Y : 10
K : (X * Y) = 100
X 와 Y 를 각각 10 씩 예치한 첫 유동성 공급 직후의 풀의 상태는 위와 같습니다.
유저가 X 10 을 지불하여, Y 로 변환하려고 합니다.
- X 에 10이 추가되어, 20 : 10 으로 Pool 상태가 변화합니다.
- 상수 K는 유지되어야 합니다. X가 Input 이므로, Y가 Output 이 됩니다.
- X * (Y - Return) = K
- 20 * (10 - Return) = 100
- 10 - Return = 5
- Return = 5
- 추가된 X 대신 Y 가 5 만큼 풀에서 제외되며, 제외된 Y 가 X 에 대한 변환 결과가 됩니다.
위의 과정을 통해, 유저는 X 10 를 Y 5로 교환 받았습니다.
Liquid Swap
Exchange 에서는 제시한 Order 의 호가가 맞는 경우에 거래가 성립합니다. Liquid Swap 은 Liquid Pool 을 사용하여 바로 환전을 체결할 수 있으며, Flash Swap 이라고 불리기도 합니다.
일반적으로 DeFi Liquid Swap 은 AMM 을 통해 구현됩니다.
Liquid Swap 을 이용할 때는 유동성이 충분히 공급되고, 거래량이 많은지 고려해야 합니다.
- AMM 은 독립적인 시장입니다. 시장 참여자가 작은 AMM 은 유동성이 충분히 공급되지 않게 됩니다.
- 실제 환율의 변동은 유동성 공급이 아닌 Swap 에 의해 발생합니다. 거래량이 충분치 않으면 시장가격과 다를 수 있습니다.
동 맥락에서, Liquid Swap 에서 유동성 공급은 중요합니다. 이를 위해 서비스에서는 유동성 공급자를 확보하기 위해, 수수료 분배 혹은 Governance 토큰을 지급합니다.
위와 같은 특징으로 인해, 시장에는 수 많은 Liquid Swap 서비스가 존재하지만, 유동성과 거래량을 확보한 소수의 서비스에 더 많은 유저가 모이는 현상이 발생합니다.
Lending
사용자는 Lending 서비스에 담보를 맡기고, 타 토큰을 대출 받을 수 있습니다.
이 서비스 역시 Token 간의 가치 산정이 필요하지만, AMM 기반이 아니라, Oracle 로부터 Price 를 입력 받는 경우가 많습니다.
- AMM 이 동작하기 위해서는 Pair 단위의 유동성을 충분히 확보해야 하며, Lending 외의 시스템이 구현되어야 합니다.
- Oracle 에 대한 Governance 가 존재하더라도, Oracle 이 존재한다는 점에서 Decentralize 로 구분할 지는 고민이 필요합니다.
Crypto Currency 의 가격 변동에 따라, 맡긴 담보는 청산될 수 있습니다.
- Smart Contract 는 스스로 청산 대상의 확인 및 처리를 할 수 없으므로, 외부에서 청산 대상을 확인하고 Contract 를 호출하는 방식으로 이뤄집니다.
하단 코드는 Compound Protocol 서비스의 청산과 관련된 코드 중 일부입니다.
function liquidateBorrowFresh(address liquidator, address borrower, uint repayAmount, CTokenInterface cTokenCollateral) internal returns (uint, uint) {
...
/* Revert if borrower collateral token balance < seizeTokens */
require(cTokenCollateral.balanceOf(borrower) >= seizeTokens, "LIQUIDATE_SEIZE_TOO_MUCH");
// If this is also the collateral, run seizeInternal to avoid re-entrancy, otherwise make an external call
uint seizeError;
if (address(cTokenCollateral) == address(this)) {
seizeError = seizeInternal(address(this), liquidator, borrower, seizeTokens);
} else {
seizeError = cTokenCollateral.seize(liquidator, borrower, seizeTokens);
}
...
}
(Reference : https://github.com/compound-finance/compound-protocol/blob/ae4388e780a8d596d97619d9704a931a2752c2bc/contracts/CToken.sol)
function seizeInternal(address seizerToken, address liquidator, address borrower, uint seizeTokens) internal returns (uint) {
...
vars.protocolSeizeTokens = mul_(seizeTokens, Exp({mantissa: protocolSeizeShareMantissa}));
vars.liquidatorSeizeTokens = sub_(seizeTokens, vars.protocolSeizeTokens);
...
/* Emit a Transfer event */
emit Transfer(borrower, liquidator, vars.liquidatorSeizeTokens);
emit Transfer(borrower, address(this), vars.protocolSeizeTokens);
emit ReservesAdded(address(this), vars.protocolSeizeAmount, vars.totalReservesNew);
...
}
(Reference : https://github.com/compound-finance/compound-protocol/blob/ae4388e780a8d596d97619d9704a931a2752c2bc/contracts/CToken.sol)
Reference
- Yi Zhang, Xiaohong Chen, and Daejun Park (2018), "Formal Specification of Constant Product (x × y = k) Market Maker Model and Implementation"
- compound protocol, BSD-3-Clause License
- gnosis conditional tokens docs
- gnosis/conditional-tokens-market-makers, LGPL 3.0