HiHuo
首页
博客
手册
工具
关于
首页
博客
手册
工具
关于
  • Web3 完整技术体系

    • Web3 完整技术体系
    • 区块链基础与密码学
    • 第一章:比特币原理与实现
    • 第二章:以太坊架构与核心概念
    • Solidity智能合约开发基础
    • 04-Solidity进阶与安全
    • 05-ERC标准详解
    • 06-DeFi核心协议-去中心化交易所
    • 07-DeFi核心协议-借贷协议
    • 08-DeFi核心协议-稳定币
    • NFT与元宇宙
    • Layer2扩容方案
    • 跨链技术
    • Web3前端开发
    • 链下数据与预言机
    • 智能合约测试与部署
    • MEV与交易优化
    • DAO治理
    • 项目实战:完整DeFi借贷协议

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 开发流程

  1. 设计阶段

    • 明确合约功能和边界
    • 识别潜在安全风险
    • 设计访问控制策略
    • 规划升级机制
  2. 编码阶段

    • 遵循检查-生效-交互模式
    • 使用经过审计的库(OpenZeppelin)
    • 添加完善的注释和文档
    • 实现全面的错误处理
  3. 测试阶段

    • 单元测试覆盖率>90%
    • 集成测试
    • 模糊测试
    • Gas优化测试
  4. 审计阶段

    • 内部代码审查
    • 外部安全审计
    • 修复发现的问题
    • 重新审计
  5. 部署阶段

    • 在测试网部署和测试
    • 逐步部署(金丝雀发布)
    • 监控和应急响应计划

4.2 代码检查清单

## 通用安全检查

- [ ] 所有外部调用都检查返回值
- [ ] 使用最新的Solidity版本(0.8.x)
- [ ] 避免使用tx.origin进行认证
- [ ] 正确处理整数溢出(0.8.x自动检查)
- [ ] 实现重入保护
- [ ] 使用pull而非push模式转账
- [ ] 避免在循环中进行外部调用
- [ ] 正确使用visibility修饰符
- [ ] 避免delegatecall到不可信地址

## 访问控制

- [ ] 实现适当的权限控制
- [ ] 使用modifier进行权限检查
- [ ] 实现多签或时间锁机制
- [ ] 限制关键函数的调用者

## 数据验证

- [ ] 验证所有输入参数
- [ ] 检查地址不为零地址
- [ ] 验证数值范围
- [ ] 检查数组长度

## 代币安全(如适用)

- [ ] 实现approve竞态条件保护
- [ ] 正确处理小数位
- [ ] 实现暂停机制
- [ ] 添加黑名单功能

## 可升级性(如适用)

- [ ] 保持存储布局兼容
- [ ] 使用初始化函数而非构造函数
- [ ] 实现升级权限控制
- [ ] 测试升级流程

## Gas优化

- [ ] 使用unchecked避免不必要的溢出检查
- [ ] 合理使用storage和memory
- [ ] 批量操作优化
- [ ] 避免不必要的存储写入

4.3 推荐工具

  1. 静态分析工具

    • Slither
    • Mythril
    • Securify
  2. 测试框架

    • Hardhat
    • Foundry
    • Truffle
  3. 审计服务

    • OpenZeppelin
    • ConsenSys Diligence
    • Trail of Bits

4.4 参考资源

  • Solidity官方文档
  • OpenZeppelin合约库
  • Smart Contract Security Best Practices
  • SWC Registry
Prev
Solidity智能合约开发基础
Next
05-ERC标准详解