DeFi基石ERC4626标准实现一个金库合约

ERC4626标准

定义
ERC4626 是 ERC20 的扩展,提供了一个标准 API,用于表示单个底层 ERC-20 代币的收益保险库份额;

举例说明:用户通过存入 ERC20 Token,从而获取一定比例的 vToken。在 ERC20 Token 存入的过程中,会在一定的时间内产生收益。在收益到期后,用户可以通过持有的 vToken 个数,获得一定比例的收益回报;

功能:
  • 存款和提取:用户可以将 ERC20 代币存入保险库,并获取相应的份额代币。用户也可以通过销毁份额代币来提取基础资产。
  • 余额查询:用户可以查询保险库中管理的基础资产总额,以及特定用户地址的存款和提取限额。
  • 转换率:提供将基础资产转换为份额代币,以及将份额代币转换为基础资产的功能。
  • 事件:定义了存款和提取时触发的事件,如 Deposit 和 Withdraw
优点:
  • 代币化:继承了 ERC20,用户在向金库存款时,将获得同样符合 ERC20 标准的金库份额。
  • 更好的流通性:用户可以在不取回基础资产的情况下,利用金库份额进行其他操作,如在 Uniswap 上提供流动性或交易。
  • 更好的可组合性:有了标准之后,用一套接口可以和所有 ERC4626 金库交互,让基于金库的应用、插件、工具开发更容易。
在DeFi领域的使用场景
  • 收益农场:用户可以将代币质押到收益农场,获取利息。
  • 借贷:用户可以将代币出借,获取存款利息和贷款。
  • 质押:用户可以将代币质押参与质押,获取生息的代币。

合约开发

合约说明:合约包含存款,退款、铸造、转换率等功能


// 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,symblelog: true,})console.log('金库合约地址',Treasury.address)
}
module.exports.tags = ["all", "Treasury"];
# 部署指令
# npx hardhat deploy

总结

以上就是ERC4626标准的保险金库合约从开发到测试再到部署的全部过程,包含了存取款,铸造,转化率等功能,更多,,https://t.me/+_QibemQqIIg1OTY1

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/31708.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android : Camera之CHI API

来自: https://www.cnblogs.com/szsky/articles/10861918.html 一、CAM CHI API功能介绍: CHI API建立在Google HAL3的灵活性基础之上,目的是将Camera2/HAL3接口分离出来用于使用相机功能,它是一个灵活的图像处理驱动程序&#…

项目部署到生产上遇到的网络问题

今天项目上线不顺利,原因就是网络能 telnet 通过,但是就是访问不到接口。 项目使用的是 docker 部署的方式。一开始以为是网络权限没开通,一直找运维部门帮忙看,也都没发现问题,网络部门已经把权限都开了。 折腾了一番后&#x…

Odoo 18 中的列表视图装饰属性

引言 列表视图装饰在 Odoo 中提供了一种基于特定条件在列表/树形视图中直观突出显示记录或字段的方式。这些装饰能够提升用户体验,使用户更轻松地识别重要记录。在 Odoo 18 中,有多个属性可用于列表视图装饰,为数据管理提供了灵活性。 以下…

SpringMVC中有关请求参数的问题(映射路径,传递不同的参数)

目录 请求映射路径 get请求与psot请求发送普通参数 get请求发送参数 post请求发送参数 post请求乱码问题 5种参数类型传递 普通参数传递(不同名) 实体类对象传递 数组传递 集合参数 json数据传递参数 JSON数组 JSON对象 ​编辑 JSON引用集…

图片查看器:用PyQt5实现本地图片预览工具

通过python代码,基于PyQt5实现本地图片预览查看工具。 我们对窗口进行了圆角设计,图片的翻页按钮半透明处理,当鼠标移动至按钮上的动画效果,当选择某一张图片,进行左右翻页则轮播同目录所有支持的图片格式。 import …

算法优选系列(1.双指针_下)

目录 五. 有效三角形的个数(medium) 题目链接:有效三角形的个数 解法: 代码: 六:和为 s 的两个数字(easy) 题目链接:和为 s 的两个数字 解法: 代码; 七&#xf…

【数据结构】2算法及分析

0 章节 1.4到1.5小节。 掌握算法概念、特性、描述、算法性能时间复杂度和空间复杂度; 理解递归含义? 掌握实现递归的条件和时机; 应用简单递归问题的算法设计; 重点 算法…

要在Unreal Engine 5(UE5)中实现角色打击怪物并让怪物做出受击反应,

UE5系列文章目录 文章目录 UE5系列文章目录前言一、实现思路二、最终效果 前言 ue5角色受击没有播放受击动画,主角达到怪物身上没有反应 一、实现思路 要在Unreal Engine 5(UE5)中实现角色打击怪物并让怪物做出受击反应,你需要…

Java糊涂包(Hutool)的安装教程并进行网络爬虫

Hutool的使用教程 1:在官网下载jar模块文件 Central Repository: cn/hutool/hutool-all/5.8.26https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.8.26/ 下载后缀只用jar的文件 2:复制并到idea当中,右键这个模块点击增加到库 3&…

C++从零实现Json-Rpc框架

文章目录 一、项目介绍1. 基本原理2. 涉及到的技术栈3. 最终实现的效果 二、 第三方库的介绍与使用1. JsonCpp库Json的数据格式JsonCpp介绍封装Json工具类 2. muduo库muduo库是什么Muduo库常见接口介绍 3. C11异步操作std::future 三、框架设计1. 服务端模块划分NetworkProtoco…

用伪元素和jquery实现tab标签切换(下标线样式)

HTML代码 <div class"title"><div class"tab-item active">按场景</div><div class"tab-item">按名称</div><div class"tab-item">按手机号</div> </div> CSS代码 .active{positio…

Python写一个查星座的小程序,适合初学者练手——字典和if语句练习

一、界面预览 二、完整代码 # 导入必要的库 import tkinter as tk from tkinter import ttk # 导入ttk模块用于更现代的控件 from PIL import Image, ImageTk # 用于处理图片 import os # 用于文件路径操作class ZodiacApp:def __init__(self, root):self.root rootself.r…

【A2DP】蓝牙A2DP协议剖析:从架构到规范

目录 一、A2DP 协议架构 1.1 A2DP 协议栈结构组成 1.2 协议栈各部分的关系与作用 二、设备配置与角色定义&#xff08;Configurations and roles &#xff09; 2.1 角色定义 2.2 配置示例与角色体现 三、用户需求与场景 3.1 用户需求与场景 3.2 协议限制 3.3 协议要求…

C语言for循环语句的用法(非常详细)

在 C语言中&#xff0c;除了 while 和 do while&#xff0c;使用 for 语句也可以实现循环结构。 C语言for循环的基本用法 for 循环语句的一般形式如下&#xff1a; for(表达式1;表达式2;表达式3) {语句块; } 有以下几点说明&#xff1a; for 是循环结构中的关键字之一。表…

Flutter 学习之旅 之 flutter 不使用插件,实现简单带加载动画的 LoadingToast 功能

Flutter 学习之旅 之 flutter 不使用插件&#xff0c;实现简单带加载动画的 LoadingToast 功能 目录 Flutter 学习之旅 之 flutter 不使用插件&#xff0c;实现简单带加载动画的 LoadingToast 功能 一、简单介绍 二、LoadingToast 三、简单案例实现 四、关键代码 一、简单…

289. 生命游戏

根据 百度百科 &#xff0c; 生命游戏 &#xff0c;简称为 生命 &#xff0c;是英国数学家约翰何顿康威在 1970 年发明的细胞自动机。 给定一个包含 m n 个格子的面板&#xff0c;每一个格子都可以看成是一个细胞。每个细胞都具有一个初始状态&#xff1a; 1 即为 活细胞 &am…

滑动窗口及边缘化直观理解

文章目录 问题例子example求解思路边缘化边缘化原理边缘化的实际步骤marg先验约束公式先验约束公式1先验约束公式2 marg的问题及FEJ实例分析&#xff1a;VINS-Mono中的滑动窗口策略 边缘化的代码实现&#xff08;伪代码&#xff09; 参考 本文简要介绍VIO常用的滑动窗口及边缘化…

类和对象(下)

一.再谈构造函数 构造函数有构造函数体赋值实现和初始化列表两种方式 1.构造函数体赋值 在创建对象时&#xff0c;编译器通过调用构造函数&#xff0c;给对象中各个成员变量一个合适的初始值. 虽然上述构造函数调用之后&#xff0c;对象中已经有了一个初始值&#xff0c;但是…

在资源有限中逆势突围:从抗战智谋到寒门高考的破局智慧

目录 引言 一、历史中的非对称作战&#xff1a;从李牧到八路军的智谋传承 李牧戍边&#xff1a;古代军事博弈中的资源重构 八路军的游击战&#xff1a;现代战争中的智慧延续 二、创业界的逆袭之道&#xff1a;小米与拼多多的资源重构 从MVP到杠杆解 社交裂变与资源错配 …

eLection: 1靶场渗透测试

eLection: 1 来自 <eLection: 1 ~ VulnHub> 1&#xff0c;将两台虚拟机网络连接都改为NAT模式 2&#xff0c;攻击机上做namp局域网扫描发现靶机 nmap -sn 192.168.23.0/24 那么攻击机IP为192.168.23.182&#xff0c;靶场IP192.168.23.196 3&#xff0c;对靶机进行端口服…