Skip to content

ERC4626 标准实现

1. 系统概述

ERC4626 标准实现是一个基于 Solidity 实现的代币化资金库合约,完全符合 ERC4626 标准接口规范。该实现提供了标准化的收益承载代币功能,支持存款、提款和收益分配等操作。

1.1 主要特点

  • 标准兼容:完全符合 ERC4626 标准
  • 基础功能:存款、提款、份额计算等基本操作
  • 收益管理:支持收益计算和分配
  • 事件追踪:完整的事件日志记录
  • 安全检查:完善的安全性验证
  • 精确计算:支持高精度计算

2. 合约实现

solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/math/Math.sol";

/**
 * @title ERC4626 Token Vault
 * @dev ERC4626 代币化资金库标准实现
 */
contract ERC4626 is ERC20 {
    using Math for uint256;
    using SafeERC20 for IERC20;

    // 事件定义
    event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares);
    event Withdraw(
        address indexed caller,
        address indexed receiver,
        address indexed owner,
        uint256 assets,
        uint256 shares
    );

    // 状态变量
    IERC20 private immutable _asset;
    uint8 private immutable _underlyingDecimals;

    /**
     * @dev 构造函数
     */
    constructor(
        IERC20 asset_,
        string memory name_,
        string memory symbol_
    ) ERC20(name_, symbol_) {
        _asset = asset_;
        _underlyingDecimals = ERC20(address(asset_)).decimals();
    }

    /**
     * @dev 获取基础资产
     */
    function asset() public view virtual returns (address) {
        return address(_asset);
    }

    /**
     * @dev 获取总资产
     */
    function totalAssets() public view virtual returns (uint256) {
        return _asset.balanceOf(address(this));
    }

    /**
     * @dev 将资产转换为份额
     */
    function convertToShares(uint256 assets) public view virtual returns (uint256) {
        uint256 supply = totalSupply();
        return supply == 0 ? assets : assets.mulDiv(supply, totalAssets());
    }

    /**
     * @dev 将份额转换为资产
     */
    function convertToAssets(uint256 shares) public view virtual returns (uint256) {
        uint256 supply = totalSupply();
        return supply == 0 ? shares : shares.mulDiv(totalAssets(), supply);
    }

    /**
     * @dev 计算最大可存入资产数量
     */
    function maxDeposit(address) public view virtual returns (uint256) {
        return type(uint256).max;
    }

    /**
     * @dev 计算存入资产可获得的份额
     */
    function previewDeposit(uint256 assets) public view virtual returns (uint256) {
        return convertToShares(assets);
    }

    /**
     * @dev 存入资产
     */
    function deposit(uint256 assets, address receiver) public virtual returns (uint256) {
        require(assets <= maxDeposit(receiver), "ERC4626: deposit more than max");

        uint256 shares = previewDeposit(assets);
        require(shares != 0, "ERC4626: cannot mint 0 shares");

        _asset.safeTransferFrom(msg.sender, address(this), assets);
        _mint(receiver, shares);

        emit Deposit(msg.sender, receiver, assets, shares);

        return shares;
    }

    /**
     * @dev 计算最大可铸造份额
     */
    function maxMint(address) public view virtual returns (uint256) {
        return type(uint256).max;
    }

    /**
     * @dev 计算铸造份额需要的资产数量
     */
    function previewMint(uint256 shares) public view virtual returns (uint256) {
        uint256 supply = totalSupply();
        return supply == 0 ? shares : shares.mulDiv(totalAssets(), supply);
    }

    /**
     * @dev 铸造份额
     */
    function mint(uint256 shares, address receiver) public virtual returns (uint256) {
        require(shares <= maxMint(receiver), "ERC4626: mint more than max");

        uint256 assets = previewMint(shares);
        require(assets != 0, "ERC4626: cannot deposit 0 assets");

        _asset.safeTransferFrom(msg.sender, address(this), assets);
        _mint(receiver, shares);

        emit Deposit(msg.sender, receiver, assets, shares);

        return assets;
    }

    /**
     * @dev 计算最大可提取资产数量
     */
    function maxWithdraw(address owner) public view virtual returns (uint256) {
        return convertToAssets(balanceOf(owner));
    }

    /**
     * @dev 计算提取资产需要销毁的份额
     */
    function previewWithdraw(uint256 assets) public view virtual returns (uint256) {
        uint256 supply = totalSupply();
        return supply == 0 ? assets : assets.mulDiv(supply, totalAssets());
    }

    /**
     * @dev 提取资产
     */
    function withdraw(
        uint256 assets,
        address receiver,
        address owner
    ) public virtual returns (uint256) {
        require(assets <= maxWithdraw(owner), "ERC4626: withdraw more than max");

        uint256 shares = previewWithdraw(assets);
        
        if (msg.sender != owner) {
            uint256 allowed = allowance(owner, msg.sender);
            require(allowed >= shares, "ERC4626: withdraw amount exceeds allowance");
            _approve(owner, msg.sender, allowed - shares);
        }

        _burn(owner, shares);
        _asset.safeTransfer(receiver, assets);

        emit Withdraw(msg.sender, receiver, owner, assets, shares);

        return shares;
    }

    /**
     * @dev 计算最大可赎回份额
     */
    function maxRedeem(address owner) public view virtual returns (uint256) {
        return balanceOf(owner);
    }

    /**
     * @dev 计算赎回份额可获得的资产数量
     */
    function previewRedeem(uint256 shares) public view virtual returns (uint256) {
        return convertToAssets(shares);
    }

    /**
     * @dev 赎回份额
     */
    function redeem(
        uint256 shares,
        address receiver,
        address owner
    ) public virtual returns (uint256) {
        require(shares <= maxRedeem(owner), "ERC4626: redeem more than max");

        uint256 assets = previewRedeem(shares);
        
        if (msg.sender != owner) {
            uint256 allowed = allowance(owner, msg.sender);
            require(allowed >= shares, "ERC4626: redeem amount exceeds allowance");
            _approve(owner, msg.sender, allowed - shares);
        }

        _burn(owner, shares);
        _asset.safeTransfer(receiver, assets);

        emit Withdraw(msg.sender, receiver, owner, assets, shares);

        return assets;
    }

    /**
     * @dev 获取小数位数
     */
    function decimals() public view virtual override returns (uint8) {
        return _underlyingDecimals;
    }

    /**
     * @dev 内部转账前钩子函数
     */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual override {
        super._beforeTokenTransfer(from, to, amount);
    }
}

3. 功能说明

3.1 基础功能

  • 资产管理:存入、提取、铸造、赎回
  • 份额计算:资产与份额的转换
  • 限额查询:最大存入、提取限额
  • 预览功能:操作结果预览

3.2 收益管理

  • 总资产计算:计算资金库总资产
  • 份额价值:计算份额对应的资产价值
  • 收益分配:通过份额价值自动分配收益
  • 精确计算:高精度的数学计算

3.3 扩展功能

  • 授权管理:支持授权操作
  • 事件记录:详细的操作日志
  • 钩子函数:支持自定义逻辑扩展

4. 安全机制

4.1 数值验证

  • 零值检查:禁止零值操作
  • 限额检查:验证操作限额
  • 精度处理:处理小数位数差异

4.2 权限检查

  • 所有权验证:验证操作权限
  • 授权验证:检查授权额度
  • 地址验证:验证地址有效性

4.3 状态管理

  • 原子性操作:确保状态更新的原子性
  • 事件记录:记录所有重要操作
  • 安全转账:使用安全的转账方法

5. 使用示例

5.1 部署合约

javascript
const asset = "0x1234..."; // 基础资产地址
const name = "Vault Token";
const symbol = "vTKN";
const vault = await ERC4626.deploy(asset, name, symbol);
await vault.deployed();

5.2 存入资产

javascript
const assets = ethers.utils.parseEther("100");
await vault.deposit(assets, receiver);

5.3 提取资产

javascript
const assets = ethers.utils.parseEther("50");
await vault.withdraw(assets, receiver, owner);

5.4 查询份额

javascript
const assets = ethers.utils.parseEther("100");
const shares = await vault.convertToShares(assets);

6. 总结

该 ERC4626 标准实现提供了一个完整的代币化资金库合约,包括:

  • 完全符合 ERC4626 标准的接口实现
  • 安全的资产管理机制
  • 精确的收益计算和分配
  • 完善的安全检查机制
  • 详细的事件记录系统

通过严格遵循 ERC4626 标准并添加必要的安全检查,该实现为开发者提供了一个可靠、安全的资金库合约基础。

Released under the MIT License by Vogeb.