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: 在时间 t,exchange 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 相同。
状态更改:
- 用户在时间 $t$ 将 $d_a$ assets 存入池中($d_a$ 可以为负,这意味着从池中提取)。将创建 $d_s = d_a / ExchangeRate(t)$ 新的 shares 并提供给用户(或者当 $d_a$ 为负时从用户处移除并销毁)。
- 池在时间 $t$ 赚取 $d_a$(如果 $d_a$ 为负,则损失 $−d_a$)assets。由于额外的资产,exchange rate 简单地增加(如果 $d_a$ 为负,则减少)。
- 池赚取 $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 的可选元数据扩展
name
、symbol
和decimals
,这些扩展 SHOULD 反映底层 GYGP 的会计资产的name
、symbol
和decimals
。 - MAY 实现 ERC-2612 以改善在各种集成上批准 SY 代币的 UX。
- 如果 SY 代币不可转让,MAY 在调用
transfer
和transferFrom
时恢复。 - ERC-20 操作
balanceOf
、transfer
、totalSupply
等 SHOULD 在代表 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);
此只读方法返回用户如果存入 amountTokenToDeposit 的 tokenIn 将收到的 shares 数量。
- MUST 返回小于或等于
deposit
方法的实际返回值的 amountSharesOut,并且 SHOULD NOT 返回大于deposit
方法的实际返回值。 - SHOULD ONLY 在禁止使用输入的参数铸造 SY 代币时恢复(例如,超过供应上限)。
function previewRedeem(address tokenOut, uint256 amountSharesToRedeem)
external
view
returns (uint256 amountTokenOut);
此只读方法返回用户如果赎回 amountSharesToRedeem 的 tokenOut 将收到的 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 代币实现实现可选的元数据扩展,即 name
、decimals
和 symbol
语义。
安全考虑事项
符合接口的恶意实现可能会使用户面临风险。建议所有集成商(例如钱包、聚合器或其他智能合约协议)审查实现,以避免可能的漏洞利用和用户资金损失。
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.