Solidity 基础
Solidity 编程相关
Solidity 是一门类似 JavaScript 的编程语言,用于编写智能合约,与其他智能合约语言相比,它使用最为广泛,相关编程的技巧和误区的研究也较成熟,是入门智能合约编程的首选。
一个简单的合约
如上代码,便是一个最简单的 Solidity 合约。
L1: pragma solidity >0.5.9
表示当前合约应该使用的编译器版本,因为 Solidity 每个版本都有所更新,尤其是有一些语法不兼容的更新。所以需要在合约的开头通过 pragma
限制版本号。版本号限制语法同 npm 。
L3: 注释,注释语法同 JavaScript 。
L4-L14: 合约的主体,所有合约的储存变量接口函数都在此定义。
L5: 这里定义了一个 uint256
类型的合约状态变量 value
, internal
表示它是内部的,外部无法直接通过 value
这个名字访问。
L7-L9: 定义了 setValue(uint256)
方法,external
表示它只能被外部调用,即通过区块链相关 API 调用合约的该方法。在方法实现中,状态变量 value
, 被设置为函数的入参。
L11-L13: 定义了 getValue()
方法, view
表示它不可以更改合约状态变量, returns
表示其返回类型,合约调用的发起者可以获得该返回值。
由此,我们得到一个合约的标准结构,首先是 pragma
表示编译器版本,其次是定义了合约方法,合约状态变量的合约主体结构。部分较复杂的合约还会在 pragma
之后有 import
语句引入其他库函数。
Solidity 语言基础
基础类型
从上一节的合约例子,我们已经见到了最基础了类型 uint256
. Solidity 还支持如下基础类型:
bool
: 即true
和false
, Big endian 扩充 256 位。整数类型:
int
和uint
, 支持后缀数字表示位长,最小从uint8
到最长uint256
. Big endian.address
: 地址类型,表示合约或普通账户地址,为 20 字节。内部处理时高位补齐到 32 字节。同时在 TVM 优化中,也处理了 21 字节地址,即支持前缀 0x41定长字节:
bytes1
,bytes2
,bytes3
, …,bytes32
.byte
是bytes1 的别名
,高位补齐到 32 字节。
字面常量 - literals
地址,数值。 Solidity 没有八进制数值常量。数值常量可以用科学计数法表示,例如 2.5e1
。
字符串字面常量。支持单引号或双引号。支持常见转义,支持 \uNNNN
转义。
十六进制字面常量。hex"00112233cafe"
。
枚举类型 - enum
枚举类型可以通过显式类型转换语法和整数类型互相转换。不支持隐式类型转换。例如 uint(val)
.
由于 ABI 不支持自定义类型,所以出现在 ABI 中的枚举,会自动转换为合适的 uintN
类型。例如 function getChoice() public view returns (ActionChoices)
会变为 getChoice() returns (uint8)
.
引用类型 - reference tyes
以上的可以放入一个 uint256
的类型都叫值类型 value type. 与此对应的,是引用类型。
引用类型一般被放入 storage
或 memory
。默认情况下函数参数和返回值中的引用类型是 memory
,合约状态变量是 storage
。(data location)
另外有种特殊的 data location 叫 calldata
隶属于外部函数调用时候传递的参数,类似 memory
但是不可修改。
变长类型 - dynamic size type
bytes
和 string
这样的长度动态指定的类型,被叫做 dynamic size type. 虚拟机在操作时代价高于定长类型,应避免使用。两者不同的地方在于,虽然底层表示都是字节,但 string
不允许索引操作(utf8 string)。
数组类型 - array
数组可以是编译时确定大小,或动态大小。支持高维数组。例如 uint[][5]
。
bytes
类型类似 byte[]
但代价更小,需要尽量避免使用 byte[]
。
创建 array 使用 uint[] memory a = new uint
。
支持 .length (rw) .push() 操作。
结构体 - struct
java-tron ABI 无法支持结构体的描述,所以出现在 ABI 中的结构体统一表述为 tuple
结构。实际合约中应避免在 ABI 中出现结构体。
参考: StackOverflow
映射 - mapping
即 k-v 哈希字典。mapping(_KeyType => _ValueType) .
key 可以是除 mapping 动态数组,枚举,结构体之外的类型。value 可以是任意类型。
需要注意的是,和其他语言不同, Solidity 中的映射没有 size 的概念,一旦初始化,所有 key 对应的 value 都有默认值(全0值)。所以 mapping 是不可迭代结构。这是由于:
mapping 的内部实现是通过对 key 进行 hash 运算得到一个储存位置,返回这个储存位置对应的值。而对于合约储存而言,储存位置默认都是 0.
类型转换
有了类型,就有类型间的转换。值域属于子集的类型,可以隐式类型转换,否则需要显式类型转换。例如:
此时, x 将等于 -3 的二进制补码表示。
Storage
合约的 Storage 生命周期同合约本身。同 state variable.
private public external internal
自动为 public storage 变量创建 getter.
表达式和控制结构
程序的功能和逻辑,离不开基础表达式和控制结构。
Solidity 的表达式基本同 C/JavaScript 系语言,相关参考见: 运算符优先级
Solidity 的基本控制结构同样来自 C/JavaScript 系语言,即: if
, else
, while
, do
, for
, break
, continue
, return
.
此外,对于外部调用和合约创建调用, Solidity 还支持 try/catch
语句。(TRON 目前对合约创建合约的支持较弱)
TRON 目前(4.0.1)只支持合约创建合约的 CREATE
调用, CREATE2
调用并没有实现。
函数
合约的主要逻辑在函数中定义。函数定义的语法如下:
如果函数作为类型出现,那么只需要 function (<parameter types>) {internal|external} [pure|constant|view|payable] [returns (<return types>)]
。
这里的 internal/external 是访问控制关键字,四种访问控制关键字如下:
private
: only from this contractinternal
: this contract and derived contractexternal
: can not be accessed internallypublic
: may be called internally
实际使用中,应选择最小范围的访问控制,例如使用 external
而非 public
.
constant
: 不需要网络确认的操作,例如查询 storage, 新版 Solidity 已经废弃了该关键字view
: 用于替换原有的constant
关键字,表示函数不会修改 storagepure
: 比view
更严格,不允许查询 storage
而 payable
是单独的概念,用于表示该合约函数可以处理代币转账。其中无函数名的特殊 payable
函数叫 fallback function:
函数相关操作
函数修饰符 - function modifier
函数修饰符用于改变函数的行为,例如自动权限检查。
事件 - Event
Event 通过 EVM/TVM 的 logging 机制,提供了高效的事件记录机制。
合约的构造函数和继承
继承
Solidity 支持合约的多重继承机制。父合约可以定义虚函数,由子合约实现,虚合约,抽象合约。
多重继承不会导致创建多个合约,最终只有一个合约被创建,相关父合约的代码会被 copy 到最终合约。
构造函数
默认构造函数 contructor() public {}
.
构造函数可以被标记为 public
或 internal
. internal
相当于抽象合约(类)。
父类构造函数也可以通过如下方式传递:
接口 - interface
接口相当于抽象类。
Cannot inherit other contracts or interfaces.
Cannot define constructor.
Cannot define variables.
Cannot define structs.
Cannot define enums.
库 - library
库的存在,避免了自己实现轮子的麻烦。保证了多个合约共享逻辑的一致性。
需要区分库和父合约。库其实是一个单独的合约。代码独立与调用者存在,需要通过 DELEGATECALL
指令调用。
库和合约必须单独部署,且库不产生 ABI 文件。库先部署完毕后,得到一个合约地址, 将该合约地址转换为 20 字节地址,填入目标合约 .bin
文件的占位符后。再部署最终合约。
占位符格式在不同的版本的 Solidity 编译器下有所不同。
参考: Libraries.
最后更新于