Solidity基础八

news2024/11/27 10:24:06

别慌,月亮也在大海某处迷茫 

目录

一、Solidity 编程风格

1. 代码布局 

2. 代码中各部分的顺序

3. 命名约定

二、Solidity 智能合约编写过程

1. solidity Hello World

2. 版本声明

3. 导入声明

4. 合约声明

三、Solidity 合约结构

智能合约 Test

四、Solidity 常见编译错误

五、Solidity 调用合约

1.调用内部合约

2.调用外部合约

通过接口方式调用

通过签名方式调用

签名方式调用,发送Eth

六、Solidity 自毁合约 selfdestruct

1.销毁合约示例

2.强制发送资金示例

七、Solidity 哈希算法 keccak256 

八、Solidity 权限控制合约

十、Solidity 验证签名 

十一、ABI、bytecode、EVM 

十二、Solidity内置对象(全局变量、全局函数)


一、Solidity 编程风格

良好统一的编程风格,有助于提高代码的可读性和可维护性。

下面是有关Solidity编程风格的几条建议。

1. 代码布局 

  • 缩进
    使用4个空格代替制表符作为缩进,避免空格与制表符混用。
  • 空2行规则
    2个合约定义之间空2行。
pragma solidity ^0.5.0;

contract LedgerBalance {
    //...
}


contract Updater {
    //...
}
  • 空1行规则
    2个函数之间空1行。在只有声明的情况下,不需要空行。
pragma solidity ^0.5.0;

contract A {
    function balance() public pure;
    function account() public pure;
}


contract B is A {
    function balance() public pure {
        // ...
    }

    function account() public pure {
        // ...
    }
}
  • 行长度
    一行不超过79个字符。
  • 换行规则
    函数声明中左括号不换行,每个参数一行并缩进,右括号换行,并对齐左括号所在行。
function_with_a_long_name(
    longArgument1,
    longArgument2,
    longArgument3
);

variable = function_with_a_long_name(
    longArgument1,
    longArgument2,
    longArgument3
);

event multipleArguments(
    address sender,
    address recipient,
    uint256 publicKey,
    uint256 amount,
    bytes32[] options
);

MultipleArguments(
    sender,
    recipient,
    publicKey,
    amount,
    options
);
  • 源码编码
    UTF-8
  • Import
    Import语句应该放在文件的顶部,pragma声明之后。
  • 函数顺序
    函数应该根据它们的可见性来分组。
pragma solidity ^0.5.0;

contract A {
    constructor() public {
        // ...
    }

    function() external {
        // ...
    }

    // External functions
    // ...

    // External view functions
    // ...

    // External pure functions 
    // ...

    // Public functions
    // ...

    // Internal functions
    // ...

    // Private functions
    // ...
}
  • 避免多余空格
    避免在圆括号、方括号或大括号后有空格。
  • 控制结构
    大括号的左括号不换行,右括号换行,与左括号所在行对齐。
pragma solidity ^0.5.0;

contract Coin {
    struct Bank {
        address owner;
        uint balance;
    }
}

if (x < 3) {
    x += 1;
} else if (x > 7) {
    x -= 1;
} else {
    x = 5;
}
if (x < 3)
    x += 1;
else
    x -= 1;
  • 函数声明
    使用上面的大括号规则。添加可见性标签。可见性标签应该放在自定义修饰符之前。
function kill() public onlyowner {
    selfdestruct(owner);
}
  • 映射
    在声明映射变量时避免多余空格。
mapping(uint => uint) map; // 不是 mapping (uint => uint) map;
mapping(address => bool) registeredAddresses;
mapping(uint => mapping(bool => Data[])) public data;
mapping(uint => mapping(uint => s)) data;
  • 变量声明
    声明数组变量时避免多余空格。
uint[] x;  // 不是 unit [] x;
  • 字符串声明
    使用双引号声明字符串,而不是单引号。
str = "foo";
str = "Hamlet says, 'To be or not to be...'";

2. 代码中各部分的顺序

代码中各部分顺序如下:

  • Pragma 语句
  • Import 语句
  • Interface
  • Contract

在Interface、Library或Contract中,各部分顺序应为:

  • Type declaration / 类型声明(enum,struct)
  • State variable / 状态变量
  • Event / 事件
  • Function / 函数

3. 命名约定

  • 合约和库应该使用驼峰式命名。例如,SmartContract, Owner等。
  • 合约和库名应该匹配它们的文件名。
  • 如果文件中有多个合约/库,请使用核心合约/库的名称。

owned.sol

pragma solidity ^0.8.0;

// Owned.sol
contract Owned {
    address public owner;

    constructor() public {
      owner = msg.sender;
    }

    modifier onlyOwner {
        //....
    }

    function transferOwnership(address newOwner) public onlyOwner {
        //...
    }
}

Congress.sol

pragma solidity ^0.8.0;

// Congress.sol
import "./Owned.sol";

contract Congress is Owned, TokenRecipient {
    //...
}
  • 结构体名称
    驼峰式命名,例如: SmartCoin
  • 事件名称
    驼峰式命名,例如:AfterTransfer
  • 函数名
    驼峰式命名,首字母小写,比如:initiateSupply
  • 局部变量和状态变量
    驼峰式命名,首字母小写,比如creatorAddress、supply
  • 常量
    大写字母单词用下划线分隔,例如:MAX_BLOCKS
  • 修饰符的名字
    驼峰式命名,首字母小写,例如:onlyAfter
  • 枚举的名字
    驼峰式命名,例如:TokenGroup

 

二、Solidity 智能合约编写过程

要写智能合约有好几种语言可选:有点类似 Javascript 的 Solidity, 文件扩展名是.sol。Python 接近的Serpent, 文件名以.se结尾。还有类似 Lisp 的LLL,但现在最流行而且最稳定的要算是 Solidity 了。

1. solidity Hello World

pragma solidity ^0.4.0;
import "./A.sol";
contract HelloWorld {
  function hello() returns(string){
      return "hello world";
  }
}

solidity文件扩展名为.sol,主合约名要和solidity文件名相同,一份合约包含版本声明,导入声明,合约声明

2. 版本声明

pragma solidity ^0.4.0;

pragmas(编译指令)是告知编译器如何处理源代码的指令,^表示向上兼容,版本操作符可以为:^ ~ >= > < <= = 之一,0.4.0代表solidity版本(版本字面量形如x.x.x),^0.4.0表示solidity的版本在0.4.0 ~ 0.5.0(不包含0.5.0)的版本,这是为了确保合约不会在新的编译器版本中突然行为异常

3. 导入声明

import导入其他源文件,例如:

import "./A.sol";
从"A"中导入所有的全局标志到当前全局范围,`./`表示当前目录

其他方式:

(1). 创建新的全局符号 symbolName,其成员都是来自“A”的全局。

 import * as symbolName from "A";/import "A" as symbolName;

(2).创建新的全局符号“alias”和“symbol2”,它将分别从”A” 引入symbol1 和 symbol2。

import {symbol1 as alias, symbol2} from "A";

4. 合约声明

包括:contract,interface,library。

(1).contract

contract HelloWorld

这里HelloWorld指合约名称,contract即为其他语言中的class,HelloWorld即为类名,创建合约实例则用HelloWorld helloworld = new HelloWorld();

(2).interface 接口

 interface A{   
   function testA();
}

函数不允许有函数体。

(3).library 库

库与合约类似,但它的目的是在一个指定的地址,且仅部署一次,然后通过 EVM 的特性来复用代码。

library Set {
    struct Data { mapping(uint => bool) flags; }
    function test(){
    }
}

其他合约调用库文件的内容直接通过库文件名.方法名例如:Set.test()。

注:一份源文件可以包含多个版本声明、多个导入声明和多个合约声明。

 

三、Solidity 合约结构

本章节主要讲述智能合约中合约的基本结构,及基本关键字的使用。

合约中可包含内容:

usingFor声明,状态变量(State Variables),结构类型(Structs Types),构造函数,函数修饰符(Function Modifiers),函数(Functions),事件(Events),枚举类型(Enum Types)

1.智能合约 Test

pragma solidity ^0.4.0;    //版本声明
import "./A.sol";   //导入声明
contract SolidityStructure{   //合约声明

   uint balance;//状态变量

   address owner;

    struct Hello { // 结构类型
        uint helloNum;
        address hello;

    constructor() public{  //构造函数
        owner = msg.sender;
    }
    //function HelloWorld(){
    //} 这种方式也可以

    modifier onlySeller() { // 修饰器
        require(
            msg.sender != owner
        );
        _;
    }

    function test() public {  //函数
       uint step = 10;
       if (owner == msg.sender) {
          balance = balance + step;
       }
    }

    function update(uint amount) constant returns (address, uint){ //带返回值的函数
        balance += amount;
        return (msg.sender, balance);
    }

   using LibraryTest for uint; //using声明 

   uint a = 1;

   function getNewA()returns (uint){ 
       return a.add();
   }

    function kill() {   //析构函数
        if (owner == msg.sender) { 
             selfdestruct(owner);
         }
     }     

    event HighestBidIncreased(address bidder, uint amount);//事件 log日志打印

    function bid() public payable {
        emit HighestBidIncreased(msg.sender, msg.value); // 触发事件打印相关日志
    }

     enum State { Created, Locked, Inactive } // 枚举
 }

状态变量

uint balance;

类似java中类的属性变量,状态变量是永久的存储在合约中的值(强制是storage类型)
状态变量可以被定义为constant即常量,例如:uint constant x = 1;

结构类型

struct Hello { // 结构类型
        uint helloNum;
        address hello;
}

自定义的将几个变量组合在一起形成的类型,有点类似javabean

构造函数

constructor() public{
   owner = msg.sender;
}

构造函数可用constructor关键字进行声明,也可用function HelloWorld(){} 这种方式声明,当合约对象创建时会先调用构造函数对数据进行初始化操作,构造函数只允许存在一个

函数修饰符(函数修改器)

modifier onlySeller() { // 修饰器
    require(
        msg.sender != owner
    );
    _;
 }

函数修饰符用于’增强语义’,可以用来轻易的改变一个函数的行为,比如用于在函数执行前检查某种前置条件。修改器是一种合约属性,可被继承,同时还可被派生的合约重写。_表示使用修改符的函数体的替换位置。当然函数修饰器可以传参数

成员函数

function test() public    /    function update(uint amount) constant returns (address,uint) 

这两种都可以为合约的成员函数,成员函数类似java中基本函数,但是略有不同,不同点在于有返回值时在函数上指定返回值returns(uint),函数调用方式可以设置为内部(Internal)的和外部(External)的,在权限章节会进行介绍
注意:constant只是一个承诺,承诺该函数不会修改区块链上的状态
###using for
使用方式是using A for B
用来把A中的函数关联到到任意类型B,B类型的对象调用A里面的函数,被调用的函数,将会默认接收B类型的对象的实例作为第一个参数。

pragma solidity ^0.4.0;    
library LibraryTest{
function use(uint a) returns(uint){
    return a+1;
    }
}

contract usingTest{
    using LibraryTest for uint;//把LibraryTest中的函数关联到uint类型
    uint test = 1;
    function testusing() returns (uint){
        return test.use();//uint类型的对象实例test调用LibraryTest里的函数add();add()会默认接收test作为第一个参数。
    }
}

析构函数

selfdestruct()

所谓的析构函数是和构造函数相对应,构造函数是初始化数据,而析构函数是销毁数据

事件

event HighestBidIncreased(address bidder, uint amount);//事件 log日志打印
function bid() public payable {
    emit HighestBidIncreased(msg.sender, msg.value); // 触发事件打印相关日志
} 

事件是以太坊虚拟机(EVM)日志基础设施提供的一个便利接口。用于获取当前发生的事件。事件在合约中可被继承。

枚举

enum State { Created, Locked, Inactive } // 枚举

他可以显式的转换与整数进行转换,但不能进行隐式转换。显示的转换会在运行时检查数值范围,如果不匹配,将会引起异常。 枚举类型应至少有一名成员。

四、Solidity 常见编译错误

1、报错:Expected token Semicolon got 'eth_compileSolidity' funtion setFunder(uint _u,uint _amount){

解决:funtion关键字错了,需要用function;

2、报错:Variable is declared as a storage pointer. Use an explicit "storage" keyword to silence this warning. Funder f = funders[_u]; ^------^

解决:Funder f,定义指针需要加关键字storage ;修改为Funder storage f = funders[_u];

3、报错:Invoking events without "emit" prefix is deprecated. e("newFunder",_add,_amount); ^-------------------------^

解决:调用事件需要在前面加上emit关键字,修改为emit e("newFunder",_add,_amount);

4、报错:No visibility specified. Defaulting to "public". function newFunder(address _add,uint _amount) returns (uint){ ^ (Relevant source part starts here and spans across multiple lines).

解决:定义函数必须加上public关键字,修改为function newFunder(address _add,uint _amount) public returns (uint){

5、报错:"msg.gas" has been deprecated in favor of "gasleft()" uint public _gas = msg.gas; ^-----^

解决:msg.gas已经被gasleft()替换了。修改为uint public _gas = gasleft();

6、报错:"throw" is deprecated in favour of "revert()", "require()" and "assert()". throw ;

解决:solidity已经不支持thorw了,需要使用require,用法require()

throw 写法:

if(msg.sender !=chairperson ||voters[_voter].voted ){

throw ;

}

require写法:

require(msg.sender !=chairperson ||voters[_voter].voted);

7、报错:This declaration shadows an existing declaration. Voter delegate = voters[to]; ^------------^

解决:变量重复定义,变量名和函数名不能相同。

8、报错:error: Function state mutability can be restricted to pure

解决:以前版本是可以不指定类型internal pure(外部不可调用),public pure(外部可调用)(如不指定表示函数为可变行,需要限制)

9、报错:"sha3" has been deprecated in favour of "keccak256"

解决:sha3已经替换为keccak256

五、Solidity 调用合约

Solidity 支持一个合约调用另一个合约。两个合约既可以位于同一sol文件,也可以位于不同的两个sol文件。

Solidity 还能调用已经上链的其它合约。

1.调用内部合约

内部合约是指位于同一sol文件中的合约,它们不需要额外的声明就可以直接调用。

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

contract Hello {
    function echo() external pure returns(string memory){
       return "Hello World!";
   }
}

contract SoldityTest {
   function callHello(address addr) external pure returns(string memory){
      // 调用外部合约 Hello 的方法 echo
      return Hello(addr).echo();
   }

   // 另外一种写法
   function callHelloOr(Hello hello) external pure returns(string memory){
       // 调用外部合约 Hello 的方法 echo
      return hello.echo();
   }
}

我们在部署上面两个合约的时候,首先要部署 Hello,得到它的地址,例如:0x78FD83768c7492aE537924c9658BE3D29D8ffFc1。

然后再部署合约 SoldityTest,调用 SoldityTest 的方法 callHello,传入参数 0x78FD83768c7492aE537924c9658BE3D29D8ffFc1 ,就会输出调用结果:"Hello World!"。

2.调用外部合约

外部合约是指位于不同文件的外部合约,以及上链的合约。

调用外部合约有两种方法:通过接口方式调用 和 通过签名方式调用。

通过接口方式调用

通过接口方式调用合约,需要在调用者所在的文件中声明被调用者的接口。

被调用者合约 hello.sol:

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

contract IHello {
    function echo() external pure returns(string memory){
       return "Hello World!";
   }
}

调用者合约 test.sol:

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

// 被调用者接口
interface IHello {
    // 被调用的方法
    function echo() external pure returns(string memory);
}

// 调用者合约
contract SoldityTest {
   function callHello(address addr) external pure returns(string memory){
      // 调用外部合约Hello的方法:echo
      return IHello(addr).echo();
   }
}

我们首先要部署 hello.sol 文件中的合约 Hello,得到它的地址,例如:0x78FD83768c7492aE537924c9658BE3D29D8ffFc1。

然后再部署合约 SoldityTest,调用 SoldityTest 的方法 callHello,传入参数 0x78FD83768c7492aE537924c9658BE3D29D8ffFc1 ,就会输出调用结果:"Hello World!"。

通过签名方式调用

通过签名方式调用合约,只需要传入被调用者的地址和调用方法声明。

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

// 调用者合约
contract SoldityTest {
   function callHello(address addr) external returns(string memory){
       // 调用合约
       (bool success,bytes memory data) = addr.call(abi.encodeWithSignature("echo()"));
       if(success){
           return abi.decode(data,(string));
        } else {
            return "error";
        }
   }
}

另一种写法:

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

// 调用者合约
contract SoldityTest {
   function callHello(address addr) external view returns(string memory){
       // 编码被调用者的方法签名
       bytes4 methodId = bytes4(keccak256("echo()"));
       
       // 调用合约
       (bool success,bytes memory data) = addr.staticcall(abi.encodeWithSelector(methodId));
       if(success){
           return abi.decode(data,(string));
        } else {
            return "error";
        }
   }
}

我们首先要部署 hello.sol 文件中的合约 Hello,得到它的地址,例如:0x78FD83768c7492aE537924c9658BE3D29D8ffFc1。

然后再部署合约 SoldityTest,调用 SoldityTest 的方法 callHello,传入参数 0x78FD83768c7492aE537924c9658BE3D29D8ffFc1 ,就会输出调用结果:"Hello World!"。

签名方式调用,发送Eth

通过签名方式调用合约,可以发送Eth。

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

// 调用者合约
contract SoldityTest {
   function callHello(address addr) external returns(string memory){
       // 调用合约
       (bool success,bytes memory data) = addr.call{value:1000}(abi.encodeWithSignature("echo()"));
       if(success){
           return abi.decode(data,(string));
        } else {
            return "error";
        }
   }
}

六、Solidity 自毁合约 selfdestruct

Solidity 自毁函数 selfdestruct 由以太坊智能合约提供,用于销毁区块链上的合约系统。

当合约执行自毁操作时,合约账户上剩余的以太币会强制发送给指定的目标,然后其存储和代码从状态中被移除。

所以,Solidity selfdestruct 做两件事。

  • 它使合约变为无效,有效地删除该地址的字节码。
  • 它把合约的所有资金强制发送到目标地址。

1.销毁合约示例

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

contract Kill {
    function kill() external {
        selfdestruct(payable(msg.sender));
    }

    function test() external pure returns(uint) {
        return 100;
    }
}

部署后,先调用 test 函数,将会输出 100。然后调用 kill 函数,再次调用 test 函数,结果输出为 0,表明合约被销毁。

2.强制发送资金示例

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

contract Kill {
    function kill() external {
        selfdestruct(payable(msg.sender));
    }

    function test() external pure returns(uint) {
        return 100;
    }
}

部署后,先调用 test 函数,将会输出 100。然后调用 kill 函数,再次调用 test 函数,结果输出为 0,表明合约被销毁。

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

contract Kill {
    constructor() payable {}

    function kill(address payable to) external {
        selfdestruct(to);
    }
}

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

首先部署 Receive 合约,用于接收资金。再部署 Kill 合约,初始转入 Eth 123 wei,然后调用 kill 方法,并将 Receive 的地址作为参数。

我们通过 Receive 合约的 getBalance 方法查看余额,资金为 123 wei。

Receive 合约没有定义 fallback 和 receive 函数,正常情况下无法接收资金,但依然被 Receive 合约的 selfdestruct 方法强制转入了资金。

七、Solidity 哈希算法 keccak256 

Solidity 的哈希算法使用一个内置函数 keccak256。

通常字符串不能进行比较,但是讲字符串进行哈希算法加密,即可进行字符串比较

keccak256函数原型:

keccak256(bytes) returns (bytes32)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Hash {
    function hash(string memory _text, uint _num, address _addr) 
    public pure returns (bytes32) {
        return keccak256(abi.encodePacked(_text, _num, _addr));
    }
}

我们通常使用 abi.encodePacked 打包所有数据,然后再进行 keccak256 哈希。

但是,我们使用 abi.encodePacked 要非常小心,当将多个动态数据类型传递给 abi.encodePacked 时,可能会发生哈希冲突。

abi 编码函数除了 abi.encodePacked 外,还有函数 abi.encode。 abi.encodePacked 只是将参数转为 16 进制,再直接进行拼接,而 abi.encode 需要先进行补零 ,再进行转码拼接。

我们可以看一个例子:

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

contract Hash {
    function encode1() external pure returns(bytes memory){
       return abi.encodePacked("aa","bb");
    }

    function encode2() external pure returns(bytes memory){
       return abi.encodePacked("aab","b");
    }
}

两个方法返回的内容都是0x61616262,但两者的输入参数并不同。在这种情况下,您应该使用 abi.encode 代替。

或者使用 encodePacked,但是在两个参数之间再添加一个固定数字参数即可。

例如:

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

contract Hash {
    function encode1() external pure returns(bytes memory){
       return abi.encodePacked("aa",uint(1),"bb");
    }

    function encode2() external pure returns(bytes memory){
       return abi.encodePacked("aab",uint(1),"b");
    }
}

八、Solidity 权限控制合约

Solidity 合约中一般会有多种针对不同数据的操作,例如对于存证内容的增加、更新及查询,所以需要制定一套符合要求的权限控制。

如何对合约的权限进行划分?我们针对Solidity语言设计了一套通过地址标记的解决方案。

合约中划分了角色和账户两级权限,如下所示:

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

contract Access {
   mapping(bytes32 =>mapping(address =>bool)) public roles;

   // 0xf23ec0bb4210edd5cba85afd05127efcd2fc6a781bfed49188da1081670b22d8
   bytes32 constant private ADMIN = keccak256("admin");

   // 0xcb61ad33d3763aed2bc16c0f57ff251ac638d3d03ab7550adfd3e166c2e7adb6
   bytes32 constant private USER = keccak256("user");

   // 授权合约部署者 ADMIN 权限
   constructor() {
      _grantRole(ADMIN, msg.sender);
   }

   modifier onlyAdmin(address _account) {
      require(roles[ADMIN][_account], "not authorized");
      _;
   }

   function _grantRole(bytes32 _role, address _account) internal {
      roles[_role][_account] = true;
   }

   // 授权
   function grantRole(bytes32 _role, address _account) external onlyAdmin(_account) {
      _grantRole(_role, _account);
   }

   function _revokeRole(bytes32 _role, address _account) internal {
      roles[_role][_account] = false;
   }

   // 撤销授权
   function revokeRole(bytes32 _role, address _account) external onlyAdmin(_account) {
      _revokeRole(_role, _account);
   }
}

 

十、Solidity 验证签名 

Solidity有一个ecrecover 指令,可以根据消息hash 和签名,返回签名者的地址:

ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)

根据恢复的签名地址,与验证地址对比,就可以验证签名。

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

contract Signature {
   function verify(address _signer, string memory _message, bytes memory _signature) 
   external pure returns(bool) {
      bytes32 hash = getHash(_message);
      bytes32 ethSignedHash = getEthHash(hash);
      return recover(ethSignedHash,_signature) == _signer;
   }
   
   function getHash(string memory _message) public pure returns(bytes32) {
      return keccak256(abi.encodePacked(_message));
   }

   function getEthHash(bytes32 _hash) public pure returns(bytes32) {
      return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", _hash));
   }

   function recover(bytes32 ethSignedHash, bytes memory _signature) public pure returns(address) {
      (bytes32 r, bytes32 s, uint8 v) = _split(_signature);
      return ecrecover(ethSignedHash, v, r, s);
   }

   function _split(bytes memory _signature) internal pure returns(bytes32 r, bytes32 s, uint8 v){
      require(_signature.length == 65, "invalid signaure length");
      assembly {
         r := mload(add(_signature, 32))
         s := mload(add(_signature, 64))
         v := byte(0, mload(add(_signature, 96)))
      }
   }
}

十一、ABI、bytecode、EVM 

  •     ABI:接口      合约编译后会给出一个ABI地址,可以用于其他合约来实现(调用)接口
  •     bytecode:字节码     合约编译后会给出bytecode地址,字节码用来部署合约
  •     EVM:以太坊虚拟机  

    //以太坊虚拟机运行的是合约字节码,所以需要在部署到EVM前将代码先编译,编译成bytecode

    //以太坊虚拟机是一个被沙箱封装起来、完全隔离的运行环境。运行在EVM内部的代码

    //不能接触到网络、文件系统、或其他进程,即使同是智能合约,也只能进行有限交互

ABI

在以太坊生态系统中,应用程序二进制接口是从区块链外部与合约进行交互,以及合约合约之间进行交互的一种标准形式。简单来说就是以太坊调用合约时的接口说明,即定义操作函数签名、参数编码、返回结果编码等,但没有说明状态变量

ABI的系统函数

abi.encode(参数值)            //计算参数值的abi编码

abi.encodeWithSignature(“函数名()”,参数,····)   //计算函数和参数的ABI编码

十二、Solidity内置对象(全局变量、全局函数)

solidiy全局变量以及函数可认为是solidity提供的API,这些全局变量以及函数主要分为以下几类

API:接口

  1. 有关区块和交易的属性
  2. ABI编码函数
  3. 有关错误处理
  4. 有关数学和加密功能
  5. 地址相关
  6. 合约相关

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

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

相关文章

Solidity基础六

生活本来就是平凡琐碎的&#xff0c;哪有那么多惊天动地的大事&#xff0c;快乐的秘诀就是不管对大事小事都要保持热情 目录 一、Solidity的特殊变量(全局) 二、Solidity的不可变量 immutable的赋值方式 三、Solidity的事件与日志 事件和日志加深理解 四、Solidity的异常…

EMLP2021 | Google大模型微调经典论文prompt tuning

一、概述 title&#xff1a;The Power of Scale for Parameter-Efficient Prompt Tuning 论文地址&#xff1a;https://arxiv.org/abs/2104.08691 代码&#xff1a;GitHub - google-research/prompt-tuning: Original Implementation of Prompt Tuning from Lester, et al, …

系列一、RuoYi前后端分离(登录密码加密)

一、部署前后端服务 http://doc.ruoyi.vip/ruoyi-vue/ 二、现象 若依前后端环境分离版本&#xff0c;本地部署好前后端环境后&#xff0c;访问登录接口密码是明文的&#xff0c;这样显然hi不安全的&#xff0c;如下图所示&#xff1a; 三、解决方法 3.1、加密流程 ①、后端…

Linux-0.11 文件系统namei.c详解

Linux-0.11 文件系统namei.c详解 模块简介 namei.c是整个linux-0.11版本的内核中最长的函数&#xff0c;总长度为700行。其核心是namei函数&#xff0c;即根据文件路径寻找对应的i节点。 除此以外&#xff0c;该模块还包含一些创建目录&#xff0c;删除目录&#xff0c;创建目…

Day2:Windows网络编程-TCP

今天开始进入Windows网络编程的学习&#xff0c;在学习的时候总是陷入Windows复杂的参数&#xff0c;纠结于这些。从老师的讲解中&#xff0c;这些内容属于是定式&#xff0c;主要学习写的逻辑。给自己提个醒&#xff0c;要把精力放在正确的位置&#xff0c;不要无端耗费精力。…

【JavaScript】文件分片上传

文章目录 普通文件上传分片上传整体流程技术点分析文件选择方式隐藏input框&#xff0c;自定义trigger拖拽上传 分片动态分片 计算哈希workerrequestIdleCallback抽样 请求并发控制进度展示手动中止/暂停 合并流式并发合并 反思分片命名问题并发控制代码实现的问题 参考文献 普…

ChatGPT桌面客户端支持gpt4模型,附使用说明

#软件核心功能&#xff1a; 1、支持OpenAI官方秘钥及API2D双秘钥使用&#xff1b;如果全局魔法&#xff0c;可以自己用官方秘钥&#xff1b;没魔法国内可直接使用API2D秘钥&#xff1b; 2、内置GPT4模型选项&#xff0c;如果你的官方秘钥支持可直接使用&#xff1b;你也可以注册…

【Labview如何显示数据库表格中的内容】

Labview如何显示数据库表格中的内容 前提操作思路框图 前提 已经成功将数据库与Labview相连接&#xff0c;若还没有链接可以查看&#xff1a;Labview与SQL Server互联 进行操作 操作 思路 首先创建一个表格控件&#xff0c;通过一个按钮启动程序&#xff0c;通过程序调用数…

SAP MM 根据采购订单反查采购申请

如何通过采购订单号查询到其前端的采购申请号。 首先从采购申请的相关报表着手&#xff0c;比如ME5A, 发现它是可以满足需求的。 例如&#xff1a;如下的采购订单&#xff0c; 该订单是由采购申请10003364转过来的。 如果想通过这个采购订单找到对应的采购申请&#xff0c;在…

Packet Tracer – 配置命名标准 IPv4 ACL

Packet Tracer – 配置命名标准 IPv4 ACL 地址分配表 设备 接口 IP 地址 子网掩码 默认网关 R1 F0/0 192.168.10.1 255.255.255.0 N/A F0/1 192.168.20.1 255.255.255.0 N/A E0/0/0 192.168.100.1 255.255.255.0 N/A E0/0/1 192.168.200.1 255.255.2…

第五十五天学习记录:C语言进阶:动态内存管理Ⅲ

柔性数组 C99中&#xff0c;结构中的最后一个元素允许是未知大小的数组&#xff0c;这就叫做柔性数组成员。 柔性数组的特点&#xff1a; 。结构体中的柔性数组成员前面必须至少有一个其他成员。 。sizeof返回的这种结构大小不包括柔性数组的内存。 。包含柔性数组成员的结构…

【C++学习】智能指针

&#x1f431;作者&#xff1a;一只大喵咪1201 &#x1f431;专栏&#xff1a;《C学习》 &#x1f525;格言&#xff1a;你只管努力&#xff0c;剩下的交给时间&#xff01; 智能指针 &#x1f96e;智能指针&#x1f362;为什么需要智能指针&#x1f362;RAII &#x1f96e;au…

chatgpt赋能python:Python自动开机:提高效率的必备工具

Python 自动开机&#xff1a;提高效率的必备工具 随着科技的发展&#xff0c;计算机在我们的日常生活中扮演了越来越重要的角色。为了提高工作效率和使用体验&#xff0c;越来越多的人开始探索利用自动化工具来简化日常操作。 Python 称得上是自动化领域中的一把利器。通过代…

SAP-MM费用类采购通过物料组确定科目

一、WRX的配置&#xff0c;分两类GR/IR科目&#xff1a; 1、做库存管理物料的GR/IR科目&#xff0c;需要配置评估类&#xff0c;此评估类就是物料主数据里配置的评估类&#xff1b; 2、非库存管理费用化物料的GR/IR科目&#xff0c;如固定资产、办公用品、低值易耗品等等&#…

chatgpt赋能python:Python生成C代码:如何用Python快速高效地生成C代码

Python生成C代码&#xff1a;如何用Python快速高效地生成C代码 在现代编程中&#xff0c;有许多原因需要编写C代码。C是一种高性能语言&#xff0c;它允许程序员直接操作计算机的硬件。但是&#xff0c;编写C代码需要花费大量的时间和精力。幸运的是&#xff0c;Python可以帮助…

Spring Boot问题汇总

1.IDEA里yaml文件编辑时没有提示 网上很多教程说在设置里的File Types里把yaml格式加入到关联中 但其实我打开IDEA默认就是这么设置的&#xff0c;所以并没有什么用处。 不过在翻看这篇教程&#xff08;IDEA创建yml文件不显示小树叶创建失败问题的解决方法-eolink官网&#x…

网络安全学习心得分享~

我的学习心得&#xff0c;我认为能不能自学成功的要素有两点。 第一点就是自身的问题&#xff0c;虽然想要转行学习安全的人很多&#xff0c;但是非常强烈的想要转行学好的人是小部分。而大部分人只是抱着试试的心态来学习安全&#xff0c;这是完全不可能的。 所以能不能学成并…

【Python】字符串操作

知识目录 一、写在前面✨二、字符串逆序三、打印菱形四、总结撒花&#x1f60a; 一、写在前面✨ 大家好&#xff01;我是初心&#xff0c;很高兴再次跟大家见面。&#xff08;相遇就是缘分啊&#xff09; 今天跟大家分享的文章是 Python中的字符串操作 &#xff0c;希望能帮助…

SAP-物料主数据-质量管理视图字段解析

过账到质检库存&#xff1a;要勾选&#xff0c;否则收货后库存不进入质检库存HU检验&#xff1a;收货到启用HU管理的库位时产生检验批&#xff0c;例如某个成品物料是收货到C002库位&#xff0c;该库位启用了HU管理&#xff0c;那么此处要勾选。但是如果勾选了&#xff0c;却收…

全网最全最有用的网络安全学习路线!整整一晚上才整理出来!

正文&#xff1a; 废话不多说&#xff0c;先上一张图镇楼&#xff0c;看看网络安全有哪些方向&#xff0c;它们之间有什么关系和区别&#xff0c;各自需要学习哪些东西。 在这个圈子技术门类中&#xff0c;工作岗位主要有以下三个方向&#xff1a; 安全研发安全研究&#xff1…