一些零碎的关于合约测试,ERC20调用的知识

文章目录

  • 前言
  • 一、vm.startPrank(user)是什么?
  • 二、approve 方法
      • 场景设定
      • 步骤 1: 用户授权
      • 步骤 2: 合约使用授权
      • 步骤 3: 检查授权状态
      • 示例代码
    • ERC20 approve的源代码
      • 代码解释
      • 代码解释
      • 应用场景
      • 示例
      • 注意事项
      • 代码解释
      • 可能的重载版本 `_approve`
      • 事件 `Approval`
      • 总结
      • 代码解释
      • 事件 `Approval`
      • 错误处理
      • 总结
      • 代码解释
      • 事件的作用
      • 如何触发事件
      • 总结


前言

本章节提供一些常用的合约测试中遇到的函数,和零碎的知识

一、vm.startPrank(user)是什么?

vm.startPrank(user); 是在使用Foundry测试框架时的一条指令,它的作用是模拟以太坊虚拟机(EVM)的行为,允许当前的测试环境以指定的账户(在这个例子中是user)的身份执行操作,而不会消耗真实的gas。这种模拟对于智能合约的测试非常有用,因为它可以减少测试成本并提高测试速度。

以下是vm.startPrank的一些关键点:

  • 模拟账户行为:在vm.startPrank(user)vm.stopPrank()之间的代码,都会以user账户的身份执行。这意味着任何需要支付gas的操作,如发送交易或调用合约函数,都将被视为由user执行。

  • 无需支付gas:在vm.startPrank的作用域内,所有的操作都不会消耗gas,这对于测试环境来说是一个很大的优势,因为它允许开发者在不担心成本的情况下测试合约。

  • 结束模拟:使用vm.stopPrank()来结束模拟,之后的代码将不再以user账户的身份执行,除非再次调用vm.startPrank

  • 作用域限制vm.startPrank的效果仅限于它和对应的vm.stopPrank之间的代码块。这意味着一旦离开这个作用域,就需要再次调用vm.startPrank来继续模拟。

  • 测试框架特性:这是Foundry测试框架提供的特性之一,类似的测试框架如Truffle和OpenZeppelin Test Helpers也有类似的功能。

在实际的测试脚本中,vm.startPrank通常与vm.expectRevert等其他测试断言一起使用,以验证智能合约在特定条件下的行为是否符合预期。

二、approve 方法

在智能合约中,approve 方法是 ERC-20 代币标准的一部分,它允许代币的持有者授权另一个地址(通常是另一个智能合约)来代表他们使用一定数量的代币。这种授权机制是 ERC-20 代币的一个重要特性,它允许代币在不同的合约之间安全地转移,而不需要持有者直接发送代币。

以下是 approve 方法的一个使用示例,我们将结合你提供的 YeildERC20 合约进行说明:

场景设定

假设我们有一个名为 handlerStatefulFuzzCatches 的智能合约,这个合约需要代表用户执行一些操作,比如自动复投或者参与流动性挖矿。为了能够代表用户转移代币,它需要用户的授权。

步骤 1: 用户授权

首先,用户需要调用 YeildERC20 合约的 approve 方法,授权 handlerStatefulFuzzCatches 合约使用一定数量的代币。这里,amount 是用户愿意授权的数量。

// 用户调用 approve 方法授权
yeildERC20.approve(address(handlerStatefulFuzzCatches), amount);

步骤 2: 合约使用授权

一旦用户授权了代币,handlerStatefulFuzzCatches 合约就可以使用这些代币了。例如,它可能会调用 transferFrom 方法,从用户的地址转移代币到另一个地址。

// 合约调用 transferFrom 方法使用授权的代币
yeildERC20.transferFrom(userAddress, anotherAddress, amount);

步骤 3: 检查授权状态

用户或合约可以随时检查某个地址的授权状态,这通过查询 allowance 方法实现。

// 检查用户对合约的授权额度
uint256 remainingAllowance = yeildERC20.allowance(userAddress, address(handlerStatefulFuzzCatches));

示例代码

假设我们有一个用户想要授权 handlerStatefulFuzzCatches 合约使用 1000 个 YeildERC20 代币:

pragma solidity 0.8.20;import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";contract YeildERC20 is ERC20 {uint256 public constant INITIAL_SUPPLY = 1_000_000e18;address public immutable owner;constructor() ERC20("MockYeildERC20", "MYEILD") {owner = msg.sender;_mint(msg.sender, INITIAL_SUPPLY);}function approve(address spender, uint256 amount) public virtual override returns (bool) {super.approve(spender, amount);return true;}
}contract Handler {function handleToken(address tokenAddress, address user, address to, uint256 amount) public {YeildERC20 yeildERC20 = YeildERC20(tokenAddress);yeildERC20.approve(address(this), amount);yeildERC20.transferFrom(user, to, amount);}
}

在这个示例中,Handler 合约首先调用 approve 方法来获得代币的授权,然后使用 transferFrom 方法将代币从用户地址转移到另一个地址。这个过程展示了如何在智能合约中安全地管理和使用代币授权。

ERC20 approve的源代码

这段代码是一个Solidity智能合约中的approve函数的实现,它遵循ERC-20代币标准。approve函数允许代币的持有者授权另一个地址(称为“spender”)来使用一定数量的代币。以下是对这段代码的详细解释:

代码解释

function approve(address spender, uint256 value) public virtual returns (bool) {address owner = _msgSender();_approve(owner, spender, value);return true;
}
  1. 函数签名

    • function approve(address spender, uint256 value) public virtual returns (bool)
    • 这是一个公共函数,可以被外部调用。它接受两个参数:spender(将要被授权的地址)和value(授权的代币数量)。函数标记为virtual,意味着它可以在继承的合约中被重写。
  2. 获取调用者地址

    • address owner = _msgSender();
    • 这行代码使用_msgSender()内置函数来获取当前调用approve函数的地址,即代币的持有者。这个地址被存储在局部变量owner中。
  3. 调用内部批准函数

    • _approve(owner, spender, value);
    • 这行代码调用了一个内部函数_approve,它实际上执行了授权操作。_approve函数接受三个参数:代币持有者的地址、被授权的地址和授权的代币数量。
  4. 返回值

    • return true;
    • 函数返回true,表示授权操作成功。这是ERC-20标准中approve函数的预期行为。

这段代码是Solidity智能合约中的一个函数定义。在Solidity 0.8.x版本中,msg.sender 被标记为不安全的,因为它可能被重入攻击利用。为了提高安全性,Solidity 0.8.x引入了_msgSender()_msgData()这两个内置函数。

代码解释

function _msgSender() internal view virtual returns (address) {return msg.sender;
}
  1. _msgSender()

    • 这是一个内置函数,用于返回当前调用的发送者(即调用这个合约函数的地址)。
  2. internal

    • 表示这个函数只能在定义它的合约内部或者继承它的合约内部被访问。
  3. view

    • 表示这个函数不会修改合约状态,它只读取数据。
  4. virtual

    • 表示这个函数可以被继承的合约重写。
  5. returns (address)

    • 表示这个函数返回一个地址类型的值。
  6. return msg.sender;

    • 返回当前调用的发送者地址。

应用场景

这个函数通常用于确保合约函数的调用者是经过验证的。在Solidity 0.8.x版本中,推荐使用_msgSender()而不是直接使用msg.sender,因为_msgSender()提供了更好的安全性和可读性。

示例

假设我们有一个合约,需要确保只有合约所有者可以调用某个函数:

pragma solidity 0.8.20;contract MyContract {address private _owner;constructor() {_owner = msg.sender;}function onlyOwner() public {require(_msgSender() == _owner, "Not owner");// 只有所有者可以执行的代码}
}

在这个示例中,onlyOwner 函数使用_msgSender()来检查调用者是否是合约所有者。如果不是,函数会通过require语句抛出异常。

注意事项

  • _msgSender()函数在Solidity 0.8.x版本中引入,如果你的合约需要兼容0.7.x版本,你需要使用msg.sender
  • 虽然_msgSender()提供了更好的安全性,但在某些情况下,你可能需要使用msg.sender,比如在合约的构造函数中,因为构造函数不能被重写,所以_msgSender()在这里没有提供额外的安全性。

总的来说,_msgSender()是Solidity 0.8.x版本中推荐使用的一个内置函数,用于提高合约的安全性和可读性。

这段代码是Solidity智能合约中用于更新代币允许量的内部函数,它是一个简化的示例,展示了如何在ERC-20代币合约中实现代币授权逻辑。这个函数是_approve的重载版本,它接受四个参数,但在这里只展示了接受三个参数的版本。让我们逐步分析这个函数:

代码解释

function _approve(address owner, address spender, uint256 value) internal {_approve(owner, spender, value, true);
}
  1. 函数签名

    • function _approve(address owner, address spender, uint256 value) internal
    • 这是一个内部函数,只能在定义它的合约及其派生合约中调用。它接受三个参数:owner(代币持有者的地址)、spender(被授权的地址)和value(授权的代币数量)。
  2. 内部调用

    • _approve(owner, spender, value, true);
    • 这行代码调用了另一个重载版本的_approve函数,这个重载版本接受一个额外的布尔参数。在这里,传递了true作为第四个参数。

可能的重载版本 _approve

由于代码中提到了调用另一个版本的_approve函数,我们可以假设存在一个重载的_approve函数,它接受一个额外的布尔参数。这个布尔参数可能用于控制是否应该触发授权事件。例如:

function _approve(address owner, address spender, uint256 value, bool emitEvent) internal {// 更新允许量_allowed[owner][spender] = value;// 如果emitEvent为true,则触发授权事件if (emitEvent) {emit Approval(owner, spender, value);}
}

在这个假设的重载版本中,_approve函数首先更新代币持有者对特定spender的允许量。然后,如果emitEvent参数为true,它会触发一个名为Approval的事件,这个事件是ERC-20标准的一部分,用于通知客户端(如钱包)授权状态的变化。

事件 Approval

在ERC-20标准中,Approval事件用于记录代币授权的变化。这个事件有三个参数:代币持有者的地址、被授权的地址和授权的代币数量。当调用emit Approval时,这个事件会被记录在区块链上,客户端可以通过订阅这个事件来更新它们的UI,显示最新的授权状态。

总结

这段代码展示了如何在智能合约中实现代币授权逻辑的一部分。通过调用重载版本的_approve函数,可以在更新允许量的同时控制是否触发授权事件。这种设计允许合约在不同的上下文中灵活地处理授权逻辑,例如在某些情况下可能不需要立即触发事件。

这段代码是一个Solidity智能合约中的_approve函数实现,它是ERC-20代币标准的一部分。这个函数用于更新代币持有者对另一个地址(即“spender”)的代币授权额度。以下是对这段代码的详细解释:

代码解释

function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {if (owner == address(0)) {revert ERC20InvalidApprover(address(0));}if (spender == address(0)) {revert ERC20InvalidSpender(address(0));}_allowances[owner][spender] = value;if (emitEvent) {emit Approval(owner, spender, value);}
}
  1. 函数签名

    • function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual
    • 这是一个内部函数,只能在定义它的合约及其派生合约中调用。它被标记为virtual,意味着它可以在派生合约中被重写。函数接受四个参数:代币持有者的地址(owner)、被授权的地址(spender)、授权的代币数量(value)和一个布尔值(emitEvent),后者用于控制是否触发授权事件。
  2. 参数验证

    • if (owner == address(0)) { revert ERC20InvalidApprover(address(0)); }
    • 这行代码检查owner地址是否为0(即空地址)。如果是,合约将调用revert函数并提供错误信息,这将导致交易失败。
    • if (spender == address(0)) { revert ERC20InvalidSpender(address(0)); }
    • 类似地,这行代码检查spender地址是否为0。如果是,同样会导致交易失败。
  3. 更新授权额度

    • _allowances[owner][spender] = value;
    • 这行代码更新代币持有者对spender的授权额度。_allowances是一个映射(mapping),它跟踪每个持有者对每个spender的授权额度。
  4. 触发事件

    • if (emitEvent) { emit Approval(owner, spender, value); }
    • 如果emitEventtrue,则触发Approval事件,记录授权操作。这个事件是ERC-20标准的一部分,它允许客户端(如钱包应用)监听授权变化。

事件 Approval

Approval事件是ERC-20代币标准中定义的一个事件,它在代币授权额度更新时被触发。事件有三个参数:代币持有者的地址、被授权的地址和新的授权额度。这个事件对于客户端来说非常重要,因为它们可以利用这些信息来更新用户界面,反映最新的授权状态。

错误处理

  • revert ERC20InvalidApprover(address(0));
  • revert ERC20InvalidSpender(address(0));
  • 这两行代码中的revert语句用于在不合法的地址(如空地址)被用作ownerspender时中止交易。ERC20InvalidApproverERC20InvalidSpender是自定义错误消息,它们提供了关于错误原因的更多信息。

总结

这个_approve函数是一个典型的ERC-20代币授权逻辑实现。它包括了参数验证、授权额度更新和事件触发等关键步骤,确保了代币授权操作的安全性和透明性。通过这种方式,ERC-20代币合约能够支持复杂的代币管理和交易逻辑。

这段代码定义了一个名为 Approval 的事件,它是遵循 ERC-20 代币标准的一部分。在 Solidity 智能合约中,事件用于将智能合约的执行情况通知外界,例如前端应用程序或者区块链浏览器。事件通常在智能合约的状态发生变化时被触发。

代码解释

event Approval(address indexed owner, address indexed spender, uint256 value);
  1. 事件名称

    • Approval 是事件的名称。
  2. 参数

    • address indexed owner:代币持有者的地址。indexed 关键字用于提供对事件日志的优化搜索能力。
    • address indexed spender:被授权的地址,即可以代表 owner 地址使用一定数量代币的地址。同样,indexed 关键字用于提供搜索优化。
    • uint256 value:授权的代币数量。
  3. 事件触发

    • 当智能合约中的 approve 函数被调用,并且成功更新了代币的授权额度时,Approval 事件会被触发。

事件的作用

  • 记录:事件被记录在区块链上,提供了一种不可变的审计跟踪,表明代币授权何时以及如何被改变。
  • 通知:智能合约的前端用户界面可以订阅这些事件,以实时更新用户界面,反映授权状态的变化。
  • 搜索和分析:由于 indexed 关键字的使用,这些事件可以被高效地搜索和分析,这对于区块链分析和审计工具非常有用。

如何触发事件

在智能合约中,事件通过 emit 关键字被触发。例如,在 _approve 函数中,如果授权成功,事件将被触发:

function _approve(address owner, address spender, uint256 value) internal {// ... 省略了其他代码 ...// 更新授权额度_allowances[owner][spender] = value;// 触发 Approval 事件emit Approval(owner, spender, value);
}

总结

Approval 事件是 ERC-20 代币标准中定义的一个关键组件,它确保了代币授权操作的透明性和可追踪性。通过在智能合约中正确地使用和触发事件,开发者可以创建更加安全、可靠的应用程序,同时为用户提供更好的体验。

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

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

相关文章

关于武汉芯景科技有限公司的实时时钟芯片XJ8337开发指南(兼容DS1337)

一、芯片引脚介绍 1.芯片引脚 2.引脚描述 二、系统结构图 三、功能描述 1.时钟功能 2.闹钟功能(两个闹钟) 3.振荡器停止控制 4.频率输出(1HZ、1.096KHZ、8.192KHZ、32.768KHZ) 5.振荡器停止检测 6.闹钟中断标志 四、程序代…

MySQL 查询优化详解

在使用 MySQL 数据库时,查询性能往往是影响应用程序整体性能的关键因素。通过对查询进行优化,可以显著提升数据库的响应速度和处理能力。本文将深入探讨 MySQL 查询优化的几个重要方面,包括查询分析与执行计划(EXPLAIN&#xff09…

#C++ 笔记三

七、异常处理 1.概念 异常是程序在执行期间产生的问题。 C异常是指在程序运行时发生的特殊情况,比如下标越界等。 异常提供了一种转移程序控制权的方式。 2.抛出异常 throw语句的操作数可以是任意表达式,表达式结果的类型决定了抛出异常的类型。 …

k8s-pod 实战六 (如何在不同的部署环境中调整startupprobe的参数?)

在不同的部署环境中(如开发、测试、生产环境),你可能希望对 startupProbe 的参数进行调整,以适应不同的需求和条件。以下是几种常见的方法和实践: 方法一:使用 Kustomize 1. 目录结构 假设你的项目目录结构如下: my-app/ ├── base/ │ └── deployment.yaml …

进程和线程(操作系统八股文part2)

一个操作系统的进程和线程部分的笔记,大部分来源于:小林coding和Javaguide,以及操作系统黑书。 进程和线程 什么是进程 运行中的程序叫进程**(Process)**。 进程是资源分配的最小单位,线程是执行的最小…

redis分布式是如何实现的(面试版)

需要结合项目中的业务进行回答,通常情况下,分布式锁使用的场景:集群情况下的定时任务、抢单、幂等性场景。 下面先来看一个抢卷场景: 以下情况会出现超卖情况: 因为线程会交替执行,所以线程查询优惠价的数…

Socket编程---UDP篇

目录 一. UDP协议 二. Socket编程 2.1 sockaddr家族 2.2 接口介绍 三. 服务端实现 四. 服务端调用实现 五. 客户端实现 六. 效果展示 一. UDP协议 何为UDP协议的含义,上篇粗略提及了一下TCP与UDP的区别: TCP: •…

SpringBoot集成kafka-消息转发@sendTo()注解

SpringBoot集成kafka-消息转发sendTo 1、消费者2、生产者3、实体类对象4、JSON工具类5、配置文件application.yml6、测试类7、测试 1、消费者 启动消费者进行消息监听,消费者A监听到生产者发送的消息使用sendTo()注解将消息转发给消费者B package com.power.consu…

Django+Vue二手交易平台的设计与实现

目录 1 项目介绍2 项目截图3 核心代码3.1 需要的环境3.2 Django接口层3.3 实体类3.4 config.ini3.5 启动类3.5 Vue 4 数据库表设计5 文档参考6 计算机毕设选题推荐7 源码获取 1 项目介绍 博主个人介绍:CSDN认证博客专家,CSDN平台Java领域优质创作者&…

react中关于token的两个场景

场景一 在react项目中,路由跳转前需判断是否存在token,有则正常跳转,没有则去登录页面。 实现 这里使用的是localstorage存储token(也可以使用redux管理token) // src\components\AuthRoute.js // 封装高阶组件 //…

cesium 地形获取和生成

1.先从网上下载12.5m精度的地形,然后叠加无人机的地形数据 2.使用global mapper pro合并并导出完整的tiff 3.使用cesiumLab进行tiff的文件数据切片生成terrain格式的数据

cocos发布unity平台试玩广告失败问题

前言 关于如何输出试玩广告和平台文档中的说明就不赘述了,下面主要介绍在发布过程中遇到的问题。 检测不到mraid.open()或应用商店链接 提示: Creative pack validation failed: Your responsive playable is missing mraid.open() Your responsive p…

防御Nginx负载均衡中的拒绝服务攻击:策略与实践

拒绝服务攻击(DoS)和分布式拒绝服务攻击(DDoS)是网络安全的主要威胁之一,它们通过过载服务器资源或网络带宽,使得合法用户无法访问服务。Nginx作为一种流行的负载均衡器,提供了多种机制来帮助防…

第二讲__提炼

1.多态 1.定义 常见的两种情况: 1.继承 一个类A里面定义了一些方法,另一个类B继承了这个类并重写了A类的部分方法,主函数中用A类(父类)接收了一个B类(子类)对象,此时即时多态。 2.接口 一个接口A里面定义类很多抽象方法&#xff0…

从零开始学习Spring Cloud Consul:服务治理的完整解决方案

从零开始学习Spring Cloud Consul:服务治理的完整解决方案 在微服务架构中,服务的注册与发现、配置管理、负载均衡、健康检查等服务治理功能是保障系统稳定性和可扩展性的关键。Spring Cloud Consul作为Spring Cloud生态系统中与Consul集成的模块&#…

.NET HandyControl 深度解析:一个现代化的UI控件库

文章目录 前言一、选择HandyControl的原因二、如何使用HandyControl1.安装HandyControl2.使用代码例子 总结 前言 在.NET开发领域,UI(用户界面)设计的美观性和易用性对于应用程序的成功至关重要。为了帮助开发者快速构建现代化、美观且用户友…

如何使用住宅代理获取价格对比和更多选择

在购物和预订过程中,网站通常会根据用户的地理位置提供不同的价格和库存信息。这种做法称为地理定价或区域定价。使用静态住宅代理可以帮助用户准确查看他们想要了解的区域,获得更多选择和更优惠的价格。 查找更低的价格: 价格差异&#xff1…

CSS\JS实现页面背景气泡logo上浮效果

效果图&#xff1a; 单容器显示气泡&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Bu…

c++习题27-大整数减法

目录 一&#xff0c;题目 二&#xff0c;思路 三&#xff0c;代码 一&#xff0c;题目 输入 共2行&#xff0c;第1行是被减数a&#xff0c;第2行是减数b(a > b)。每个大整数不超过200位&#xff0c;不会有多余的前导零。 输出 一行&#xff0c;即所求的差。 样例输入1…

PostgreSQL遍历所有的表并为其创建基于某个字段的索引

完整代码 以下以"collect_time"字段为例&#xff0c;其他字段请自行全局替换 DO $$ DECLAREtable_name TEXT;index_name TEXT; BEGIN-- 遍历 public 模式下的所有表FOR table_name IN (SELECT table_nameFROM information_schema.tablesWHERE table_schema publicOR…