智能合约开发

智能合约(solidity)编程入门

2021-07-08  本文已影响0人  95加不满

一. 智能合约概述

前言
区块链1.0: 2009年比特币的诞生。
区块链2.0: 以太坊的诞生,结合了区块链与智能合约技术的平台。

什么是智能合约
1996年,Nick Szabo在文章《Smart Contracts: Building Blocks For Digital Markets》中提出了智能合约的概念。
所谓“合约”,就是条文、合同一类的东西,里面记录了发生的条件与对应执行的条款,以支持确权等操作;所谓"智能",就意味着自动化、可编程。

所以,智能合约就是可编程的合同,也可以理解为一段自动执行的条文合同,在计算机中,就是一段自动执行的程序片段。它更易于合约保存,并且由确定的算法运行,给定输入,就得到对应的输出,极大保障了合约的执行力。

智能合约的现状与前景
从编程角度而言,智能合约就是一段代码。相比常规代码,智能合约具有许多差别与限制,例如:

显然,就目前的生态而言,智能合约对现实世界的影响力有限。

但事物总是在发展的。目前,已有许多致力于突破这些限制的研究,典型的有Oracle(谕言机,但常被称为预言机),它允许智能合约和链外进行交互,这样就能大大提高智能合约的使用场景,彷佛一台电脑通上了网;再比如那些突破链自身性能瓶颈的尝试,例如支付通道、跨链、plasma、rollup,它们都从不同角度打破安全与性能的枷锁。

毋庸置疑,智能合约将扮演着越来越重要的角色,将来随着以太坊2.0的落地,也许会开启新一个区块链时代。

智能合约技术
以太坊采用了Solidity作为智能合约语言,Solidity 是一门为实现智能合约而创建的高级编程语言,能在允许以太坊程序的节点上运行。该语言吸收了C++、JavaScript的一些特性,例如它是静态类型语言,支持继承、库等。

二. solidity开发讲解

简单的示例:

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0;

contract SimpleStorage {
    uint storedData;

    function set(uint x) public {
        storedData = x;
    }

    function get() public view returns (uint) {
        return storedData;
    }
}

1. 源文件结构

源文件中可以包含任意多个 合约定义 、导入源文件指令 、 版本标识 指令、 结构体 、 枚举 和 函数 定义.

SPDX许可标识

SPDX:The Software Package Data Exchange

// SPDX-License-Identifier: MIT

// SPDX-License-Identifier: UNLICENSED

版本标识

pragma solidity ^0.8.4;或者 pragma solidity >=0.4.16 <0.9.0;

ABI Coder Pragma

Solidity 0.7.4 之前:pragma experimental ABIEncoderV2

Solidity 0.7.4 之后:pragma abicoder v2

导入文件

import "filename";
示例:
import "./helper.sol";

注释

// This is a single-line comment.

/*
This is a
multi-line comment.
*/

2. 合约结构

状态变量

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.0 <0.9.0;

contract SimpleStorage {
    uint storedData; // State variable
    // ...
}

函数

/ SPDX-License-Identifier: GPL-3.0
pragma solidity >0.7.0 <0.9.0;

contract TinyAuction {
    function Mybid() public payable { // 定义函数
        // ...
    }
}

// Helper function defined outside of a contract
function helper(uint x) pure returns (uint) {
    return x * 2;
}

函数修饰器

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.22 <0.9.0;

contract Purchase {
    address public seller;

    modifier onlySeller() { // Modifier
        require(
            msg.sender == seller,
            "Only seller can call this."
        );
        _;
    }

    function abort() public view onlySeller { // Modifier usage
        // ...
    }
}

事件

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.21 <0.9.0;

contract SimpleAuction {
    event HighestBidIncreased(address bidder, uint amount); // Event

    function bid() public payable {
        // ...
        emit HighestBidIncreased(msg.sender, msg.value); // Triggering event
    }
}

异常处理

使用revert或者require(推荐)

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.4;

/// Not enough funds for transfer. Requested `requested`,
/// but only `available` available.
error NotEnoughFunds(uint requested, uint available);

contract Token {
    mapping(address => uint) balances;
    function transfer(address to, uint amount) public {
        uint balance = balances[msg.sender];
        if (balance < amount)
            revert NotEnoughFunds(amount, balance);
        balances[msg.sender] -= amount;
        balances[to] += amount;
        // ...
    }
    
     function transfer2(address to, uint amount) public {
        uint balance = balances[msg.sender];
        require(balance > amount," balance must be greater than amount");
        balances[msg.sender] -= amount;
        balances[to] += amount;
        // ...
    }
}

结构

pragma solidity >=0.4.0 <0.9.0;

contract Ballot {
    struct Voter { // 结构体
        uint weight;
        bool voted;
        address delegate;
        uint vote;
    }
}

枚举

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.25 <0.9.0;

contract Purchase {
    enum State { Created, Locked } // Enum
}

3. 常用信息

数据类型

特殊变量和函数

命名规范:

智能合约命名并没有一个标准,不过团队内部可以按照一个行业共识的规范执行。经过实战,推荐以下风格(不强制),如下代码块。

4. 示例

myContract.solhelper.sol

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.4;

// pragma experimental ABIEncoderV2;

import "./helper.sol";

/**
 * @title Storage
 * @dev Store & retrieve value in a variable
 */
contract Storage {

    address public seller;
    constructor(){
        seller = msg.sender;
    }
    
    modifier onlySeller() { // Modifier
        require(
            msg.sender == seller,
            "Only seller can call this."
        );
        _;
    }

    uint256 number;
    
    event storeEvent(string name,uint256 value);
    /**
     * @dev Store value in variable
     * @param num value to store
     */
    function store(uint256 num) public {
        number = num;
        emit storeEvent("number",number);
    }

    /**
     * @dev Return value 
     * @return value of 'number'
     */
    function retrieve() public view returns (uint256){
        return number;
    }
    
    
    // only call by seller
    function storeOnlySeller(uint256 num) public onlySeller {
        // number = num;
        number = Helper.add(num,1);
        emit storeEvent("number",number);
    }
    
     // destory the contract
    function destory() public onlySeller {
        selfdestruct(payable(msg.sender));
    }
    
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.4;

library Helper {

    function add(uint256 a,uint256 b) public pure returns(uint256) {
        return (a+b);
    }
    
}

4. 编译器

5. 常见问题

EVM栈溢出

ComplierError: Stack too deep ,try removing local variables.

参数个数限制:

版本 入参个数 出参个数 参数总数
0.4.25 16 13 16
0.8.4(>=0.7.0 <0.9.0) 11 12 16

解决方案: 当参数太多时,可以使用struct进行参数封装。

示例:

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.4.25;

contract SimpleStorage {

//ComplierError: Stack too deep ,try removing local variables.

// ^0.4.25: max size of in :16 
function testInParamterSize(
  string memory name,
  bytes32 addr,
  uint8 sex,
  uint age,
  bool isRich,
  
  string memory name2,
  bytes32 addr2,
  uint8 sex2,
  uint age2,
  bool isRich2,
  
  string memory name3,
  bytes32 addr3,
  uint8 sex3,
  uint age3,
  bool isRich3,
  
  string memory name4
)
  public
{
    //TODO
}


//^0.4.25:  max size of out :13
function testOutParamterSize(
)
  public
  returns(bool,bool,bool,string memory,uint8,
  bool,bool,bool,string memory,uint8,
  bool,bool,bool)
{
    //TODO
}


//^0.4.25: in 3,out 13   total:16
function testTotalParamerSize(
   string memory name,
  bytes32 addr,
  uint8 sex
  
)
 public
  returns(bool,bool,bool,string memory,uint8,
  bool,bool,bool,string memory,uint8,
  bool,bool,bool)
{
    //TODO
}

//^0.4.25: in 15,out 1   total:16
function testTotalParamerSize1(
   string memory name,
  bytes32 addr,
  uint8 sex,
  uint age,
  bool isRich,
  
  string memory name2,
  bytes32 addr2,
  uint8 sex2,
  uint age2,
  bool isRich2,
  
  string memory name3,
  bytes32 addr3,
  uint8 sex3,
  uint age3,
  bool isRich3
  
)
  public
  returns(bool)
{
    //TODO
}

}
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.4;
contract SimpleStorage {

//ComplierError: Stack too deep ,try removing local variables.

// max size of in :11 
function testInParamterSize(
  string memory name,
  bytes32 addr,
  uint8 sex,
  uint age,
  bool isRich,
  
  string memory name2,
  bytes32 addr2,
  uint8 sex2,
  uint age2,
  bool isRich2,
  
  string memory name3
)
  public
{
    //TODO
}



//^0.8.4:  max size of out :12
function testOutParamterSize(
)
  public
  returns(bool,bool,bool,string memory,uint8,
  bool,bool,bool,string memory,uint8,
  bool,bool)
{
    //TODO
}

//^0.8.4:  in 4,out 12   total:16
function testTotalParamerSize(
   string memory name,
  bytes32 addr,
  uint8 sex,
   uint age2
  
)
 public
  returns(bool,bool,bool,string memory,uint8,
  bool,bool,bool,string memory,uint8,
  bool,bool)
{
    //TODO
}

//^0.8.4: in 11,out 5   total:16
function testTotalParamerSize(
  string memory name,
  bytes32 addr,
  uint8 sex,
  uint age,
  bool isRich,
  
  string memory name2,
  bytes32 addr2,
  uint8 sex2,
  uint age2,
  bool isRich2,
  
  string memory name3
)
  public
  returns(bool,bool,bool,string memory,uint8)
{
    //TODO
}

}

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.4;

contract SimpleStorage {

    //^0.8.4: in 11,out 5   total:16
    function testTotalParamerSize(
      string memory name,
      bytes32 addr,
      uint8 sex,
      uint age,
      bool isRich,
      
      string memory name2,
      bytes32 addr2,
      uint8 sex2,
      uint age2,
      bool isRich2,
      
      string memory name3
    )
      public
      returns(bool,bool,bool,string memory,zxl memory)
    {
        //TODO
    }
    
    
    struct zxl{
        string name;
        bytes32 addr;
        uint8 sex;
        uint age;
        bool isRich;
        
        string name2;
        bytes32 addr2;
        uint8 sex2;
        uint age2;
        bool isRich2;
        
        string name3;
        bytes32 addr3;
        uint8 sex3;
        uint age3;
        bool isRich3;
        
        string name4;
        bytes32 addr4;
        uint8 sex4;
        uint age4;
        bool isRich4;
    }
    

}

无法正常debug

编写Solidity代码无法像其他高级语言一样进行debug。

解决方案:使用事件的log进行打印和展示更多内部变量。

示例如下
合约文件:

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.4;

contract HelloWorld {
    string public name;
    
    event Log(string oldValue, string newValue, address sender);
    
    constructor() public{
       name = "Hello, World!";
    }
  
    function set(string memory newValue) public{
      emit Log(name, newValue, msg.sender);
      name = newValue;
    }
    
    function get() public view returns(string memory){
      return name;
    } 
}

日志信息:

 logs   [ { "from": "0xf8e81D47203A594245E36C48e151709F0C19fBe8", "topic": "0xf35471203c6fa92fdd58f4bc31d50b93ecf6f4e792a9fb3ae964ca758a3a4aa6", "event": "Log", "args": { "0": "Hello, World!", "1": "zxl", "2": "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4", "oldValue": "Hello, World!", "newValue": "zxl", "sender": "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4" } } ] 

上一篇 下一篇

猜你喜欢

热点阅读