Solidity基础七

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

目录

一、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 方法。

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

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

相关文章

【Linux性能优化】你知道什么是平衡负载么

什么是平衡负载 首先大家思考一下&#xff0c;当你发现自己的服务变慢时&#xff0c;你会首先使用什么命令来排查&#xff1f;我通常做的第一件事&#xff0c;就是执行top或者uptime命令来了解系统的负载情况。比如像下面这样&#xff0c;我在命令行里输入top命令&#xff0c;…

[高光谱]使用PyTorch的dataloader加载高光谱数据

本文实验的部分代码参考 Hyperspectral-Classificationhttps://github.com/eecn/Hyperspectral-Classification如果对dataloader的工作原理不太清楚可以参见 [Pytorch]DataSet和DataLoader逐句详解https://blog.csdn.net/weixin_37878740/article/details/129350390?spm1001…

网络货运平台源码 管理平台端+司机端APP+货主端APP源码

网络货运平台系统源码&#xff0c;网络货运平台源码 管理平台端司机端APP货主端APP 遵循政策要求的八项基本功能&#xff0c;结合货主、实际承运人、监管方等多方业务场景&#xff0c;构建人、车、货、企一体的标准化网络货运平台系统。具有信息发布、线上交易、全程监控、金融…

数据库基础——6.排序与分页

这篇文章来讲一下数据库的排序与分页 目录 1.排序数据 1.1排序规则 1.2 单列排序 1.3 多列排序 2.分页 2.1 背景 2.2 实现规则 2.3 拓展 1.排序数据 1.1排序规则 使用 ORDER BY 子句排序 ASC&#xff08;ascend&#xff09;&#xff1a;升序 &#xff1b; DESC&a…

vue项目中使用depcheck检查缺失的依赖项目

使用depcheck检查缺失的项目依赖 由来&#xff1a;今天在做地铁的时候&#xff0c;刷短视频发现一个非常好用的东西&#xff0c;分享一下 它可以帮助我们找出问题&#xff0c;在 package.json 中&#xff0c;每个依赖包如何被使用、哪些依赖包没有用处、哪些依赖包缺失。它是解…

2023年,推荐10个让你事半功倍的CSS在线生产力工具

1、CSS Gradient CSS Gradient 是一个在线工具&#xff0c;可以帮助用户创建并生成 CSS 渐变代码。用户可以使用该工具中提供的图形用户界面来调整颜色、方向和渐变类型&#xff0c;然后生成相应的 CSS 代码。用户可以将生成的代码复制并粘贴到自己的 CSS 样式表中&#xff0c…

Linux——操作系统详解

目录 一.操作系统的含义 1.操作系统是什么&#xff1f; 2.那么操作系统为什么要对软硬件资源进行管理呢&#xff1f;这样做的好处在哪里&#xff1f; 3.操作系统又是怎么进行管理的&#xff1f; 如何理解“先描述&#xff0c;再组织”&#xff1f; 二.总结&#xff1a; …

音乐小白乐器选择,如何学一手才艺,推荐尤克里里

乐器难度说明 注意&#xff1a;这里的难度说明是音准的难度&#xff0c;就是能不能发出标准的声音 乐器按照演奏方式分类&#xff0c;分为 演奏方式乐器举例难度等级难度说明敲击木鱼&#xff0c;架子鼓&#xff0c;钢琴1敲击乐是音最准的&#xff0c;敲哪个地方就发什么音&…

记录--超长溢出头部省略打点,坑这么大,技巧这么多?

这里给大家分享我在网上总结出来的一些知识&#xff0c;希望对大家有所帮助 在业务中&#xff0c;有这么一种场景&#xff0c;表格下的某一列 ID 值&#xff0c;文本超长了&#xff0c;正常而言会是这样&#xff1a; 通常&#xff0c;这种情况都需要超长省略溢出打点&#xff0…

Kali-linux Gerix Wifi Cracker破解无线网络

Gerix Wifi Cracker是另一个aircrack图形用户界面的无线网络破解工具。本节将介绍使用该工具破解无线网络及创建假的接入点。 9.3.1 Gerix破解WEP加密的无线网络 在前面介绍了手动使用Aircrack-ng破解WEP和WPA/WPA2加密的无线网络。为了方便&#xff0c;本小节将介绍使用Geri…

学习RabbitMQ高级特性

目标&#xff1a; 了解熟悉RabbitMQ的高级特性 学习步骤&#xff1a; 高级特性主要分为以下几点, 官网介绍 1、消息可靠性投递 【confirm 确认模式、return 退回模式】 2、Consumer ACK 【acknowledge】 3、消费端限流 【prefetch】 4、TTL过期时间 【time to live】 5、死信队…

没有权限merge到源git仓库?一招教你如何解决。

在git上贡献项目的时候&#xff0c;一般步骤是&#xff0c;clone源项目到本地&#xff0c;切出一个新的分支&#xff0c;然后在新分支上开发&#xff0c;最后push到远程&#xff0c;然后提出mr。但是对于一些非开源的项目&#xff0c;可能会出现&#xff1a; 这就是说明没有权…

【C++】布隆过滤器

文章目录 布隆过滤器的引入布隆过滤器的概念如何选择哈希函数个数和布隆过滤器长度布隆过滤器的实现布隆过滤器的优缺点 布隆过滤器的引入 我们在使用新闻客户端看新闻时&#xff0c;它会给我们不停地推荐新的内容&#xff0c;它每次推荐时要去重&#xff0c;去掉那些已经看过…

【高级语言程序设计(一)】第 8 章:结构体类型和自定义类型

目录 前言 一、结构体类型定义 &#xff08;1&#xff09;结构体类型定义的一般形式 &#xff08;2&#xff09;结构体类型定义的说明 二、结构体类型变量 &#xff08;1&#xff09;结构体类型变量的定义和初始化 ① 先定义结构体类型、后定义结构体类型的变量&#xf…

84.Rem和max-width如何工作

max-width 首先我们先看普通的width是什么样的效果&#xff01; 首先给个测试的div <div class"test">TEST</div>● 然后CSS给定一个宽度 .test {width: 1000px;background-color: red;padding: 100px; }如上图&#xff0c;不管你的浏览器窗口如何改变…

HTMLCSS中的树形结构图

我们可以只使用 html 和 css 创建树视图(可折叠列表) &#xff0c;而不需要 JavaScript。可访问性软件将看到树形视图作为列表嵌套在披露窗口小部件中&#xff0c;并且自动支持标准键盘交互。 1、HTML 我们就从简单嵌套列表的 html 开始: <ul><li>Giant planets&…

Hbase操作

(1) 启动 启动顺序&#xff1a;Hadoop--zookeeper—hbase 主进程&#xff1a;HMaster 从进程&#xff1a;HRegionServer 确认进程是否正常 (2) 进入终端 [rootmaster ~]# hbase shell (3) 查看状态 命令&#xff1a;status 表示有3台机器&#xff0c;0台down掉&…

位操作集锦

位操作集锦 异或运算两两交换数据签名检测两个数是否拥有不同的符号&#xff0c;即一个正数&#xff0c;一个负数寻找只出现一次的一个数字1寻找只出现两次的一个数字寻找只出现一次的一个数字2寻找只出现一次的两个数字 与和位移运算判断奇偶数二进制数中1的个数二进制数中最右…

MFC 给对话框添加图片背景

在windows开发当中做界面的主要技术之一就是使用MFC&#xff0c;通常我们看到的QQ,360,暴风影音这些漂亮的界面都可以用MFC来实现。今天我们来说一下如何用MFC美化对话框&#xff0c;默认情况下&#xff0c;对话框的背景如下&#xff1a; 那么&#xff0c;我们如何将它的背景变…

C++服务器框架开发3——协程与线程的简单理解/并发与并行

该专栏记录了在学习一个开发项目的过程中遇到的疑惑和问题。 其教学视频见&#xff1a;[C高级教程]从零开始开发服务器框架(sylar) 上一篇&#xff1a;C服务器框架开发2——头文件memory/typedef C服务器框架开发3——协程与线程的简单理解/并发与并行 目前进度协程与线程的简…