Solidity基础七

news2024/11/27 4:14:32

无论风暴将我带到什么岸边,我都将以主人的身份上岸

目录

一、Solidity的单位 

1. 货币Ether

2. 时间单位Time

二、地址的形成

三、以太坊的账户

1.内部账户(简称CA)

2.外部账户(简称EOA)

3.内部账户和外部账户的比较 

4.判断是内部账户还是外部账户的方法

四、消息调用和余额查询

五、交易

六、Solidity this 和 msg.sender 、msg.value的用法

七、Solidity的转账函数

1.transfer()

2.send()

3.call()

4.transfer、send、call的区别和用法

八、Solidity 接收函数

九、Solidity 发送Eth

十、Solidity 支付Eth

十一、Solidity 回退函数


一、Solidity的单位 

1. 货币Ether

Ether的单位关键字有wei, gwei, finney, szabo, ether,换算格式如下:

  • 1 ether = 1 * 10^18 wei
  • 1 ether = 1 * 10^9 gwei
  • 1 ether = 1 * 10^6 szabo
  • 1 ether = 1* 10^3 finney
pragma solidity 0.4.20;
/**
 * 对 比特币 Ether 的几个单位进行测试
 */
contract testEther {
    // 定义全局变量
    uint public balance;

    function testEther() public{
        balance = 1 ether;  //1000000000000000000
    }

    function fFinney() public{
      balance = 1 finney; //1000000000000000
    }

    function fSzabo() public{
      balance = 1 szabo;  //1000000000000
    }

    function fWei() public{
      balance = 1 wei; //1
    }
}

2. 时间单位Time

Time的单位关键字有seconds, minutes, hours, days, weeks, years,换算格式如下:

  • 1 == 1 seconds
  • 1 minutes == 60 seconds
  • 1 hours == 60 minutes
  • 1 days == 24 hours
  • 1 weeks == 7 days
  • 1 years == 365 days

//他们都会放在uint中,全部转换为秒单位

//变量now将返回当前的unix时间戳(自1970年1月1日以来到现在的秒数)

return now

return 1 days   ---> unit秒数

如果你需要进行使用这些单位进行日期计算,需要特别小心,因为不是每年都是365天,且并不是每天都有24小时,因为还有闰秒。由于无法预测闰秒,必须由外部的oracle来更新从而得到一个精确的日历库(内部实现一个日期库也是消耗gas的)。

pragma solidity 0.4.20;
/**
 * 对 Time 单位进行测试
 */
contract testTime {

    // 定义全局变量
    uint time;

    function testTime() public{
      time = 100000000;
    }

    function fSeconds() public view returns(uint){
      return time + 1 seconds; //100000001
    }

    function fMinutes() public view returns(uint){
      return time + 1 minutes; //100000060
    }

    function fHours() public view returns(uint){
      return time + 1 hours; //100003600
    }

    function fWeeks() public view returns(uint){
      return time + 1 weeks; //100604800
    }

    function fYears() public view returns(uint){
      return time + 1 years; //131536000
    }
}

二、地址的形成

1.地址是由公钥Keccak-256单向哈希,取最后20个字节(160位)派生出来的标识符

2.在solidity中,地址类型使用address来表示

3.地址类型在以太坊中非常重要,因为以太坊的账户需要用地址来表示

4.地址占用20个字节,共160位,即以太坊的地址长度是20个字节

5.地址类型的声明 address 地址名=0x十六进制数

 地址是所有合约的基础,支持的比较运算符有 <  >  <=  >=  ==  !

三、以太坊的账户

以太坊中的两类账户:

  • 内部账户:由智能合约的代码控制
  • 外部账户 :由密钥控制

以太坊中账户不用申请,而实用户根据需要在钱包中生成,然后连接到以太坊

它们共用EVM中同一个地址空间  账户地址空间

无论账户是否存储代码,这两类账户对EVM来说处理方式是一样的

每个账户在EVM中都有一个键值对形式的持久化存储,其中key和value的长度都是256位

账户信息也就是地址信息

1.内部账户(简称CA)

内部账户也就是合约账户,合约地址就代表该内部账户地址

有的以太币余额,有关联代码,可通过交易或者来自其他合约的调用信息来触发代码执行

执行代码时可以操作自己的存储空间,也可以调用合约,没有私钥控制,其codeHash非空

2.外部账户(简称EOA)

外部账户:

外部账户就是非合约账户,是由第三方钱包app所创建的账户,例如metamask

有对应的以太币余额,没有关联代码,可发送交易(转币或促发合约代码),由用户私钥控制,其codeHash为空

3.内部账户和外部账户的比较 

比较外部账户合约账户
拥有私钥
codeHash内容为空非空
主动发起交易否,只能被动发起交易
拥有余额
地址长度20字节20字节

4.判断是内部账户还是外部账户的方法

采用extcodesize来判断,它可以获取地址关联代码长度

通过判断账户关联代码长度

合约地址大于0 外部账户地址为0 

//extcodesize获取地址关联代码长度 合约地址大于0 外部账户地址为0 

contract IsCadd {
    function isContract(address addr) returns (bool) {
    uint size;
    assembly { size := extcodesize(addr) }
    return size > 0;
  }
}

四、消息调用和余额查询

合约可以通过消息调用的方式来调用其他合约或者发送以太币到非合约账户

balance属性用于查询账户余额  

格式:

地址名.balance    

五、交易

1.签名的数据包,可以包含二进制数据负载和以太币,由外部账户发送到另一个账户的消息

 2.既可以是外部账户的交易也可以是内部账户的交易

 3.交易一经创建,每笔交易都需要消耗gas,目的是限制执行交易所需的工作量和为交易支付手续费。EVM执行交易时,gas将按特定规则逐渐耗尽

  4.gas price是交易发送者设置的一个值,发送者账户需要预支付手续费=gas_price*gas。如果交易后还有剩余,gas会原路返还

六、Solidity this 和 msg.sender 、msg.value的用法

Solidity 中 this 代表合约对象本身,可以通过 address(this) 获取合约地址。合约地址与合约创建者地址、合约调用者地址并不相同。

Solidity 中 msg.sender 代表合约调用者地址。一个智能合约既可以被合约创建者调用,也可以被其它人调用。

合约创建者,即合约拥有者,也就是指合约部署者,它的地址可以在合约的 constructor() 中,通过 msg.sender 获得,因为合约在部署的时候会首先调用 constructor()

Solidity中mas.value代表调用者输入的值,通常用来让调用者自定义转账数量

1. 范例

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

contract SolidityTest {
    address public owner;

    event log(address);

    constructor()  {
        owner = msg.sender;
        emit log(msg.sender);
        emit log(address(this));
    }
}

owner 被赋值为合约部署者的地址。

log(msg.sender) 在日志中输出了合约部署者的地址。

log(address(this)) 在日志中输出了合约地址。

查看合约在部署时的日志结果:

[
    {
        "from": "0xE3Ca443c9fd7AF40A2B5a95d43207E763e56005F",
        "topic": "0x2c2ecbc2212ac38c2f9ec89aa5fcef7f532a5db24dbf7cad1f48bc82843b7428",
        "event": "log",
        "args": {
            // 合约部署者的地址
            "0": "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4"
        }
    },
    {
        "from": "0xE3Ca443c9fd7AF40A2B5a95d43207E763e56005F",
        "topic": "0x2c2ecbc2212ac38c2f9ec89aa5fcef7f532a5db24dbf7cad1f48bc82843b7428",
        "event": "log",
        "args": {
            // 合约地址
            "0": "0xE3Ca443c9fd7AF40A2B5a95d43207E763e56005F"
        }
    }
]

七、Solidity的转账函数

使用 Solidity 智能合约转账可以使用 transfer 函数等其他转账函数。智能合约里面需要有一定的以太,不然合约将无法给调用者发送以太,可以在创建合约时给合约发送一定的以太来测试。

  

转账双方可以是:

1.外部账户向外部账户转账  

2.内部账户向内部账户转账

3.外部向内部,内部向外部账户转账

具有转账功能的智能合约的 constructor 必须显式的指定为 payable。,具有接收和转账功能的函数也需要加上payable

 

谁给谁转就消耗谁的余额,谁调用函数就消耗谁的gas

 

注意事项:非payble类型地址不能

1.transfer()

transfer()方法

接收者地址.transfer(数量)

如果当前合约的余额不够大或者 Ether转账被接收账户拒绝,转账功能将失败。接收方智能合约应定义回退函数,否则转账调用将引发错误。transfer函数在失败时恢复。另外它被硬编码以防止重入攻击(这句话不是很能理解)。

// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
contract  cs{
    constructor() payable{
    }
    
    function getETH() public{
        require(address(this).balance>=1 ether,"no money");
        address payable _owner = msg.sender;
        _owner.transfer(1 ether);
    }
    
    fallback() external{
    }
    
    receive() payable external{
    }
}

2.send()

格式:

接收者地址.send(数量)

Send是和Transfer具有同等功能的低级api。如果执行失败,当前合约不会因为异常而停止,但会返回false。

    function send(address payable _to) public payable {
        bool isSend = _to.send(msg.value);
        require(isSend, "Send fail");
    }

3.call()

格式:

接收者地址.call(数量)

这是将 ETH 发送到智能合约的推荐方式。空参数触发接收地址的回退功能(fallback function)

 function calls(address payable _to) public payable {
 
        (bool isSuccess, /* memory data */ ) = _to.call{value: msg.value}("");
        require(isSuccess, "Failure! Ether not send.");
    }

使用call,还可以触发合约中定义的其他功能,并发送固定数量的gas来执行该功能。交易状态作为布尔值发送,返回值在数据变量(bytes memory data)中发送。 

更具体使用的格式如下:

(bool sent, bytes memory data) = _to.call{gas :10000, value: msg.value}("func_signature(uint256 args)");
 2019年,solidity官方已经弃用了send和transfer,推荐call方法进行转账操作,但还是要小心使用官方给出了的警告

4.transfer、send、call的区别和用法

1. transfer

  • 如果异常会转账失败,抛出异常(等价于require(send()))(合约地址转账)
  • 有gas限制,最大2300
  • 函数原型:<address payable>.transfer(uint256 amount)

2. send

  • 如果异常会转账失败,仅会返回false,不会终止执行(合约地址转账)
  • 有gas限制,最大2300
  • 函数原型:<address payable>.send(uint256 amount) returns (bool)

3. call

  • 如果异常会转账失败,仅会返回false,不会终止执行(调用合约的方法并转账)
  • 没有gas限制
  • <address>.call(bytes memory) returns (bool, bytes memory)

共同点

  • addr.transfer(1 ether)、addr.send(1 ether)、addr.call.value(1 ether)的接收方都是addr。
  • 如果使用addr.transfer(1 ether)、addr.send(1 ether),addr合约中必须增加fallback回退函数!
  • 如果使用addr.call.value(1 ether),那么被调用的方法必须添加payable修饰符,否则转账失败!
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;

contract  cs{
    constructor() payable{
    }
    
    function getETH() public returns(bool) {
        address payable _owner = msg.sender;
        return(_owner.send(1 ether));
    }
    # 如果使用transfer或send函数必须添加fallback回退函数
    fallback() external{
    }
    
    receive() payable external{
    }
}
}

八、Solidity 接收函数

solidity 接收函数 receive 没有参数、没有返回值。

solidity 向合约转账,发送 Eth,就会执行 receive 函数。

如果没有定义接收函数 receive,就会执行 fallback 函数。

向合约转账

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

contract Fallback {
   event eventFallback(string);

   fallback() external payable {
      emit eventFallback("fallbak");
   }

   receive() external payable {
      emit eventFallback("receive");
   }
   //  查看合约账户余额
   function getBalance() external view returns(uint) {
      return address(this).balance;
   }
}  

我们向合约 Fallback 发送一笔 123 wei 的交易,查看日志:

[
{
"from": "0xd457540c3f08f7F759206B5eA9a4cBa321dE60DC",
"topic": "0x39684f4c14ee0aafaa34ed83629676cd0fbe71653659c3353ef0c33f630e7eab",
"event": "eventFallback",
"args": {
"0": "receive"
}
}
]

我们调用合约 Fallback 的 getBalance 方法,查看合约地址的余额为 123 wei。

receive 和 fallback 调用流程

向一个合约发送 Eth,何时调用 receive 或者 fallback 呢?下面是两者的调用流程。

             发送 Eth
                |
            msg.data 是否为空
              /    \
            是      否
           /         \
 是否定义了receive   fallback
        /  \
       是   否
      /      \
 receive     fallback

九、Solidity 发送Eth

Solidity 在智能合约中有三种方式发送 Eth。

transfer:使用 transfer 发送 Eth,会带有 2300 个gas,如果失败,就会 revert。

send:使用 send 发送 Eth,会带有 2300 个gas,并且返回一个 bool 值表示是否成功。

call:使用 call 发送 Eth,会发送所有剩余的 gas,并且返回表示是否成功 bool 值和 data 数据。

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

contract SendEther {
    constructor() payable{}

    // 允许接收 Eth
    receive() external payable {}

    function transferEth(address payable _to) external payable {
        _to.transfer(100);
    }
        
    function sendEth(address payable _to) external payable {
        bool success = _to.send(100);
        require(success, "send failed");
    }

    function callEth(address payable _to) external payable {
        (bool success, ) = _to.call{value:100}("");
        require(success, "call failed");
    }
}

contract ReceiveEther {
    event log(uint amount, uint gas);

    // 允许接收 Eth
    receive() external payable {
        emit log(msg.value, gasleft());
    }
}

十、Solidity 支付Eth

使用 payable 标记的 Solidity 函数可以用于发送和接收 Eth。payable 意味着在调用这个函数的消息中可以附带 Eth。

使用 payable 标记的 Solidity 地址变量,允许发送和接收 Eth。

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

contract Payable {
    // owner 可用于收费 eth
    address payable public owner;

    constructor() {
       // msg.sender 默认不能收发 eth,需转换
        owner = payable(msg.sender); 
    }  

    function deposit() external payable{
    }
} 

payable 地址变量可以通过 balance 属性,来查看余额。

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

contract Payable {
   function deposit() external payable{
   }

   function getBalance() external view returns(uint) {
      return address(this).balance;
   }
}  

十一、Solidity 回退函数

solidity 回退函数 fallback 没有参数、没有返回值。

solidity 回退函数在两种情况被调用:

  • 向合约转账,发送 Eth,就会执行Fallback函数
  • 如果请求的合约方法不存在,就会执行Fallback函数

向合约转账

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

contract Fallback {
   event eventFallback(string);

   fallback() external payable {
      emit eventFallback("fallbak");
   }

   //  查看合约账户余额
   function getBalance() external view returns(uint) {
      return address(this).balance;
   }
}  

我们向合约 Fallback 发送一笔 123 wei 的交易,查看日志:

[
{
"from": "0xd457540c3f08f7F759206B5eA9a4cBa321dE60DC",
"topic": "0x39684f4c14ee0aafaa34ed83629676cd0fbe71653659c3353ef0c33f630e7eab",
"event": "eventFallback",
"args": {
"0": "fallbak"
}
}
]

我们调用合约 Fallback 的 getBalance 方法,查看合约地址的余额为 123 wei。

请求的合约方法不存在

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

contract Fallback {
   event eventFallback(string);

   fallback() external payable {
      emit eventFallback("fallbak");
   }
}  

contract SoldityTest {
   // 外部合约
   address private fb;

   constructor(address addr) {
      fb = addr;
   }

   function callFallback() external view returns(string memory) {
     // 调用合约 Fallback 不存在的方法 echo()
      bytes4 methodId = bytes4(keccak256("echo()"));
       
      // 调用合约
      (bool success,bytes memory data) = fb.staticcall(abi.encodeWithSelector(methodId));
      if(success){
         return abi.decode(data,(string));
      } else {
         return "error";
      }
   }
}

我们先部署合约 Fallback,再使用 Fallback 的地址来部署 SoldityTest,调用 Fallback 方法 echo 方法,就会触发 Fallback 的 fallback 方法。

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

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

相关文章

dom中的事件处理

事件参考 | MDN (mozilla.org) 什么是事件 事件监听方式 直接在html中编写JavaScript代码(了解) <button οnclick"console.log(按钮1发生了点击~);">按钮1</button> DOM属性&#xff0c;通过元素的on.....来监听事件 // 2.onclick属性// function h…

如何在华为OD机试中获得满分?Java实现【任务总执行时长】一文详解!

✅创作者&#xff1a;陈书予 &#x1f389;个人主页&#xff1a;陈书予的个人主页 &#x1f341;陈书予的个人社区&#xff0c;欢迎你的加入: 陈书予的社区 &#x1f31f;专栏地址: Java华为OD机试真题&#xff08;2022&2023) 文章目录 1. 题目描述2. 输入描述3. 输出描述…

Visual Studio2022编译器实用调试技巧

目录 1.什么是bug 2.调试是什么&#xff1f; 3.debug和release的介绍 4.windows环境调试介绍 4.1 调试环境的准备 4.2 学会快捷键 4.3 调试的时候查看程序当前信息 4.4 查看内存信息 5.如果写出好&#xff08;易于调试&#xff09;的代码 7.编程常见的错误 1.什么是b…

android MutableLiveData与AndroidViewModel避坑小提示,Java

android MutableLiveData与AndroidViewModel避坑小提示&#xff0c;Java import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.lifecycle.AndroidViewModel; import androidx.lifecycle.LifecycleOwner; import androidx.l…

Tomcat部署项目后,验证码不显示问题

在使用Tomcat服务器部署项目后&#xff0c;发现验证码不显示&#xff0c;在浏览器按f12查询后出现以下页面 查看源码发现一切正常 查阅相关资料后&#xff0c;得到以下方法&#xff1a; 1.在tomcat配置文件catalina.sh文件中找到-Djava.io.tmpdir"$CATALINA_TMPDIR" …

day38|动态规划-爬楼梯问题

DP问题类型&#xff1a; 动态规划比较重要的是找到前后两个状态之间的联系&#xff0c;在向后遍历的过程中注意遍历的顺序和初始化操作。 动归基础类问题 背包问题 打家劫舍 股票问题 子序列问题 DP问题的一些注意事项&#xff1a; 动态规划类的问题代码都是比较简洁的&…

数据结构之排序专题 —— 快速排序原理以及改进方法(添加随机,三路快排)

内容概述 尽管此类博客已经非常非常多&#xff0c;而且也有很多写得很好&#xff0c;但还是想记录一下&#xff0c;用最容易理解的方式&#xff0c;并且多补充了一些例子。 整理云盘的时候发现大一时候的笔记&#xff0c;用的是 txt 文本文件记录的&#xff0c;格式之丑陋可想…

SAP-MM-采购申请审批那些事!

1、ME55不能审批删除行项目的PR 采购申请审批可以设置行项目审批或抬头审批。如果设置为抬头审批时&#xff0c;ME55集中审批时&#xff0c;就会发现有些采购申请时不能审批的&#xff0c; 那么这些采购申请时真的不需要审批么&#xff1f;不是的&#xff0c;经过核对这些采购申…

solr快速上手:managed-schema标签详解(三)

0. 引言 core核心是solr中的重中之重&#xff0c;类似数据库中的表&#xff0c;在搜索引擎中也叫做索引&#xff0c;在solr中索引的建立&#xff0c;要先创建基础的数据结构&#xff0c;即schema的相关配置&#xff0c;今天继续来学习solr的核心知识&#xff1a; solr快速上手…

chatgpt赋能python:Python绑定CPU:提高性能的利器

Python 绑定 CPU&#xff1a;提高性能的利器 介绍 Python 作为一门通用编程语言&#xff0c;具有易学易用、开发效率高等优点&#xff0c;但由于其解释型的特性&#xff0c;执行效率相对较低&#xff0c;尤其是在处理大量计算时&#xff0c;性能瓶颈更为明显。在这种情况下&a…

chatgpt赋能python:用Python发送短信的简单方法

用Python发送短信的简单方法 在今天的数字时代&#xff0c;没有任何事情比即时通讯更方便。然而&#xff0c;短信仍然是一种极为有用的通信方式。 实际上&#xff0c;正如您所看到的&#xff0c;本文将告诉您如何使用Python在几步内轻松地发送短信。 发送短信的三种方法 要发…

Unity之TileMap

1、创建瓦片资源 教程中老师在Asset---Create---Tile创建&#xff0c;但是新版本Unity不能这样创建 新版本是在Asset---Create---2D--Tile里面选择&#xff0c;跟老师的不太一样&#xff0c;暂时也不懂怎么解决 所以我们可以用方法二创建&#xff1a; 在Window---2D---Tile…

Linux---phy外设调试(二)

文章目录 一、mdio与rmii/sgmii二、主控mac控制器配置三、phy driver与device的匹配规则 一、mdio与rmii/sgmii 接上一篇文章《Linux—phy外设调试&#xff08;一&#xff09;》&#xff0c;在上一篇中我们说到我们还遗留了几个问题没有解释&#xff0c;其中提到的有mdio总线和…

海量数据中找出前k大数(topk问题),一篇文章教会你

&#x1f4af; 博客内容&#xff1a;【数据结构】向上调整建堆和向下调整建堆的天壤之别以及堆排序算法 &#x1f600; 作  者&#xff1a;陈大大陈 &#x1f680; 个人简介&#xff1a;一个正在努力学技术的准前端&#xff0c;专注基础和实战分享 &#xff0c;欢迎私信&…

[极客大挑战 2019]PHP1

既然提到了备份网站估计也是存在着网站备份文件&#xff0c;可以先用御剑扫一下 啥都没扫出来&#xff0c;但是上回做文件备份的题目时收集了一些关于常用备份文件的文件名和后缀&#xff0c;可以直接使用burp抓包爆破&#xff0c;果然爆破出一个www.zip文件 访问下载好文件就有…

电子科技大学编译原理复习笔记(四):程序语言的设计

目录 前言 重点一览 语言的定义 比较&#xff1a;生成观点与识别观点 语义又该怎么描述&#xff1f; 符号串 符号串集合 ⭐文法&#xff08;超重点&#xff09; 定义 组成 表示 ⭐分类&#xff08;重点&#xff09; 文法产生的语言 ⭐短语、直接短语和句柄&…

幂等问题解决

什么是幂等性&#xff1f; 幂等&#xff08;idempotent、idempotence&#xff09;是一个数学与计算机学概念&#xff0c;常见于抽象代数中。 在数学中&#xff0c;主要有两个定义 如果在一元运算中&#xff0c;x 为某集合中的任意数&#xff0c;如果满足 f(f(x))f(x)&#xff0…

软件质量测试笔记-合工大

第一章 软件质量和测试背景 应从以下几个方面考虑软件质量&#xff1a; 软件结构功能与性能开发标准与文档 IEEE关于软件质量的定义&#xff1a; 系统&#xff0c;部件&#xff0c;过程满足规定需求的程度系统&#xff0c;部件&#xff0c;过程满足顾客或者用户需要的期望程…

vue css变量实现多主题皮肤切换

实现方式 多主题皮肤切换有很多种实现方式&#xff0c;可以用css预处理器实现&#xff0c;可以用js实现&#xff0c;其实最近简单的一种方式是用css变量(css variable)实现 单页面应用中&#xff0c;可以通过设置body的css变量爱控制整个系统的颜色&#xff0c;body添加一个属…

chatgpt赋能python:Python中的绝对值函数简介

Python中的绝对值函数简介 绝对值是数学中一个基本的概念&#xff0c;在Python中也有相应的函数来实现求绝对值&#xff0c;本文将为大家介绍Python中的绝对值函数及其使用方法。 什么是绝对值 绝对值是一个数的大小&#xff0c;与数本身的符号无关。即如果一个数为正数&…