SolidityFoundry ERC4626
ERC4626简介
ERC4626 协议是一种用于代币化保险库的标准。
我们经常说 DeFi 是货币乐高,可以通过组合多个协议来创造新的协议; ERC4626 扩展了 ERC20 代币标准,旨在推动收益金库的标准化,它是 DeFi 乐高中的基础,它允许你把底层资产质押到金库合约中,从而获取一定比例的金库代币;你存入的底层资产存储在金库中的这段时间,会产生一定的收益——例如被用于借贷平台、收益聚合、流动资金池等;你可以在任何时间拿着金库代币赎回本金以及一定收益。
ERC4626 主要逻辑
ERC4626 继承了 ERC20,金库代币就是用 ERC20 代币代表的;底层资产(比如 WETH)可以是任何有价值的代币,提前存入金库;
用户将特定的 ERC20 底层资产存进金库,金库会给他铸造特定数量的金库代币;相关函数为 deposit() 和 mint()。deposit(uint assets, address receiver) 函数让用户存入 assets 数量的资产,并铸造相应数量的金库代币给 receiver 地址。mint(uint shares, address receiver) 与它类似,只不过是以将铸造的金库代币数量作为参数
当用户从金库中提取底层资产时,会销毁相应数量的金库代币。相关函数为 withdraw() 和 redeem(),前者以取出底层资产数量为参数,后者以销毁的金库代币数量为参数。
接口中定义
- asset(): 返回金库的底层资产代币地址。
- totalAssets(): 返回金库中管理的底层资产总额。
- convertToShares(): 返回利用一定数量底层资产可以换取的金库代币。
- convertToAssets(): 返回利用一定数量金库代币可以换取的底层资产。
- deposit(): 存款,向金库存入 assets 数量的底层资产,然后增发相应比例的shares 金库代币给 receiver 。触发 Deposit 事件。
- maxDeposit(): 返回单次存款可存的最大底层资产数额。
- previewDeposit(): 在当前链上环境模拟存款一定数额的底层资产能够获得的金库代币。
- mint(): 铸造,指定想获得的 shares 数量的金库代币,计算出需要存入的 assets 数量的底层资产金库从用户账户转出 assets 数量的底层资产,再给 receiver 铸造相应数量的金库代币。触发 Deposit 事件。
- maxMint(): 返回单次可以铸造的最大金库代币额度。
- previewMint(): 用于用户在当前链上环境模拟铸造一定数额的金库额度需要存款的基础资产数量。
- withdraw(): 提款,金库将assets数量的底层资产发送给 receiver ,owner 销毁相应数量的金库代币。触发 Withdraw 事件。
- maxWithdraw(): 返回某个用户地址单次取款可以提取的最大基础资产额度。
- previewWithdraw(): 当前链上环境模拟提款一定数量的底层资产需要的金库代币数量。
- redeem(): 赎回,owner 销毁shares 数量的金库代币,然后金库将相应数量的底层资产发给 receiver,触发 Withdraw 事件。
- maxRedeem(): 返回单次赎回,可以销毁的最大金库代币。
- previewRedeem(): 在当前链上环境模拟销毁制定数量的金库代币能够赎回的底层资产数量。
IERC4626 接口合约共包含 2 个事件:
- Deposit : 存款时触发事件。
- Withdraw : 取款时触发事件。
代码分析
这里直接使用openzeppelin库的源码
abstract contract ERC4626 is ERC20, IERC4626 {using Math for uint256;IERC20 private immutable _asset; //底层资产地址uint8 private immutable _decimals; //shares的decimal//初始化constructor(IERC20 asset_) {(bool success, uint8 assetDecimals) = _tryGetAssetDecimals(asset_);_decimals = success ? assetDecimals : super.decimals();_asset = asset_;}//获取底层资产decimalfunction _tryGetAssetDecimals(IERC20 asset_) private view returns (bool, uint8) {//使用底层资产地址通过staticcall,获取decimal(bool success, bytes memory encodedDecimals) = address(asset_).staticcall(abi.encodeWithSelector(IERC20Metadata.decimals.selector));//返回成功,且有数据,就判断数据是否<=255,满足就返回该值;if (success && encodedDecimals.length >= 32) {uint256 returnedDecimals = abi.decode(encodedDecimals, (uint256));if (returnedDecimals <= type(uint8).max) {return (true, uint8(returnedDecimals));}}return (false, 0);}//获取金库代币的decimal,注意这是对ERC20中函数的重写;function decimals() public view virtual override(IERC20Metadata, ERC20) returns (uint8) {return _decimals;}//底层资产地址function asset() public view virtual override returns (address) {return address(_asset);}//金库中底层资产的数量function totalAssets() public view virtual override returns (uint256) {return _asset.balanceOf(address(this));}//指定数量的底层资产可换取金库代币的数量function convertToShares(uint256 assets) public view virtual override returns (uint256 shares) {return _convertToShares(assets, Math.Rounding.Down);}//指定数量的金库代币可换取底层资产的数量function convertToAssets(uint256 shares) public view virtual override returns (uint256 assets) {return _convertToAssets(shares, Math.Rounding.Down);}//如何金库底层资产数量>0 || 金库代币=0,则返回2^256-1 否则为0;function maxDeposit(address) public view virtual override returns (uint256) {return _isVaultCollateralized() ? type(uint256).max : 0;}//单次最大铸造金库代币,返回2^256-1 function maxMint(address) public view virtual override returns (uint256) {return type(uint256).max;}//返回owner账户下金库代币,可以兑换出来的底层资产数量 function maxWithdraw(address owner) public view virtual override returns (uint256) {return _convertToAssets(balanceOf(owner), Math.Rounding.Down);}//返回owner账户下金库代币数量function maxRedeem(address owner) public view virtual override returns (uint256) {return balanceOf(owner);}//计算此时存入指定数量的底层资产,可以铸造多少金库代币;实际金库代币要低于理论值function previewDeposit(uint256 assets) public view virtual override returns (uint256) {return _convertToShares(assets, Math.Rounding.Down);}//计算此时要铸造指定数量的金库代币,可以质押的底层资产数量;实际质押的底层资产数量要高于理论值function previewMint(uint256 shares) public view virtual override returns (uint256) {return _convertToAssets(shares, Math.Rounding.Up);}//计算此时赎回指定数量的底层资产,需要多少金库代币;实际金库代币要高于理论值function previewWithdraw(uint256 assets) public view virtual override returns (uint256) {return _convertToShares(assets, Math.Rounding.Up);}//计算此时指定数量的金库代币,可以赎回的底层资产数量;实际兑换的底层资产数量要小于理论值function previewRedeem(uint256 shares) public view virtual override returns (uint256) {return _convertToAssets(shares, Math.Rounding.Down);}//存入指定数量的底层资产,并为receiver铸造相应的金库代币function deposit(uint256 assets, address receiver) public virtual override returns (uint256) {//校验参数,质押的底层资产数量,不可大于receiver能质押的最大值require(assets <= maxDeposit(receiver), "ERC4626: deposit more than max");//计算可获取金库代币数量uint256 shares = previewDeposit(assets);//用户向金库质押assets数量的底层资产,为receiver增发shares数量的金库代币_deposit(_msgSender(), receiver, assets, shares);return shares;}//要铸造指定数量的金库代币,并扣除相应的底层资产;function mint(uint256 shares, address receiver) public virtual override returns (uint256) {//校验参数require(shares <= maxMint(receiver), "ERC4626: mint more than max");//计算增发shares数量的代币金库,需要多少底层资产uint256 assets = previewMint(shares);//用户向金库质押assets数量的底层资产,为receiver增发shares数量的金库代币_deposit(_msgSender(), receiver, assets, shares);return assets;}//赎回指定数量的底层资产,并扣除相应的金库代币function withdraw(uint256 assets,address receiver,address owner) public virtual override returns (uint256) {require(assets <= maxWithdraw(owner), "ERC4626: withdraw more than max");//计算赎回指定的底层资产,需要的金库代币数量uint256 shares = previewWithdraw(assets);//销毁owner名下,shares数量的金库代币,给receiver转入assets数量的底层资产_withdraw(_msgSender(), receiver, owner, assets, shares);return shares;}//销毁指定数量的金库代币,获取想要数量的底层资产function redeem(uint256 shares,address receiver,address owner) public virtual override returns (uint256) {require(shares <= maxRedeem(owner), "ERC4626: redeem more than max");//计算指定数量的金库代币,可以赎回底层资产的数量uint256 assets = previewRedeem(shares);//销毁owner名下,shares数量的金库代币,给receiver转入assets数量的底层资产_withdraw(_msgSender(), receiver, owner, assets, shares);return assets;}//计算此时指定数量的底层资产,返回可以铸造多少金库代币;function _convertToShares(uint256 assets, Math.Rounding rounding) internal view virtual returns (uint256 shares) {uint256 supply = totalSupply();return(assets == 0 || supply == 0)? _initialConvertToShares(assets, rounding): assets.mulDiv(supply, totalAssets(), rounding);//返回assets * 金库代币总量 /底层资产总量}//金库代币总量为0时或者底层资产为0,金库代币增发数量function _initialConvertToShares(uint256 assets,Math.Rounding /*rounding*/) internal view virtual returns (uint256 shares) {return assets;//默认1:1}//计算此时指定数量的金库代币,返回可以兑换的底层资产数量;function _convertToAssets(uint256 shares, Math.Rounding rounding) internal view virtual returns (uint256 assets) {uint256 supply = totalSupply();return(supply == 0) ? _initialConvertToAssets(shares, rounding) : shares.mulDiv(totalAssets(), supply, rounding);//返回shares* 底层资产总量 /金库代币总量}//金库代币总量为0时,可兑换底层资产的数量function _initialConvertToAssets(uint256 shares,Math.Rounding /*rounding*/) internal view virtual returns (uint256 assets) {return shares; //默认1:1}//caller向金库质押assets数量的底层资产,为receiver增发shares数量的金库代币function _deposit(address caller,address receiver,uint256 assets,uint256 shares) internal virtual {//使用safeerc20的safetransferfrom方法,进行底层资产的转账;SafeERC20.safeTransferFrom(_asset, caller, address(this), assets);//增发金库代币_mint(receiver, shares);emit Deposit(caller, receiver, assets, shares);}//销毁owner名下,shares数量的金库代币,给receiver转入assets数量的底层资产function _withdraw(address caller,address receiver,address owner,uint256 assets,uint256 shares) internal virtual {if (caller != owner) {_spendAllowance(owner, caller, shares);}//销毁金库代币_burn(owner, shares);//使用safeerc20的safetransfer方法,进行底层资产的转账SafeERC20.safeTransfer(_asset, receiver, assets);emit Withdraw(caller, receiver, owner, assets, shares);}//检测金库是否还有底层资产 || 金库代币=0,这两种情况,都认为是正常 function _isVaultCollateralized() private view returns (bool) {return totalAssets() > 0 || totalSupply() == 0;}
}
测试
测试使用foundry,可以直接看-----github代码,就不做过多分析了;
相关文章:
SolidityFoundry ERC4626
ERC4626简介 ERC4626 协议是一种用于代币化保险库的标准。 我们经常说 DeFi 是货币乐高,可以通过组合多个协议来创造新的协议; ERC4626 扩展了 ERC20 代币标准,旨在推动收益金库的标准化,它是 DeFi 乐高中的基础,它允…...

大模型时代的操作系统:融合 Rust 和大模型,打造 AI 操作系统
每次技术革命,无论是个人电脑、互联网还是移动设备,总是从硬件开始,然后演化到软件层。而操作系统是计算机系统的核心,没有它,计算机就只是一堆硬件,无法运行任何程序。 微软 CEO 萨蒂亚纳德拉曾将生成式 …...

【ML】为什么要做batch normlization,怎么做batch normlization
为什么要做batch normlization,怎么做batch normlization 1. batch normlization1.1 批量归一化是什么:1.2 为什么要做批量归一化: 2. feature normalization2.1 特征归一化是什么:2.2 为什么要做特征归一化: 3. batc…...

【C++指南】命名空间
💓 博客主页:倔强的石头的CSDN主页 📝Gitee主页:倔强的石头的gitee主页 ⏩ 文章专栏:《C指南》 期待您的关注 目录 一、命名空间的重要性 1. C语言中没有命名空间而存在的问题 2. C引入了命名空间解决的问题 3.…...

RocketMQ Dashboard安装
RocketMQ Dashboard 是一个基于 Web 的管理工具,用于监控和管理 RocketMQ 集群。它提供了一个用户友好的界面,使管理员能够轻松地查看和操作 RocketMQ 系统中的各种组件和状态。 主要功能包括: 集群管理: 监控和管理 NameServer 和 Broker …...

前端web开发HTML+CSS3+移动web(0基础,超详细)——第3天
目录 一,列表-无序和有序的定义列表 二,表格-基本使用与表格结构标签 三,合并单元格 四,表单-input标签 五,表单-下拉菜单 六,表单-文本域 七,表单-label标签 八,表单-按钮 …...
认识MySQL
目录 数据库是什么呢?MySQL 数据库是什么呢? 在我们开始学习MySQL之前,先来了解一下,什么是数据库呢?我相信此时很多人会说是管理数据的,完全正确!用数据库我们可以去存储大量的数据。我来给你…...

尚品汇-创建ES索引库(二十七)
目录: (1)商品检索功能介绍 (2)根据业务搭建数据结构 (3)nested 介绍 (4)搭建service-list服务 (5)构建实体与es mapping建立映射关系 &…...
设计模式-六大原则
概述 设计模式的六大原则是设计模式的基础,了解设计模式的原则,有利于设计模式实际应用的理解,在真实使用的时候,一般不太可能一个程序满足所有的设计模式六大原则,或多或少都会有违背设计模的地方,所以不…...

MyBatis搭建和增删改查
MyBatis是一个开源的持久层框架,用于处理数据库的增删改查操作。它能够将Java对象与数据库中的数据进行映射关系的配置,并自动生成对应的SQL语句,从而简化了数据库操作的编码工作。 MyBatis的核心思想是将SQL语句与Java代码分离,…...

【一图学技术】6.反向代理 vs API网关 vs 负载均衡的原理和使用场景
反向代理 vs API网关 vs 负载均衡 一、概念 🌏反向代理(Reverse Proxy)是一种位于服务器和客户端之间的代理服务器。 它接收来自客户端的请求,并将其转发给后端服务器,然后将后端服务器的响应返回给客户端。客…...
python爬虫番外篇 | Reuqests库高级用法(1)
文章目录 1.会话对象(Session Objects)2.请求和响应对象(Request and Response Objects)3.准备好的请求(Prepared Requests)4.SSL证书验证5.客户端证书6.CA 证书7.正文内容工作流程(Body Conten…...

【链表OJ】常见面试题 3
文章目录 1.[环形链表II](https://leetcode.cn/problems/linked-list-cycle-ii/description/)1.1 题目要求1.2 快慢指针1.3 哈希法 2.[随机链表的复制](https://leetcode.cn/problems/copy-list-with-random-pointer/description/)2.1 题目要求2.2 迭代法 1.环形链表II 1.1 题目…...

Linux学习笔记9(Linux包管理)
目录 归档包管理 归档 查看归档包 解归档包 压缩包管理 Zip/unzip gzip/gunzip bzip2/bunzip2 源码包安装软件 三大步: 预备步骤:安装依赖的编译库 一、./configure --prefix/usr/local/nginx 二、make 三、make install 软件包安装 配置…...
论文阅读《Geometric deep learning of RNA structure》
引入了机器学习方法,通过少量的数据学习。只使用原子坐标作为输入。 预测RNA三维结构比预测蛋白质结构更困难。 设计了一个原子旋转等变评分器ARES,由每个原子的三维坐标和化学元素类型(输入)指定,ARES预测模型与未知真…...

宏集方案 | 传统建筑智能化改造,迈向物联新时代
前言 智能建筑涉及多个系统的集成,如照明、空调、安防等,这些系统的兼容性和协调运作是一大挑战。然而,传统的工业建筑和商业楼宇受早期设计的局限,多个控制系统间互不兼容,并且难以重新部署通信线缆。 针对传统建筑…...
如果服务器更改Web端口会减少被攻击的风险吗?
通过更改服务器的Web端口,是否能够显著降低被攻击的风险?首先,理解Web服务默认使用的端口是关键。HTTP协议通常使用80端口,而HTTPS则默认使用443端口。这些端口因其广泛认知而成为黑客攻击的首要目标。理论上,将Web服务迁移到非标…...

vim列编辑模式
在编辑文本时,经常会有这样的需求,对特定列进行进行批量编辑。比如批量注释一段代码,或者删除待定字符(如一列空格)。幸运的是VIM支持列编辑模式。 假设文本内容: Maximum length of a custom vocabulary…...

如何实现pxe安装部署
此实验环境:rhel7主机 一、kickstart自动化安装脚本 1、安装可视化图形 [rootlocalhost ~]# yum group install "Server with GUI" 2、关闭vmware dhcp功能(编辑-虚拟网络编辑器) 3、httpd 1、安装httpd服务 [rootlocalhost …...

机器学习常见模型
1、线性模型 线性模型是机器学习最基本的算法类型,它试图学到一个通过多个特征(属性)计算的线性组合来预测的函数,简单的线性回归形式如yaxb,其中,x代表特征,而y代表结果,一旦a和b的…...

装饰模式(Decorator Pattern)重构java邮件发奖系统实战
前言 现在我们有个如下的需求,设计一个邮件发奖的小系统, 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其…...

python/java环境配置
环境变量放一起 python: 1.首先下载Python Python下载地址:Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个,然后自定义,全选 可以把前4个选上 3.环境配置 1)搜高级系统设置 2…...
linux 错误码总结
1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...

Psychopy音频的使用
Psychopy音频的使用 本文主要解决以下问题: 指定音频引擎与设备;播放音频文件 本文所使用的环境: Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...

【MATLAB代码】基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),附源代码|订阅专栏后可直接查看
文章所述的代码实现了基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),针对传感器观测数据中存在的脉冲型异常噪声问题,通过非线性加权机制提升滤波器的抗干扰能力。代码通过对比传统KF与MCC-KF在含异常值场景下的表现,验证了后者在状态估计鲁棒性方面的显著优…...

pikachu靶场通关笔记19 SQL注入02-字符型注入(GET)
目录 一、SQL注入 二、字符型SQL注入 三、字符型注入与数字型注入 四、源码分析 五、渗透实战 1、渗透准备 2、SQL注入探测 (1)输入单引号 (2)万能注入语句 3、获取回显列orderby 4、获取数据库名database 5、获取表名…...
嵌入式常见 CPU 架构
架构类型架构厂商芯片厂商典型芯片特点与应用场景PICRISC (8/16 位)MicrochipMicrochipPIC16F877A、PIC18F4550简化指令集,单周期执行;低功耗、CIP 独立外设;用于家电、小电机控制、安防面板等嵌入式场景8051CISC (8 位)Intel(原始…...
es6+和css3新增的特性有哪些
一:ECMAScript 新特性(ES6) ES6 (2015) - 革命性更新 1,记住的方法,从一个方法里面用到了哪些技术 1,let /const块级作用域声明2,**默认参数**:函数参数可以设置默认值。3&#x…...

若依登录用户名和密码加密
/*** 获取公钥:前端用来密码加密* return*/GetMapping("/getPublicKey")public RSAUtil.RSAKeyPair getPublicKey() {return RSAUtil.rsaKeyPair();}新建RSAUti.Java package com.ruoyi.common.utils;import org.apache.commons.codec.binary.Base64; im…...
Vue3中的computer和watch
computed的写法 在页面中 <div>{{ calcNumber }}</div>script中 写法1 常用 import { computed, ref } from vue; let price ref(100);const priceAdd () > { //函数方法 price 1price.value ; }//计算属性 let calcNumber computed(() > {return ${p…...