定义:
ERC4626 是 ERC20 的扩展,提供了一个标准 API,用于表示单个底层 ERC-20 代币的收益保险库份额;
举例说明:用户通过存入 ERC20 Token,从而获取一定比例的 vToken。在 ERC20 Token 存入的过程中,会在一定的时间内产生收益。在收益到期后,用户可以通过持有的 vToken 个数,获得一定比例的收益回报;
Deposit
和 Withdraw
。合约说明:合约包含存款,退款、铸造、转换率等功能
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol";
import {ERC20, IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "hardhat/console.sol";
contract ERC4626 is ERC20, IERC4626 {
/*//
状态变量
//*/
ERC20 private immutable _asset; //
uint8 private immutable _decimals;
constructor(
ERC20 asset_,
string memory name_,
string memory symbol_
) ERC20(name_, symbol_) {
_asset = asset_;
_decimals = asset_.decimals();
}
/** @dev See {IERC4626-asset}. */
function asset() public view virtual override returns (address) {
return address(_asset);
}
/**
* See {IERC20Metadata-decimals}.
*/
function decimals() public view virtual override(IERC20Metadata, ERC20) returns (uint8) {
return _decimals;
}
/*//
存款/提款逻辑
//*/
/** @dev See {IERC4626-deposit}. */
function deposit(uint256 assets, address receiver) public virtual returns (uint256 shares) {
// 利用 previewDeposit() 计算将获得的金库份额
shares = previewDeposit(assets);
// 先 transfer 后 mint,防止重入
_asset.transferFrom(msg.sender, address(this), assets);
_mint(receiver, shares);
// 释放 Deposit 事件
emit Deposit(msg.sender, receiver, assets, shares);
}
/** @dev See {IERC4626-mint}. */
function mint(uint256 shares, address receiver) public virtual returns (uint256 assets) {
// 利用 previewMint() 计算需要存款的基础资产数额
assets = previewMint(shares);
// 先 transfer 后 mint,防止重入
_asset.transfer(address(this), assets*10**18);
// _asset.transferFrom(msg.sender, address(this), assets);
_mint(receiver, shares*10**18);
// 释放 Deposit 事件
emit Deposit(msg.sender, receiver, assets, shares);
}
/** @dev See {IERC4626-withdraw}. */
function withdraw(
uint256 assets,
address receiver,
address owner
) public virtual returns (uint256 shares) {
// 利用 previewWithdraw() 计算将销毁的金库份额
shares = previewWithdraw(assets);
// 如果调用者不是 owner,则检查并更新授权
if (msg.sender != owner) {
_spendAllowance(owner, msg.sender, shares);
}
// 先销毁后 transfer,防止重入
_burn(owner, shares*10**18);
_asset.transfer(receiver, assets);
// 释放 Withdraw 函数
emit Withdraw(msg.sender, receiver, owner, assets, shares);
}
/** @dev See {IERC4626-redeem}. */
function redeem(
uint256 shares,
address receiver,
address owner
) public virtual returns (uint256 assets) {
// 利用 previewRedeem() 计算能赎回的基础资产数额
assets = previewRedeem(shares);
// 如果调用者不是 owner,则检查并更新授权
if (msg.sender != owner) {
_spendAllowance(owner, msg.sender, shares);
}
// 先销毁后 transfer,防止重入
_burn(owner, shares*10**18);
_asset.transfer(receiver, assets*10**18);
// 释放 Withdraw 函数
emit Withdraw(msg.sender, receiver, owner, assets, shares);
}
/*//
会计逻辑
//*/
/** @dev See {IERC4626-totalAssets}. */
function totalAssets() public view virtual returns (uint256){
// 返回合约中基础资产持仓
return _asset.balanceOf(address(this));
}
/** @dev See {IERC4626-convertToShares}. */
function convertToShares(uint256 assets) public view virtual returns (uint256) {
uint256 supply = totalSupply();
// 如果 supply 为 0,那么 1:1 铸造金库份额
// 如果 supply 不为0,那么按比例铸造
return supply == 0 ? assets : assets * supply / totalAssets();
}
/** @dev See {IERC4626-convertToAssets}. */
function convertToAssets(uint256 shares) public view virtual returns (uint256) {
uint256 supply = totalSupply();
// 如果 supply 为 0,那么 1:1 赎回基础资产
// 如果 supply 不为0,那么按比例赎回
return supply == 0 ? shares : shares * totalAssets() / supply;
}
/** @dev See {IERC4626-previewDeposit}. */
function previewDeposit(uint256 assets) public view virtual returns (uint256) {
return convertToShares(assets);
}
/** @dev See {IERC4626-previewMint}. */
function previewMint(uint256 shares) public view virtual returns (uint256) {
return convertToAssets(shares);
}
/** @dev See {IERC4626-previewWithdraw}. */
function previewWithdraw(uint256 assets) public view virtual returns (uint256) {
return convertToShares(assets);
}
/** @dev See {IERC4626-previewRedeem}. */
function previewRedeem(uint256 shares) public view virtual returns (uint256) {
return convertToAssets(shares);
}
/*//
DEPOSIT/WITHDRAWAL LIMIT LOGIC
//*/
/** @dev See {IERC4626-maxDeposit}. */
function maxDeposit(address) public view virtual returns (uint256) {
return type(uint256).max;
}
/** @dev See {IERC4626-maxMint}. */
function maxMint(address) public view virtual returns (uint256) {
return type(uint256).max;
}
/** @dev See {IERC4626-maxWithdraw}. */
function maxWithdraw(address owner) public view virtual returns (uint256) {
return convertToAssets(balanceOf(owner));
}
/** @dev See {IERC4626-maxRedeem}. */
function maxRedeem(address owner) public view virtual returns (uint256) {
return balanceOf(owner);
}
}
# 编译指令
# npx hardhat compile
const {ethers,getNamedAccounts,deployments} = require("hardhat");
const { assert,expect } = require("chai");
describe("Treasury",function(){
let token;
let treasury;
let addr1;
let addr2;
let firstAccount;
let secondAccount;
beforeEach(async function(){
await deployments.fixture(["token","Treasury"]);
[addr1,addr2]=await ethers.getSigners();
firstAccount=(await getNamedAccounts()).firstAccount;
secondAccount=(await getNamedAccounts()).secondAccount;
//代币合约
const tokenDeployment = await deployments.get("MyToken");
token = await ethers.getContractAt("MyToken",tokenDeployment.address);//已经部署的合约交互
//金库合约
const treasuryDeployment = await deployments.get("ERC4626");
treasury = await ethers.getContractAt("ERC4626",treasuryDeployment.address);//已经部署的合约交互
});
describe("金库合约",function(){
it("金库合约测试",async function(){
//获取代币的余额
const balance = await token.balanceOf(firstAccount);
console.log("余额",`${ethers.utils.formatEther(balance)} ETH`);
//将代币授权给ERC4626合约
await token.approve(treasury.address,balance);
//将代币存入金库
await treasury.deposit(balance,firstAccount);
//获取金库中的代币余额
const treasuryBalance = await treasury.balanceOf(firstAccount);
console.log("金库余额",`${ethers.utils.formatEther(treasuryBalance)} ETH`);
//铸造1000个代币
await treasury.mint(1000,firstAccount);
//将代币存入金库
let treasuryBalance1=await treasury.balanceOf(firstAccount);
console.log("金库余额",`${ethers.utils.formatEther(treasuryBalance1)} ETH`);
//提款
await treasury.withdraw(100,firstAccount,firstAccount);
let treasuryBalance2=await treasury.balanceOf(firstAccount);
console.log("金库余额提款",`${ethers.utils.formatEther(treasuryBalance2)} ETH`);
//赎回
await treasury.redeem(900,firstAccount,firstAccount);
let treasuryBalance3=await treasury.balanceOf(firstAccount);
console.log("金库余额",`${ethers.utils.formatEther(treasuryBalance3)} ETH`);
});
})
})
# 测试指令
# npx hardhat test ./test/xxx.js
module.exports = async function ({getNamedAccounts,deployments}) {
const firstAccount= (await getNamedAccounts()).firstAccount;
const {deploy,log} = deployments;
const MyToken=await deployments.get("MyToken");//获取代币合约地址
const TokenAddress = MyToken.address;//代币合约地址
const Treasury=await deploy("ERC4626",{
from:firstAccount,
args: [TokenAddress,"Treasury ETH","TBTH"],//参数 代币地址,name,symble
log: true,
})
console.log('金库合约地址',Treasury.address)
}
module.exports.tags = ["all", "Treasury"];
# 部署指令
# npx hardhat deploy
以上就是ERC4626标准的保险金库合约从开发到测试再到部署的全部过程,包含了存取款,铸造,转化率等功能,更多,,https://t.me/+_QibemQqIIg1OTY1