Michael.W基于Foundry精读Openzeppelin第14期——SafeMath.sol

Michael.W基于Foundry精读Openzeppelin第14期——SafeMath.sol

      • 0. 版本
        • 0.1 SafeMath.sol
      • 1. 目标合约
      • 2. 代码精读
        • 2.1 tryAdd(uint256 a, uint256 b) && trySub(uint256 a, uint256 b) && tryMul(uint256 a, uint256 b) && tryDiv(uint256 a, uint256 b) && tryMod(uint256 a, uint256 b)
        • 2.2 add(uint256 a, uint256 b) && mul(uint256 a, uint256 b)
        • 2.3 sub(uint256 a, uint256 b) && div(uint256 a, uint256 b) && mod(uint256 a, uint256 b) && sub(uint256 a, uint256 b, string memory errorMessage) && div( uint256 a, uint256 b, string memory errorMessage) && mod(uint256 a, uint256 b, string memory errorMessage)

0. 版本

[openzeppelin]:v4.8.3,[forge-std]:v1.5.6

0.1 SafeMath.sol

Github: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.8.3/contracts/utils/math/SafeMath.sol

SafeMath库是对solidity中uint256的加、减、乘、除和取模运算的一层封装。由于solidity 0.8之前的uint256运算是不做溢出检查,许多基于0.8版本之前的项目都会使用该库。0.8版本之后solidity编译器内置了整形数溢出检查,所以SafeMath也不再被广泛使用。

注:此版本的SafeMath库必须是基于solidity 0.8及之后的编译器版本使用。

1. 目标合约

封装SafeMath library成为一个可调用合约:

Github: https://github.com/RevelationOfTuring/foundry-openzeppelin-contracts/blob/master/src/utils/math/MockSafeMath.sol

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;import "openzeppelin-contracts/contracts/utils/math/SafeMath.sol";contract MockSafeMath {using SafeMath for uint;function tryAdd(uint a, uint b) external pure returns (bool, uint) {return a.tryAdd(b);}function trySub(uint a, uint b) external pure returns (bool, uint) {return a.trySub(b);}function tryMul(uint a, uint b) external pure returns (bool, uint) {return a.tryMul(b);}function tryDiv(uint a, uint b) external pure returns (bool, uint) {return a.tryDiv(b);}function tryMod(uint a, uint b) external pure returns (bool, uint) {return a.tryMod(b);}function add(uint a, uint b) external pure returns (uint){return a.add(b);}function sub(uint a, uint b) external pure returns (uint) {return a.sub(b);}function mul(uint a, uint b) external pure returns (uint){return a.mul(b);}function div(uint a, uint b) external pure returns (uint) {return a.div(b);}function mod(uint a, uint b) external pure returns (uint) {return a.mod(b);}function sub(uint a,uint b,string memory errorMessage) external pure returns (uint){return a.sub(b, errorMessage);}function div(uint a,uint b,string memory errorMessage) external pure returns (uint) {return a.div(b, errorMessage);}function mod(uint a,uint b,string memory errorMessage) external pure returns (uint) {return a.mod(b, errorMessage);}
}

全部foundry测试合约:

Github: https://github.com/RevelationOfTuring/foundry-openzeppelin-contracts/blob/master/test/utils/math/SafeMath.t.sol

2. 代码精读

2.1 tryAdd(uint256 a, uint256 b) && trySub(uint256 a, uint256 b) && tryMul(uint256 a, uint256 b) && tryDiv(uint256 a, uint256 b) && tryMod(uint256 a, uint256 b)

  • tryAdd(uint256 a, uint256 b):计算两数之和。如果加法产生溢出,返回false和0,否则返回true和结果;
  • trySub(uint256 a, uint256 b):计算两数之差。如果减法产生溢出,返回false和0,否则返回true和结果;
  • tryMul(uint256 a, uint256 b):计算两数之积。如果乘法产生溢出,返回false和0,否则返回true和结果;
  • tryDiv(uint256 a, uint256 b):计算两数之商。如果除法产生溢出,返回false和0,否则返回true和结果;
  • tryMod(uint256 a, uint256 b):计算两数的取模运算,即求余数。如果模运算产生溢出,返回false和0,否则返回true和结果。
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {// 关闭solidity 0.8的整数运算溢出检查unchecked {// 计算和uint256 c = a + b;// 如果a>和,则发生整形溢出。返回false和0if (c < a) return (false, 0);// 返回true和计算结果return (true, c);}}function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {// 关闭solidity 0.8的整数运算溢出检查unchecked {// 如果减数>被减数,发生整形溢出。返回false和0if (b > a) return (false, 0);// 返回true和计算结果 return (true, a - b);}}function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {// 关闭solidity 0.8的整数运算溢出检查unchecked {// 如果a为0,乘积必然为0。返回true和0if (a == 0) return (true, 0);// 计算乘积cuint256 c = a * b;// 做除法检查是否溢出。如果乘积c除以a不等于b,产生溢出。返回false和0if (c / a != b) return (false, 0);// 返回true和乘积creturn (true, c);}}function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {// 关闭solidity 0.8的整数运算溢出检查unchecked {// 如果除数为0,直接返回false和0if (b == 0) return (false, 0);// 由于被除数和非0除数都是uint256,必然除法不会产生溢出。直接返回true和运算结果return (true, a / b);}}// 注:同tryDiv()的内在判断逻辑一致function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {// 关闭solidity 0.8的整数运算溢出检查unchecked {// 如果除数为0,直接返回false和0if (b == 0) return (false, 0);// 由于被除数和非0除数都是uint256,必然除法不会产生溢出。直接返回true和取模运算结果return (true, a % b);}}

foundry代码验证

contract SafeMathTest is Test {MockSafeMath msm = new MockSafeMath();function test_TryAdd() external {(bool flag,uint res) = msm.tryAdd(1, 2);assertTrue(flag);assertEq(res, 3);// overflow(flag, res) = msm.tryAdd(type(uint).max, 1);assertFalse(flag);assertEq(res, 0);}function test_TrySub() external {(bool flag,uint res) = msm.trySub(3, 1);assertTrue(flag);assertEq(res, 2);// overflow(flag, res) = msm.trySub(1, 2);assertFalse(flag);assertEq(res, 0);}function test_TryMul() external {(bool flag,uint res) = msm.tryMul(2, 3);assertTrue(flag);assertEq(res, 6);// overflow(flag, res) = msm.tryMul(type(uint).max, 2);assertFalse(flag);assertEq(res, 0);}function test_TryDiv() external {(bool flag,uint res) = msm.tryDiv(7, 2);assertTrue(flag);assertEq(res, 3);// overflow(flag, res) = msm.tryDiv(1, 0);assertFalse(flag);assertEq(res, 0);}function test_TryMod() external {(bool flag,uint res) = msm.tryMod(7, 2);assertTrue(flag);assertEq(res, 1);// overflow(flag, res) = msm.tryMod(1, 0);assertFalse(flag);assertEq(res, 0);}
}

2.2 add(uint256 a, uint256 b) && mul(uint256 a, uint256 b)

  • add(uint256 a, uint256 b):计算两数之和。如果加法产生溢出,直接revert;
  • mul(uint256 a, uint256 b):计算两数之积。如果乘法产生溢出,直接revert。
    function add(uint256 a, uint256 b) internal pure returns (uint256) {// 封装solidity内置"+"运算符return a + b;}function mul(uint256 a, uint256 b) internal pure returns (uint256) {// 封装solidity内置"*"运算符return a * b;}

foundry代码验证

contract SafeMathTest is Test {MockSafeMath msm = new MockSafeMath();function test_Add() external {assertEq(msm.add(1, 2), 3);// overflowvm.expectRevert();msm.add(type(uint).max, 1);}function test_Mul() external {assertEq(msm.mul(3, 2), 6);// overflowvm.expectRevert();msm.mul(type(uint).max, 2);}
}

2.3 sub(uint256 a, uint256 b) && div(uint256 a, uint256 b) && mod(uint256 a, uint256 b) && sub(uint256 a, uint256 b, string memory errorMessage) && div( uint256 a, uint256 b, string memory errorMessage) && mod(uint256 a, uint256 b, string memory errorMessage)

  • sub(uint256 a, uint256 b):计算两数之差。如果减法产生溢出,直接revert;
  • div(uint256 a, uint256 b):计算两数之商。如果除法产生溢出(除数为0),直接revert;
  • mod(uint256 a, uint256 b):计算两数的余数。如果除法产生溢出(除数为0),直接revert;
  • sub(uint256 a, uint256 b, string memory errorMessage):计算两数之差。如果产生溢出,以自定义消息revert;
  • div( uint256 a, uint256 b, string memory errorMessage):计算两数之商。如果产生溢出,以自定义消息revert;
  • mod(uint256 a, uint256 b, string memory errorMessage):计算两数的余数。如果产生溢出,以自定义消息revert。
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {// 封装solidity内置"-"运算符return a - b;}function div(uint256 a, uint256 b) internal pure returns (uint256) {// 封装solidity内置"/"运算符return a / b;}function mod(uint256 a, uint256 b) internal pure returns (uint256) {// 封装solidity内置"%"运算符return a % b;}// 注:该方法已弃用,因为自定义revert消息作为参数会引起更多额外的memory分配。如果想自定义溢出消息,可以使用trySub()function sub(uint256 a,uint256 b,string memory errorMessage) internal pure returns (uint256) {unchecked {// 如果减数>被减数,以errorMessage revert// 注:由于这里调用了opcode `revert`,在发生revert时会返还剩余gas。而使用solidity内置减法发生revert时,会调用不合法opcode直接消耗掉剩余的gasrequire(b <= a, errorMessage);// 返回二者之差return a - b;}}// 注:该方法已弃用,因为自定义revert消息作为参数会引起更多额外的memory分配。如果想自定义溢出消息,可以使用tryDiv()function div(uint256 a,uint256 b,string memory errorMessage) internal pure returns (uint256) {// 关闭solidity 0.8的整数运算溢出检查unchecked {// 如果除数=0,以errorMessage revert// 注:由于这里调用了opcode `revert`,在发生revert时会返还剩余gas。而使用solidity内置除法发生revert时,会调用不合法opcode直接消耗掉剩余的gasrequire(b > 0, errorMessage);// 返回二者之商return a / b;}}// 注:该方法已弃用,因为自定义revert消息作为参数会引起更多额外的memory分配。如果想自定义溢出消息,可以使用tryMod()function mod(uint256 a,uint256 b,string memory errorMessage) internal pure returns (uint256) {// 关闭solidity 0.8的整数运算溢出检查unchecked {// 如果除数=0,以errorMessage revert// 注:由于这里调用了opcode `revert`,在发生revert时会返还剩余gas。而使用solidity内置取模运算符发生revert时,会调用不合法opcode直接消耗掉剩余的gasrequire(b > 0, errorMessage);// 返回两数的余数return a % b;}}
}

foundry代码验证

contract SafeMathTest is Test {MockSafeMath msm = new MockSafeMath();function test_Sub() external {assertEq(msm.sub(3, 2), 1);// overflowvm.expectRevert();msm.sub(1, 2);// with error messageassertEq(msm.sub(3, 2, "error message"), 1);vm.expectRevert("error message");msm.sub(1, 2, "error message");}function test_Div() external {assertEq(msm.div(7, 2), 3);// overflowvm.expectRevert();msm.div(1, 0);// with error messageassertEq(msm.div(7, 2, "error message"), 3);vm.expectRevert("error message");msm.div(1, 0, "error message");}function test_Mod() external {assertEq(msm.mod(7, 2), 1);// overflowvm.expectRevert();msm.mod(1, 0);// with error messageassertEq(msm.mod(7, 2, "error message"), 1);vm.expectRevert("error message");msm.mod(1, 0, "error message");}
}

ps:
本人热爱图灵,热爱中本聪,热爱V神。
以下是我个人的公众号,如果有技术问题可以关注我的公众号来跟我交流。
同时我也会在这个公众号上每周更新我的原创文章,喜欢的小伙伴或者老伙计可以支持一下!
如果需要转发,麻烦注明作者。十分感谢!

在这里插入图片描述

公众号名称:后现代泼痞浪漫主义奠基人

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

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

相关文章

php的设计模式有哪些

1&#xff0c;创建设计模式&#xff08;Creational Patterns&#xff09;(5种)&#xff1a; 用于创建对象时的设计模式。更具体一点&#xff0c;初始化对象流程的设计模式。当程序日益复杂时&#xff0c;需要更加灵活地创建对象&#xff0c;同时减少创建时的依赖。而创建设计模…

LlaMA2微调实战

LLaMA2-SFT LLaMA2-SFT, Llama-2-7B微调(transformers)/LORA(peft)/推理 Gtihub地址 https://github.com/yongzhuo/Llama2-SFT prompt text_1 f"".join(["[INST] <<SYS>>\n ""You are a helpful, respectful and honest assista…

数组专题攻破新学习笔记

数组专题攻破新学习笔记 1.移除有序数组中的重复项和移除元素2.二分查找需注意3.在排序数组中查找元素的第一个和最后一个位置4.最长回文串5.区域和检索-数组不可变 1.移除有序数组中的重复项和移除元素 思想一样但是细节不一样 思想一样就是 设置快慢指针&#xff0c;快指针…

ubuntu远程控制小车 运行rviz时报错

我买的是wheeltec的小车&#xff0c;测试rgbd相机时想在ubuntu上的rviz中显示小车的姿态和看到的rgb和depth图&#xff0c;但是ubuntu中rostopic list和rviz都找不到小车发布的话题信息&#xff0c;运行rqt_image_view时可以显示图片信息。 最终wheeltec的技术人员lucas帮我找了…

nodejs+vue+elementui汽车销售网站

前端技术&#xff1a;nodejsvueelementui,视图层其实质就是vue页面&#xff0c;通过编写vue页面从而展示在浏览器中&#xff0c;编写完成的vue页面要能够和控制器类进行交互&#xff0c;从而使得用户在点击网页进行操作时能够正常。 可以设置中间件来响应 HTTP 请求。 Express …

游戏小记-全屏与无边框

“LoL” 是指英雄联盟&#xff08;League of Legends&#xff09;&#xff0c;一款非常流行的多人在线战术游戏。在游戏设置中&#xff0c;“全屏” 和 “无边框” 是两种不同的窗口模式&#xff0c;它们有以下区别&#xff1a; 全屏模式&#xff1a; 在全屏模式下&#xff0c;…

Kafka 入门到起飞 - Kafka怎么做到保障消息不会重复消费的? 消费者组是什么?

Kafka怎么做到避免消息重复消费的&#xff1f; 消费者组是什么&#xff1f; 消费者&#xff1a; 1、订阅Topic&#xff08;主题&#xff09; 2、从订阅的Topic消费&#xff08;pull&#xff09;消息&#xff0c; 3、将消费消息的offset&#xff08;偏移量&#xff09;保存在K…

西安电子科技大学

前言 本篇文章投稿与以下活动 【西安城市开发者社区】探索西安高校&#xff1a;展现历史与创新的魅力 资料参考与百度百科 学校简介 西安电子科技大学&#xff08;Xidian University&#xff09;&#xff0c;简称“西电”&#xff0c;位于陕西省西安市&#xff0c;是中央部…

浅析C++临时变量

在C/C中&#xff0c;临时变量&#xff08;也称为临时对象&#xff09;具有常量性质&#xff0c;是由编译器自动创建的&#xff0c;并在表达式求值期间临时存在。它们主要用于存储中间结果&#xff0c;使表达式的求值过程更加高效和简洁。临时变量的生命周期仅限于表达式的求值过…

14.Netty源码之模拟简单的HTTP服务器

highlight: arduino-light 简单的 HTTP 服务器 HTTP 服务器是我们平时最常用的工具之一。同传统 Web 容器 Tomcat、Jetty 一样&#xff0c;Netty 也可以方便地开发一个 HTTP 服务器。我从一个简单的 HTTP 服务器开始&#xff0c;通过程序示例为你展现 Netty 程序如何配置启动&a…

2023年全国程序员薪酬排行天梯榜

文章目录 ⭐️ 2023年全国程序员薪酬排行天梯榜 在过去很长的一段时间内&#xff0c;网上总有一个声音&#xff1a;“大厂裁员”、“程序员内卷严重”、“程序员人员过盛”、“35岁中年危机”、“码农吃的青春饭”、“互联网寒冬” 等等等等。 讲道理&#xff0c;我对这种人为的…

【通过改变压缩视频的分辨率实现高效的视频语义分割】CVPR2022论文精度

Efficient Semantic Segmentation by Altering Resolutions for Compressed Videos Efficient Semantic Segmentation by Altering Resolutions for Compressed VideosBasic Information:论文简要 :背景信息:a. 理论背景:b. 技术路线: 结果:a. 详细的实验设置:b. 详细的实验结果…

云原生周刊:K8s v1.28 中的结构化身份验证配置

开源项目推荐 KubeLinter KubeLinter 是一种静态分析工具&#xff0c;用于检查 Kubernetes YAML 文件和 Helm 图表&#xff0c;以确保其中表示的应用程序遵循最佳实践。 DB Operator DB Operator 减轻了为 Kubernetes 中运行的应用程序管理 PostgreSQL 和 MySQL 实例的痛苦…

7.1 String StringBuffer 和 StringBuilder 的区别是什么? String 为什么是不可变的?

可变性 简单的来说&#xff1a;String 类中使用 final 关键字修饰字符数组来保存字符串&#xff0c;private final char value[]&#xff0c;所以String 对象是不可变的。 补充&#xff08;来自issue 675&#xff09;&#xff1a;在 Java 9 之后&#xff0c;String 、StringBu…

LLM Data Pipelines: 解析大语言模型训练数据集处理的复杂流程

编者按&#xff1a;在训练大语言模型的过程中,构建高质量的训练数据集是非常关键的一步&#xff0c;但关于构建大模型训练所需数据集的通用数据处理流程&#xff08;Data pipelines)的相关资料极为稀少。 本文主要介绍了基于Common Crawl数据集的数据处理流程。首先,文章概述了…

复现YOLOv8改进最新MPDIoU:有效和准确的边界盒回归的损失,打败G/E/CIoU,效果明显!!!

MPDIoU: A Loss for Efficient and Accurate Bounding Box Regression 论文简介MPDIoU核心设计思路论文方法实验部分加入YOLOv5代码论文地址:https://arxiv.org/pdf/2307.07662.pdf 论文简介 边界盒回归(Bounding box regression, BBR)广泛应用于目标检测和实例分割,是目标…

【业务功能篇56】SpringBoot 日志SLF4J Logback

3.5.1 日志框架分类与选择 3.5.1.1 日志框架的分类 日志门面 (日志抽象)日志实现JCL(Jakarta Commons Logging) SLF4J(Simple Logging Facade for Java)Jul(Java Util Logging) , Log4j , Log4j2 , Logback 记录型日志框架 Jul (Java Util Logging)&#xff1a;JDK中的日志…

ext4 - mballoc块分配机制

概述 ext4为了尽量避免block管理的碎片化有如此措施&#xff1a; 1.mballoc多块分配器。 buddy算法管理每个block group采用prellocation机制&#xff0c;氛围per-cpu local preallocation和per inode preallocation 小文件和大文件采用不同的策略小文件&#xff08;具体怎么…

Python实现指定区域桌面变化监控并报警

在这篇博客中&#xff0c;我们将使用Python编程语言和一些常用的库来实现一个简单的区域监控和变化报警系统。我们将使用Tkinter库创建一个图形界面&#xff0c;允许用户选择监控区域&#xff0c;并使用OpenCV库进行图像处理和相似性比较&#xff0c;以检测区域内的变化&#x…

基于IP地址的证书实现https

基于IP地址实现传递数据的&#xff0c;默认的HTTP很容易被不法分子劫持数据&#xff0c;网络防洪是当下的互联网为确保安全&#xff0c;要用HTTPS协议更为妥当。 使用IP地址申请证书的主要条件&#xff0c;必须在申请认证过程&#xff0c;开放IP地址外网可以访问&#xff0c;包…