Solidity 智能合约开发基础
Solidity 智能合约开发基础
Solidity 是以太坊智能合约的主流编程语言,语法类似 JavaScript/C++。本文介绍 Solidity 的核心概念和常用模式。
第一个合约
// SPDX-License-Identifier: MITpragma 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-1int256 public balance = -100; // -2^255 到 2^255-1uint8 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 是最常用的智能合约安全库:
npm install @openzeppelin/contractsimport "@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 安全合约库进行开发,避免常见安全漏洞。