Solidity02 Solidity合约组成结构

文章目录

  • 一、合约的基本构成
    • 1.1 计数器代码示例
    • 1.2 智能合约的组成
      • 1.2.1 License 许可声明
      • 1.2.2 编译器版本声明
      • 1.2.3 合约定义
      • 1.2.4 状态变量
      • 1.2.5 合约函数
  • 二、合约的组成结构

Solidity的智能合约和面向对象语言中的类很相似。你可能会好奇合约都由什么基本结构组成的呢? 我们在编写合约的时候又是如何把这些基本结构结合在一起的呢?

一、合约的基本构成

1.1 计数器代码示例

下面是一个简单的使用 Solidity 编写的智能合约:

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
 
contract Counter {
    uint counter;
    
    constructor() {
        counter = 0;
    }
    
    function count() public {
        counter = counter + 1;
    }
    
    function get() public view returns (uint) {
        return counter;
    }
}

在学习智能合约时,通常以 Counter 计数器作为入门合约,而不是通常打印 HelloWorld, 这个因为合约主要是用来处理状态的转换,另外,合约程序实际上是在节点上运行,因此是看不到打印输出的。

1.2 智能合约的组成

通常一个智能合约sol文件会包含以下几个部分:

(0)声明合约源文件的 License 声明;

(1)声明编译合约使用的编译器版本;

(2)用 import 引入其他合约文件

(3)用contract定义一个合约/用library定义一个库/用 interface 定义接口。

(4)定义合约内的状态变量、函数、事件、自定义类型等。

那么一个典型的智能合约,contract通常由 4 个部分组成,分别是:合约定义部分状态变量部分函数部分。其中,状态变量部分函数部分 是智能合约的主体。

1.2.1 License 许可声明

// SPDX-License-Identifier: MIT

SPDX-License-Identifier 是一个用于版权声明的注释语句,它声明了该智能合约代码所使用的软件许可证,规定了他人能否拥有商业开发,以及学习使用的权利。

例子中采用的是 MIT 许可证,它表明:其它人可以随便使用该代码,但出了问题不负责。

如果智能合约的代码中没有 SPDX-License-Identifier,那么编译代码时就会给出警告提示,但是程序依然可以正确运行。

版权声明通常位于合约代码的第一句,我们直接复制过来使用就可以了。

SPDX 语句通常位于 Solidity 智能合约文件的第一句。

SPDX 语句是一条注释语句,也就是说,如果合约代码中没有 SPDX 语句,那么功能也不会受到任何影响,照样可以运行。

但是,如果我们在代码中去除了 SPDX 语句,那么在编译合约的时候,就会出现下面的警告信息:

Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: " to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information. 

大意是:源文件缺少 SPDX 许可证标识,建议在发布前,使用 SPDX-License-Identifier 标识加上注释。

我们也建议在 Solidity 源文件中加入 SPDX 许可证标识语句,这样更符合 Solidity 编程规范。

SPDX 有什么用处

SPDX 即软件包数据交换(Software Package Data Exchange),它是一种用于描述软件许可信息的标准格式,它的内容包括许可证、版权和其他相关信息。

SPDX 规范由 Linux 基金会的 SPDX 工作组开发,旨在简化在不同的软件开发和分发渠道之间共享和跟踪软件许可信息的过程。

SPDX 的官方网站为:SPDX 许可证列表 |软件包数据交换 (SPDX),有需要了解细节的可以自行查看。

简而言之,SPDX 用于声明版权信息。

我们必须清楚,开源代码虽然源代码是公开的,但并不意味着可以免费使用。我们在使用别人代码的时候,先要搞清楚是否允许商用,防止出现侵犯版权的行为。

Solidity 中,就是使用 SPDX-License-Identifier 语句来声明版权许可证。

比如,SPDX-License-Identifier: MIT,就表明了这段代码使用的是 MIT 版权许可证。

我们在编写智能合约的时候,通常在文件首句使用 SPDX 来声明版权许可证。

SPDX 应该如何选择

MIT 是智能合约中最常见的版权许可证,具体描述如下:

特此免费授予获得(“软件”)副本的任何人不受限制地处理本软件的许可,包括但不限于使用、复制、修改、合并、发布、分发、再许可和/或出售本软件副本的权利。
上述版权声明和本许可声明应包含在本软件的所有副本或大部分内容中。
本软件按“原样”提供,不附带任何明示或暗示的担保,包括但不限于适销性、特定用途的适用性和不侵权的担保。
在任何情况下,对于因本软件或本软件的使用或其他交易而引起、引起或与之相关的任何索赔、损害赔偿或其他责任,无论是在合同诉讼、侵权行为还是其他方面,均不承担任何责任。

简而言之,采用 MIT 许可证,就意味着任何人都能以任何方式使用代码,但是出了问题不负责。

通常,我们都会选择 MIT 作为智能合约的版权许可证,这也是大多数知名开源项目的选择。

比如,大名鼎鼎的安全基础库 OpenZeppelin,它使用的就是 MIT 许可证。

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/ERC20.sol)

我们在编写智能合约的时候,直接抄写一遍放在合约开头即可。

// SPDX-License-Identifier: MIT

合约代码应该开源还是不开源

由于智能合约秉承了区块链数据公开的原则,它的代码通常都是开源的,这也是区块链项目取信于人的一种手段。

如果智能合约的源码不公开,那么就意味着该合约很可能埋伏着一些特殊目的,比如欺诈、后门等。

当然,也有一些项目方为了保护源码版权,或者对代码的安全性不自信,从而选择了不开源。

但是,智能合约最终是要发布到区块链上的,任何人都可以得到它的二进制代码。在代码量不大的情况下,还是非常容易被整体或者局部反编译,因而达不到保护源码的目的。

知名的 Defi 巨头 UniSwap,它的合约代码虽然是开源的,但许可证采用了 BUSL,并不是 MIT

// SPDX-License-Identifier: BUSL-1.1

BUSL 许可证与 MIT 非常相似,也是一种比较宽松的许可方式,但与 MIT 相比,还是附加了一些限制条件。

UniSwap 加入限制条件的主要原因:UniSwap 作为最著名的去中心化交易所,很多项目直接抄袭了它的代码,甚至有些项目的交易规模接近甚至反超了它,比如 SushiSwap,所以 UniSwap 出于商业利益的考虑,在版权声明中加入了一定的限制。

它们定义了代码使用、修改和分发的规则。这些许可证并不直接影响合约的功能,而是规定了合约代码的法律约束。

1.2.2 编译器版本声明

pragma solidity ^0.8.0;

**pragma** 指令用来指示编译器按照 Solidity 的哪个版本来编译合约代码。

Solidity 的发展历史上有很多版本,其中有些版本的差异比较大,我们提倡使用较新的版本,比如目前使用 0.8.0 以上版本。

**pragma** 声明版本有多种写法,可以使用 ^~=>>=<<= 符号来指示版本号范围:

pragma solidity >=0.8.0 <0.9.0 // 表示 Solidity 版本在 0.8.0 及以上,但在 0.9.0 版本之前
pragma solidity >0.8.0 // 表示 Solidity 版本在 0.8.0 以上,没有任何上限
pragma solidity =0.8.0 // 表示 Solidity 版本只能是 0.8.0
pragma solidity 0.8.0 // 与 pragma solidity =0.8.0 相同
pragma solidity ^0.8.0 // 表示 Solidity 版本在 0.8.0 及以上,但不包括 0.9.0 及以上的版本
pragma solidity ~0.8.0 // 与 pragma solidity ^0.8.0 相同

在实际开发中,以上的写法都能找到范例,但常用的写法主要有两种:

pragma solidity 0.8.7;

这种写法是指:只能使用 0.8.7 版本的编译器编译。这种声明的好处是版本明确,以后不会引起混乱。

pragma solidity ^0.8.0;

类似的还可以使用以下方式:
pragma solidity >=0.8.0 <0.9.0;

这种写法是指:可以使用 0.8.0 及以上版本的编译器编译,但是不能高于 0.9.0 及以上版本。这种声明的兼容性会更好一些,可供选择的编译器多一些。

1.2.3 合约定义

contract Counter {
...
}

**contract** 是一个保留字,用来声明一个合约。contract 后面的内容是合约的名称。

合约名称可以是任意字符串,但必须以字母或者下划线“_”开头。通常,比较规范的写法是采用 驼峰形式,以大写字母开头。

驼峰模式 是指每个单词的首字母大写,其它字母小写来表示。例如,“FirstName”、“GetCustomerName”、“NumberOfStudents” 等都是驼峰模式的示例。

驼峰模式的名称来源于其类似于驼峰形状。在许多编程语言中,驼峰模式是一种常见的命名约定,它有助于提高代码的可读性,并使变量名和函数名等更易于理解。

在上面的代码范例中,我们定义了一个智能合约,名称为 Counter

1.2.4 状态变量

uint counter;

这行代码声明了一个变量,uint 是数据类型(一个256位的无符号整数),表示一个整型,counter 是状态变量的名称。状态变量的名称可以是任意整型,但必须以字母或者下划线“_”开头。

通常,比较规范的写法是采用驼峰形式,但是整体以小写字母或者下划线“_”开头。

状态变量是智能合约的主体部分之一,用来存储区块链上的数据。

Solidity是一个静态类型语言,每个变量需要在声明时确定类型。

1.2.5 合约函数

constructor()  {
    counter = 0;
}

function count() public {
    counter = counter + 1;
}


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

**function** 是一个保留字,用来声明一个函数。函数名称可以是任意字符串,但必须以字母或者下划线“_”开头。

通常,比较规范的写法是采用驼峰形式,但是整体以小写字母或者下划线“_”开头。

上面的例子中,定义了3个函数:

  • 第一个constructor是构造函数,用来完成合约的初始化,在合约创建时执行;
  • count() 是一个普通的函数,它对counter变量加1,任何修改状态变量都需要通过一个交易提交到链上,矿工打包之后交易才算完成;
  • get()函数用来读取变量的值,这是一个视图函数,不需要提交交易。

Counter函数非常简单,没有使用 import 引入其他文件,也没有定义事件和其他自定义类型,这些内容我们将在后面的文章里进一步介绍。

注意:函数是智能合约的主体部分之一,用来实现智能合约的功能。

二、合约的组成结构

合约的七大组成结构有:

Solidity02 Solidity合约组成结构_第1张图片

你可能感兴趣的:(Solidity,区块链,Solidity,智能合约)