Solidity是一门面向合约的、为实现智能合约而创建的高级编程语言,设计的目的是能在以太坊虚拟机上运行。
本章大概介绍合约的基本信息,合约的组成,语法方面不做过多的介绍,个人建议多阅读官方文档效果更佳,后续的章节会开发ERC20代币合约案例以便于更好的学习智能合约的开发
官网文档:https://docs.soliditylang.org/en/v0.8.12/
中文文档:https://learnblockchain.cn/docs/solidity
1、第一个合约介绍
我们来看一个最简单的存取整形数据的合约代码
// 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;
}
}
第一行说明源代码在GPL 3.0版权许可,在代码中加入机器可读许可证说明很重要, 在发布源代码时在默认需要,直接照抄就行。
第二行是告诉编译器源代码适用的solidity版本为>=0.4.16 <0.9.0,如果是这样定义:pragma solidity ^0.5.2,则源文件将既不允许低于 0.5.2 版本的编译器编译, 也不允许高于(包含) 0.6.0 版本的编译器编译
contract声明一个合约,类似于java的class
uint storedData声明一个类型为 uint (256位无符号整数)的状态变量,叫做storedData,这边的变量可以认为是数据库表里面的一个字段,可以通过函数进行查询和变更。
函数set和get可以用来变更或取出变量的值,function用于定义一个函数。
2、合约组成
我们推荐使用在线remix来演示solidity的语法
https://remix.ethereum.org/
合约中可以包含注释、状态变量、函数、事件Event、结构体、和枚举类型的声明,且合约可以从其他合约继承。
注释
// 这是一个单行注释。
/*
这是一个
多行注释。
*/
状态变量
状态变量是永久地存储在合约存储中的值。
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.0 <0.9.0;
contract SimpleStorage {
uint storedData; // 定义一个无符号整形的状态变量storedData
// ...
}
函数(function)
函数是代码的可执行单元。函数通常在合约内部定义,但也可以在合约外定义。
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.1 <0.9.0;
contract SimpleAuction {
// 在合约内部定义函数
function bid() public payable { // Function
}
}
// 在合约外部定义函数
function helper(uint x) pure returns (uint) {
return x * 2;
}
函数修改器modifier
函数 修改器modifier可以用来以声明的方式修改函数语义,我们看下修改器的用法
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.22 <0.9.0;
contract MyPurchase {
address public seller;
uint256 storedData;
modifier onlySeller() { // 定义修改器
// 只有当合约的调用者==seller时才执行_;的语句,_;相当于占位符
require(msg.sender == seller,"Only seller can call this.");
_;
}
function abort() view public onlySeller {
require(msg.sender == seller,"Only seller can call this.");
storedData == 1;
}
}
上面的效果等同于
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.22 <0.9.0;
contract MyPurchase {
address public seller;
uint256 storedData;
function abort() view public {
require(msg.sender == seller,"Only seller can call this.");
storedData == 1;
}
}
事件
事件是能方便地调用以太坊虚拟机日志功能的接口,客户端调用的时候可以接收该事件。
// 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); // 触发事件
}
}
结构体
结构体可以将几个变量封装起来
// SPDX-License-Identifier: GPL-3.0
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.0 <0.9.0;
contract Purchase {
enum State { Created, Locked, Inactive } // Enum
}
数据类型
Solidity 是一种静态类型语言,意味着每个变量(状态变量和局部变量)都需要在编译时指定变量的类型。
Solidity 提供了几种基本类型,并且基本类型可以用来组合出复杂类型。
值类型
值类型常用的包含:布尔类型,整形,浮点型,地址类型,枚举类型等
布尔类型:使用bool定义一个波尔类型的变量
bool isOwner;
整型
int/uint:分别表示有符号和无符号的不同位数的整型变量,支持关键字 uint8
到 uint256
(无符号,从 8 位到 256 位)以及 int8
到 int256
uint256 storedData;
地址类型
address:保存一个20字节的值
address payable:可支付地址,payable表示可以接受以太币,如果函数内部需要用到转账功能则需要加上payable
引用类型
数组
uint[] x;
bytes和string也是数组,bytes类似与byte[]
映射
映射类型在声明时的形式为mapping(keyType => valueType),其中 _KeyType
可以是任何基本类型,即可以是任何的内建类型, bytes
和 string
或合约类型、枚举类型。 而其他用户定义的类型或复杂的类型如:映射、结构体、即除 bytes
和 string
之外的数组类型是不可以作为 _KeyType
的类型的。
mapping (address => uint256) private _balances;
3、以太币单位
以太币(Ether) 单位之间的换算就是在数字后边加上 wei
、gwei
或 ether
来实现的,如果后面没有单位,缺省为 wei
最⼩单位: Wei (伟)
10^9 Wei = 1 Gwei
10^18 Wei = 1 Ether
4、时间单位
秒是缺省时间单位,在时间单位之间,数字后面带有 seconds
、 minutes
、 hours
、 days
和 weeks
的可以进行换算,基本换算关系如下
1 == 1 seconds
1 minutes == 60 seconds
1 hours == 60 minutes
1 days == 24 hours
1 weeks == 7 days
5、特殊变量和函数
区块和交易属性
blockhash(uint blockNumber) returns (bytes32):指定区块的区块哈希——仅可用于最新的 256 个区块且不包括当前区块
block.chainid (uint): 当前链 id
block.coinbase ( address ): 挖出当前区块的矿工地址
block.difficulty ( uint ): 当前区块难度
block.gaslimit ( uint ): 当前区块 gas 限额
block.number ( uint ): 当前区块号
block.timestamp ( uint): 自 unix epoch 起始当前区块以秒计的时间戳
gasleft() returns (uint256) :剩余的 gas
msg.data ( bytes ): 完整的 calldata
msg.sender ( address ): 消息发送者(当前调用)
msg.sig ( bytes4 ): calldata 的前 4 字节(也就是函数标识符)
msg.value ( uint ): 随消息发送的 wei 的数量
tx.gasprice (uint): 交易的 gas 价格
tx.origin (address payable): 交易发起者(完全的调用链)
ABI 编码及解码函数
abi.decode(bytes memory encodedData, (...)) returns (...): 对给定的数据进行ABI解码,而数据的类型在括号中第二个参数给出 。 例如: (uint a, uint[2] memory b, bytes memory c) = abi.decode(data, (uint, uint[2], bytes))
abi.encode(...) returns (bytes): ABI - 对给定参数进行编码
abi.encodePacked(...) returns (bytes):对给定参数执行 紧打包编码 ,注意,可以不明确打包编码。
abi.encodeWithSelector(bytes4 selector, ...) returns (bytes): ABI - 对给定第二个开始的参数进行编码,并以给定的函数选择器作为起始的 4 字节数据一起返回
abi.encodeWithSignature(string signature, ...) returns (bytes):等价于abi.encodeWithSelector(bytes4(keccak256(signature), ...)
错误处理
-
assert(bool condition)
如果不满足条件,则会导致Panic 错误,则撤销状态更改 - 用于检查内部错误。
-
require(bool condition)
如果条件不满足则撤销状态更改 - 用于检查由输入或者外部组件引起的错误。
-
require(bool condition, string memory message)
如果条件不满足则撤销状态更改 - 用于检查由输入或者外部组件引起的错误,可以同时提供一个错误消息。
-
revert()
终止运行并撤销状态更改。
-
revert(string memory reason)
终止运行并撤销状态更改,可以同时提供一个解释性的字符串。
地址成员
-
<address>.balance
(uint256
)以 Wei 为单位的 地址类型 Address 的余额。
-
<address>.code
(bytes memory
)在 地址类型 Address 上的代码(可以为空)
-
<address>.codehash
(bytes32
):ref:[
](https://learnblockchain.cn/docs/solidity/units-and-global-variables.html#id9)address
的codehash -
<address payable>.transfer(uint256 amount)
向 地址类型 Address 发送数量为 amount 的 Wei,失败时抛出异常,使用固定(不可调节)的 2300 gas 的矿工费。
-
<address payable>.send(uint256 amount) returns (bool)
向 地址类型 Address 发送数量为 amount 的 Wei,失败时返回
false
,发送 2300 gas 的矿工费用,不可调节。 -
<address>.call(bytes memory) returns (bool, bytes memory)
用给定的有效载荷(payload)发出低级
CALL
调用,返回成功状态及返回数据,发送所有可用 gas,也可以调节 gas。 -
<address>.delegatecall(bytes memory) returns (bool, bytes memory)
用给定的有效载荷 发出低级
DELEGATECALL
调用 ,返回成功状态并返回数据,发送所有可用 gas,也可以调节 gas。 发出低级函数DELEGATECALL
,失败时返回false
,发送所有可用 gas,可调节。 -
<address>.staticcall(bytes memory) returns (bool, bytes memory)
用给定的有效载荷 发出低级
STATICCALL
调用 ,返回成功状态并返回数据,发送所有可用 gas,也可以调节 gas。