玩以太坊链上项目的必备技能(修改器 [modifier]-Solidity之旅十五)

news2025/1/15 6:35:01

修改器(modifier)

在讲修改器(modifier)之前,我们使用前面几篇文章所学到的知识来实现一个简单的 token 类合约。

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;

contract InheritanceModifierExample {
    mapping(address => uint) public tokenBalance;
    //拥有者
    address owner;

    uint tokenPrice = 1 ether;

    constructor() {
        owner = msg.sender;
        tokenBalance[owner] = 100;
    }

    function createNewToken() public {
        //使用 require 检查是不是合约拥有着
        require(msg.sender == owner, "You are not allowed");
        tokenBalance[owner]++;
    }

    function burnToken() public {
        require(msg.sender == owner, "You are not allowed");
        tokenBalance[owner]--;
    }

    function purchaseToken() public payable {
        require((tokenBalance[owner] * tokenPrice) / msg.value > 0, "not enough tokens");
        tokenBalance[owner] -= msg.value / tokenPrice;
        tokenBalance[msg.sender] += msg.value / tokenPrice;
    }

    function sendToken(address _to, uint _amount) public {
        require(tokenBalance[msg.sender] >= _amount, "Not enough tokens");
        tokenBalance[msg.sender] -= _amount;
        tokenBalance[_to] += _amount;
    }

}

在这里插入图片描述

我们拷贝部署合约的 account 地址0x5B38Da6a701c568545dCfcB03FcB875f56beddC4

切换成其他 account ,也拷贝出地址 0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2

在这里插入图片描述

在这里插入图片描述

查看 account Ether 是否正确,复制 0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2,将其填入 tokenBalance 的输入栏,看看 Ether 是否为1。

在这里插入图片描述

我们再来看看该账户 burn with Account会发生什么样的状况呢?

在这里插入图片描述

从上述这个简单的代币类合约,聪慧如您,想必是看出它的不足了。

没错,就是上面合约中的函数都有类似的 require 语句,作为开发者的您会将这些 require 语句抽取到一个函数中,但在 Solidity 却引入了一种特别的函数——修改器(modifier)。

Solidity 修改器(modifier)用来做什么的?

修改器(modifier)在 Solidity 中是一种特殊类型的函数,用于修改其它函数的行为。例如,开发人员可以使用修改器来检查在允许函数执行之前是否满足某个条件。

修改器(modifier)与函数类似,因为它们可以接受参数并有一个返回类型。修改器(modifier)也可以被链在一起,这意味着你可以在一个函数上有多个修改器(modifier)

然而,修改器(modifier)只能修改合约逻辑,不能修改合约的存储,包括结构。修改器(modifier)减少了开发者必须编写模板代码的数量,并且可以使您的 Solidity 代码更加可读。

多个修改器(modifier)由空格分隔。修改器(modifier)的顺序很重要。列表中的第一个修改器(modifier)将被首先执行,第二个修改器(modifier)将被第二次应用,以此类推。

例如,如果您有一个修改器(modifier)检查用户是否经过认证,另一个修改器(modifier)检查用户是否被授权查看某个资源,那么这些修改器(modifier)的应用顺序将决定用户是否能够查看该资源。

Solidity 修改器(modifier)的不同类型

Solidity修改器(modifier)有四大类:闸门检查、先决条件、过滤器和防止重入攻击。

  1. 闸门检查

它在允许一个函数执行之前检查某个条件是否为真。

例如,您可能有一个允许用户从他们的账户中取钱的函数,但在函数执行之前,开发者可能想检查用户的账户中是否有足够的钱来进行取款。这种检查被认为是一个门检查修改器

另一个闸门检查的例子是,在允许用户查看某个资源之前,检查用户是否经过认证的函数。

  1. 先决条件

它为一个函数的执行设置了环境,而不是检查某个条件是否为真。

例如,一个 Solidity 开发者可能会使用一个需要一定数量的Ether来执行函数。在这种情况下,先决条件将是设置Ether平衡的函数。

  1. 滤波器

它检查某个条件是否为真,如果是,则允许函数执行。如果条件不为真,那么该函数将不会执行。

闸门检查不同,即使条件为真,也不会自动允许函数执行,而过滤器将允许函数在条件为真时执行。

  1. 重入式攻击的预防

递归攻击是一种攻击类型,恶意行为者试图通过递归调用多次执行一个函数,以利用它。

例如,想象一下,您有一个允许用户从他们的账户中提款的函数。一个重入式攻击者可能会试图多次调用该函数,以提取比他们账户中实际拥有的更多的钱。

为了防止重入式攻击,您可以使用一个修改器来检查该函数是否被递归调用。如果是,那么该函数将不会执行。

require 和 修改器(modifier)之间的关系

require 经常与修改器互换使用,因为它们都允许您在一个函数执行之前检查某个条件是否为真。如果指定的条件不为真,那么编译器就会抛出一个错误。

例如,下面的语句使用 require 关键字,以便只有所有者才能与一个函数进行交互。

modifier onlyOwner() {
    require(msg.sender == owner);
    _;
}

require 和 修改器(modifier)之间的区别。

1、修改器(modiffier) 可用于为一个函数的执行设置环境(如先决条件的情况下)
2、require 只能用于检查某个条件是否为真 3、修改器(modiffier)` 可以被重写
4、要求不能被覆盖

什么是修改器(modiffier) 重写?

virtual关键字可以用来表示可以在派生合约override(重写)

例如,一个带有修改器(modifier)的合约,而派生合约继承自它。

假使,基合约中的修改器(modifier)被标记了virtual,那么它便可以在派生合约中被override(重写)

继承可以让您扩展合约的属性,在修改器(modifier)的上下文中,继承允许您添加新的(modifier),或覆盖现有的(modifier)

下面的简单实现演示了继承和修改器如何一起工作。

contract A {
    modifier X virtual {
    }
}	

contract B is A {
    modifier X override {
    }
}

在这个例子中,合约 B 继承了合约 A,两个合约都有一个叫做 X的修改器。然而,在合约 B 中,该修饰语被标记为override,这表明它覆盖了合约 A 中的修改器。

如何使用修改器

当使用一个修改器时,您首先需要在合约中定义修改器函数。修改器使用一个特殊的符号_,只有当修改器的条件得到满足时,才会插入函数体。

下面的合同演示了如何使用一个修改器。

contract Owner {
   address public owner = msg.sender;
   modifier onlyOwner {
      require(msg.sender == owner);
      _;
   }
   modifier costs(uint price) {
      if (msg.value >= price) {
         _;
      }
   }
}

在上面的例子中,该合约有两个修改器:onlyOwner costs

第一个修改器检查msg.sender是否是合约的所有者,第二个修改器检查msg.value是否大于或等于某个价格。

这两个修饰符都可以用在合同中的任何函数上。

_ 放在哪里?

_ 被称为合并通配符。它将函数代码与 _ 所在的修改器代码合并。

换句话说,函数的主体(修改器所连接的)将被插入修改器的定义中出现 _ 的地方。

一个修改器必须在其主体内有 _ 才能执行。它是强制性的(如果不是这样的话,Solidity 会抛出一个错误吗)。

您写将 _ 符号的地方将决定该函数是在修改器代码之前、之间还是之后执行。

modifier SomethingBefore {
    require(/* check something first */);
    _; // 继续执行功能
}

modifier SomethingAfter {
    _; // 先运行函数
    require(/* then check something */)
}

如上面的例子所示,您可以把 _ 放在修饰语体的开头、中间或结尾。

在实践中,(特别是在您真正了解修改器的工作原理之前),最安全的使用模式是将 _ 放在最后。在这种情况下,修改器的作用是一致的验证检查,所以要先检查一个条件,然后再继续。下面的代码片段显示了这个例子。

function isOkay() public view returns(bool) {
    // 做一些验证检查
    return true;
}
function isAuthorised(address _user) public view returns(bool) {
    // 逻辑检查以及账户授权
    return true; 
}
modifier OnlyIfOkAndAuthorised {
    require(isOkay());
    require(isAuthorised(msg.sender));
    _;
}

向修改器传递参数

修改器也可以接受参数。像函数一样。

modifier Fee (uint _fee) {
    if (msg.value >= _fee) {
        _;
    }
}

使用上面的例子,您可以确保调用您的一个合约函数的用户(或合约)已经发送了一些Ether来支付预先要求的费用。

让我们用一个简单的例子来说明,这个合约就像一个金库。

您想确保每个想取出储存在合约金库中的钱的用户都要向合约支付最低2.5%的费用。

一个带有参数的修改器可以模拟这种行为。请看下面的代码。

// SPDX-License-Idendifier: GPL-3.0
pragma solidity ^0.8.0;
contract Vault {
    
    modifier fee(uint _fee) {
        if (msg.value != _fee) {
            revert("You must pay a fee to withdraw your ethers");
        } else {
            _;
        }
    }
    
    function deposit(address _user, uint _amount) external {
        // ...
    }
    
    function withdraw(uint _amount) external payable fee(0.025 ether) {
        // ...
    }
 }

修改器参数允许使用任意表达式,在这种情况下,所有从函数中可见的符号都在修改器中可见。

在一个函数上应用多个修改器。

多个修改器可以应用到一个函数,您可以这样做。

contract OwnerContract {    
    address public owner = msg.sender;
    uint public creationTime = now;    
    
    modifier onlyBy(address _account) {
        require(
            msg.sender == _account,
            "Sender not authorized.
        );
        _;
    }    
    
    modifier onlyAfter(uint _time) {
        require(
            now >= _time,
            "Function called too early."
        );
        _;
    }    
    
    function disown() public onlyBy(owner) onlyAfter(creationTime + 6 weeks) {
        delete owner;
    }
 }

修改器将按照它们被定义的顺序执行,所以从左到右。所以在上面的例子中,该函数将在运行前检查以下条件。

onlyBy(...) : 调用合同的地址是否为所有者?
onlyAfter(...) : 是否有超过6周的时间是该人的所有者?

带枚举的修改器

如果您的合约持有一个枚举类型的状态变量,您可以通过传递一个可用的选项作为修改器的参数来检查它持有的值。

enum State { Created, Locked, Inactive }
State state;modifier isState(State _expectedState) {
    require(state == _expectedState);
    _;
}

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

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

相关文章

Python获取与处理文件路径/目录路径

这里写目录标题文件目录结构说明一、路径获取1.1 获取当前文件的绝对路径1.2.1 获取当前文件的所在目录1.2.2 获取当前文件的所在目录的上一级目录1.3 获取当前文件名1.4 获取当前文件对于基准路径的相对路径二、路径判断2.1 判断路径是否存在2.2 判断路径是否为绝对路径2.3 判…

【Python】matplotlib.axes.Axes.pie()实例讲解

目录:matplotlib.axes.Axes.pie函数实力讲解一、前言二、matplotlib.axes.Axes.pie()函数三、代码示例四、get_cmap函数一、前言 本文章向大家介绍Python matplotlib.axes.Axes.pie()实例讲解,主要分析其语法、参数、返回值和注意事项,并结合…

WorkPlus助力中交四航局打造数字化管理新模式,释放企业生产力

企业简介 中交四航局正式创立于1951年,集团主要从事海内外港口、公路、桥梁、铁路、市政工程、水利工程等大型基础设施建设,以及相关的投资、勘察设计、科研、工业造船和房地产业务。始终致力于“让世界更畅通,让城市更宜居,让生…

logger记录在控制台显示,但是在日志输出文件中不显示问题排查

场景: 代码中存在使用logger.info输出数据到指定的文件中,然后用logstash去收集需要的数据插入到es中。 现象: logger.info输出的记录打断点在控制台上显示,但是在配置的日志输出文件中却找不到日志输出的内容。 log4j配置 如下…

ELK搜索学习笔记--Day1

ELK搜索学习笔记–Day1 1. 课程简介 1.1 课程内容 ELK是包含但不限于Elasticsearch(简称es)、Logstash、Kibana 三个开源软件的组成的一个整体。这三个软件合成ELK。是用于数据抽取(Logstash)、搜索分析&#xff08…

Freemodbus启动流程分析

近项目有用到modbus协议,于是在网上找了些资料成功将freemodbus移植到m3,由于移植过程较简单,网上教程也很多,这里我们就不再赘述.我用到的freemodbus版本是V1.5,下面附上新的源码下载地址:http://www.freemodbus.org/index.php?idx5 下面开始分析下freemodbus得启动流程,老规…

Android设计模式详解之解释器模式

前言 解释器模式是一种使用较少的行为型模式; 提供了一种解释语言的语法或表达式的方式,通过该接口解释一个特定的上下文。 定义:给定一个语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表…

MySQL面试常问问题(高可用/性能 + 运维) —— 赶快收藏

1.数据库读写分离了解吗? 读写分离的基本原理是将数据库读写操作分散到不同的节点上,下面是基本架构图: 读写分离 读写分离的基本实现是: 数据库服务器搭建主从集群,一主一从、一主多从都可以。 数据库主机负责读写操作&#x…

洛谷——P1573 栈的操作

文章目录栈的操作题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1提示AC代码栈的操作 题目描述 现在有四个栈,其中前三个为空,第四个栈从栈顶到栈底分别为 1,2,3,⋯,n1,2,3,\cdots ,n1,2,3,⋯,n。每一个栈只支持一种操作:弹出并压入…

当云原生成为一种显学,对象存储和数据湖如何顺势而为

前言: 已经成为数字化时代显学的云原生并非单项技术,而是一种重塑了软件开发和和业务运行应用的设计思想,是一套技术体系和方法论。云原生“Cloud Native”的Cloud 是指云平台,Native则表示应用程序从设计之初即使用云环境、天生…

MyBatis学习 | SQL映射文件

文章目录一、简介二、insert、update和delete标签2.1 关于增删改2.2 获取自增主键的值三、参数处理3.1 获取不同形式的参数3.1.1 获取单个参数3.1.2 获取多个参数3.2 #{Key}3.2.1 #{}🆚${}3.2.2 #{}中设置参数规则四、select标签4.1 select标签的主要属性4.2 关于返…

即时通讯音视频开发视频编解码理论

从信息论的观点来看,描述信源的数据是信息和数据冗余之和,即:数据信息数据冗余。数据冗余有许多种,如空间冗余、时间冗余、视觉冗余、统计冗余等。将图像作为一个信源,视频压缩编码的实质是减少图像中的冗余。 视频为何…

2步就能实现给视频去色并裁剪画面

看到很多小伙伴还不知道大量的视频怎么实现批量的进行去色处理,并且裁剪视频画面大小的方法,小编今天就来教大家一个可以快速操作的简单方法,感兴趣的朋友们快进来瞧瞧吧! 首先我们来看看用这个方法操作剪辑出来的效果&#xff0c…

预焙阳极行业现状:供给格局边际将改善 “双碳”下优质产品迎新机遇

预焙阳极属于碳素制品,是电解铝生产过程中不可缺少的大宗原材料。从用途来看,预焙阳极仅用作电解铝过程中电解槽的阳极材料,既作为导体,又参与电化学反应而产生消耗,预焙阳极的品质会对原铝的质量产生重要影响。 一、预…

免费PDF阅读器有哪些? 14款强烈推荐的PDF阅读器!

即使经过这么多年,PDF 仍然是最受欢迎的阅读格式之一。从阅读电子书或填写在线表格到创建用户手册,PF 格式仍然是最受欢迎的阅读方式。虽然现在的网络浏览器已经配备了基本的 PDF 阅读功能,但您仍然需要单独下载 PDF 阅读器才能实现填写表格、…

「另类」图达通,还缺一个二次进化

作者 | 张祥威 编辑 | 于婷中国的激光雷达公司早期都很幸运,禾赛、速腾聚创和图达通三家,分别遇到了自己的伯乐——蔚小理。 比较特别的是图达通,它与蔚来的合作之紧密,程度远超另外两家,堪称命中贵人。 根据图达通联合…

p5.js 光速入门

本文简介 点赞 关注 收藏 学会了 本文的目标是和各位工友一起有序的快速上手 p5.js ,会讲解 p5.js 的基础用法。 本文会涉及到的内容包括: 项目搭建p5.js 基础2D图形文字图形样式设置图片事件(交互相关的)基础动画 其中还会…

Ubuntu四轮小车仿真教程gazebo

主要实现内容为在ROS环境下基于Gazebo仿真软件创建一个四轮小车,并实现小车的控制,如下图所示,接下来教程将会进行详细解释。 1.创建工作空间 创建ROS工作空间,命名为SmartCar,并在该工作空间中创建src文件夹。 mkdi…

数字三渔冲:打造美丽乡村新范式

年初,中共中央 国务院关于做好 2022 年全面推进乡村振兴重点工作的意见中提到,要大力推进数字乡村建设,以数字技术赋能乡村公共服务。沿着乡村振兴的战略导向,并紧随筑堡工程共同缔造号召,长阳三渔冲村引入了 SENSORO …

[ Linux ] 死锁以及如何避免死锁

目录 1.什么是死锁? 死锁 2.模拟死锁情况 3.死锁四个必要条件 4.避免死锁的方法 5.避免死锁的算法 银行家算法(了解为主) 1.什么是死锁? 死锁 死锁是指在一组进程中的各个进程均占有不会释放的资源,但因互相申…