ERC4626

EIP 문서

MUST

  • share를 표현하기 위해 ERC20 를 이용해야 한다.
    • ERC20의 메타데이터 익스텐션을 반드시 구현해야 한다.
  • 예치할 토큰(이하 asset 토큰)은 ERC20이다.
  • asset(): asset 토큰의 함수 주소를 리턴
    • revert 되어서는 안된다.
  • totalAssets(): 볼트가 관리하는 asset 토큰의 양 리턴
    • 볼트 내 자산에 부과된 모든 수수료를 포함한다.
    • revert 되어서는 안된다.
  • convertToShares(): 이상적인 상황에서, 파라미터로 주어진 만큼의 asset 토큰이 주어졌을 때 제공될 share의 양
    • 볼트가 부과하는 수수료는 포함되면 안됨
    • 호출자에 따라 다르게 리턴하면 안됨
    • 실제로 교환을 할 때 이 함수를 이용한다면, 이 함수의 리턴값은 슬리피지나 기타 온체인 상태를 반영해서는 안됨
    • 너무 큰 값 입력으로 인한 오버플로우 상황 외에는 revert 되어서는 안된다.
    • 나누기 시 내림해야 한다.
  • convertToAssets(): 이상적인 상황에서, 파라미터로 주어진 만큼의 share와 교환되는 asset 토큰의 양
    • 볼트가 부과하는 수수료는 포함되면 안됨
    • 호출자에 따라 다르게 리턴하면 안됨
    • 실제로 교환을 할 때 이 함수를 이용한다면, 이 함수의 리턴값은 슬리피지나 기타 온체인 상태를 반영해서는 안됨
    • 너무 큰 값 입력으로 인한 오버플로우 상황 외에는 revert 되어서는 안된다.
    • 나누기 시 내림해야 한다.
  • maxDeposit(): reciever 명의로 입금할 수 있는 최대 asset 양(개별 제한이 있을때 등…)
    • receiver를 대상으로 deposit 호출 시 revert 되지 않을 만큼의 양을 리턴한다. 실제로 허용될 수 있는 최대 값보다 높아서는 안되며, 필요시 과소평가 해야 한다.
    • 유저의 asset 토큰 잔금은 무한이라고 가정한다. 프로토콜 내의 제한만 고려한다. (asset.balanceOf 등에 의존하지 않음)
    • 글로벌, 유저 별 제한이 있다면 이를 적용한 값을 리턴한다.
    • 프로토콜이 일시정지 되는 등 입금이 불가능한 상황이라면 0을 리턴한다.
    • 입금 제한이 없다면 2 ** 256 - 1를 리턴한다.
    • revert 되지 않는다.
  • previewDeposit(): 온체인이나 오프체인에서 현재 블록에 파라미터만큼 asset을 입금했을 때 얼만큼의 share를 받을 지 시뮬레이션한 결과를 리턴한다.
    • 동일 트랜잭션에서 deposit 호출 시 받을 수 있는 금액 이하를 리턴해야 한다. 실제 받을 수 있는 양보다 커서는 안된다. (즉, 필요 시 과소평가)
    • 글로벌/유저 입금 한도는 고려하지 않음.
    • 수수료를 적용하여 계산한다. 연동 시 수수료가 적용된 값임을 주의해야 한다.
    • 글로벌/유저 제한 등으로 인해 revert 되면 안된다.
  • deposit(): 파라미터로 주어진 asset만큼 토큰을 예치하고 share를 발행 받음
    • Deposit 이벤트를 발행해야 한다.
    • approve-transferFrom 을 이용하여 asset 토큰을 받는다.
    • 파라미터로 요청한 만큼 asset 토큰을 예치할 수 없는 경우(한도에 도달하거나, 슬리피지, asset 토큰 부족 등) revert 한다.
  • maxMint(): receiver 명의로 발행받을 수 있는 최대 share 양(개별 제한이 있을때 등…)
    • receiver를 대상으로 mint 호출 시 revert 되지 않을 만큼의 양을 리턴한다. 실제로 허용될 수 있는 최대 값보다 높아서는 안되며, 필요시 과소평가 해야 한다.
    • 유저의 asset 토큰 잔금은 무한이라고 가정한다. 프로토콜 내의 제한만 고려한다. (asset.balanceOf 등에 의존하지 않음)
    • 글로벌, 유저 별 제한이 있다면 이를 적용한 값을 리턴한다.
    • 프로토콜이 일시정지 되는 등 입금이 불가능한 상황이라면 0을 리턴한다.
    • 입금 제한이 없다면 2 ** 256 - 1를 리턴한다.
    • revert 되지 않는다.
  • previewMint(): 온체인이나 오프체인에서 현재 블록에 파라미터만큼 share를 발행받을 때 얼만큼의 asset을 지불해야 할 지 시뮬레이션한 결과를 리턴한다.
    • 동일 트랜잭션에서 mint 호출 시 지불해야 하는 금액 이상을 리턴해야 한다. 실제로 지불해야 하는 금액보다 작으면 안된다 (즉, 필요 시 과대평가)
    • 글로벌/유저 발행 한도는 고려하지 않음.
    • 수수료를 적용하여 계산한다. 연동 시 수수료가 적용된 값임을 주의해야 한다.
    • 글로벌/유저 제한 등으로 인해 revert 되면 안된다.
  • mint(): 파라미터로 주어진 share만큼 발행받으며, 이에 해당하는 asset 토큰 양을 계산하여 지불함
    • Deposit 이벤트를 발행해야 한다.
    • approve-transferFrom 을 이용하여 asset 토큰을 받는다.
    • 파라미터로 요청한 만큼 share를 발행할 수 없는 경우(한도에 도달하거나, 슬리피지, asset 토큰 부족 등) revert 한다.
  • maxWithdraw(): owner가 출금할 수 있는 최대 asset 양을 리턴
    • owner를 대상으로 withdraw 호출 시 revert 되지 않을 만큼의 양을 리턴한다. 실제로 허용될 수 있는 최대 값보다 높아서는 안되며, 필요시 과소평가 해야 한다.
    • 글로벌, 유저 별 제한이 있다면 이를 적용한 값을 리턴한다.
    • 프로토콜이 일시정지 되는 등 출금이 불가능한 상황이라면 0을 리턴한다.
    • revert 되지 않는다.
  • previewWithdraw(): 온체인이나 오프체인에서 현재 블록에 파라미터만큼 asset을 출금하기 위해 소각해야 하는 share를 시뮬레이션한 결과를 리턴한다.
    • 동일 트랜잭션에서 withdraw 호출 시 사용될 금액 이상을 리턴해야 한다. 실제로 지불해야 하는 금액보다 작으면 안된다 (즉, 필요 시 과대평가)
    • 글로벌/유저 발행 한도는 고려하지 않음.
    • 수수료를 적용하여 계산한다. 연동 시 수수료가 적용된 값임을 주의해야 한다.
    • 글로벌/유저 제한 등으로 인해 revert 되면 안된다.
  • withdraw(): 파라미터로 주어진 asset만큼 출금하며, 이에 해당하는 share 토큰 양을 계산하여 소각함
    • Withdraw 이벤트를 발행해야 한다.
    • 호출자가 share 토큰의 주인인 경우를 처리해야 한다.
    • 호출자가 share 토큰의 주인이 아니고, approve 받은 경우를 처리해야 한다.
    • 파라미터로 요청한 만큼 asset 토큰을 출금할 수 없는 경우(한도에 도달하거나, 슬리피지, share 토큰 부족 등) revert 한다.
  • maxRedeem(): owner가 환급받을 수 있는 최대 share 양을 리턴
    • owner를 대상으로 redeem 호출 시 revert 되지 않을 만큼의 양을 리턴한다. 실제로 허용될 수 있는 최대 값보다 높아서는 안되며, 필요시 과소평가 해야 한다.
    • 글로벌, 유저 별 제한이 있다면 이를 적용한 값을 리턴한다.
    • 프로토콜이 일시정지 되는 등 출금이 불가능한 상황이라면 0을 리턴한다.
    • revert 되지 않는다.
  • previewRedeem(): 온체인이나 오프체인에서 현재 블록에 파라미터만큼 share를 소각했을 때 받을 수 있는 asset 토큰의 양을 리턴
    • 동일 트랜잭션에서 redeem 호출 시 사용될 금액 이하를 리턴해야 한다. 실제로 지불해야 하는 금액보다 크면 안된다 (즉, 필요 시 과소평가)
    • 글로벌/유저 발행 한도는 고려하지 않음.
    • 수수료를 적용하여 계산한다. 연동 시 수수료가 적용된 값임을 주의해야 한다.
    • 글로벌/유저 제한 등으로 인해 revert 되면 안된다.
  • redeem(): 파라미터로 주어진 share 만큼 소각하고, 이에 해당하는 asset 토큰 양을 계산하여 출금함
    • Withdraw 이벤트를 발행해야 한다.
    • 호출자가 share 토큰의 주인인 경우를 처리해야 한다.
    • 호출자가 share 토큰의 주인이 아니고, approve 받은 경우를 처리해야 한다.
    • 파라미터로 요청한 만큼 share 토큰을 환급받을 수 없는 경우(한도에 도달하거나, 슬리피지, share 토큰 부족 등) revert 한다.

SHOULD

  • namesymbol 함수에서는 asset 토큰의 namesymbol를 어떤 방식으로든 반영한다.
  • totalAssets()는 수익률로 인해 발생하는 모든 복리 효과를 포함한다.
  • convertToSharespreviewDeposit 사이의 차이는 주가 변동 슬리피지로 간주된다.
  • convertToAssetspreviewMint 사이의 차이는 주가 변동 슬리피지로 간주된다.
  • convertToSharespreviewWithdraw 사이의 차이는 주가 변동 슬리피지로 간주된다.
  • convertToAssetspreviewRedeem 사이의 차이는 주가 변동 슬리피지로 간주된다.
  • previewDeposit에서 유저의 asset 토큰 잔금은 무한이라고 가정한다. (asset.balanceOf 등에 의존하지 않음)
  • previewMint에서 유저의 asset 토큰 잔금은 무한이라고 가정한다. (asset.balanceOf 등에 의존하지 않음)
  • previewWithdraw 에서 유저의 share 토큰 잔금은 무한이라고 가정한다. (balanceOf 등에 의존하지 않음)
  • previewRedeem 에서 유저의 share 토큰 잔금은 무한이라고 가정한다. (balanceOf 등에 의존하지 않음)
  • withdraw에서 호출자가 owner의 자금을 지출할 수 있는지 확인해야 하며, 소각될 share의 양이 allowance 내에 있는 지 확인해야 한다.
  • redeem에서 호출자가 owner의 자금을 지출할 수 있는지 확인해야 하며, 소각될 share의 양이 allowance 내에 있는 지 확인해야 한다.

MAY

  • share를 전송 불가능하게 만드려면 transfertransferFrom에서 트랜잭션을 취소한다.
  • eip-2612 Permit 기능을 구현하여 UX를 개선한다.
  • convertToShares()는 사용자당 가격이 아니라, 평균 사용자의 가격을 리턴한다. 볼트가 사용자 별로 조건이 다르더라도 이 함수에서는 평균적인 결과를 리턴해야 한다.
  • convertToAssets()는 사용자당 가격이 아니라, 평균 사용자의 가격을 리턴한다. 볼트가 사용자 별로 조건이 다르더라도 이 함수에서는 평균적인 결과를 리턴해야 한다.
  • previewDeposit()는 동일 조건으로 deposit이 revert 되는 상황이라면 revert 할 수 있다. (글로벌/유저 리밋으로 인한 revert는 예외적으로 불가하다.)
  • deposit()approve-transferFrom 외에 추가적인 방법으로 asset 토큰을 입금 받을 수 있다. 예를 들어 deposit 호출 전 토큰을 전송해둔 후 호출하여 입금을 처리하는 방법이 있다.
  • previewMint()는 동일 조건으로 mint가 revert 되는 상황이라면 revert 할 수 있다. (글로벌/유저 리밋으로 인한 revert는 예외적으로 불가하다.)
  • mint()approve-transferFrom 외에 추가적인 방법으로 asset 토큰을 입금 받을 수 있다. 예를 들어 mint 호출 전 토큰을 전송해둔 후 호출하여 입금을 처리하는 방법이 있다.
  • previewWithdraw()는 동일 조건으로 withdraw가 revert 되는 상황이라면 revert 할 수 있다. (글로벌/유저 리밋으로 인한 revert는 예외적으로 불가하다.)
  • withdraw()는 추가적인 방법으로 share 토큰을 제공할 수 있다(일반적인 소각 방법 외에). 예를 들어 withdraw 호출 전 share 토큰을 전송해둔 후 호출하는 방법 등이 있다.
  • previewRedeem()는 동일 조건으로 redeem가 revert 되는 상황이라면 revert 할 수 있다. (글로벌/유저 리밋으로 인한 revert는 예외적으로 불가하다.)
  • redeem()는 추가적인 방법으로 share 토큰을 제공할 수 있다(일반적인 소각 방법 외에). 예를 들어 redeem 호출 전 share 토큰을 전송해둔 후 호출하는 방법 등이 있다.

주의할 점

입금이나 출금 수수료를 받을 시 이것이 view 함수에 잘 적용되었는지 확인해야 한다. ERC4626 볼트에서는 사소한 view 함수 오류도 다른 컨트랙트와의 통합에 오류를 만들 수 있으므로, MUST 스펙을 모두 지켰는지 잘 확인해야 한다. 프로토콜이 일시정지 상태일 때 어떤 값을 리턴해야 하는 지도 정의되어 있으므로 이에 주의한다.

convertTo 함수는 정확한 값보다는 추정치로 취급해야 한다. 정확한 값(슬리피지나 수수료 포함)은 preview 함수를 이용해야 한다. preview 함수는 입출금 한도는 적용하지 않으므로 통합할 때에는 max 함수를 함께 조합해 사용해야 한다.

나누기 올림 내림

내림하는 함수

  • convertToShares
  • convertToAssets
  • maxDeposit (과소평가 해야 함)
  • previewDeposit(과소평가 해야 함)
  • maxMint(과소평가 해야함)
  • maxWithdraw(과소평가 해야함)
  • maxRedeem(과소평가 해야함)

올림하는 함수

  • previewMint(과대평가 해야함)
  • previewWithdraw(과대평가 해야함)

Inflation attack

// ToDo


tags: blockchain, defi, smart contract, token vault, erc20