您当前的位置: 首页 >  LEVI_104 以太坊

以太坊学习:第七天

LEVI_104 发布时间:2022-07-10 14:22:17 ,浏览量:4

目录

Solidity的深入理解

solidity源文件布局

Solidity源文件布局-- import

Solidity值类型

Solidity引用类型

地址类型成员变量

地址成员变量用法

字符数组(Byte Arrays)

枚举(Enum)

数组(Array)

结构(Struct)

映射(Mapping) 

Solidity数据位置 

数据位置总结

 Solidity函数声明和类型

Solidity函数可见性

函数可见性例子

Solidity函数状态可变性

Solidity函数状态可变性

函数修饰器(modifier)

 回退函数(fallback)

事件(event) 

Solidity异常处理

Solidity中的单位

以太币(ether)

 时间

Solidity的深入理解 solidity源文件布局

pragma (版本杂注)

  • 源文件可以被版本 杂注pragma所注解,表明要求的编译 器版本
  • 例如:pragma solidity ^0.4.0;
  • 源文件将既不允许低于 0.4.0 版本的编译器编译, 也不允 许高于(包含) 0.5.0 版本的编译器编译(第二个条件因 使用 ^ 被添加) import(导入其它源文件)
  • Solidity 所支持的导入语句import,语法同 JavaScript(从 ES6 起)非常类似
Solidity源文件布局-- import
  • import "filename":从“filename”中导入所有的全局符号到当前全局作用域中
  • import * as symbolName from "filename":创建一个新的全局符号 symbolName,其成员均来自 “filename” 中全局符号
  • import {symbol1 as alias, symbol2} from "filename":创建新的全局符号 alias 和 symbol2,分别从 "filename" 引 用 symbol1 和 symbol2
  • import "filename" as symbolName:这条语句等同于 import * as symbolName from "filename"
Solidity值类型
  • 布尔(bool):可能的取值为字符常量值 true 或 false
  • 整型(int/uint):分别表示有符号和无符号的不同位数的整型变量; 支持 关键字 uint8 到 uint256(无符号,从 8 位到 256 位)以及 int8 到 int256, 以 8 位为步长递增
  • 定长浮点型(fixed / ufixed): 表示各种大小的有符号和无符号的定长浮 点型;在关键字 ufixedMxN 和 fixedMxN 中,M 表示该类型占用的位数, N 表示可用的小数位数
  • 地址(address):存储一个 20 字节的值(以太坊地址大小)
  • 定长字节数组:关键字有 bytes1, bytes2, bytes3, ..., bytes32
  • 枚举(enum):一种用户可以定义类型的方法,与C语言类似,默认从0 开始递增,一般用来模拟合约的状态
  • 函数(function):一种表示函数的类型
Solidity引用类型

数组(Array)

  • 数组可以在声明时指定长度(定长数组),也可以动态调整大小(变 长数组、动态数组)
  • 对于存储型(storage) 的数组来说,元素类型可以是任意的(即元 素也可以是数组类型,映射类型或者结构体);对于内存型 (memory)的数组来说,元素类型不能是映射(mapping)类型

结构(Struct):Solidity 支持通过构造结构体的形式定义新的类型

映射(Mapping):映射可以视作 哈希表 ,在实际的初始化过程中创建每个可能的 key, 并将其映射到字节形式全是零的值(类型默认值)

address:地址类型存储一个 20 字节的值(以太坊地址的大小);地址类型也有成员 变量,并作为所有合约的基础

address payable(v0.5.0引入):与地址类型基本相同,不过多出了 transfer 和 send 两个成员变量

address与address payable的区别

两者区别和转换

• Payable 地址是可以发送 ether 的地址,而普通 address 不能

• 允许从 payable address 到 address 的隐式转换,而反过来的直接转换是不 可能的(唯一方法是通过uint160来进行中间转换)

• 从0.5.0版本起,合约不再是从地址类型派生而来,但如果它有payable的回 退函数,那同样可以显式转换为 address 或者 address payable 类型

地址类型成员变量
  • .balance (uint256):该地址的 ether 余额,以Wei为单位。
  • .transfer(uint256 amount):向指定地址发送数量为 amount 的 ether(以Wei为单位),失败时抛出异常,发送 2300 gas 的矿 工费,不可调节
  • .send(uint256 amount) returns (bool):向指定地址发送数量为 amount的 ether(以Wei为单位),失败时返回 false,发送 2300 gas 的矿 工费用,不可调节
  • .call(bytes memory) returns (bool, bytes memory):发出底层函数 CALL,失败时返回 false,发送所有可用 gas,可调节
  • .delegatecall(bytes memory) returns (bool, bytes memory):发出底层函数 DELEGATECALL,失败时返回 false,发送所有可用 gas,可调节
  • .staticcall(bytes memory) returns (bool, bytes memory):发出底层函数 STATICCALL ,失败时返回 false,发送所有可用 gas,可调节
地址成员变量用法
  • balance 和 transfe:可以使用 balance 属性来查询一个地址的余额, 可以使用 transfer 函 数向一个payable地址发送 以太币Ether(以 wei 为单位)
  • address payable x = address(0x123);
    
    address myAddress = address(this);
    
    if (x.balance < 10 && myAddress.balance >= 10)
    
    x.transfer(10);
  • send:send 是 transfer 的低级版本。如果执行失败,当前的合约不会因为异 常而终止,但 send 会返回 false
  • call:也可以用call来实现转币的操作,通过添加.gas()和.value()修饰器:
  • nameReg.call.gas(1000000).value(
    
            1 ether)(abi.encodeWithSignature("register(string)", "MyName"));

字符数组(Byte Arrays)

定长字符数组

  • 属于值类型,bytes1,bytes2,…,bytes32分别代表了长度为1到32的字 节序列
  • 有一个.length属性,返回数组长度(只读)

变长字符数组

  • 属于引用类型,包括 bytes和string,不同的是bytes是Hex字符串,而string 是UTF-8编码的字符串
枚举(Enum)
  • 枚举类型用来用户自定义一组常量值
  • 与C语言的枚举类型非常相似,对应整型值
pragma solidity >=0.4.0 =0.4.16 =0.4.0  _ValueType)
  • _KeyType可以是任何基本类型。这意味着它可以是任何内置值类型加上字节和字符串。不允许使用用户定义的或复 杂的类型,如枚举,映射,结构以及除bytes和string之外的任何数组类型。
  • _ValueType可以是任何类型,包括映射。
  • pragma solidity >=0.4.0  uint) public balances; 
        function update(uint newBalance) public {
            balances[msg.sender] = newBalance; 
    } } 
    
    contract MappingUser { 
        function f() public returns (uint) { 
            MappingExample m = new MappingExample(); 
            m.update(100); 
            return m.balances(address(this)); 
    } }
    
  • Solidity数据位置 
    • 所有的复杂类型,即数组 、结构 和映射 类型,都有一个额外属性, “数据位置”,用来说明数据是保存在内存 memory 中还是 存储 storage 中
    • 根据上下文不同,大多数时候数据有默认的位置,但也可以通过在 类型名后增加关键字 storage 或 memory 进行修改
    • 函数参数(包括返回的参数)的数据位置默认是 memory, 局部变量 的数据位置默认是 storage,状态变量的数据位置强制是 storage
    • 另外还存在第三种数据位置, calldata ,这是一块只读的,且不会永 久存储的位置,用来存储函数参数。 外部函数的参数(非返回参数) 的数据位置被强制指定为 calldata ,效果跟 memory 差不多
    数据位置总结

    强制指定的数据位置

    • 外部函数的参数(不包括返回参数): calldata;
    • 状态变量: storage

    默认数据位置

    • 函数参数(包括返回参数): memory;
    • 引用类型的局部变量: storage
    • 值类型的局部变量:栈(stack)

    特别要求

    • 公开可见(publicly visible)的函数参数一定是 memory 类型,如果 要求是 storage 类型 则必须是 private 或者 internal 函数,这是为了 防止随意的公开调用占用资源
    • 例子:
    • pragma solidity ^0.4.0; 
      contract C { 
          uint[] data1; 
          uint[] data2; 
          function appendOne() public { 
              append(data1); 
          } 
          function appendTwo() public { 
              append(data2); 
          } 
          function append(uint[] storage d) internal { 
              d.push(1); 
          } 
      }
      // // 下面代码包含一个错误
      pragma solidity ^0.4.0; 
      contract C { 
          uint someVariable; 
          uint[] data; 
          function f() public { 
              uint[] x; 
              x.push(2); 
          data = x; 
          } 
      }
      // 下面代码编译错误
      pragma solidity ^0.4.0; 
      contract C { 
          uint[] x; 
          function f(uint[] memoryArray) public { 
              x = memoryArray; 
              uint[] y = x; 
              y[7]; 
              y.length = 2; 
              delete x; 
              y = memoryArray;
              delete y;
              g(x);
              h(x); 
      }
      function g(uint[] storage storageArray) internal {} 
      function h(uint[] memoryArray) public {} }
      
      // 下面我们一起来玩一个猜数字游戏
      pragma solidity >0.4.22;
      contract Honeypot{
          uint luckyNum = 52;
          uint public last;
          struct Guess{ address player; uint number; }
          Guess[] public guessHistory;
          address owner = msg.sender;
          function guess(uint _num) public payable{
              Guess newGuess;
              newGuess.player = msg.sender;
              newGuess.number = _num;
              guessHistory.push( newGuess );
              if( _num == luckyNum )
              msg.sender.transfer( msg.value * 2 );
              last = now;
          }
      } 
      
     Solidity函数声明和类型

     图1:来自尚硅谷

    函数的值类型有两类:- 内部(internal)函数和 外部(external) 函数

    • 内部函数只能在当前合约内被调用(更具体来说,在当前代码块内,包括内 部库函数和继承的函数中),因为它们不能在当前合约上下文的外部被执行。 调用一个内部函数是通过跳转到它的入口标签来实现的,就像在当前合约的 内部调用一个函数。
    • 外部函数由一个地址和一个函数签名组成,可以通过外部函数调用传递或者 返回
    • 调用内部函数:直接使用名字 f
    • 调用外部函数:this.f(当前合约),a.f(外部合约)
    Solidity函数可见性

     函数的可见性可以指定为 external,public ,internal 或者 private; 对于状态变量,不能设置为 external ,默认是 internal。

    • external :外部函数作为合约接口的一部分,意味着我们可以从其他合 约和交易中调用。 一个外部函数 f不能从内部调用(即 f 不起作用, 但 this.f() 可以)。 当收到大量数据的时候,外部函数有时候会更有效 率。
    • public :public 函数是合约接口的一部分,可以在内部或通过消息调用。 对于 public 状态变量, 会自动生成一个 getter 函数。
    • internal :这些函数和状态变量只能是内部访问(即从当前合约内部或 从它派生的合约访问),不使用 this 调用。
    • private :private 函数和状态变量仅在当前定义它们的合约中使用,并 且不能被派生合约使用。
    // 以下代码编译错误
    pragma solidity >=0.4.0 =0.4.16 =0.4.22 0.4.99 = start + daysAfter * 1 days) {
         // ...
         } 
    }
    
    关注
    打赏
    1688896170
    查看更多评论
    0.0368s