Alert Source Discuss
⚠️ Draft Standards Track: ERC

ERC-5115: SY 代币

用于封装的生息代币的接口。

Authors Vu Nguyen (@mrenoon), Long Vuong (@UncleGrandpa925), Anton Buenavista (@ayobuenavista)
Created 2022-05-30
Discussion Link https://ethereum-magicians.org/t/eip-5115-super-composable-yield-token-standard/9423
Requires EIP-20

摘要

本标准提出了智能合约中封装的生息代币的 API。它是 ERC-20 代币的扩展,提供了转移、充值、提取代币以及读取余额的基本功能。

动机

产生收益的机制有各种形状和大小,因此每当一个协议构建在另一个协议的生息机制之上时,都需要手动集成。

ERC-4626 通过标准化 vaults 的接口,解决了这种碎片化问题的重要部分,vaults 是各种生息机制中的一个主要类别。

在这个 ERC 中,我们将覆盖范围扩展到 ERC-4626 无法触及的资产,即:

  • 生息资产,其用于铸造的输入代币与用于计算池价值的输入代币不同。
    • 此类别包括 AMM 流动性代币(即产生交换费用的生息资产),因为池的价值以“流动性单位”衡量(例如,UniswapV2 中的 $\sqrt k$,如 UniswapV2 白皮书中所定义),该流动性单位无法存入(因为它们不是代币)。
    • 这扩展了铸造生息资产的灵活性。例如,可能有一个 ETH vault 想要允许用户直接存入 cETH 而不是 ETH,以提高 gas 效率或改善 UX。
  • 默认情况下具有奖励代币的资产(例如,在 Compound 中供应的 COMP 奖励)。奖励代币预计将出售并复利到相同的资产中。
  • 此 ERC 可以进一步扩展,以包括奖励的处理,例如,领取累积的多个奖励代币。

虽然 ERC-4626 是一个经过精心设计且适用于大多数 vaults 的标准,但不可避免地会有一些生息机制不属于其类别(例如 LP 代币)。需要一个更灵活的标准来标准化与所有类型的生息机制的交互。

因此,我们提出了 Standardized Yield (SY),这是一个用于封装的生息代币的灵活标准,可以涵盖 DeFi 中的大多数机制。我们预见到:

  • ERC-4626 仍然是一个流行的 vault 标准,大多数 vault 应采用该标准。
  • SY 代币可以封装 DeFi 中的大多数生息机制,包括构建在生息代币之上的项目的 ERC-4626 vaults。
  • 任何需要 SY 功能的人都可以与现有的 SY 代币集成,或编写一个新的 SY(以封装目标生息代币)。
  • 奖励处理可以从 SY 代币扩展。

用例

此 ERC 旨在提供灵活性,旨在适应尽可能多的生息机制。特别是,此标准旨在足够通用,以支持以下用例及更多:

  • 货币市场供应头寸
    • 在 Compound 中借出 DAI,获得 DAI 利息和 COMP 奖励
    • 在 BenQi 中借出 ETH,获得 ETH 利息和 QI + AVAX 奖励
    • 在 Aave 中借出 USDC,获得 USDC 利息和 stkAAVE 奖励
  • AMM 流动性提供
    • 向 SushiSwap 中的 ETHUSDC 池提供 ETH + USDC,获得更多 ETH+USDC 的交换费用
    • 向 SushiSwap 中的 ETHUSDC 池提供 ETH + USDC,并将其质押在 Sushi Onsen 中,获得交换费用和 SUSHI 奖励
    • 向 3crv 池提供 USDC+DAI+USDT,并将其质押在 Convex 中,获得 3crv 交换费用和 CRV + CVX 奖励
  • Vault 头寸
    • 将 ETH 提供给 Yearn ERC-4626 vault,其中 vault 从 Yearn 的 ETH 策略中获得收益
    • 将 DAI 提供给 Harvest 并进行质押,获得 DAI 利息和 FARM 奖励
  • 流动性质押头寸
    • 持有 stETH (在 Lido 中),获得更多 stETH 的收益
  • 流动性挖矿计划
    • 在 Stargate 中提供 USDC,获得 STG 奖励
    • 在 LooksRare 中提供 LOOKS,获得 LOOKS 收益和 WETH 奖励
  • 重新定价代币
    • 将 OHM 质押到 sOHM/gOHM 中,获得 OHM 重新定价收益

ERC 希望最大限度地减少(如果不能完全消除)使用自定义 adapters 以便与许多不同形式的生息代币机制进行交互。

规范

通用生息池

我们将首先介绍 Generic Yield Generating Pool (GYGP),这是一个描述 DeFi 中大多数生息机制的模型。在每种生息机制中,都有一个资金池,其价值以 assets 衡量。许多用户为池提供流动性,以换取池的 shares,这代表了池的所有权单位。随着时间的推移,池的价值(以 assets 衡量)会增长,因此每个 share 随着时间的推移而更有价值。池可能会随着时间的推移而获得一些 reward tokens,这些代币会根据一些逻辑分发给用户(例如,按 shares 的数量成比例)。

以下是这些术语的更具体的定义:

GYGP 定义:

  • asset: 是衡量池价值的单位。在时间 t,池的总价值为 TotalAsset(t) assets
  • shares: 是代表池所有权的单位。在时间 t,总共有 TotalShares(t) shares
  • reward tokens: 随着时间的推移,池会获得 $n_{rewards}$ 种类型的奖励代币 $(n_{rewards} \ge 0)$。在时间 t,$TotalRewards_i(t)$ 是截至时间 t 池中累积的 reward token i 的数量。
  • exchange rate: 在时间 texchange rate ExchangeRate(t) 只是每个 shares 值多少 assets,$ExchangeRate(t) = \frac{TotalAsset(t)}{TotalShares(t)}$
  • users: 在时间 t,每个用户 u 在池中拥有 $shares_u(t)$ shares,价值为 $asset_u(t) = shares_u(t) \cdot ExchangeRate(t)$ assets。直到时间 t,用户 u 有权接收总共 $rewards_{u_i}(t)$ reward token i。所有用户的 shares、assets 和 rewards 的总和应与整个池的总 shares、assets 和 rewards 相同。

状态更改:

  1. 用户在时间 $t$ 将 $d_a$ assets 存入池中($d_a$ 可以为负,这意味着从池中提取)。将创建 $d_s = d_a / ExchangeRate(t)$ 新的 shares 并提供给用户(或者当 $d_a$ 为负时从用户处移除并销毁)。
  2. 池在时间 $t$ 赚取 $d_a$(如果 $d_a$ 为负,则损失 $−d_a$)assets。由于额外的资产,exchange rate 简单地增加(如果 $d_a$ 为负,则减少)。
  3. 池赚取 $d_r$ reward token $i$。每个用户将收到一定数量的 reward token $i$。

DeFi 中 GYGP 的示例:

生息机制 Asset Shares Reward tokens Exchange rate
在 Compound 中供应 USDC USDC cUSDC COMP 每个 cUSDC 的 USDC 价值,随着 USDC 供应利息的增加而增加
在 Lido 中进行 ETH 流动性质押 stETH wstETH None 每个 wstETH 的 stETH 价值,随着 ETH 质押奖励的增加而增加
在 LooksRare Compounder 中质押 LOOKS LOOKS shares (在合约中) WETH 每个 shares 的 LOOKS 价值,随着 LOOKS 奖励的增加而增加
在 $APE Compounder 中质押 APE sAPE shares (在合约中) APE 每个 shares 的 sAPE 价值,随着 APE 奖励的增加而增加
在 Sushiswap 上提供 ETH+USDC 流动性 ETHUSDC 流动性(x ETH + y USDC 的池具有 sqrt(xy) ETHUSDC 流动性) ETHUSDC Sushiswap LP (SLP) token None 每个 ETHUSDC SLP 的 ETHUSDC 流动性价值,由于交换费用而增加
在 Sushiswap 上提供 ETH+USDC 流动性并质押到 Onsen 中 ETHUSDC 流动性(x ETH + y USDC 的池具有 sqrt(xy) ETHUSDC 流动性) ETHUSDC Sushiswap LP (SLP) token SUSHI 每个 ETHUSDC SLP 的 ETHUSDC 流动性价值,由于交换费用而增加
在 Balancer 中提供 BAL+WETH 流动性(80% BAL,20% WETH) BALWETH 流动性(x BAL + y WETH 的池具有 x^0.8*y^0.2 BALWETH 流动性) BALWETH Balancer LP token None 每个 BALWETH Balancer LP token 的 BALWETH 流动性,由于交换费用而增加
在 Curve 中提供 USDC+USDT+DAI 流动性 3crv 池的流动性(每个 3crv 代币的 D 数量) 3crv token CRV 每个 3crv 代币的 3crv 池流动性,由于交换费用而增加
在 Curve 中提供 FRAX+USDC 流动性然后在 Convex 中质押 LP BALWETH 流动性(x BAL + y WETH 的池具有 x^0.8*y^0.2 BALWETH 流动性) BALWETH Balancer LP token None 每个 BALWETH Balancer LP token 的 BALWETH 流动性,由于交换费用而增加

标准化收益代币标准

概述:

Standardized Yield (SY) 是符合 GYGP 模型的任何生息机制的代币标准。每个 SY 代币代表 GYGP 中的 shares,并允许通过标准界面与 GYGP 交互。

所有 SY 代币:

  • MUST 实现 ERC-20 来表示底层 GYGP 中的 shares。
  • MUST 实现 ERC-20 的可选元数据扩展 namesymboldecimals,这些扩展 SHOULD 反映底层 GYGP 的会计资产的 namesymboldecimals
  • MAY 实现 ERC-2612 以改善在各种集成上批准 SY 代币的 UX。
  • 如果 SY 代币不可转让,MAY 在调用 transfertransferFrom 时恢复。
  • ERC-20 操作 balanceOftransfertotalSupplySHOULD 在代表 GYGP 底层持有的部分所有权的 GYGP “shares” 上运行。

SY 定义:

除了上面 GYGP 的定义之外,我们还需要定义 2 个概念:

  • input tokens: 是可以转换成资产以进入池中的代币。每个 SY 可以接受几种可能的输入代币 $tokens_{in_{i}}$

  • output tokens: 是从池中退出时可以从资产中兑换的代币。每个 SY 可以有几种可能的输出代币 $tokens_{out_{i}}$

接口

interface IStandardizedYield {
    event Deposit(
        address indexed caller,
        address indexed receiver,
        address indexed tokenIn,
        uint256 amountDeposited,
        uint256 amountSyOut
    );

    event Redeem(
        address indexed caller,
        address indexed receiver,
        address indexed tokenOut,
        uint256 amountSyToRedeem,
        uint256 amountTokenOut
    );

    function deposit(
        address receiver,
        address tokenIn,
        uint256 amountTokenToDeposit,
        uint256 minSharesOut,
        bool depositFromInternalBalance
    ) external returns (uint256 amountSharesOut);

    function redeem(
        address receiver,
        uint256 amountSharesToRedeem,
        address tokenOut,
        uint256 minTokenOut,
        bool burnFromInternalBalance
    ) external returns (uint256 amountTokenOut);

    function exchangeRate() external view returns (uint256 res);

    function getTokensIn() external view returns (address[] memory res);

    function getTokensOut() external view returns (address[] memory res);

    function yieldToken() external view returns (address);

    function previewDeposit(address tokenIn, uint256 amountTokenToDeposit)
        external
        view
        returns (uint256 amountSharesOut);

    function previewRedeem(address tokenOut, uint256 amountSharesToRedeem)
        external
        view
        returns (uint256 amountTokenOut);

    function name() external view returns (string memory);

    function symbol() external view returns (string memory);

    function decimals() external view returns (uint8);
}

方法

function deposit(
    address receiver,
    address tokenIn,
    uint256 amountTokenToDeposit,
    uint256 minSharesOut,
    bool depositFromInternalBalance
) external returns (uint256 amountSharesOut);

此函数将存入输入代币 $i$ (tokenIn) 的 amountTokenToDeposit 以铸造新的 SY shares。

如果 depositFromInternalBalance 设置为 false,则 msg.sender 需要首先将输入代币 $i$ (tokenIn) 的 amountTokenToDeposit 存入 SY 合约,然后此函数会将输入代币 $i$ 的 amountTokenToDeposit 转换为价值 $d_a$ 的 asset,并将此金额存入 receiver 的池中,receiver 将收到 amountSharesOut 的 SY 代币 (shares)。如果 depositFromInternalBalance 设置为 true,则将直接从 receiver(作为 msg.sender)获取输入代币 $i$ (tokenIn) 的 amountTokenToDeposit,并且将以与第一种情况类似的方式转换并返回给 receiver。

如果 $amountSharesOut \lt minSharesOut$,此函数应恢复。

  • MUST 发出 Deposit 事件。
  • MUST 支持 ERC-20 的 approve / transferFrom 流程,其中 tokenIn 直接从 receiver(作为 msg.sender)获取,或者如果 msg.sender 对 receiver 的输入代币具有 ERC-20 批准的 allowance。
  • MUST 在 $amountSharesOut \lt minSharesOut$ 时恢复(由于达到存款限制、滑点或用户未批准足够的 tokenIn 给 SY 合约 等)。
  • 如果 tokenIn 存款资产是链的原生货币(例如 ETH),MAY 是可支付的。
function redeem(
    address receiver,
    uint256 amountSharesToRedeem,
    address tokenOut,
    uint256 minTokenOut,
    bool burnFromInternalBalance
) external returns (uint256 amountTokenOut);

此函数将从池中赎回 $d_s$ shares,这相当于 $d_a = d_s \times ExchangeRate(t)$ assets。$d_a$ assets 将被转换为完全 amountTokenOut 的输出代币 $i$ (tokenOut)。

如果 burnFromInternalBalance 设置为 false,则用户需要首先将 amountSharesToRedeem 存入 SY 合约,然后此函数将销毁 SY 合约中浮动的 $d_s$ SY 代币 (shares) 以赎回为输出代币 $i$ (tokenOut)。此模式类似于 UniswapV2,它允许以更节省 gas 的方式与合约交互。如果 burnFromInternalBalance 设置为 true,则此函数将直接从用户处销毁 amountSharesToRedeem $d_s$ 的 SY 代币以赎回为输出代币 $i$ (tokenOut)。

如果 $amountTokenOut \lt minTokenOut$,此函数应恢复。

  • MUST 发出 Redeem 事件。
  • MUST 支持 ERC-20 的 approve / transferFrom 流程,其中 shares 直接从 receiver(作为 msg.sender)销毁,或者如果 msg.sender 对 receiver 的 shares 具有 ERC-20 批准的 allowance。
  • MUST 在 $amountTokenOut \lt minTokenOut$ 时恢复(由于达到赎回限制、滑点或用户未批准足够的 amountSharesToRedeem 给 SY 合约等)。
function exchangeRate() external view returns (uint256 res);

此方法更新并返回最新的 exchange rate,它是从 SY 代币数量到资产数量的 exchange rate,按 1e18 的固定比例因子缩放。

  • MUST 返回 $ExchangeRate(t_{now})$,使得 $ExchangeRate(t_{now}) \times syBalance / 1e18 = assetBalance$。
  • MUST NOT 包括针对 SY 合约中底层收益代币收取的费用。
function getTokensIn() external view returns (address[] memory res);

此只读方法返回可用于存入 SY 合约的所有输入代币的列表。

  • MUST 返回 ERC-20 代币地址。
  • MUST 返回至少一个地址。
  • MUST NOT 恢复。
function getTokensOut() external view returns (address[] memory res);

此只读方法返回退出 SY 合约时可以转换成的所有输出代币的列表。

  • MUST 返回 ERC-20 代币地址。
  • MUST 返回至少一个地址。
  • MUST NOT 恢复。
function yieldToken() external view returns (address);

此只读方法返回底层生息代币(代表 GYGP)地址。

  • MUST 返回符合 ERC-20 接口的代币地址,或零地址
  • MUST NOT 恢复。
  • 如果 SY 代币是封装的代币,MUST 反映确切的底层生息代币地址。
  • 如果 SY 代币是原生实现的,而不是来自封装,MAY 返回 0x 或零地址。
function previewDeposit(address tokenIn, uint256 amountTokenToDeposit)
    external
    view
    returns (uint256 amountSharesOut);

此只读方法返回用户如果存入 amountTokenToDeposittokenIn 将收到的 shares 数量。

  • MUST 返回小于或等于 deposit 方法的实际返回值的 amountSharesOut,并且 SHOULD NOT 返回大于 deposit 方法的实际返回值。
  • SHOULD ONLY 在禁止使用输入的参数铸造 SY 代币时恢复(例如,超过供应上限)。
function previewRedeem(address tokenOut, uint256 amountSharesToRedeem)
    external
    view
    returns (uint256 amountTokenOut);

此只读方法返回用户如果赎回 amountSharesToRedeemtokenOut 将收到的 tokenOut 数量。

  • MUST 返回小于或等于 redeem 方法的实际返回值的 amountTokenOut,并且 SHOULD NOT 返回大于 redeem 方法的实际返回值。
  • SHOULD ONLY 在禁止使用输入的参数销毁 SY 代币时恢复。

事件

event Deposit(
    address indexed caller,
    address indexed receiver,
    address indexed tokenIn,
    uint256 amountDeposited,
    uint256 amountSyOut
);

caller 已将精确的 tokenIn 代币转换为 SY (shares) 并将这些 SY 转移到 receiver

  • MUST 在通过 deposit 方法将输入代币存入 SY 合约时发出。
event Redeem(
    address indexed caller,
    address indexed receiver,
    address indexed tokenOut,
    uint256 amountSyToRedeem,
    uint256 amountTokenOut
);

caller 已将精确的 SY (shares) 转换为输入代币并将这些输入代币转移到 receiver

  • MUST 在通过 redeem 方法从 SY 合约中赎回输入代币时发出。

“SY”一词的选择:

“SY”(发音:/sʌɪ/),Standardized Yield 的缩写,被认为适合描述广泛的标准化可组合生息数字资产。

理由

强制执行 ERC-20,因为诸如转移、代币批准和余额计算之类的实现细节直接延续到 SY 代币。这种标准化使 SY 代币立即与所有 ERC-20 用例兼容。

如果您希望集成检测 IStandardizedYield 接口实现,则可以选择实现 ERC-165

可以选择实现 ERC-2612,以改善在各种集成上批准 SY 代币的 UX。

向后兼容性

此 ERC 完全向后兼容,因为它的实现扩展了 ERC-20 的功能,但是必须为所有 SY 代币实现实现可选的元数据扩展,即 namedecimalssymbol 语义。

安全考虑事项

符合接口的恶意实现可能会使用户面临风险。建议所有集成商(例如钱包、聚合器或其他智能合约协议)审查实现,以避免可能的漏洞利用和用户资金损失。

yieldToken 必须强烈反映底层封装的生息代币的地址。对于 SY 代币不封装生息代币,而是原生表示 GYGP share 的原生实现,则返回的地址 MAY 为零地址。否则,对于封装的代币,您可能会对 SY 代币所代表的内容产生混淆,或者可能被认为是恶意的。

版权

CC0 下放弃版权和相关权利。

Citation

Please cite this document as:

Vu Nguyen (@mrenoon), Long Vuong (@UncleGrandpa925), Anton Buenavista (@ayobuenavista), "ERC-5115: SY 代币 [DRAFT]," Ethereum Improvement Proposals, no. 5115, May 2022. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-5115.