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 标准并添加必要的安全检查,该实现为开发者提供了一个可靠、安全的资金库合约基础。