Solidity基础六

生活本来就是平凡琐碎的,哪有那么多惊天动地的大事,快乐的秘诀就是不管对大事小事都要保持热情

目录

一、Solidity的特殊变量(全局)

二、Solidity的不可变量

immutable的赋值方式 

三、Solidity的事件与日志

事件和日志加深理解

四、Solidity的异常处理

1.require()

2.assert()

3.revert()

4.三者使用例子

5.require、assert、revert区别

6 require、assert 使用场景

7.抛弃的异常语句和其他新增的异常语句 

五、Solidity 工厂合约 


一、Solidity的特殊变量(全局)

特殊变量,是全局可用的变量,提供关于区块链的信息。下面列出了常用的特殊变量 

名称返回
blockhash(uint blockNumber) returns (bytes32)给定区块的哈希值 – 只适用于256最近区块, 不包含当前区块。
block.coinbase (address payable)当前区块矿工的地址
block.difficulty (uint)当前区块的难度
block.gaslimit (uint)当前区块的gaslimit
block.number (uint)当前区块的number
block.timestamp (uint)当前区块的时间戳,为unix纪元以来的秒
gasleft() returns (uint256)剩余 gas
msg.data (bytes calldata)完成 calldata
msg.sender (address payable)消息发送者 (当前 caller)
msg.sig (bytes4)calldata的前四个字节 (function identifier)
msg.value (uint)当前消息的wei值
now (uint)当前块的时间戳
tx.gasprice (uint)交易的gas价格
tx.origin (address payable)交易的发送方

 

二、Solidity的不可变量

Solidity 的不可变量是另一种常量的表达方式。与常量类似,但是不必强制定义就需要赋值,可以在构造函数时传值,部署后无法改变。它是一种修饰符,被它修饰的变量就称之为不可变量

关键字:immutable

immutable 不可变量同样不会占用状态变量存储空间,在部署时,变量的值会被追加的运行时字节码中, 因此它比使用状态变量便宜的多,也同样带来了更多的安全性。

immutable  特性在很多时候非常有用, 最常见的如 ERC20 代币用来指示小数位置的 decimals 变量,它是一个不能修改的变量,很多时候我们需要在创建合约的时候指定它的值,这时 immutable 就大有用武之地, 类似的还有要保创建者地址、关联合约地址等。

immutable的赋值方式 

第一种:定义后就赋值

数据类型 修饰符 immutable 不可变量名 = 值;
例如 :address public immutable owner = msg.sender;

第二种:构造函数赋值

constructor (参数列表) {

        不可变量名 = 值

}

例如:

contract Immutable {
   address public immutable owner;
address public immutable owners; 

   constructor(address _owner) {
      owner = _owner;
        owners=msg.sender;
   }

 

三、Solidity的事件与日志

Soliddity 事件是以太坊虚拟机(EVM)日志基础设施提供的一个便利接口。是以太坊提供的基本功能,用于将数据记录成日志保存到区块链上,用户可以自定义需要记录的数据,以及topic和索引 ,

事件和日志的区别:

事件强调行为操作、日志强调存储内容,两者是完全不同的概念

事件本质上也相当于一个特殊的函数,称为事件函数

事件在合约中可以被继承

在DAPP的应用中,如果监听了某个事件,当事件发生时,会进行回调执行一系列操作

事件的关键字:event 

事件的定义格式:

event 事件名( 所要记录的参数列表 ) 

事件的触发格式:

emit 事件名( 所要传递给事件的参数列表 )

日志:logs

在以太坊的语境里,日志对事件和触发该事件产生的交易信息的存储

日志的组成:

  1. address: 交易地址
  2. args:事件状态变量存储对象(我们所保存的状态变量值就在这里面)
  3. blockHash: 哈希难度
  4. blockNumber: 区块号
  5. event: 事件名
  6. logIndex:
  7. removed: 
  8. transactionHash: 交易哈希
  9. transactionIndex: 

日志的作用

  • 记录了事件指定的状态变量保存在区块链上的数据(记录了事件不同的状态)
  • 通过日志来得到某个事件定义的状态变量中以前和现在的值

注意事项:在web3中采用事件监听所返回的值是该事件的日志,可以通过该日志.args.状态变量名    来获取日志中某个状态变量的值,而这个返回的结果是一个数组,它存储了该状态变量变化的值

当触发了事件,那么remix控制台里面的一个信息里面就有logs(日志)信息

里面记录了ages由值0变成值2

//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
contract Test{
    int age;
    string a=unicode'a';
    event  evt(int ages);
    function set(int _age) public returns(string memory){
        age=_age;
        emit evt(_age);
        return a;
    }
}

事件和日志加深理解

当定义的事件触发时,我们可以将事件存储到EVM的交易日志中,日志其实是区块链中一种特殊的数据结构

日志与合约关联,与合约合并存储到区块链中,只要某个区块可以访问,其相关的日志也能访问,但在合约中,我们不能直接访问日志和事件数据

可以通过日志实现简单支付验证SPV,如果一个外部实体提供了一个带有这种证明的合约,它可以检查日志是否真实存在区块链中

四、Solidity的异常处理

异常和报错

异常是指程序可以解决的一种错

报错是指程序无法解决的一种错

异常处理

Solidity处理异常和我们常见的语言不一样,solidity是通过回退状态的方式来处理错误,同时给调用者返回一个错误标识。

如果不用回退状态的方式来处理,那么异常后,状态发生错误的改变,在区块链上是需要相当大的代价来完成的

异常处理过程

发生异常时会撤销当前调用(及其所有子调用)所改变的的状态,同时给调用者返回一个错误标识

异常处理模式

早期是if....throw这个会消耗所有的gas

Solidity 0.4.10后发布了新的异常处理方法:异常处理函数

0.6.0版本还出现了try...catch

主要还是以下3种:

  • require()  
  • assert()  
  • revert()  

解决了以前的不好的地方,特别地,  assert() 、 require() 代码会 “确保” 提高合约代码l逻辑条理的清晰,所有我们需要好好学习并区别使用它们

1.require()

格式:

require( 判断表达式 ,  <string>)

<string> 提供了一个自定义错误消息输出的选项

如果不满足条件也就是判断为false,则此函数调用将恢复到原始状态,此函数用于检查输入或外部组件的错误。这个方法一般是用来处理

require 可以有返回值,例如:require(condition, 'Something bad happened');。

返回:'Something bad happened'

require 的返回值不宜过长,因为返回信息需要消耗 gas。

使用 require() 的场景

  1. 验证一个用户输入是否合法 : require(input>20)
  2. 验证外部合约的调用结果,例如:require(external.send(amount))
  3. 判断执行一段语句的前置条件;  require(balance[msg.sender]>=amount)
  4. require应该被经常用到

2.assert()

assert(判断表达式)  

如果不满足条件也就是判断为false,则此函数调用将导致一个无效的操作码,对状态所做的任何更改将被还原,这个一般方法是用来处理内部错误的

使用 assert() 的场景

  • 检查溢出
  • 检查不变量
  • 更改后验证状态
  • 预防永远不会发生的情况
  • 避免本不应该发生的情况出现,如程序bug

一般来说,使用assert()的频率较少,通常用于函数的结尾。

assert算是最后防线,因为它会在执行的最后来检查行为的合法性

3.revert()

revert()  

一执行revert()就将中止执行并将所作的更改还原为执行前的状态 

它可以搭配if分支来实现和require和assert的效果 

一个交易最终只会有两种状态: cmmit & revert

适用revert的时候

因为该操作是已知不应该出现的时候,所以通常同来检查overflow/underflow、检查被修改过的状态变量是否合法,避免不应该出现的条件发生

4.三者使用例子

uint public num=0;

function testRevert() public {
num++;
if (num>3){
revert(“revert返回的错误”);
}
num++;

}

function testAssert() public {
num++;
assert(num<13);
num++;

}

function testRequire()public {
num++
require(	num<23,”require报错信息”);
num++;

}
总结:不管是哪个回退,都是回退到不满足条件前满足条件的状态

5.require、assert、revert区别

require、assert、revert共同点:

assert()与require()语句都需要满足括号中的条件,才能进行后续操作,若不满足则抛出错误。而revert()就抛出错误

以下三个语句的功能完全相同:

// revert
if(msg.sender != owner) { 
   revert();
 }
// require
require(msg.sender == owner);

// assert
assert(msg.sender == owner);

require、assert 不同点

assert(false) 编译为 0xfe,这是一个无效的操作码,所以会消耗掉所有剩余的 gas,并恢复所有的操作。

require(false) 编译为 0xfd,这是revert()的操作码,所以会退还所有剩余的 gas,同时可以返回一个自定义的报错信息。 

同样作为判断一个条件是否满足的函数,require会回退剩下的gas,而assert会烧掉所有的gas

所以require 的 gas 消耗要小于 assert,而且可以有返回值,使用更为灵活。

6 require、assert 使用场景

  • require() 函数用于检测输入变量或状态变量是否满足条件,以及验证调用外部合约的返回值。
  • require() 语句的失败报错,应该被看作一个正常的判断语句流程不能通过的事件。
  • assert()语句的失败报错,意味着发生了代码层面的错误事件,很大可能是合约中有一个bug需要修复

基本上,require() 应该用于检查条件,而 assert() 只是为了防止发生任何非常糟糕的事情。

7.抛弃的异常语句和其他新增的异常语句 

throw的介绍------throw已被废弃

throw在solidity会被编译成invalid opcode,因此执行到这里,EVM会终止tx(交易)且没收所有的gas

Throrw算是一个误用,实际上solidity并没有错误处理的catch机制。因为它在语言上不算好词

官方建议修改用revert()代替

  

try...catch介绍

我们在当前合约发起对外部合约调用的话,如果外部合约调用执行失败被 revert,外部合约状态会被回滚,当前合约状态也会被回滚。

但有时候我们并不想这样,要是能够捕获外部合约调用异常,然后根据情况做自己的处理不是更好吗?所以,这种场景下适应于使用 try...catch 语句。

和require对比

以下代码将会触发 catch Error(string memory reason) ,最终输出 require error。

pragma solidity ^0.8.0;
contract Manager {
    function count() public pure returns(int){
        require(1==2,"require error");
        return 2;
    }
    
    function test() public view returns(string memory) {
        try this.count()  {
            return "success";
        } catch Error(string memory reason/* 出错原因 */) {
            // 调用 count() 失败时执行,通常是不满足 require 语句条件或触发 revert 语句时所引起的调用失败
            return reason;
        } catch (bytes memory) {
            // 调用 count() 异常时执行,通常是触发 assert 语句或除 0 等比较严重错误时会执行
            return "assert error";
        }
    }
}

和assert()对比

以下代码将会触发 catch (bytes memory) ,最终输出 assert error。

pragma solidity ^0.8.0;
contract Manager {
    function count() public pure returns(int){
        assert(1==2);
        return 2;
    }
    
    function test() public view returns(string memory) {
        try this.count()  {
            return "success";
        } catch Error(string memory reason/* 出错原因 */) {
            // 调用 count() 失败时执行,通常是不满足 require 语句条件或触发 revert 语句时所引起的调用失败
            return reason;
        } catch (bytes memory) {
            // 调用 count() 异常时执行,通常是触发 assert 语句或除 0 等比较严重错误时会执行
            return "assert error";
        }
    }
}

五、Solidity 工厂合约 

Solidity 工厂合约是一种批量部署合约的方式。

通过一个工厂合约创建部署合约,并记录下所有部署合约的地址。

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Account {
    address public bank;
    address public owner;

    constructor(address _owner) payable{
        bank = msg.sender;
        owner = _owner;
    }
}

contract Factory {
    Account[] public accounts;

    function createAccount(address owner) external payable{
       accounts.push(new Account{value:123}(owner));
       accounts.push(new Account{value:456}(owner));
    }
}

 我们只需要部署 Factory 合约,运行 createAccount 方法,就会自动创建其它合约。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ChatGPT1论文解读《Improving Language Understanding by Generative Pre-Training》

论文总结 以下是我阅读完整篇论文做的个人总结&#xff0c;基本包含了chatGPT1设计的完整框架思路&#xff0c;可以仅看【论文总结】章节。 在GPT1实现的核心架构中&#xff0c;包含两个阶段。 第一阶段 在第一阶段基于一个包含7000本书籍内容的海量未标注文本数据集进行无…

注解-反射-XML配置原理

java刚开始原本是直接在方法中创建对象执行程序等&#xff0c;部分代码重复率高&#xff0c;后来就发展成方法封装调用&#xff0c;再后来出现的像spring框架等&#xff0c;引入了XML配置&#xff0c;使得程序更加简洁&#xff0c;方便等&#xff0c;其中XML配置也是基于java反…

java+iClientOpenlayers实现污水排放扩散模拟(湖库污染排放扩散模拟)

软件实现效果 一、应用背景 湖库污染是一个日益严峻的环境问题。随着城市化和工业化的加速发展&#xff0c;越来越多的有害物质被排放入湖库中&#xff0c;导致湖库污染加剧并扩散到周围地区。本文将探讨湖库污染扩散的原因、影响和解决方法。 首先&#xff0c;湖库污染扩散的…

堆排序之——TopK问题

思维导图&#xff1a; 一&#xff0c;TopK算法的运用 TopK的算法在我们的日常生活中可谓是大有用处&#xff0c;比如你在点外卖时外卖榜单上的销量前几名的筛选&#xff0c;富豪排行榜的榜单人物的筛选&#xff0c;游戏排位……等等领域都会有TopK算法的涉及。TopK问题的用处可…

github创建仓库和拉取代码

目录 一、git创建仓库 第一步&#xff1a;首先登录github 第二步&#xff1a;进入建立的仓库(或者新建仓库) 第三步&#xff1a;创建成功 第四步&#xff1a;在本地新建一个文件夹&#xff0c;然后在文件夹下打开git bash 第五步&#xff1a;在git bash命令框执行git init…

专业解读财务共享实现财务数智化转型的有效路径

近年来&#xff0c;随着数字经济的飞速发展&#xff0c;各大企业全面开启数智化转型之路&#xff0c;作为企业数智化转型的重要内容&#xff0c;财务数智化转型始于财务共享服务。然而&#xff0c;财务共享建设并不是一蹴而就的&#xff0c;如何通过财务共享实现财务数智化转型…

什么是分布式软件系统

:什么是分布式软件系统&#xff1f;分布式软件系统是什么意思&#xff1f; 分布式软件系统(Distributed Software Systems)是支持分布式处理的软件系统,是在由通信网络互联的多处理机体系结构上执行任务的系统。它包括分布式操作系统、分布式程序设计语言及其编译(解释)系统、分…

阻抗板是否高可靠,华秋有话说

随着高频高速电子产品的快速发展&#xff0c;信号传输过程更容易出现反射、串扰等信号完整性问题&#xff0c;且频率越高、传输速率越快&#xff0c;信号损耗越严重&#xff0c;如何降低信号在传输过程中的损耗、保证信号完整性是高频高速PCB发展中的巨大挑战。 在高速PCB设计…

Spring源码(一) — 序言

序言 Java程序员的日常开发一定都离不开Spring的框架&#xff0c;从Spring、SpringMVC、SpringBoot、SpringCloud… 而Spring框架就是Spring家族中最基础也是最重要的一个框架。 Spring 我们常说的Spring往往都绕不开IOC&#xff08;控制反转&#xff09;和AOP&#xff08;切…

【TellMeCode】使用VSCODE + ChatGPT辅助分析推测源码

【TellMeCode】使用VSCODE ChatGPT辅助分析推测源码 0x00 功能简介 根据代码上下文相关信息&#xff0c;如工作区文件夹名称&#xff0c;代码所在路径等一系列信息&#xff0c;提供给大模型更多元和尽可能多的信息&#xff0c;利用其自身优势去检索相关的文档和博客&#xf…

QT开发实战-动态壁纸软件

动态壁纸软件开发 项目源代码在下面链接获取: ----------------------------- 开发者:CodeSharkSJ 希望此项目能加强你对Qt的应用 文章目录 项目图与开发环境核心技术原理自定义窗口程序UI布局背景绘制样式表基本实现QWebEngineQMedia使用系统托盘隐藏记忆功能应用程序打包 …

RestCloud荣膺广东省优秀软件产品奖,引领国内数据集成领域!

近日&#xff0c;“2022年广东软件风云榜”名单公布&#xff0c;“谷云ETL数据交换软件”凭借其在助力企业数字化转型升级过程中的卓越表现&#xff0c;荣获由羊城晚报报业集团、广东软件行业协会、广东省大数据协会联合颁发的“优秀软件产品和解决方案”奖。 数字化转型是推动…

【P38】JMeter 随机控制器(Random Controller)

文章目录 一、随机控制器&#xff08;Random Controller&#xff09;参数说明二、测试计划设计2.1、测试计划一2.2、测试计划二2.3、勾选忽略子控制器块 一、随机控制器&#xff08;Random Controller&#xff09;参数说明 可以让控制器内部的逻辑随机执行一个&#xff0c;一般…

深度学习-第T8周——猫狗识别

深度学习-第T8周——猫狗识别 深度学习-第T8周——猫狗识别一、前言二、我的环境三、前期工作1、导入数据集2、查看图片数目 四、数据预处理1、 加载数据1.1、设置图片格式1.2、划分训练集1.3、划分验证集1.4、查看标签1.5、再次检查数据1.6、配置数据集 2、数据可视化 五、搭建…

机器学习常识 7: 决策树

摘要: 决策树是一种与人类思维一致, 可解释的模型. 1. 决策树的结构 人类的很多知识以决策规则的形式存储: 如果今天是阴天 (outlook overcast), 就去打球.如果今天出太阳 (outlook sunny) 而且湿度不高于 70% (humidity ≤ \le ≤ 70), 就去打球.如果今天出太阳 (outloo…

1688商品ID采集一件代发详情页面数据

本篇博文介绍了对1688商品详情API的二次封装&#xff0c;将URL参数封装成Python函数&#xff0c;直接传入参数即可获取搜索结果&#xff0c;例如1688商品标题、价格、一件代发、sku属性和URL等。提供了详细的代码示例和接口调用Demo。 1688.item_get-获得1688商品详情数据 1.请…

APP开发死亡潮来临 小程序是否会取而代之?

移动互联网的发展&#xff0c; APP开发行业也迎来了它的大时代。据有关数据显示&#xff0c;2017年上半年国内新增的 App数量达到了创纪录的449万款&#xff0c;用户使用时长超过了200亿分钟。移动互联网已成为名副其实的“流量”产业&#xff0c;也因此诞生出一大批 APP开发公…

Maven 概述及下载安装

一、为什么要学习 Maven 我们构建一个项目需要用到很多第三方的类库&#xff0c;就需要引入大量的jar包&#xff0c;并且Jar包之间的关系错综复杂&#xff0c;缺少任何一个Jar包都会导致项目编译失败。Maven 能帮助我们下载及管理依赖。 本地项目代码开发完成后&#xff0c;我…

类和对象【3】初始化列表

全文目录 引言初始化列表定义特性 总结 引言 上一篇文章中介绍了构造函数&#xff0c;它可以在实例化一个类对象的时候自动调用&#xff0c;以初始化类对象&#xff1a; 戳我看默认成员函数详解 但是&#xff0c;不难发现&#xff0c;在构造函数体中对成员变量的初始化其实是属…

gdb调试 与 coredump

gdb调试 与 coredump调试 1. 启动gdb2.gdb中的相关命令3. coredump调试&#xff08;附属于gdb调试中一种&#xff0c;当程序出现错误时&#xff0c;会使用coredump调试&#xff09;1&#xff09;coredump是什么&#xff1f;2&#xff09;前期设置3&#xff09;什么情况下会导致…
最新文章