Solidity智能合约开发
Solidity是以太坊智能合约的主要开发语言。本章将介绍Solidity的基础语法、合约结构、高级特性以及最佳实践。
合约基础
1. 合约结构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 | // SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract MyContract {
// 状态变量
string public name;
uint256 public balance;
address public owner;
// 事件
event Transfer(address indexed from, address indexed to, uint256 value);
// 构造函数
constructor(string memory _name) {
name = _name;
owner = msg.sender;
}
// 修饰器
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
_;
}
// 函数
function deposit() public payable {
balance += msg.value;
emit Transfer(msg.sender, address(this), msg.value);
}
}
|
2. 数据类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 | contract DataTypes {
// 值类型
bool public flag = true;
uint256 public number = 42;
int256 public signedNumber = -42;
address public addr = 0x123...;
bytes32 public hash = keccak256("Hello");
// 数组
uint256[] public dynamicArray;
uint256[5] public fixedArray;
// 映射
mapping(address => uint256) public balances;
mapping(address => mapping(address => uint256)) public allowances;
// 结构体
struct User {
string name;
uint256 balance;
bool isActive;
}
User public user;
// 枚举
enum Status { Pending, Active, Inactive }
Status public status;
}
|
函数和修饰器
1. 函数类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 | contract Functions {
// 公共函数
function publicFunction() public pure returns (string memory) {
return "Public function";
}
// 私有函数
function privateFunction() private pure returns (string memory) {
return "Private function";
}
// 内部函数
function internalFunction() internal pure returns (string memory) {
return "Internal function";
}
// 外部函数
function externalFunction() external pure returns (string memory) {
return "External function";
}
// 视图函数
function viewFunction() public view returns (uint256) {
return block.number;
}
// 纯函数
function pureFunction(uint256 x) public pure returns (uint256) {
return x * 2;
}
}
|
2. 修饰器模式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37 | contract Modifiers {
address public owner;
bool public locked;
constructor() {
owner = msg.sender;
}
// 基本修饰器
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
_;
}
// 带参数的修饰器
modifier minimumValue(uint256 value) {
require(msg.value >= value, "Value too low");
_;
}
// 防重入修饰器
modifier noReentrant() {
require(!locked, "Reentrant call");
locked = true;
_;
locked = false;
}
// 使用修饰器
function withdraw()
public
onlyOwner
noReentrant
{
// 提款逻辑
}
}
|
继承和接口
1. 合约继承
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 | // 基础合约
contract Ownable {
address public owner;
constructor() {
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
_;
}
}
// 代币合约
contract Token is Ownable {
mapping(address => uint256) public balances;
function mint(address to, uint256 amount) public onlyOwner {
balances[to] += amount;
}
}
|
2. 接口定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 | // 接口定义
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 amount) external returns (bool);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address from, address to, uint256 amount) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
// 实现接口
contract MyToken is IERC20 {
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 private _totalSupply;
function totalSupply() external view override returns (uint256) {
return _totalSupply;
}
function balanceOf(address account) external view override returns (uint256) {
return _balances[account];
}
// 实现其他接口函数...
}
|
高级特性
1. 事件和日志
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 | contract Events {
// 事件定义
event UserCreated(address indexed user, string name, uint256 age);
event ValueChanged(address indexed user, uint256 oldValue, uint256 newValue);
// 触发事件
function createUser(string memory name, uint256 age) public {
// 业务逻辑
emit UserCreated(msg.sender, name, age);
}
// 带索引的事件
function updateValue(uint256 newValue) public {
uint256 oldValue = 100;
// 业务逻辑
emit ValueChanged(msg.sender, oldValue, newValue);
}
}
|
2. 库合约
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 | // 数学库
library SafeMath {
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "SafeMath: subtraction overflow");
return a - b;
}
}
// 使用库
contract Calculator {
using SafeMath for uint256;
function calculate(uint256 a, uint256 b) public pure returns (uint256) {
return a.add(b);
}
}
|
安全最佳实践
1. 重入攻击防护
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 | contract Vault {
mapping(address => uint256) public balances;
bool private locked;
modifier noReentrant() {
require(!locked, "Reentrant call");
locked = true;
_;
locked = false;
}
function deposit() public payable {
balances[msg.sender] += msg.value;
}
function withdraw() public noReentrant {
uint256 balance = balances[msg.sender];
require(balance > 0, "No balance");
balances[msg.sender] = 0;
(bool success, ) = msg.sender.call{value: balance}("");
require(success, "Transfer failed");
}
}
|
2. 整数溢出保护
1
2
3
4
5
6
7
8
9
10
11
12
13 | contract SafeCounter {
using SafeMath for uint256;
uint256 private _counter;
function increment() public {
_counter = _counter.add(1);
}
function decrement() public {
_counter = _counter.sub(1);
}
}
|
3. 访问控制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 | contract AccessControl {
mapping(address => bool) public admins;
mapping(bytes32 => mapping(address => bool)) public roles;
// 角色常量
bytes32 public constant MINTER_ROLE = keccak256("MINTER");
bytes32 public constant BURNER_ROLE = keccak256("BURNER");
modifier onlyAdmin() {
require(admins[msg.sender], "Not admin");
_;
}
modifier onlyRole(bytes32 role) {
require(roles[role][msg.sender], "Not authorized");
_;
}
function grantRole(bytes32 role, address account) public onlyAdmin {
roles[role][account] = true;
}
function revokeRole(bytes32 role, address account) public onlyAdmin {
roles[role][account] = false;
}
}
|
Gas优化技巧
1. 存储优化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 | contract StorageOptimization {
// 打包结构体
struct User {
uint128 balance; // 16 bytes
uint64 lastUpdate; // 8 bytes
uint64 index; // 8 bytes
} // 总共32 bytes
// 使用定长数组
uint256[10] public fixedArray;
// 避免动态数组增长
function batchProcess(uint256[] memory data) public {
uint256 len = data.length;
for(uint256 i = 0; i < len; i++) {
// 处理逻辑
}
}
}
|
2. 计算优化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 | contract ComputationOptimization {
// 缓存数组长度
function processArray(uint256[] memory data) public pure returns (uint256) {
uint256 sum = 0;
uint256 len = data.length;
for(uint256 i = 0; i < len; i++) {
sum += data[i];
}
return sum;
}
// 使用位运算
function multiply(uint256 x) public pure returns (uint256) {
return x << 1; // 相当于 x * 2
}
function divide(uint256 x) public pure returns (uint256) {
return x >> 1; // 相当于 x / 2
}
}
|
测试和部署
1. 合约测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 | const { expect } = require("chai");
describe("Token", function() {
let token;
let owner;
let addr1;
let addr2;
beforeEach(async function() {
const Token = await ethers.getContractFactory("Token");
[owner, addr1, addr2] = await ethers.getSigners();
token = await Token.deploy();
await token.deployed();
});
it("Should assign total supply to owner", async function() {
const ownerBalance = await token.balanceOf(owner.address);
expect(await token.totalSupply()).to.equal(ownerBalance);
});
it("Should transfer tokens", async function() {
await token.transfer(addr1.address, 50);
expect(await token.balanceOf(addr1.address)).to.equal(50);
});
});
|
2. 部署脚本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | async function main() {
const [deployer] = await ethers.getSigners();
console.log("Deploying contracts with account:", deployer.address);
const Token = await ethers.getContractFactory("Token");
const token = await Token.deploy();
await token.deployed();
console.log("Token deployed to:", token.address);
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
|
练习题
- 创建一个简单的ERC20代币合约:
| // 练习:实现基本的ERC20功能
contract MyToken {
// 你的代码
}
|
- 实现一个带有访问控制的众筹合约:
| // 练习:实现众筹功能
contract Crowdfunding {
// 你的代码
}
|
- 编写一个防重入的支付合约:
| // 练习:实现安全支付
contract SafePayment {
// 你的代码
}
|
- 创建一个多签名钱包合约:
| // 练习:实现多签钱包
contract MultiSigWallet {
// 你的代码
}
|
- 实现一个代币锁定合约:
| // 练习:实现代币锁定
contract TokenLock {
// 你的代码
}
|
参考资源
- Solidity官方文档
- OpenZeppelin合约
- Solidity by Example
- 以太坊智能合约最佳实践
- Hardhat文档