跳转到内容

Solidity 智能合约开发基础

Solidity 智能合约开发基础

Solidity 是以太坊智能合约的主流编程语言,语法类似 JavaScript/C++。本文介绍 Solidity 的核心概念和常用模式。

第一个合约

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract HelloWorld {
string public greeting = "Hello, World!";
function setGreeting(string memory _greeting) public {
greeting = _greeting;
}
function getGreeting() public view returns (string memory) {
return greeting;
}
}

数据类型

值类型

// 布尔值
bool public isActive = true;
// 整数(有符号和无符号)
uint256 public count = 0; // 0 到 2^256-1
int256 public balance = -100; // -2^255 到 2^255-1
uint8 public small = 255; // 0 到 255
// 地址
address public owner;
address payable public recipient; // 可接收 ETH
// 字节
bytes32 public hash;
bytes1 public singleByte;
// 枚举
enum Status { Pending, Active, Closed }
Status public currentStatus = Status.Pending;

引用类型

// 数组
uint256[] public dynamicArray;
uint256[5] public fixedArray;
// 映射(哈希表)
mapping(address => uint256) public balances;
mapping(address => mapping(address => uint256)) public allowances;
// 结构体
struct User {
address addr;
string name;
uint256 balance;
bool isActive;
}
User public user;
User[] public users;

字符串和字节

string public name = "以太坊";
bytes public data = "0x1234";
// 字符串比较(需要哈希)
function compareStrings(string memory a, string memory b)
public pure returns (bool)
{
return keccak256(bytes(a)) == keccak256(bytes(b));
}

函数

函数可见性

contract Visibility {
// public: 内部和外部都可调用
function publicFn() public { }
// private: 只有本合约可调用
function privateFn() private { }
// internal: 本合约和继承合约可调用
function internalFn() internal { }
// external: 只能从外部调用(节省 Gas)
function externalFn() external { }
}

状态可变性

contract Mutability {
uint256 public value = 10;
// pure: 不读取也不修改状态
function add(uint256 a, uint256 b) public pure returns (uint256) {
return a + b;
}
// view: 读取状态但不修改
function getValue() public view returns (uint256) {
return value;
}
// payable: 可以接收 ETH
function deposit() public payable {
// msg.value 包含发送的 ETH 数量
}
// 默认(无修饰符): 可以修改状态
function setValue(uint256 _value) public {
value = _value;
}
}

函数修饰符(Modifier)

contract Ownable {
address public owner;
constructor() {
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner, "Not the owner");
_; // 继续执行函数体
}
modifier nonZero(uint256 amount) {
require(amount > 0, "Amount must be positive");
_;
}
function transferOwnership(address newOwner) public onlyOwner {
owner = newOwner;
}
function withdraw(uint256 amount) public onlyOwner nonZero(amount) {
payable(msg.sender).transfer(amount);
}
}

构造函数

contract Token {
string public name;
string public symbol;
uint256 public totalSupply;
address public owner;
mapping(address => uint256) public balances;
constructor(string memory _name, string memory _symbol, uint256 _initialSupply) {
name = _name;
symbol = _symbol;
totalSupply = _initialSupply;
owner = msg.sender;
balances[msg.sender] = _initialSupply;
}
}

事件(Events)

事件用于在链上记录重要操作,前端可以监听这些事件。

contract EventExample {
// 定义事件(indexed 参数可以被过滤搜索)
event Transfer(
address indexed from,
address indexed to,
uint256 value
);
event Approval(
address indexed owner,
address indexed spender,
uint256 value
);
function transfer(address to, uint256 amount) public {
// ... 执行转账逻辑
// 发出事件
emit Transfer(msg.sender, to, amount);
}
}

错误处理

contract ErrorHandling {
uint256 public maxValue = 100;
// require: 检查条件,失败时退还剩余 Gas 并回滚
function checkRequire(uint256 value) public view {
require(value <= maxValue, "Value exceeds maximum");
require(msg.sender != address(0), "Invalid sender");
}
// revert: 主动回滚,可带自定义错误信息
function checkRevert(uint256 value) public view {
if (value > maxValue) {
revert("Value too large");
}
}
// 自定义错误(更节省 Gas,Solidity 0.8+)
error ValueTooLarge(uint256 provided, uint256 maximum);
function checkCustomError(uint256 value) public view {
if (value > maxValue) {
revert ValueTooLarge(value, maxValue);
}
}
// assert: 检查不变量,失败时消耗所有 Gas(用于内部逻辑错误)
function checkAssert(uint256 a, uint256 b) public pure returns (uint256) {
uint256 result = a + b;
assert(result >= a); // 防止溢出(Solidity 0.8+ 已内置溢出检查)
return result;
}
}

继承

// 基础合约
contract Animal {
string public name;
constructor(string memory _name) {
name = _name;
}
function speak() public virtual returns (string memory) {
return "...";
}
}
// 继承合约
contract Dog is Animal {
constructor(string memory _name) Animal(_name) {}
// override 重写父合约函数
function speak() public override returns (string memory) {
return "Woof!";
}
}
// 多重继承
contract Cat is Animal {
constructor() Animal("Cat") {}
function speak() public override returns (string memory) {
return "Meow!";
}
}

接口

// 定义接口
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;
uint256 private _totalSupply;
function totalSupply() external view override returns (uint256) {
return _totalSupply;
}
function balanceOf(address account) external view override returns (uint256) {
return _balances[account];
}
// ... 实现其他函数
}
// 调用任意 ERC20 代币
function getTokenBalance(address tokenAddress, address user) external view returns (uint256) {
return IERC20(tokenAddress).balanceOf(user);
}

库(Library)

// 定义库
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 MyContract {
using SafeMath for uint256;
function calculate(uint256 a, uint256 b) public pure returns (uint256) {
return a.add(b); // 等价于 SafeMath.add(a, b)
}
}

特殊变量

Solidity 提供了一些内置的全局变量:

contract GlobalVars {
function getBlockInfo() public view returns (
address sender,
uint256 value,
uint256 blockNumber,
uint256 timestamp,
address blockCoinbase,
uint256 gasLimit
) {
return (
msg.sender, // 调用者地址
msg.value, // 发送的 ETH(wei)
block.number, // 当前区块号
block.timestamp, // 当前区块时间戳(Unix 时间)
block.coinbase, // 当前区块矿工/验证者地址
block.gaslimit // 当前区块 Gas 限制
);
}
function getTxInfo() public view returns (address origin, uint256 gasPrice) {
return (
tx.origin, // 最初发起交易的 EOA 地址
tx.gasprice // 交易 Gas 价格
);
}
}

接收 ETH

contract ETHReceiver {
event Received(address sender, uint256 amount);
event Fallback(address sender, uint256 amount, bytes data);
// 接收纯 ETH 转账(无数据)
receive() external payable {
emit Received(msg.sender, msg.value);
}
// 接收带数据的调用或未匹配函数时触发
fallback() external payable {
emit Fallback(msg.sender, msg.value, msg.data);
}
// 发送 ETH 的三种方式
function sendETH(address payable recipient) external payable {
// transfer: 固定 2300 Gas,失败自动 revert(推荐用于简单转账)
recipient.transfer(msg.value);
// send: 固定 2300 Gas,返回 bool
bool success = recipient.send(msg.value);
require(success, "Send failed");
// call: 转发所有 Gas,最灵活(推荐用于合约交互)
(bool ok, ) = recipient.call{value: msg.value}("");
require(ok, "Call failed");
}
}

OpenZeppelin 合约库

OpenZeppelin 是最常用的智能合约安全库:

Terminal window
npm install @openzeppelin/contracts
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
contract MyToken is ERC20, Ownable, ReentrancyGuard {
constructor() ERC20("My Token", "MTK") {
_mint(msg.sender, 1000000 * 10**decimals());
}
function mint(address to, uint256 amount) public onlyOwner {
_mint(to, amount);
}
function deposit() external payable nonReentrant {
// 防止重入攻击
}
}

总结

Solidity 是功能丰富的智能合约语言,核心概念包括:

  • 数据类型:值类型(uint、bool、address)和引用类型(数组、映射、结构体)
  • 函数可见性:public、private、internal、external
  • 状态可变性:pure、view、payable
  • 修饰符:用于添加前置/后置检查
  • 事件:用于记录链上活动
  • 继承与接口:实现代码复用和标准化
  • 错误处理:require、revert、assert、自定义错误

建议配合 OpenZeppelin 安全合约库进行开发,避免常见安全漏洞。