Qaupot Blog
Software Engineering, Trip

이 글을 이해하기 위해서 컴퓨터 공학 지식이 필요하지 않습니다.

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 로 묶은 유동성 풀을 통해 상대적 가격을 산출합니다.

Token X 와 Token Y 를 Pair 로 묶어 유동성 풀을 구성했다고 가정합시다.

X : 10
Y : 10
K : (X * Y) = 100

X 와 Y 를 각각 10 씩 예치한 첫 유동성 공급 직후의 풀의 상태는 위와 같습니다.

유저가 X 10 을 지불하여, Y 로 변환하려고 합니다.

  1. X 에 10이 추가되어, 20 : 10 으로 Pool 상태가 변화합니다.
  2. 상수 K는 유지되어야 합니다. X가 Input 이므로, Y가 Output 이 됩니다.
    • X * (Y - Return) = K
    • 20 * (10 - Return) = 100
    • 10 - Return = 5
    • Return = 5
  3. 추가된 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

이 블로그는 개인 블로그입니다. 게시글은 오류를 포함하고 있을 수 있지만, 저자는 오류를 해결하기 위해 노력하고 있습니다.
게시글에 별도의 고지가 없는 경우, 크리에이티브 커먼즈 저작자표시-비영리-변경금지 4.0 라이선스를 따릅니다.

This blog is personal blog. published posts may contain some errors, but author doing efforts to clear errors.
If post have not notice of license, it under creative commons Attribution-NonCommercial-NoDerivatives 4.0.