04-Solidity进阶与安全
第一部分:Solidity进阶特性
1.1 继承机制
Solidity支持多重继承,包括函数重写、修饰器继承等特性。
1.1.1 基础继承
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// 基础合约
contract Owned {
address public owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor() {
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
_;
}
function transferOwnership(address newOwner) public onlyOwner {
require(newOwner != address(0), "Invalid address");
emit OwnershipTransferred(owner, newOwner);
owner = newOwner;
}
}
// 继承合约
contract MyContract is Owned {
uint256 public value;
function setValue(uint256 _value) public onlyOwner {
value = _value;
}
}
1.1.2 多重继承
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract A {
event Log(string message);
function foo() public virtual {
emit Log("A.foo");
}
function bar() public virtual {
emit Log("A.bar");
}
}
contract B is A {
function foo() public virtual override {
emit Log("B.foo");
super.foo();
}
function bar() public virtual override {
emit Log("B.bar");
super.bar();
}
}
contract C is A {
function foo() public virtual override {
emit Log("C.foo");
super.foo();
}
function bar() public virtual override {
emit Log("C.bar");
super.bar();
}
}
// 多重继承,继承顺序很重要
// C3线性化算法:从右到左,深度优先
contract D is B, C {
function foo() public override(B, C) {
super.foo(); // 调用顺序:C.foo -> B.foo -> A.foo
}
function bar() public override(B, C) {
super.bar(); // 调用顺序:C.bar -> B.bar -> A.bar
}
}
1.1.3 构造函数继承
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Base1 {
uint256 public x;
constructor(uint256 _x) {
x = _x;
}
}
contract Base2 {
uint256 public y;
constructor(uint256 _y) {
y = _y;
}
}
// 方式1:在继承列表中初始化
contract Derived1 is Base1(10), Base2(20) {
}
// 方式2:在构造函数中初始化
contract Derived2 is Base1, Base2 {
constructor(uint256 _x, uint256 _y) Base1(_x) Base2(_y) {
}
}
// 方式3:混合方式
contract Derived3 is Base1(10), Base2 {
constructor(uint256 _y) Base2(_y) {
}
}
1.2 接口(Interface)
接口定义了合约的外部可调用函数,用于合约间的标准化交互。
1.2.1 接口定义和使用
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// ERC20接口定义
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address sender, address recipient, 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 ERC20Token is IERC20 {
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 private _totalSupply;
string public name;
string public symbol;
uint8 public decimals;
constructor(string memory _name, string memory _symbol) {
name = _name;
symbol = _symbol;
decimals = 18;
}
function totalSupply() external view override returns (uint256) {
return _totalSupply;
}
function balanceOf(address account) external view override returns (uint256) {
return _balances[account];
}
function transfer(address recipient, uint256 amount) external override returns (bool) {
_transfer(msg.sender, recipient, amount);
return true;
}
function allowance(address owner, address spender) external view override returns (uint256) {
return _allowances[owner][spender];
}
function approve(address spender, uint256 amount) external override returns (bool) {
_approve(msg.sender, spender, amount);
return true;
}
function transferFrom(address sender, address recipient, uint256 amount) external override returns (bool) {
uint256 currentAllowance = _allowances[sender][msg.sender];
require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
unchecked {
_approve(sender, msg.sender, currentAllowance - amount);
}
_transfer(sender, recipient, amount);
return true;
}
function _transfer(address sender, address recipient, uint256 amount) internal {
require(sender != address(0), "ERC20: transfer from zero address");
require(recipient != address(0), "ERC20: transfer to zero address");
uint256 senderBalance = _balances[sender];
require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[sender] = senderBalance - amount;
}
_balances[recipient] += amount;
emit Transfer(sender, recipient, amount);
}
function _approve(address owner, address spender, uint256 amount) internal {
require(owner != address(0), "ERC20: approve from zero address");
require(spender != address(0), "ERC20: approve to zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
function _mint(address account, uint256 amount) internal {
require(account != address(0), "ERC20: mint to zero address");
_totalSupply += amount;
_balances[account] += amount;
emit Transfer(address(0), account, amount);
}
}
1.2.2 接口用于合约交互
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IERC20 {
function transfer(address recipient, uint256 amount) external returns (bool);
function balanceOf(address account) external view returns (uint256);
}
// 使用接口与其他合约交互
contract TokenSwap {
IERC20 public tokenA;
IERC20 public tokenB;
constructor(address _tokenA, address _tokenB) {
tokenA = IERC20(_tokenA);
tokenB = IERC20(_tokenB);
}
// 1:1兑换
function swap(uint256 amount) external {
require(tokenA.balanceOf(msg.sender) >= amount, "Insufficient tokenA");
require(tokenB.balanceOf(address(this)) >= amount, "Insufficient liquidity");
// 从用户转入tokenA
require(tokenA.transferFrom(msg.sender, address(this), amount), "TransferFrom failed");
// 转出tokenB给用户
require(tokenB.transfer(msg.sender, amount), "Transfer failed");
}
}
1.3 库(Library)
库是可重用的代码单元,可以部署一次,被多个合约使用。
1.3.1 SafeMath库
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// 注意:Solidity 0.8.x 默认检查溢出,但了解SafeMath仍然重要
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;
}
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: division by zero");
return a / b;
}
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: modulo by zero");
return a % b;
}
}
// 使用库
contract MyToken {
using SafeMath for uint256;
mapping(address => uint256) public balances;
function transfer(address to, uint256 amount) external {
balances[msg.sender] = balances[msg.sender].sub(amount);
balances[to] = balances[to].add(amount);
}
}
1.3.2 Address库
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
library Address {
// 检查是否为合约地址
function isContract(address account) internal view returns (bool) {
uint256 size;
assembly {
size := extcodesize(account)
}
return size > 0;
}
// 安全发送ETH
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value");
}
// 函数调用
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call(data);
return verifyCallResult(success, returndata, errorMessage);
}
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
// 使用Address库
contract PaymentContract {
using Address for address;
using Address for address payable;
function pay(address payable recipient, uint256 amount) external {
recipient.sendValue(amount);
}
function checkContract(address addr) external view returns (bool) {
return addr.isContract();
}
}
1.3.3 自定义库示例
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// 数组操作库
library ArrayUtils {
function remove(uint256[] storage arr, uint256 index) internal {
require(index < arr.length, "Index out of bounds");
arr[index] = arr[arr.length - 1];
arr.pop();
}
function contains(uint256[] storage arr, uint256 value) internal view returns (bool) {
for (uint256 i = 0; i < arr.length; i++) {
if (arr[i] == value) {
return true;
}
}
return false;
}
function sum(uint256[] memory arr) internal pure returns (uint256) {
uint256 total = 0;
for (uint256 i = 0; i < arr.length; i++) {
total += arr[i];
}
return total;
}
}
// 字符串操作库
library StringUtils {
function length(string memory str) internal pure returns (uint256) {
return bytes(str).length;
}
function concat(string memory a, string memory b) internal pure returns (string memory) {
return string(abi.encodePacked(a, b));
}
function equal(string memory a, string memory b) internal pure returns (bool) {
return keccak256(bytes(a)) == keccak256(bytes(b));
}
}
// 使用自定义库
contract DataManager {
using ArrayUtils for uint256[];
using StringUtils for string;
uint256[] public numbers;
function addNumber(uint256 num) external {
numbers.push(num);
}
function removeNumber(uint256 index) external {
numbers.remove(index);
}
function hasNumber(uint256 num) external view returns (bool) {
return numbers.contains(num);
}
function totalSum() external view returns (uint256) {
return ArrayUtils.sum(numbers);
}
}
1.4 抽象合约
抽象合约包含至少一个未实现的函数。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// 抽象合约
abstract contract Animal {
string public name;
constructor(string memory _name) {
name = _name;
}
// 抽象函数,没有实现
function makeSound() public virtual returns (string memory);
// 已实现的函数
function sleep() public pure returns (string memory) {
return "Zzz...";
}
}
// 实现抽象合约
contract Dog is Animal {
constructor(string memory _name) Animal(_name) {}
function makeSound() public pure override returns (string memory) {
return "Woof!";
}
}
contract Cat is Animal {
constructor(string memory _name) Animal(_name) {}
function makeSound() public pure override returns (string memory) {
return "Meow!";
}
}
1.5 代理模式(Proxy Pattern)
代理模式是实现合约可升级性的关键。
1.5.1 透明代理模式
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// 存储合约(代理)
contract TransparentProxy {
// 实现合约地址
address public implementation;
// 管理员地址
address public admin;
constructor(address _implementation) {
admin = msg.sender;
implementation = _implementation;
}
modifier onlyAdmin() {
require(msg.sender == admin, "Not admin");
_;
}
// 升级实现
function upgradeTo(address newImplementation) external onlyAdmin {
implementation = newImplementation;
}
// 转移管理员
function changeAdmin(address newAdmin) external onlyAdmin {
admin = newAdmin;
}
// 回退函数,委托调用到实现合约
fallback() external payable {
address impl = implementation;
assembly {
// 复制calldata
calldatacopy(0, 0, calldatasize())
// 委托调用
let result := delegatecall(gas(), impl, 0, calldatasize(), 0, 0)
// 复制返回数据
returndatacopy(0, 0, returndatasize())
// 根据调用结果返回或revert
switch result
case 0 { revert(0, returndatasize()) }
default { return(0, returndatasize()) }
}
}
receive() external payable {}
}
// 逻辑合约 V1
contract LogicV1 {
// 注意:存储布局必须与代理保持一致
address public implementation;
address public admin;
uint256 public value;
function setValue(uint256 _value) external {
value = _value;
}
function getValue() external view returns (uint256) {
return value;
}
}
// 逻辑合约 V2(升级版本)
contract LogicV2 {
address public implementation;
address public admin;
uint256 public value;
function setValue(uint256 _value) external {
value = _value;
}
function getValue() external view returns (uint256) {
return value;
}
// 新增功能
function increment() external {
value += 1;
}
function decrement() external {
require(value > 0, "Value is zero");
value -= 1;
}
}
1.5.2 UUPS代理模式
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// UUPS代理
contract UUPSProxy {
// 实现合约地址存储在特定slot,避免存储冲突
// keccak256("eip1967.proxy.implementation") - 1
bytes32 private constant IMPLEMENTATION_SLOT =
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
constructor(address _implementation) {
_setImplementation(_implementation);
}
function _implementation() internal view returns (address impl) {
bytes32 slot = IMPLEMENTATION_SLOT;
assembly {
impl := sload(slot)
}
}
function _setImplementation(address newImplementation) private {
bytes32 slot = IMPLEMENTATION_SLOT;
assembly {
sstore(slot, newImplementation)
}
}
fallback() external payable {
address impl = _implementation();
assembly {
calldatacopy(0, 0, calldatasize())
let result := delegatecall(gas(), impl, 0, calldatasize(), 0, 0)
returndatacopy(0, 0, returndatasize())
switch result
case 0 { revert(0, returndatasize()) }
default { return(0, returndatasize()) }
}
}
receive() external payable {}
}
// UUPS逻辑合约基类
abstract contract UUPSUpgradeable {
bytes32 private constant IMPLEMENTATION_SLOT =
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
event Upgraded(address indexed newImplementation);
function upgradeTo(address newImplementation) external virtual {
_authorizeUpgrade(newImplementation);
_upgradeToAndCall(newImplementation, bytes(""));
}
function upgradeToAndCall(address newImplementation, bytes memory data) external payable virtual {
_authorizeUpgrade(newImplementation);
_upgradeToAndCall(newImplementation, data);
}
function _upgradeToAndCall(address newImplementation, bytes memory data) internal {
bytes32 slot = IMPLEMENTATION_SLOT;
assembly {
sstore(slot, newImplementation)
}
emit Upgraded(newImplementation);
if (data.length > 0) {
(bool success, ) = newImplementation.delegatecall(data);
require(success, "Upgrade call failed");
}
}
// 子合约必须实现此函数来控制升级权限
function _authorizeUpgrade(address newImplementation) internal virtual;
}
// UUPS实现合约
contract MyUUPSContract is UUPSUpgradeable {
address public owner;
uint256 public value;
function initialize(address _owner) external {
require(owner == address(0), "Already initialized");
owner = _owner;
}
function setValue(uint256 _value) external {
require(msg.sender == owner, "Not owner");
value = _value;
}
function _authorizeUpgrade(address) internal view override {
require(msg.sender == owner, "Not owner");
}
}
1.5.3 信标代理模式
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// 信标合约
contract Beacon {
address public implementation;
address public admin;
event Upgraded(address indexed implementation);
constructor(address _implementation) {
admin = msg.sender;
implementation = _implementation;
}
modifier onlyAdmin() {
require(msg.sender == admin, "Not admin");
_;
}
function upgradeTo(address newImplementation) external onlyAdmin {
implementation = newImplementation;
emit Upgraded(newImplementation);
}
}
// 信标代理
contract BeaconProxy {
address public beacon;
constructor(address _beacon) {
beacon = _beacon;
}
function _implementation() internal view returns (address) {
return Beacon(beacon).implementation();
}
fallback() external payable {
address impl = _implementation();
assembly {
calldatacopy(0, 0, calldatasize())
let result := delegatecall(gas(), impl, 0, calldatasize(), 0, 0)
returndatacopy(0, 0, returndatasize())
switch result
case 0 { revert(0, returndatasize()) }
default { return(0, returndatasize()) }
}
}
receive() external payable {}
}
1.6 可升级合约
1.6.1 存储布局注意事项
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// 错误示例:V2改变了存储布局
contract BadLogicV1 {
uint256 public a;
uint256 public b;
}
contract BadLogicV2 {
uint256 public b; // 错误:改变了顺序
uint256 public a;
uint256 public c;
}
// 正确示例:V2保持存储布局兼容
contract GoodLogicV1 {
uint256 public a;
uint256 public b;
}
contract GoodLogicV2 {
uint256 public a; // 保持原有变量位置
uint256 public b;
uint256 public c; // 只能在后面添加新变量
}
1.6.2 完整的可升级合约示例
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// 初始化接口
interface IInitializable {
function initialize(address owner) external;
}
// 可升级代币合约 V1
contract UpgradeableTokenV1 is IInitializable {
// 存储变量
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 private _totalSupply;
string public name;
string public symbol;
uint8 public decimals;
address public owner;
bool private _initialized;
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
_;
}
// 初始化函数(替代构造函数)
function initialize(address _owner) external override {
require(!_initialized, "Already initialized");
name = "Upgradeable Token";
symbol = "UPG";
decimals = 18;
owner = _owner;
_initialized = true;
}
function totalSupply() external view returns (uint256) {
return _totalSupply;
}
function balanceOf(address account) external view returns (uint256) {
return _balances[account];
}
function transfer(address recipient, uint256 amount) external returns (bool) {
_transfer(msg.sender, recipient, amount);
return true;
}
function allowance(address tokenOwner, address spender) external view returns (uint256) {
return _allowances[tokenOwner][spender];
}
function approve(address spender, uint256 amount) external returns (bool) {
_approve(msg.sender, spender, amount);
return true;
}
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool) {
uint256 currentAllowance = _allowances[sender][msg.sender];
require(currentAllowance >= amount, "Transfer amount exceeds allowance");
unchecked {
_approve(sender, msg.sender, currentAllowance - amount);
}
_transfer(sender, recipient, amount);
return true;
}
function mint(address account, uint256 amount) external onlyOwner {
_mint(account, amount);
}
function _transfer(address sender, address recipient, uint256 amount) internal {
require(sender != address(0), "Transfer from zero address");
require(recipient != address(0), "Transfer to zero address");
uint256 senderBalance = _balances[sender];
require(senderBalance >= amount, "Transfer amount exceeds balance");
unchecked {
_balances[sender] = senderBalance - amount;
}
_balances[recipient] += amount;
emit Transfer(sender, recipient, amount);
}
function _approve(address tokenOwner, address spender, uint256 amount) internal {
require(tokenOwner != address(0), "Approve from zero address");
require(spender != address(0), "Approve to zero address");
_allowances[tokenOwner][spender] = amount;
emit Approval(tokenOwner, spender, amount);
}
function _mint(address account, uint256 amount) internal {
require(account != address(0), "Mint to zero address");
_totalSupply += amount;
_balances[account] += amount;
emit Transfer(address(0), account, amount);
}
}
// 可升级代币合约 V2(添加销毁和暂停功能)
contract UpgradeableTokenV2 is IInitializable {
// 保持V1的存储布局
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 private _totalSupply;
string public name;
string public symbol;
uint8 public decimals;
address public owner;
bool private _initialized;
// V2新增存储变量
bool public paused;
mapping(address => bool) public blacklist;
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
event Paused(address account);
event Unpaused(address account);
event Blacklisted(address indexed account);
event Unblacklisted(address indexed account);
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
_;
}
modifier whenNotPaused() {
require(!paused, "Paused");
_;
}
modifier notBlacklisted(address account) {
require(!blacklist[account], "Blacklisted");
_;
}
function initialize(address _owner) external override {
require(!_initialized, "Already initialized");
name = "Upgradeable Token";
symbol = "UPG";
decimals = 18;
owner = _owner;
_initialized = true;
}
// V1的所有函数...(省略,与V1相同)
function totalSupply() external view returns (uint256) {
return _totalSupply;
}
function balanceOf(address account) external view returns (uint256) {
return _balances[account];
}
function transfer(address recipient, uint256 amount)
external
whenNotPaused
notBlacklisted(msg.sender)
notBlacklisted(recipient)
returns (bool)
{
_transfer(msg.sender, recipient, amount);
return true;
}
// V2新增功能
function pause() external onlyOwner {
paused = true;
emit Paused(msg.sender);
}
function unpause() external onlyOwner {
paused = false;
emit Unpaused(msg.sender);
}
function addToBlacklist(address account) external onlyOwner {
blacklist[account] = true;
emit Blacklisted(account);
}
function removeFromBlacklist(address account) external onlyOwner {
blacklist[account] = false;
emit Unblacklisted(account);
}
function burn(uint256 amount) external whenNotPaused {
_burn(msg.sender, amount);
}
function _burn(address account, uint256 amount) internal {
require(account != address(0), "Burn from zero address");
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "Burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
}
_totalSupply -= amount;
emit Transfer(account, address(0), amount);
}
function _transfer(address sender, address recipient, uint256 amount) internal {
require(sender != address(0), "Transfer from zero address");
require(recipient != address(0), "Transfer to zero address");
uint256 senderBalance = _balances[sender];
require(senderBalance >= amount, "Transfer amount exceeds balance");
unchecked {
_balances[sender] = senderBalance - amount;
}
_balances[recipient] += amount;
emit Transfer(sender, recipient, amount);
}
}
第二部分:智能合约安全
2.1 重入攻击(Reentrancy Attack)
重入攻击是最著名的智能合约漏洞之一,曾导致The DAO被黑客攻击。
2.1.1 漏洞示例
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// 存在重入漏洞的合约
contract VulnerableBank {
mapping(address => uint256) public balances;
function deposit() external payable {
balances[msg.sender] += msg.value;
}
// 漏洞:先转账,后更新状态
function withdraw(uint256 amount) external {
require(balances[msg.sender] >= amount, "Insufficient balance");
// 危险:在更新状态前转账
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Transfer failed");
balances[msg.sender] -= amount;
}
function getBalance() external view returns (uint256) {
return address(this).balance;
}
}
// 攻击合约
contract ReentrancyAttacker {
VulnerableBank public bank;
uint256 public attackAmount = 1 ether;
constructor(address _bankAddress) {
bank = VulnerableBank(_bankAddress);
}
function attack() external payable {
require(msg.value >= attackAmount, "Need 1 ether to attack");
bank.deposit{value: attackAmount}();
bank.withdraw(attackAmount);
}
// 接收ETH时重入
receive() external payable {
if (address(bank).balance >= attackAmount) {
bank.withdraw(attackAmount);
}
}
function getBalance() external view returns (uint256) {
return address(this).balance;
}
}
2.1.2 防御方案
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// 方案1:检查-生效-交互模式(CEI Pattern)
contract SafeBankCEI {
mapping(address => uint256) public balances;
function deposit() external payable {
balances[msg.sender] += msg.value;
}
function withdraw(uint256 amount) external {
// 检查
require(balances[msg.sender] >= amount, "Insufficient balance");
// 生效(先更新状态)
balances[msg.sender] -= amount;
// 交互(最后转账)
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Transfer failed");
}
}
// 方案2:重入锁
contract SafeBankWithLock {
mapping(address => uint256) public balances;
bool private locked;
modifier noReentrant() {
require(!locked, "No reentrancy");
locked = true;
_;
locked = false;
}
function deposit() external payable {
balances[msg.sender] += msg.value;
}
function withdraw(uint256 amount) external noReentrant {
require(balances[msg.sender] >= amount, "Insufficient balance");
balances[msg.sender] -= amount;
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Transfer failed");
}
}
// 方案3:使用OpenZeppelin的ReentrancyGuard
abstract contract ReentrancyGuard {
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
modifier nonReentrant() {
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
_status = _ENTERED;
_;
_status = _NOT_ENTERED;
}
}
contract SafeBankWithGuard is ReentrancyGuard {
mapping(address => uint256) public balances;
function deposit() external payable {
balances[msg.sender] += msg.value;
}
function withdraw(uint256 amount) external nonReentrant {
require(balances[msg.sender] >= amount, "Insufficient balance");
balances[msg.sender] -= amount;
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Transfer failed");
}
}
2.2 整数溢出(Integer Overflow/Underflow)
在Solidity 0.8.0之前,整数溢出是常见漏洞。
2.2.1 漏洞示例(0.7.x)
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
// 在0.7.x版本中存在溢出风险
contract VulnerableToken {
mapping(address => uint256) public balances;
function transfer(address to, uint256 amount) external {
// 下溢风险
balances[msg.sender] -= amount;
// 上溢风险
balances[to] += amount;
}
}
2.2.2 防御方案
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// 方案1:使用0.8.x版本(自动检查溢出)
contract SafeToken {
mapping(address => uint256) public balances;
function transfer(address to, uint256 amount) external {
balances[msg.sender] -= amount; // 自动检查下溢
balances[to] += amount; // 自动检查上溢
}
// 如需要不检查溢出的行为,使用unchecked
function unsafeTransfer(address to, uint256 amount) external {
unchecked {
balances[msg.sender] -= amount;
balances[to] += amount;
}
}
}
// 方案2:显式检查(适用于所有版本)
contract ExplicitCheckToken {
mapping(address => uint256) public balances;
function transfer(address to, uint256 amount) external {
require(balances[msg.sender] >= amount, "Insufficient balance");
require(balances[to] + amount >= balances[to], "Overflow");
balances[msg.sender] -= amount;
balances[to] += amount;
}
}
2.3 权限控制漏洞
2.3.1 漏洞示例
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// 缺少权限控制
contract VulnerableContract {
address public owner;
uint256 public value;
constructor() {
owner = msg.sender;
}
// 危险:任何人都可以调用
function setValue(uint256 _value) external {
value = _value;
}
// 危险:任何人都可以转移所有权
function transferOwnership(address newOwner) external {
owner = newOwner;
}
}
2.3.2 防御方案
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// 完善的权限控制
contract SecureContract {
address public owner;
mapping(address => bool) public admins;
uint256 public value;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
event AdminAdded(address indexed admin);
event AdminRemoved(address indexed admin);
constructor() {
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
_;
}
modifier onlyAdmin() {
require(admins[msg.sender] || msg.sender == owner, "Not admin");
_;
}
function setValue(uint256 _value) external onlyAdmin {
value = _value;
}
function transferOwnership(address newOwner) external onlyOwner {
require(newOwner != address(0), "Invalid address");
emit OwnershipTransferred(owner, newOwner);
owner = newOwner;
}
function addAdmin(address admin) external onlyOwner {
require(admin != address(0), "Invalid address");
admins[admin] = true;
emit AdminAdded(admin);
}
function removeAdmin(address admin) external onlyOwner {
admins[admin] = false;
emit AdminRemoved(admin);
}
}
// 基于角色的访问控制(RBAC)
contract RBACContract {
mapping(bytes32 => mapping(address => bool)) private _roles;
bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");
event RoleGranted(bytes32 indexed role, address indexed account);
event RoleRevoked(bytes32 indexed role, address indexed account);
constructor() {
_grantRole(ADMIN_ROLE, msg.sender);
}
modifier onlyRole(bytes32 role) {
require(hasRole(role, msg.sender), "Unauthorized");
_;
}
function hasRole(bytes32 role, address account) public view returns (bool) {
return _roles[role][account];
}
function grantRole(bytes32 role, address account) external onlyRole(ADMIN_ROLE) {
_grantRole(role, account);
}
function revokeRole(bytes32 role, address account) external onlyRole(ADMIN_ROLE) {
_revokeRole(role, account);
}
function _grantRole(bytes32 role, address account) internal {
if (!_roles[role][account]) {
_roles[role][account] = true;
emit RoleGranted(role, account);
}
}
function _revokeRole(bytes32 role, address account) internal {
if (_roles[role][account]) {
_roles[role][account] = false;
emit RoleRevoked(role, account);
}
}
}
2.4 前端运行(Front-running)
前端运行是指攻击者观察待处理交易,并通过支付更高gas费用使自己的交易先执行。
2.4.1 漏洞场景
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// 容易被抢先交易的拍卖合约
contract VulnerableAuction {
address public highestBidder;
uint256 public highestBid;
event NewBid(address bidder, uint256 amount);
function bid() external payable {
require(msg.value > highestBid, "Bid too low");
// 退还前一个出价者
if (highestBidder != address(0)) {
payable(highestBidder).transfer(highestBid);
}
highestBidder = msg.sender;
highestBid = msg.value;
emit NewBid(msg.sender, msg.value);
}
}
2.4.2 防御方案
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// 使用承诺-揭示方案
contract SecureAuction {
struct Bid {
bytes32 commitment;
uint256 deposit;
bool revealed;
}
mapping(address => Bid) public bids;
address public highestBidder;
uint256 public highestBid;
uint256 public biddingEnd;
uint256 public revealEnd;
event BidCommitted(address bidder);
event BidRevealed(address bidder, uint256 amount);
constructor(uint256 biddingDuration, uint256 revealDuration) {
biddingEnd = block.timestamp + biddingDuration;
revealEnd = biddingEnd + revealDuration;
}
// 阶段1:提交承诺
function commitBid(bytes32 commitment) external payable {
require(block.timestamp < biddingEnd, "Bidding ended");
require(bids[msg.sender].commitment == bytes32(0), "Already committed");
bids[msg.sender] = Bid({
commitment: commitment,
deposit: msg.value,
revealed: false
});
emit BidCommitted(msg.sender);
}
// 阶段2:揭示承诺
function revealBid(uint256 amount, string memory secret) external {
require(block.timestamp >= biddingEnd, "Bidding not ended");
require(block.timestamp < revealEnd, "Reveal period ended");
Bid storage bid = bids[msg.sender];
require(!bid.revealed, "Already revealed");
require(bid.commitment != bytes32(0), "No commitment");
// 验证承诺
bytes32 hash = keccak256(abi.encodePacked(amount, secret));
require(hash == bid.commitment, "Invalid reveal");
require(bid.deposit >= amount, "Insufficient deposit");
bid.revealed = true;
if (amount > highestBid) {
// 退还前一个最高出价者
if (highestBidder != address(0)) {
payable(highestBidder).transfer(highestBid);
}
highestBidder = msg.sender;
highestBid = amount;
}
// 退还多余的押金
uint256 refund = bid.deposit - amount;
if (refund > 0) {
payable(msg.sender).transfer(refund);
}
emit BidRevealed(msg.sender, amount);
}
}
// 使用时间锁
contract TimeLockAuction {
struct Bid {
uint256 amount;
uint256 timestamp;
}
mapping(address => Bid) public bids;
address public highestBidder;
uint256 public highestBid;
uint256 public constant MIN_DELAY = 10 minutes;
function bid() external payable {
require(msg.value > highestBid, "Bid too low");
bids[msg.sender] = Bid({
amount: msg.value,
timestamp: block.timestamp
});
}
function executeBid() external {
Bid storage userBid = bids[msg.sender];
require(userBid.amount > 0, "No bid");
require(block.timestamp >= userBid.timestamp + MIN_DELAY, "Too early");
require(userBid.amount > highestBid, "Bid too low");
if (highestBidder != address(0)) {
payable(highestBidder).transfer(highestBid);
}
highestBidder = msg.sender;
highestBid = userBid.amount;
delete bids[msg.sender];
}
}
2.5 拒绝服务(DoS)
2.5.1 漏洞示例
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// DoS漏洞:依赖外部调用成功
contract VulnerableAuction {
address public highestBidder;
uint256 public highestBid;
function bid() external payable {
require(msg.value > highestBid, "Bid too low");
// 危险:如果transfer失败,整个函数revert
if (highestBidder != address(0)) {
payable(highestBidder).transfer(highestBid);
}
highestBidder = msg.sender;
highestBid = msg.value;
}
}
// 攻击合约
contract DoSAttacker {
VulnerableAuction public auction;
constructor(address _auction) {
auction = VulnerableAuction(_auction);
}
function attack() external payable {
auction.bid{value: msg.value}();
}
// 拒绝接收ETH,导致其他人无法出价
receive() external payable {
revert("I don't accept refunds!");
}
}
2.5.2 防御方案
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// 使用拉取模式(Pull Pattern)
contract SecureAuction {
address public highestBidder;
uint256 public highestBid;
mapping(address => uint256) public pendingReturns;
event BidPlaced(address bidder, uint256 amount);
function bid() external payable {
require(msg.value > highestBid, "Bid too low");
// 记录待退款,不直接转账
if (highestBidder != address(0)) {
pendingReturns[highestBidder] += highestBid;
}
highestBidder = msg.sender;
highestBid = msg.value;
emit BidPlaced(msg.sender, msg.value);
}
// 用户自己提取退款
function withdraw() external {
uint256 amount = pendingReturns[msg.sender];
require(amount > 0, "No funds to withdraw");
pendingReturns[msg.sender] = 0;
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Transfer failed");
}
}
// Gas限制保护
contract GasLimitProtection {
address[] public users;
mapping(address => uint256) public balances;
// 危险:循环可能消耗过多gas
function distributeRewardsBad() external {
for (uint256 i = 0; i < users.length; i++) {
balances[users[i]] += 1 ether;
}
}
// 安全:分批处理
function distributeRewardsSafe(uint256 startIndex, uint256 count) external {
uint256 endIndex = startIndex + count;
require(endIndex <= users.length, "Invalid range");
for (uint256 i = startIndex; i < endIndex; i++) {
balances[users[i]] += 1 ether;
}
}
}
2.6 delegatecall漏洞
2.6.1 漏洞示例
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// 库合约
contract Library {
uint256 public someValue;
function setValue(uint256 _value) public {
someValue = _value;
}
}
// 存在漏洞的合约
contract VulnerableContract {
address public library;
address public owner; // slot 1
uint256 public value; // slot 2
constructor(address _library) {
library = _library;
owner = msg.sender;
}
// 危险:任何人可以delegatecall任意函数
fallback() external {
address lib = library;
assembly {
calldatacopy(0, 0, calldatasize())
let result := delegatecall(gas(), lib, 0, calldatasize(), 0, 0)
returndatacopy(0, 0, returndatasize())
switch result
case 0 { revert(0, returndatasize()) }
default { return(0, returndatasize()) }
}
}
}
// 攻击合约
contract DelegatecallAttacker {
address public library; // slot 0
address public owner; // slot 1
function attack() public {
// 修改slot 1,覆盖owner
owner = msg.sender;
}
}
2.6.2 防御方案
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// 安全的delegatecall使用
contract SecureProxy {
address public implementation;
address public admin;
mapping(bytes4 => bool) public allowedSelectors;
event Upgraded(address indexed newImplementation);
constructor(address _implementation) {
admin = msg.sender;
implementation = _implementation;
}
modifier onlyAdmin() {
require(msg.sender == admin, "Not admin");
_;
}
// 只允许admin升级
function upgradeTo(address newImplementation) external onlyAdmin {
implementation = newImplementation;
emit Upgraded(newImplementation);
}
// 白名单机制
function addAllowedSelector(bytes4 selector) external onlyAdmin {
allowedSelectors[selector] = true;
}
function removeAllowedSelector(bytes4 selector) external onlyAdmin {
allowedSelectors[selector] = false;
}
fallback() external payable {
// 检查函数选择器
bytes4 selector = bytes4(msg.data);
require(allowedSelectors[selector], "Function not allowed");
address impl = implementation;
assembly {
calldatacopy(0, 0, calldatasize())
let result := delegatecall(gas(), impl, 0, calldatasize(), 0, 0)
returndatacopy(0, 0, returndatasize())
switch result
case 0 { revert(0, returndatasize()) }
default { return(0, returndatasize()) }
}
}
receive() external payable {}
}
// 存储隔离
contract StorageIsolation {
// 使用特定的storage slot避免冲突
bytes32 private constant IMPLEMENTATION_SLOT =
keccak256("eip1967.proxy.implementation");
bytes32 private constant ADMIN_SLOT =
keccak256("eip1967.proxy.admin");
function _setImplementation(address newImplementation) internal {
bytes32 slot = IMPLEMENTATION_SLOT;
assembly {
sstore(slot, newImplementation)
}
}
function _getImplementation() internal view returns (address impl) {
bytes32 slot = IMPLEMENTATION_SLOT;
assembly {
impl := sload(slot)
}
}
}
2.7 随机数漏洞
2.7.1 漏洞示例
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// 不安全的随机数生成
contract VulnerableLottery {
address public winner;
function draw() external {
// 危险:可预测的随机数
uint256 random = uint256(keccak256(abi.encodePacked(
block.timestamp,
block.difficulty,
msg.sender
))) % 100;
if (random > 50) {
winner = msg.sender;
}
}
}
2.7.2 防御方案
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// 使用Chainlink VRF获取可验证的随机数
interface VRFCoordinatorV2Interface {
function requestRandomWords(
bytes32 keyHash,
uint64 subId,
uint16 minimumRequestConfirmations,
uint32 callbackGasLimit,
uint32 numWords
) external returns (uint256 requestId);
}
contract SecureLottery {
VRFCoordinatorV2Interface private vrfCoordinator;
bytes32 private keyHash;
uint64 private subscriptionId;
uint32 private callbackGasLimit = 100000;
uint16 private requestConfirmations = 3;
mapping(uint256 => address) public requestToSender;
address public winner;
event RandomnessRequested(uint256 requestId);
event WinnerSelected(address winner);
constructor(
address _vrfCoordinator,
bytes32 _keyHash,
uint64 _subscriptionId
) {
vrfCoordinator = VRFCoordinatorV2Interface(_vrfCoordinator);
keyHash = _keyHash;
subscriptionId = _subscriptionId;
}
function enter() external {
uint256 requestId = vrfCoordinator.requestRandomWords(
keyHash,
subscriptionId,
requestConfirmations,
callbackGasLimit,
1
);
requestToSender[requestId] = msg.sender;
emit RandomnessRequested(requestId);
}
function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal {
uint256 randomNumber = randomWords[0] % 100;
if (randomNumber > 50) {
winner = requestToSender[requestId];
emit WinnerSelected(winner);
}
}
}
// 承诺-揭示方案
contract CommitRevealLottery {
struct Commitment {
bytes32 commitment;
bool revealed;
}
mapping(address => Commitment) public commitments;
address[] public participants;
uint256 public revealDeadline;
address public winner;
uint256 private seed;
event CommitmentMade(address participant);
event NumberRevealed(address participant, uint256 number);
event WinnerSelected(address winner);
constructor(uint256 revealDuration) {
revealDeadline = block.timestamp + revealDuration;
}
// 阶段1:提交承诺
function commit(bytes32 commitment) external {
require(block.timestamp < revealDeadline, "Commit period ended");
require(commitments[msg.sender].commitment == bytes32(0), "Already committed");
commitments[msg.sender] = Commitment({
commitment: commitment,
revealed: false
});
participants.push(msg.sender);
emit CommitmentMade(msg.sender);
}
// 阶段2:揭示数字
function reveal(uint256 number, string memory secret) external {
require(block.timestamp >= revealDeadline, "Reveal period not started");
Commitment storage commitment = commitments[msg.sender];
require(!commitment.revealed, "Already revealed");
bytes32 hash = keccak256(abi.encodePacked(number, secret, msg.sender));
require(hash == commitment.commitment, "Invalid reveal");
commitment.revealed = true;
seed ^= number; // XOR所有数字
emit NumberRevealed(msg.sender, number);
}
// 阶段3:选择赢家
function selectWinner() external {
require(winner == address(0), "Winner already selected");
uint256 winnerIndex = seed % participants.length;
winner = participants[winnerIndex];
emit WinnerSelected(winner);
}
}
2.8 时间戳依赖
2.8.1 漏洞示例
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// 依赖时间戳的漏洞
contract VulnerableTimelock {
uint256 public unlockTime;
constructor() {
unlockTime = block.timestamp + 1 days;
}
function claim() external {
// 矿工可以轻微操纵timestamp(约15秒)
require(block.timestamp >= unlockTime, "Still locked");
// 执行操作...
}
}
2.8.2 防御方案
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// 使用区块号而不是时间戳
contract SecureTimelock {
uint256 public unlockBlock;
uint256 private constant BLOCKS_PER_DAY = 6500; // 以太坊约13秒一个块
constructor() {
unlockBlock = block.number + BLOCKS_PER_DAY;
}
function claim() external {
require(block.number >= unlockBlock, "Still locked");
// 执行操作...
}
}
// 如果必须使用时间戳,增加安全边界
contract SafeTimestamp {
uint256 public unlockTime;
uint256 private constant SAFETY_MARGIN = 15 minutes;
constructor(uint256 lockDuration) {
// 添加安全边界
unlockTime = block.timestamp + lockDuration + SAFETY_MARGIN;
}
function claim() external {
require(block.timestamp >= unlockTime, "Still locked");
// 执行操作...
}
}
2.9 tx.origin认证漏洞
2.9.1 漏洞示例
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// 使用tx.origin的漏洞合约
contract VulnerableWallet {
address public owner;
constructor() {
owner = msg.sender;
}
// 危险:使用tx.origin进行认证
function transfer(address payable to, uint256 amount) external {
require(tx.origin == owner, "Not owner");
to.transfer(amount);
}
receive() external payable {}
}
// 攻击合约
contract TxOriginAttacker {
address payable public attacker;
VulnerableWallet public wallet;
constructor(address payable _wallet) {
attacker = payable(msg.sender);
wallet = VulnerableWallet(_wallet);
}
function attack() external {
// 如果钱包owner调用此函数,资金将被转走
// 因为tx.origin是owner
wallet.transfer(attacker, address(wallet).balance);
}
}
2.9.2 防御方案
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// 使用msg.sender而不是tx.origin
contract SecureWallet {
address public owner;
constructor() {
owner = msg.sender;
}
// 安全:使用msg.sender
function transfer(address payable to, uint256 amount) external {
require(msg.sender == owner, "Not owner");
to.transfer(amount);
}
receive() external payable {}
}
2.10 未检查的外部调用
2.10.1 漏洞示例
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// 未检查返回值
contract VulnerableContract {
function sendEther(address payable recipient, uint256 amount) external {
// 危险:未检查返回值
recipient.send(amount);
}
function callExternal(address target, bytes memory data) external {
// 危险:未检查返回值
target.call(data);
}
}
2.10.2 防御方案
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// 检查返回值
contract SecureContract {
function sendEther(address payable recipient, uint256 amount) external {
bool success = recipient.send(amount);
require(success, "Send failed");
}
// 更好:使用transfer(自动revert)
function sendEtherSafe(address payable recipient, uint256 amount) external {
recipient.transfer(amount);
}
// 更灵活:使用call
function sendEtherFlexible(address payable recipient, uint256 amount) external {
(bool success, ) = recipient.call{value: amount}("");
require(success, "Transfer failed");
}
function callExternal(address target, bytes memory data) external returns (bytes memory) {
(bool success, bytes memory returnData) = target.call(data);
require(success, "Call failed");
return returnData;
}
}
2.11 自毁合约漏洞
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// 强制发送ETH的攻击
contract ForceFeeder {
constructor(address payable target) payable {
selfdestruct(target);
}
}
// 存在漏洞的合约
contract VulnerableContract {
uint256 public balance;
function deposit() external payable {
balance += msg.value;
}
// 危险:假设balance等于address(this).balance
function withdraw() external {
require(address(this).balance == balance, "Balance mismatch");
// ...
}
}
// 安全的合约
contract SecureContract {
uint256 public deposits;
function deposit() external payable {
deposits += msg.value;
}
function withdraw() external {
// 不依赖address(this).balance
require(deposits > 0, "No deposits");
uint256 amount = deposits;
deposits = 0;
payable(msg.sender).transfer(amount);
}
}
第三部分:完整的安全合约示例
3.1 安全的ERC20代币合约
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @title SecureToken
* @dev 包含多重安全机制的ERC20代币合约
*/
contract SecureToken {
// 状态变量
string public name;
string public symbol;
uint8 public decimals;
uint256 private _totalSupply;
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
// 安全特性
address public owner;
bool public paused;
mapping(address => bool) public blacklist;
// 重入保护
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
// 事件
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
event Paused(address account);
event Unpaused(address account);
event Blacklisted(address indexed account);
event RemovedFromBlacklist(address indexed account);
// 修饰器
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
_;
}
modifier whenNotPaused() {
require(!paused, "Paused");
_;
}
modifier notBlacklisted(address account) {
require(!blacklist[account], "Blacklisted");
_;
}
modifier nonReentrant() {
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
_status = _ENTERED;
_;
_status = _NOT_ENTERED;
}
constructor(
string memory _name,
string memory _symbol,
uint256 initialSupply
) {
name = _name;
symbol = _symbol;
decimals = 18;
owner = msg.sender;
_status = _NOT_ENTERED;
_mint(msg.sender, initialSupply);
}
// ERC20 标准函数
function totalSupply() external view returns (uint256) {
return _totalSupply;
}
function balanceOf(address account) external view returns (uint256) {
return _balances[account];
}
function transfer(address recipient, uint256 amount)
external
whenNotPaused
notBlacklisted(msg.sender)
notBlacklisted(recipient)
nonReentrant
returns (bool)
{
_transfer(msg.sender, recipient, amount);
return true;
}
function allowance(address tokenOwner, address spender) external view returns (uint256) {
return _allowances[tokenOwner][spender];
}
function approve(address spender, uint256 amount)
external
whenNotPaused
notBlacklisted(msg.sender)
notBlacklisted(spender)
returns (bool)
{
_approve(msg.sender, spender, amount);
return true;
}
function transferFrom(address sender, address recipient, uint256 amount)
external
whenNotPaused
notBlacklisted(sender)
notBlacklisted(recipient)
notBlacklisted(msg.sender)
nonReentrant
returns (bool)
{
uint256 currentAllowance = _allowances[sender][msg.sender];
require(currentAllowance >= amount, "Transfer amount exceeds allowance");
unchecked {
_approve(sender, msg.sender, currentAllowance - amount);
}
_transfer(sender, recipient, amount);
return true;
}
// 内部函数
function _transfer(address sender, address recipient, uint256 amount) internal {
require(sender != address(0), "Transfer from zero address");
require(recipient != address(0), "Transfer to zero address");
uint256 senderBalance = _balances[sender];
require(senderBalance >= amount, "Transfer amount exceeds balance");
unchecked {
_balances[sender] = senderBalance - amount;
}
_balances[recipient] += amount;
emit Transfer(sender, recipient, amount);
}
function _approve(address tokenOwner, address spender, uint256 amount) internal {
require(tokenOwner != address(0), "Approve from zero address");
require(spender != address(0), "Approve to zero address");
_allowances[tokenOwner][spender] = amount;
emit Approval(tokenOwner, spender, amount);
}
function _mint(address account, uint256 amount) internal {
require(account != address(0), "Mint to zero address");
_totalSupply += amount;
_balances[account] += amount;
emit Transfer(address(0), account, amount);
}
function _burn(address account, uint256 amount) internal {
require(account != address(0), "Burn from zero address");
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "Burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
}
_totalSupply -= amount;
emit Transfer(account, address(0), amount);
}
// 管理功能
function mint(address account, uint256 amount) external onlyOwner {
_mint(account, amount);
}
function burn(uint256 amount) external whenNotPaused nonReentrant {
_burn(msg.sender, amount);
}
function pause() external onlyOwner {
paused = true;
emit Paused(msg.sender);
}
function unpause() external onlyOwner {
paused = false;
emit Unpaused(msg.sender);
}
function addToBlacklist(address account) external onlyOwner {
require(account != address(0), "Invalid address");
blacklist[account] = true;
emit Blacklisted(account);
}
function removeFromBlacklist(address account) external onlyOwner {
blacklist[account] = false;
emit RemovedFromBlacklist(account);
}
function transferOwnership(address newOwner) external onlyOwner {
require(newOwner != address(0), "Invalid address");
emit OwnershipTransferred(owner, newOwner);
owner = newOwner;
}
// 增加allowance(防止approve竞态条件)
function increaseAllowance(address spender, uint256 addedValue) external returns (bool) {
_approve(msg.sender, spender, _allowances[msg.sender][spender] + addedValue);
return true;
}
function decreaseAllowance(address spender, uint256 subtractedValue) external returns (bool) {
uint256 currentAllowance = _allowances[msg.sender][spender];
require(currentAllowance >= subtractedValue, "Decreased allowance below zero");
unchecked {
_approve(msg.sender, spender, currentAllowance - subtractedValue);
}
return true;
}
}
3.2 安全的众筹合约
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @title SecureCrowdfunding
* @dev 包含多重安全机制的众筹合约
*/
contract SecureCrowdfunding {
address public owner;
address payable public beneficiary;
uint256 public goal;
uint256 public deadline;
uint256 public totalFunds;
mapping(address => uint256) public contributions;
bool public finalized;
bool public goalReached;
// 重入保护
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
event ContributionReceived(address indexed contributor, uint256 amount);
event GoalReached(uint256 totalFunds);
event FundsWithdrawn(address indexed beneficiary, uint256 amount);
event RefundIssued(address indexed contributor, uint256 amount);
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
_;
}
modifier nonReentrant() {
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
_status = _ENTERED;
_;
_status = _NOT_ENTERED;
}
constructor(
address payable _beneficiary,
uint256 _goal,
uint256 _durationInDays
) {
require(_beneficiary != address(0), "Invalid beneficiary");
require(_goal > 0, "Goal must be positive");
require(_durationInDays > 0, "Duration must be positive");
owner = msg.sender;
beneficiary = _beneficiary;
goal = _goal;
deadline = block.timestamp + (_durationInDays * 1 days);
_status = _NOT_ENTERED;
}
function contribute() external payable nonReentrant {
require(block.timestamp < deadline, "Campaign ended");
require(!finalized, "Campaign finalized");
require(msg.value > 0, "Contribution must be positive");
contributions[msg.sender] += msg.value;
totalFunds += msg.value;
emit ContributionReceived(msg.sender, msg.value);
if (totalFunds >= goal && !goalReached) {
goalReached = true;
emit GoalReached(totalFunds);
}
}
function finalize() external onlyOwner nonReentrant {
require(block.timestamp >= deadline, "Campaign not ended");
require(!finalized, "Already finalized");
finalized = true;
if (totalFunds >= goal) {
goalReached = true;
emit GoalReached(totalFunds);
}
}
function withdraw() external nonReentrant {
require(finalized, "Not finalized");
require(goalReached, "Goal not reached");
require(msg.sender == beneficiary, "Not beneficiary");
require(totalFunds > 0, "No funds");
uint256 amount = totalFunds;
totalFunds = 0;
(bool success, ) = beneficiary.call{value: amount}("");
require(success, "Transfer failed");
emit FundsWithdrawn(beneficiary, amount);
}
function refund() external nonReentrant {
require(finalized, "Not finalized");
require(!goalReached, "Goal reached");
uint256 amount = contributions[msg.sender];
require(amount > 0, "No contribution");
contributions[msg.sender] = 0;
totalFunds -= amount;
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Refund failed");
emit RefundIssued(msg.sender, amount);
}
function getTimeLeft() external view returns (uint256) {
if (block.timestamp >= deadline) {
return 0;
}
return deadline - block.timestamp;
}
function getContribution(address contributor) external view returns (uint256) {
return contributions[contributor];
}
}
第四部分:安全开发最佳实践
4.1 开发流程
设计阶段
- 明确合约功能和边界
- 识别潜在安全风险
- 设计访问控制策略
- 规划升级机制
编码阶段
- 遵循检查-生效-交互模式
- 使用经过审计的库(OpenZeppelin)
- 添加完善的注释和文档
- 实现全面的错误处理
测试阶段
- 单元测试覆盖率>90%
- 集成测试
- 模糊测试
- Gas优化测试
审计阶段
- 内部代码审查
- 外部安全审计
- 修复发现的问题
- 重新审计
部署阶段
- 在测试网部署和测试
- 逐步部署(金丝雀发布)
- 监控和应急响应计划
4.2 代码检查清单
## 通用安全检查
- [ ] 所有外部调用都检查返回值
- [ ] 使用最新的Solidity版本(0.8.x)
- [ ] 避免使用tx.origin进行认证
- [ ] 正确处理整数溢出(0.8.x自动检查)
- [ ] 实现重入保护
- [ ] 使用pull而非push模式转账
- [ ] 避免在循环中进行外部调用
- [ ] 正确使用visibility修饰符
- [ ] 避免delegatecall到不可信地址
## 访问控制
- [ ] 实现适当的权限控制
- [ ] 使用modifier进行权限检查
- [ ] 实现多签或时间锁机制
- [ ] 限制关键函数的调用者
## 数据验证
- [ ] 验证所有输入参数
- [ ] 检查地址不为零地址
- [ ] 验证数值范围
- [ ] 检查数组长度
## 代币安全(如适用)
- [ ] 实现approve竞态条件保护
- [ ] 正确处理小数位
- [ ] 实现暂停机制
- [ ] 添加黑名单功能
## 可升级性(如适用)
- [ ] 保持存储布局兼容
- [ ] 使用初始化函数而非构造函数
- [ ] 实现升级权限控制
- [ ] 测试升级流程
## Gas优化
- [ ] 使用unchecked避免不必要的溢出检查
- [ ] 合理使用storage和memory
- [ ] 批量操作优化
- [ ] 避免不必要的存储写入
4.3 推荐工具
静态分析工具
- Slither
- Mythril
- Securify
测试框架
- Hardhat
- Foundry
- Truffle
审计服务
- OpenZeppelin
- ConsenSys Diligence
- Trail of Bits