ERC-4626: Tokenized Vault Standard

ERC-4626 是一个以太坊代币化金库(Tokenized Vault)标准,它定义了一个用于代币化金库的接口,这些金库代表对底层代币(如 ERC-20)的份额。该标准由 Joey Santoro 等人于 2022 年 3 月提出。

标准概述

ERC-4626 的主要目标是标准化代币化金库的接口,使得:

  1. 金库可以统一地表示和管理资产
  2. 集成者可以构建通用的工具来与任何 ERC-4626 金库交互
  3. 用户可以轻松比较不同金库的特性

核心接口

interface IERC4626 is IERC20, IERC165 {
    // 返回底层代币的地址
    function asset() external view returns (address assetTokenAddress);

    // 返回金库管理的总资产数量
    function totalAssets() external view returns (uint256 totalManagedAssets);

    // 将资产转换为份额
    function convertToShares(uint256 assets) external view returns (uint256 shares);

    // 将份额转换为资产
    function convertToAssets(uint256 shares) external view returns (uint256 assets);

    // 返回用户可以存入的最大资产数量
    function maxDeposit(address receiver) external view returns (uint256 maxAssets);

    // 返回用户可以铸造的最大份额数量
    function maxMint(address receiver) external view returns (uint256 maxShares);

    // 返回用户可以提取的最大资产数量
    function maxWithdraw(address owner) external view returns (uint256 maxAssets);

    // 返回用户可以赎回的最大份额数量
    function maxRedeem(address owner) external view returns (uint256 maxShares);

    // 预览存款操作将铸造的份额数量
    function previewDeposit(uint256 assets) external view returns (uint256 shares);

    // 预览铸造操作所需的资产数量
    function previewMint(uint256 shares) external view returns (uint256 assets);

    // 预览提取操作将赎回的份额数量
    function previewWithdraw(uint256 assets) external view returns (uint256 shares);

    // 预览赎回操作将提取的资产数量
    function previewRedeem(uint256 shares) external view returns (uint256 assets);

    // 存款资产并铸造份额
    function deposit(uint256 assets, address receiver) external returns (uint256 shares);

    // 铸造份额
    function mint(uint256 shares, address receiver) external returns (uint256 assets);

    // 提取资产
    function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);

    // 赎回份额
    function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);
}

主要功能

  1. 资产转换

    • convertToSharesconvertToAssets 用于在资产和份额之间进行转换
    • 这些函数考虑了金库的费用和滑点
  2. 存款和铸造

    • deposit 允许用户存入资产并获得相应份额
    • mint 允许用户直接铸造特定数量的份额
  3. 提取和赎回

    • withdraw 允许用户提取特定数量的资产
    • redeem 允许用户赎回特定数量的份额
  4. 预览功能

    • 提供预览函数来估算操作结果
    • 帮助用户了解操作的具体影响

安全考虑

  1. 重入攻击防护

    • 实现时需要注意防止重入攻击
    • 建议使用重入锁或检查-效果-交互模式
  2. 精度损失

    • 在资产和份额转换时需要注意精度损失
    • 建议使用足够大的精度(如 18 位小数)
  3. 权限控制

    • 需要正确实现权限控制
    • 特别是 withdrawredeem 函数的所有者检查

应用场景

  1. 收益聚合器

    • 自动将资产分配到最佳收益机会
    • 用户通过持有金库代币获得收益
  2. 借贷协议

    • 作为抵押品的代币化表示
    • 简化借贷流程
  3. 流动性池

    • 代币化流动性提供者的份额
    • 便于追踪和交易
  4. 保险库

    • 代币化保险策略
    • 便于保险产品的交易和转移

最佳实践

  1. 实现检查

    • 实现所有必需的功能
    • 确保符合 ERC-165 标准
  2. 事件记录

    • 记录所有重要操作
    • 包括存款、提取、铸造和赎回
  3. 错误处理

    • 提供清晰的错误信息
    • 使用 revert 而不是 require
  4. 测试覆盖

    • 编写完整的测试用例
    • 包括边界条件和错误情况

总结

ERC-4626 为代币化金库提供了一个标准化的接口,使得不同协议之间的互操作性成为可能。它简化了金库的集成和使用,同时保持了足够的灵活性以适应各种用例。在实现时,需要注意安全性、精度和用户体验等方面。

ABI 定义

[
  {
    "inputs": [],
    "name": "asset",
    "outputs": [{ "name": "", "type": "address" }],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "totalAssets",
    "outputs": [{ "name": "", "type": "uint256" }],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [{ "name": "assets", "type": "uint256" }],
    "name": "convertToShares",
    "outputs": [{ "name": "", "type": "uint256" }],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [{ "name": "shares", "type": "uint256" }],
    "name": "convertToAssets",
    "outputs": [{ "name": "", "type": "uint256" }],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [{ "name": "receiver", "type": "address" }],
    "name": "maxDeposit",
    "outputs": [{ "name": "", "type": "uint256" }],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [{ "name": "receiver", "type": "address" }],
    "name": "maxMint",
    "outputs": [{ "name": "", "type": "uint256" }],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [{ "name": "owner", "type": "address" }],
    "name": "maxWithdraw",
    "outputs": [{ "name": "", "type": "uint256" }],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [{ "name": "owner", "type": "address" }],
    "name": "maxRedeem",
    "outputs": [{ "name": "", "type": "uint256" }],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [{ "name": "assets", "type": "uint256" }],
    "name": "previewDeposit",
    "outputs": [{ "name": "", "type": "uint256" }],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [{ "name": "shares", "type": "uint256" }],
    "name": "previewMint",
    "outputs": [{ "name": "", "type": "uint256" }],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [{ "name": "assets", "type": "uint256" }],
    "name": "previewWithdraw",
    "outputs": [{ "name": "", "type": "uint256" }],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [{ "name": "shares", "type": "uint256" }],
    "name": "previewRedeem",
    "outputs": [{ "name": "", "type": "uint256" }],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      { "name": "assets", "type": "uint256" },
      { "name": "receiver", "type": "address" }
    ],
    "name": "deposit",
    "outputs": [{ "name": "", "type": "uint256" }],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      { "name": "shares", "type": "uint256" },
      { "name": "receiver", "type": "address" }
    ],
    "name": "mint",
    "outputs": [{ "name": "", "type": "uint256" }],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      { "name": "assets", "type": "uint256" },
      { "name": "receiver", "type": "address" },
      { "name": "owner", "type": "address" }
    ],
    "name": "withdraw",
    "outputs": [{ "name": "", "type": "uint256" }],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      { "name": "shares", "type": "uint256" },
      { "name": "receiver", "type": "address" },
      { "name": "owner", "type": "address" }
    ],
    "name": "redeem",
    "outputs": [{ "name": "", "type": "uint256" }],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "anonymous": false,
    "inputs": [
      { "indexed": true, "name": "caller", "type": "address" },
      { "indexed": true, "name": "owner", "type": "address" },
      { "indexed": false, "name": "assets", "type": "uint256" },
      { "indexed": false, "name": "shares", "type": "uint256" }
    ],
    "name": "Deposit",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      { "indexed": true, "name": "caller", "type": "address" },
      { "indexed": true, "name": "receiver", "type": "address" },
      { "indexed": true, "name": "owner", "type": "address" },
      { "indexed": false, "name": "assets", "type": "uint256" },
      { "indexed": false, "name": "shares", "type": "uint256" }
    ],
    "name": "Withdraw",
    "type": "event"
  }
]