reserve-protocol/reserve-index-dtf
GitHub: reserve-protocol/reserve-index-dtf
一个用于创建和管理链上 ERC20 指数资产组合的 DeFi 协议,通过荷兰式拍卖机制实现去中心化的资产再平衡。
Stars: 33 | Forks: 13
# Reserve Folio
## 概述
Reserve Folio 是一个用于在链上完全创建和管理符合 ERC20 标准资产组合的协议。Folio 旨在作为资产配置的唯一可信来源,实现复杂多资产组合的可组合性。
为了调整其构成,Folio 支持再平衡过程,在此过程中,`AUCTION_LAUNCHER`(或任何其他人,在延迟后)可以运行荷兰式拍卖来重新平衡 Folio。每次荷兰式拍卖表现为两个价格极值之间的指数衰减,其假设理想出清价格(包括滑点)位于价格边界之间。每次拍卖的规模由相对于逐步缩小的代币与份额比率的盈余和赤字定义(在赤字情况下单调递增;在盈余情况下单调递减)。
`AUCTION_LAUNCHER` 被信任为再平衡过程提供额外输入: 荷拍卖中包含哪些代币; 用于确定盈余/赤字的篮子限额调整; 如果设置了 `PriceControl.weightControl`,则调整篮子单位中的单个代币权重;以及 如果设置了 `PriceControl.priceControl`,则调整价格。在所有情况下,`AUCTION_LAUNCHER` 必须在 `REBALANCE_MANAGER` 设定的范围内行事。如果拍卖是免许可开启的,而不是由 `AUCTION_LAUNCHER` 开启的,则调用者对拍卖的任何细节没有影响,并且始终基于最新的现货估算针对再平衡中的所有代币进行。
`REBALANCE_MANAGER` 预期是与 Folio 关联的再平衡治理者的时间锁。Folio 的一个主要设计目标是即使在时间锁延迟下也能实现高保真的资产管理和再平衡。
`AUCTION_LAUNCHER` 预期是一个半信任的 EOA 或 multisig。他们可以在治理设定的范围内开启拍卖,希望能增加篮子和定价的精度。如果他们离线,拍卖可以通过免许可途径开启。如果 `AUCTION_LAUNCHER` 不仅离线而且 actively evil(主动作恶),至多他们可以在治理授予的范围内最大程度地偏离最终投资组合,或者完全阻止 Folio 再平衡。在 `RebalanceControl.priceControl == PriceControl.PARTIAL` 的情况下,他们还可能导致价值泄漏,但不能保证他们自己是受益人;在 `RebalanceControl.priceControl == PriceControl.ATOMIC_SWAP` 的情况下,他们可能导致价值泄漏并使自己成为受益人。
除了再平衡的 TTL 外,在再平衡期间可以开启的拍卖数量没有限制。`AUCTION_LAUNCHER` 始终有机会在免许可(无限制)期开始之前开启另一场拍卖或关闭再平衡。
### 架构
#### 0. **DAO 合约**
- **FolioDAOFeeRegistry.sol**: 处理 Folio 缴纳的更广泛生态系统 DAO 相关的费用。
- **FolioVersionRegistry.sol**: 跟踪由 DAO 拥有的各种版本的 `FolioDeployer`。
虽然未直接包含,但 `FolioVersionRegistry` 和 `FolioDAOFeeRegistry` 也依赖于现有的 `RoleRegistry` 实例。该合约必须遵守 [contracts/interfaces/IRoleRegistry.sol](contracts/interfaces/IRoleRegistry.sol) 接口。
#### 1. **Folio 合约**
- **Folio.sol**: 系统中的主要合约。代表一个 ERC20 资产组合,并包含使其能够再平衡其持有的拍卖逻辑。
- **FolioDeployer.sol**: 管理 Folio 新实例的部署。
- **FolioProxy.sol**: 一个代理合约,用于将调用委托给 Folio 实现,该实现通过 `FolioVersionRegistry` 检查升级。
#### 2. **治理**
- **FolioGovernor.sol**: 系统中的规范治理者,基于时间。
- **GovernanceDeployer.sol**: 部署质押代币和治理系统。
#### 3. **质押**
- **StakingVault.sol**: 一个保管库合约,持有质押代币并允许用户同时在多个奖励代币中赚取奖励。所有类型治理的核心投票代币。
### 角色
##### Folio
一个 Folio 有 3 个角色:
1. `DEFAULT_ADMIN_ROLE`
- 预期:Slow Folio Governor 的时间锁
- 可以添加/移除资产、设置费用、配置拍卖时长、设置拍卖延迟和平仓拍卖
- 可以配置 `REBALANCE_MANAGER` / `AUCTION_LAUNCHER`
- Folio 的主要所有者
2. `REBALANCE_MANAGER`
- 预期:Fast Folio Governor 的时间锁
- 可以开始再平衡,结束再平衡/拍卖
3. `AUCTION_LAUNCHER`
- 预期:EOA 或 multisig
- 可以开始和结束拍卖,可选择在批准的范围内设置拍卖参数
##### StakingVault
质押保管库只有一个所有者:
- 预期:Community Governor 的时间锁
- 可以添加/移除奖励代币、设置奖励半衰期和设置解除质押延迟
### 再平衡
##### 再平衡生命周期
1. 再平衡由 `REBALANCE_MANAGER` 启动,指定所有变量(代币、再平衡限额、代币权重和价格)的范围
2. 在最初提供的范围的子集内开启拍卖
a. ...由拍卖启动器开启(可选地调整再平衡限额、权重或价格)
b. ...或免许可开启(在受限期过去后,且不对原始配置进行任何更改)
3. 在拍卖中包含的任何非零规模的代币对上进行出价
4. 拍卖到期
一个再平衡一次只能运行 1 个拍卖。`AUCTION_LAUNCHER` 始终可以覆盖现有拍卖,但免许可调用者必须等待正在进行的拍卖关闭后才能开启新拍卖。随时可以关闭当前拍卖或开始新的再平衡,这也将关闭正在运行的拍卖。
##### 再平衡使用
###### 拍卖启动器窗口
再平衡首先经过一个受限期,在此期间只有 `AUCTION_LAUNCHER` 可以开启拍卖。这是为了确保 `AUCTION_LAUNCHER` 始终有时间先行行动。如果他们正在积极使用,他们的时间会被延长。此外,在免许可开启拍卖之前始终有 >= 120s 的缓冲时间。
###### TTL
再平衡有一个生存时间 (TTL),控制再平衡可以运行多长时间。在此期间可以开启任意数量的拍卖,如果接近结束,`AUCTION_LAUNCHER` 可以延长它。注意:拍卖可以在 `ttl - 1` 开启并运行超过再平衡的 TTL。
##### 再平衡目标
`REBALANCE_MANAGER` 配置大量的再平衡范围,包括在回退到无限制情况时使用的现货估算。一个“已完成”的再平衡是指再平衡中所有变量的范围 delta 都已达到 0,例如 `low == spot == high`。
```
/// Target limits for rebalancing
struct RebalanceLimits {
uint256 low; // D18{BU/share} (0, 1e27] to buy assets up to
uint256 spot; // D18{BU/share} (0, 1e27] point estimate to be used in the event of unrestricted caller
uint256 high; // D18{BU/share} (0, 1e27] to sell assets down to
}
/// Range of basket weights for BU definition
struct WeightRange {
uint256 low; // D27{tok/BU} [0, 1e54] lowest possible weight in the basket
uint256 spot; // D27{tok/BU} [0, 1e54] point estimate to be used in the event of unrestricted caller
uint256 high; // D27{tok/BU} [0, 1e54] highest possible weight in the basket
}
/// Individual token price ranges
/// @dev Unit of Account can be anything as long as it's consistent; nanoUSD is most common
struct PriceRange {
uint256 low; // D27{UoA/tok} (0, 1e45]
uint256 high; // D27{UoA/tok} (0, 1e45]
}
```
###### 再平衡限额
在开始再平衡时,`REBALANCE_MANAGER` 提供一系列目标篮子限额,定义整体再平衡的路径。`low` 点代表要购买多少篮子单位,`high` 点代表要出售多少篮子单位。`spot` 点是一个不断修正的点估计值,用于无限制调用者的情况。它必须始终位于 `low` 和 `high` 点之间。
###### 篮子权重
对于提供给再平衡的每个代币,`REBALANCE_MANAGER` 提供 `low`、`spot` 和 `high` 权重估算。与再平衡限额类似,`low` 点代表购买上限,`high` 点代表出售下限。`spot` 是一个不断修正的点估计值,用于无限制调用者的情况。它必须始终位于 `low` 和 `high` 点之间。
如果设置了 `RebalanceControl.weightControl`,`AUCTION_LAUNCHER` 可以帮助随着拍卖的进行定义篮子单位。这最适合旨在始终保持特定资产百分比细分的 Folio,而不是具有可以纯粹通过再平衡限额处理的单一月度或季度目标的 Folio。
###### 价格
对于提供给再平衡的每个代币,`REBALANCE_MANAGER` 必须提供 `low` 和 `high` 价格估算。这些设置应确保在绝大多数情况下,即使经过任何时间锁延迟,资产稍后在二级市场上的价格也将位于提供的范围内。区块间价格不精确导致的滑点也不能太大。代币允许的最大价格范围是 `1e2`,因此拍卖最大可超过 4 个数量级。这是一个极端情况,并不是希望保持最佳执行的非激进 Folio 的典型用法。
注意:如果资产的价格超出其批准范围,可能会导致 Folio 持有者的价值损失,价值流向 MEV 搜索者。在这种情况下,`AUCTION_LAUNCHER` 的工作是在损失发生之前结束再平衡。
当拍卖开始时,两个资产的 `low` 和 `high` 价格用于计算拍卖的 `startPrice` 和 `endPrice`,其中 `startPrice` 代表最乐观的价格,`endPrice` 代表最悲观的价格。
如果 `RebalanceControl.priceControl == PriceControl.PARTIAL`,`AUCTION_LAUNCHER` 可以选择整个 `low-high` 范围的子集价格范围用于每次拍卖。这赋予了 `AUCTION_LAUNCHER` 额外的责任,允许他们实现更好的执行,但也赋予他们以不诚实的价格开始拍卖的能力,一旦拍卖开始并发生 gas 战,这些价格会将价值泄漏给 MEV 搜索者。
如果 `RebalanceControl.priceControl == PriceControl.ATOMIC_SWAP`,`AUCTION_LAUNCHER` 可以更进一步,只要在预先批准的 `low-high` 范围内,就可以以固定价格执行原子交换。这允许 `AUCTION_LAUNCHER` 设定出清价格并内化与定价相关的 MEV,从而完全阻止拍卖。作为最佳实践,`AUCTION_LAUNCHER` 还应在所有成交完成后结束再平衡,作为其捆绑包中的最后一笔 tx。
###### 价格曲线

注意:如果第一个区块没有完全发生在 `start` 时间戳上,其价格可能不完全等于 `startPrice`。同样,如果最后一个区块没有完全发生在 `end` 时间戳上,`endPrice` 在最后一个区块中可能不完全等于 `endPrice`。
###### 批量 sizing(Lot Sizing)
拍卖的规模基于当前余额与 Folio 在给定篮子 `limit * weight` 下所需余额之间的差额。盈余相对于 `RebalanceLimits.high` 定义,而赤字相对于 `RebalanceLimits.low` 定义。每次拍卖,`AUCTION_LAUNCHER` 都能逐步缩小此范围,直到最终运行 `RebalanceLimits.high == RebalanceLimits.low` 为真的拍卖。再平衡也参考单个代币层面的现货权重。
拍卖 `sellAmount` 代表可以在不违反代币对中任一代币的 `limits` 的情况下交易的最大卖出代币数量。
一般来说,随着时间的推移,`sellAmount` 可能会增加或减少,具体取决于卖出代币的盈余还是买入代币的赤字是限制因素。
1. 如果卖出代币的盈余是限制因素,`sellAmount` 将随着时间的推移而增加。
2. 如果买入代币的赤字是限制因素,`sellAmount` 将随着时间的推移而减少。
###### 拍卖参与
只要满足 `price` 汇率,任何人都可以在任何拍卖中出价,最高可达 `sellAmount` 规模。
```
/// @return sellAmount {sellTok} The amount of sell token on sale in the auction at a given timestamp
/// @return bidAmount {buyTok} The amount of buy tokens required to bid for the full sell amount
/// @return price D27{buyTok/sellTok} The price at the given timestamp as an 27-decimal fixed point
function getBid(
uint256 auctionId,
IERC20 sellToken,
IERC20 buyToken,
uint256 timestamp,
uint256 maxSellAmount
) external view returns (uint256 sellAmount, uint256 bidAmount, uint256 price);
```
### 费用结构
Folio 支持 2 种类型的费用。两者都有一个 DAO 部分,其底层运作方式相同,从而限制了费用的最低额度。
##### `tvlFee`
**按单位时间计算的 AUM 费用**
DAO 提成,最低底线为 15 bps。其结果是 Folio 每年至少膨胀 15 bps。如果 tvl fee 设置为 15 bps,则 100% 的膨胀归 DAO 所有。
最高:每年 10%
##### `mintFee`
**铸造费用**
DAO 提成,最低底线为 15 bps。DAO 始终至少获得铸造价值的 15 bps。如果 mint fee 设置为 15 bps,则 100% 的铸造费用由 DAO 收取。
最高:5%
#### 费用底线
通用的 15 bps 费用底线可以由 DAO 降低,也可以在每个 Folio 基础上设置(仅限降低)。
### 单位
单位在整个代码库中用花括号 (`{}`) 记录,并使用额外的 `D18` 或 `D27` 前缀来表示何时应用了额外的小数精度,例如在比率的情况下。百分比在整个代码库中通常为 18 位小数,而汇率和价格为 27 位小数。
单位:
- `{tok}` 或 `{share}` 或 `{reward}`: 代币余额
- `D27`: 1e27
- `D18`: 1e18
- `D18{1}`: 具有 18 位小数额外精度的百分比值
- `D27{tok/share}`: 代币量子与 Folio 份额量子的比率,具有 27 位小数精度
- `D27{UoA/tok}`: 每 代币量子 的 nanoUSD 价格,具有 27 位小数精度
- `D27{tok1/tok2}`: 两个代币余额的比率,具有 27 位小数精度
- `{s}`: 秒
示例:
```
// {share} = {share} * D18{1} / D18
uint256 shares = (pendingFeeShares * feeRecipients[i].portion) / D18;
```
### 有效范围
假设代币在以下范围内:
| | Folio | Folio Collateral | StakingVault | StakingVault underlying/rewards |
| ------------ | ----- | ---------------- | ------------ | ------------------------------- |
| **Supply** | 1e36 | 1e36 | 1e36 |1e36 |
| **Decimals** | | 27 | | 21 |
治理的职责是确保 Folio 供应量不超过 1e36 供应量。
再平衡限额的汇率允许高达 1e36,并且是 18 位小数定点数。
每个代币的篮子权重允许高达 1e54,并且是 27 位小数定点数。
单个代币的 UoA (nanoUSD) 价格允许高达 1e45,并且是 27 位小数定点数。
### 奇怪的 ERC20
某些 ERC20 **不**被支持
| Weirdness | Folio | StakingVault |
| ------------------------------ | ----- | ------------ |
| Multiple Entrypoints | ❌ | ❌ |
| Pausable / Blocklist | ❌ | ❌ |
| Fee-on-transfer | ❌ | ❌ |
| ERC777 / Callback | ❌ | ❌ |
| Upward-rebasing | ✅ | ❌ |
| Downward-rebasing | ✅ | ❌ |
| Revert on zero-value transfers | ✅ | ✅ |
| Flash mint | ✅ | ✅ |
| Missing return values | ✅ | ✅ |
| No revert on failure | ✅ | ✅ |
### 链假设
假设链的区块时间等于或低于 30s。
### 治理准则
- 如果治理者计划通过 `Folio.removeFromBasket()` 将代币从篮子中移除,用户只有有限的时间在代币变得不可访问之前进行赎回。仅当奖励代币变得恶意或受损时才应使用移除。
- 如果由于价格过度波动超过交易治理者的预期而导致再平衡变得危险,`AUCTION_LAUNCHER` 有责任在损失发生之前结束再平衡。
### 发布
- [1.0.0](https://github.com/reserve-protocol/reserve-index-dtf/releases/tag/r1.0.0): 初始发布:不可重复的成对拍卖
- [2.0.0](https://github.com/reserve-protocol/reserve-index-dtf/releases/tag/r2.0.0): 可重复的成对拍卖
- 3.0.0 (跳过;从未部署):围绕再平衡的成对拍卖
- 4.0.0: 围绕再平衡的篮子拍卖
### 未来工作 / 尚未实现
1. **`delegatecall` 功能 / 领取奖励的方式**
目前无法领取奖励,例如作为持有质押 Aerodrome 头寸的结果领取 AERO。为了将这种头寸放入 Folio,需要一个自动复利层,例如 beefy 或 yearn
2. **替代社区治理系统**
我们希望在未来添加替代方案,例如 基于治理;以及 ERC20 公平启动系统
### 开发
1. 所需工具:
- Foundry
- Node v20+
- Yarn
2. 安装依赖: `yarn install`
3. 构建: `yarn compile`
4. 测试:
- 基本测试: `yarn test`
- 极限测试: `yarn test:extreme`
- 所有测试: `yarn test:all`
- 覆盖率: `forge coverage`
5. 部署:
- 部署: `yarn deploy --rpc-url --verify --verifier etherscan`
将 ETHERSCAN_API_KEY 环境变量设置为您目标的任何网络(basescan、etherscan、arbiscan 等)的 API 密钥
标签:DeFi, ERC20, MITM代理, Timelock, 代币化资产, 以太坊, 再平衡, 占用监测, 去中心化金融, 定量金融, 投资组合, 指数协议, 智能合约, 治理, 算法交易, 荷兰拍卖, 资产管理, 链上组合