什么是智能合约
智能合约是运行在链上的程序,合约开发者可以通过智能合约实现与链上资产、数据进行交互,用户可以通过自己的链上账户来调用合约、访问资产与数据。
与一般程序的差异在于,智能合约的部署与后续写入需要一定费用,所以会更看重资源的消耗。
什么是Solidity
Solidity
是一门面向合约的、为实现智能合约而创建的高级编程语言,在 EVM
虚拟机上运行,语法与 JavaScript
类似,是目前最流行的智能合约语言,也是入门区块链与web3必须掌握的语言。
如何部署智能合约
Solidity
是以 .sol
为后缀的文件,无法直接执行,需要编译为 EVM
可识别的字节码才能在链上运行。
开发框架/工具
Solidity核心语法
基本数据类型
- boolean
- int
- uint
- address
- bytes
- …
复杂数据类型
枚举 enum
enum Status {
Unknown,
Start,
End,
Pause
}
// 实例化枚举类型
Status public status;
// 更新枚举值
function pause() public {
status = Status.Pause;
}
// 初始化枚举值
function reset() public {
delete status;
}
数组 array
// 定义数组类型
uint[7] public arr;
// 添加数据
arr.push(7);
// 删除某个索引值数据
delete arr[1];
// 获取数组长度
uint len = arr.length;
映射 mapping
类型,key
必须是基本数据类型,并且需要自己实现一个迭代器,并不能被原生迭代器遍历。
// 定义嵌套 mapping 类型
mapping(string => mapping(string => string)) nestedMap;
// 设置值
nestedMap[id][key] = "01";
// 读取值
string value = nestedMap[id][key];
// 删除值
delete nestedMap[id][key];
结构体 struct
类型
contract Struct {
struct Data {
string id;
string hash;
}
Data public data;
// 添加数据
function create(string calldata _id) public {
data = Data { id: _id, hash: "0000" };
}
// 更新数据
function update(string _id) public {
// 查询数据
string id = data.id;
// 更新
data.hash = "1111";
}
}
变量
类型:
- local(只存在于方法里,不会存储在链上)
- state(可以存储在链上)
- global
关键字声明:
- storage(声明的变量可以存在链上)
- memory(只有方法被调用时,声明的变量才会被定义)
- calldata(特殊的memory)
常量
- constant(值不可变,节约 gas fee)
- immutable(可以在constructor中初始化,但不可以再次改变)
函数
- 可见性
- public
- private
- internal
- external
- 关键字
- view(可以读取变量,但是不能更改)
- pure(不可以读取和更改)
- 函数修饰符
- modifier(可以在函数运行前或运行后被调用,常用于参数校验和权限控制)
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
_; // 表明开始执行真正的合约代码的逻辑
}
modifier validAddress(address _addr) {
require(_addr != address(0), "Not valid address");
_;
}
modifier noReentrancy() {
require(!locked, "No reentrancy");
locked = true;
_;
locked = false;
}
function changeOwner(address _newOwner) public onlyOwner validAddress(_newOwner) {
owner = _newOwner;
}
function decrement(uint i) public noReentrancy {
x -= i;
if (i > 1) {
decrement(i - 1);
}
}
函数选择器
addr.call(abi.encodeWithSignature("transfer(address,uint256)", 0xSomeAddress, 123));
contract FunctionSelector {
function getSelector(string calldata _func) external pure returns(bytes4) {
return bytes4(keccak256(bytes(_func)));
}
}
合约
-
constructor
constructor(string memory _name) { name = _name; }
-
interface
contract Counter { uint public count; function increment() external { count += 1; } } interface ICounter { function count() external view returns (uint); function increment() external; } contract MyContract() { function incrementCounter(address _counter) external { ICounter(_counter).increment(); } function getCount(address _counter) external view returns (uint) { return ICounter(_counter).count(); } }
-
导入库
library SafeMath { function add(uint x, uint y) internal pure returns (uint) { uint z = x + y; require(z >= x, "uint overflow"); return z; } } contract TestSafeMath { using SafeMath for uint; }
事件
// 定义事件
event Log(address indexed sender, string message);
event AnotherLog();
// 抛出事件
emit Log(msg.sender, "Hello World");
emit Log(msg.sender, "Hello EVM");
emit AnotherLog();
错误处理
通过 require / revert / assert
处理错误
function testRequire(uint _i) public pure {
require(_i > 10, "Input must be greater than 10");
}
function testRevert(uint _i) public pure {
if (_i <= 10) {
revert("Input must be greater than 10");
}
}
function testAssert() public view {
assert(num === 0);
}