×
¥
查看详情
🔥 会员专享 文生文 其它

智能合约安全开发最佳实践指南

👁️ 105 次查看
📅 Dec 3, 2025
💡 核心价值: 本提示词专为区块链开发者设计,提供系统化的智能合约安全开发最佳实践指导。通过角色扮演区块链开发专家,结合具体开发场景,输出5项核心安全实践方案,涵盖代码审计、权限控制、漏洞防护等关键领域。提示词采用技术文档写作风格,确保内容精确、清晰且具有实操性,帮助开发者构建更安全的去中心化应用。

🎯 可自定义参数(4个)

开发场景
智能合约的具体应用场景
合约类型
智能合约的功能类型
技术栈
开发使用的技术框架和工具
安全等级
要求的安全防护等级

🎨 效果示例

总体安全原则概述

  • 最小权限与时延治理:所有敏感操作(参数调整、升级、暂停)必须受角色控制并通过时间锁执行,避免单点失误或恶意更改。
  • 显式不变量与边界:在利率、清算、抵押比率等核心逻辑中设置可验证的不变量、上下边界与断言,确保状态演进安全。
  • 外部交互保守策略:遵循Checks-Effects-Interactions,使用重入防护与安全代币转账,禁止对未验证外部合约进行delegatecall。
  • 价格与时间稳健性:对预言机数据执行完整性、时效性、幅度与小数对齐检查;在异常时进入只读或限制模式。
  • 全生命周期验证:采用静态分析、属性测试和不变式测试贯穿开发与CI,确保改动不破坏核心安全性。

5项最佳实践

实践1:角色治理与参数安全(AccessControl + Timelock + Pausable)

  • 核心价值:防止参数被越权或快速更改引发系统性风险;在紧急情况下安全降级(仅允许偿还/提款)。
  • 具体措施:
    • 使用OpenZeppelin AccessControl定义OWNER、RISK_ADMIN、PAUSE_GUARDIAN、UPGRADE_ADMIN等角色。
    • 通过TimelockController执行风险参数变更与升级,设置足够的最小延时(例如48小时)。
    • 所有风险参数设置器带上限/下限与渐进调整(例如每次变更不超过一定bps)。
    • 合约可Pausable:暂停后禁止新的借款与清算,但允许还款与提款。
  • 实施要点:
    • 角色分离与两步所有权转移(Ownable2Step)。
    • 参数变更事件化与链上记录审计。
    • 在部署脚本中将所有敏感函数指向Timelock执行器。
  • 代码示例:
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.21;
    
    import "@openzeppelin/contracts/access/AccessControl.sol";
    import "@openzeppelin/contracts/security/Pausable.sol";
    import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
    
    contract RiskParams is AccessControl, Pausable, ReentrancyGuard {
        bytes32 public constant OWNER_ROLE = keccak256("OWNER_ROLE");
        bytes32 public constant RISK_ADMIN_ROLE = keccak256("RISK_ADMIN_ROLE");
        bytes32 public constant PAUSE_GUARDIAN_ROLE = keccak256("PAUSE_GUARDIAN_ROLE");
    
        // basis points
        uint256 public maxLTVBps;             // e.g., 8000 = 80%
        uint256 public liquidationThresholdBps;// e.g., 8500 = 85%
        uint256 public closeFactorBps;         // e.g., 5000 = 50%
        uint256 public liquidationBonusBps;    // e.g., 10500 = 105%
    
        // Hard caps to prevent admin mistakes
        uint256 public constant MAX_LTV_CAP = 8500;
        uint256 public constant MIN_CLOSE_FACTOR = 1000;
        uint256 public constant MAX_CLOSE_FACTOR = 9000;
        uint256 public constant MIN_LIQ_BONUS = 10100;
        uint256 public constant MAX_LIQ_BONUS = 12000;
    
        event ParamsUpdated(uint256 maxLTV, uint256 liqThreshold, uint256 closeFactor, uint256 liqBonus);
    
        constructor(address owner, address riskAdmin, address pauseGuardian) {
            _grantRole(OWNER_ROLE, owner);
            _grantRole(RISK_ADMIN_ROLE, riskAdmin);
            _grantRole(PAUSE_GUARDIAN_ROLE, pauseGuardian);
        }
    
        function setParams(
            uint256 _maxLTV,
            uint256 _liqThreshold,
            uint256 _closeFactor,
            uint256 _liqBonus
        ) external onlyRole(RISK_ADMIN_ROLE) {
            require(_maxLTV <= MAX_LTV_CAP, "LTV too high");
            require(_liqThreshold >= _maxLTV && _liqThreshold <= 9000, "bad liq threshold");
            require(_closeFactor >= MIN_CLOSE_FACTOR && _closeFactor <= MAX_CLOSE_FACTOR, "bad close factor");
            require(_liqBonus >= MIN_LIQ_BONUS && _liqBonus <= MAX_LIQ_BONUS, "bad liq bonus");
            maxLTVBps = _maxLTV;
            liquidationThresholdBps = _liqThreshold;
            closeFactorBps = _closeFactor;
            liquidationBonusBps = _liqBonus;
            emit ParamsUpdated(_maxLTV, _liqThreshold, _closeFactor, _liqBonus);
        }
    
        function pause() external onlyRole(PAUSE_GUARDIAN_ROLE) { _pause(); }
        function unpause() external onlyRole(OWNER_ROLE) { _unpause(); }
    }
    
  • 配置说明(Hardhat + Timelock):
    // hardhat script snippet
    const { ethers } = require("hardhat");
    async function main() {
      const [deployer] = await ethers.getSigners();
      const minDelay = 48 * 60 * 60;
      const proposers = [deployer.address];
      const executors = [deployer.address];
    
      const Timelock = await ethers.getContractFactory("TimelockController");
      const timelock = await Timelock.deploy(minDelay, proposers, executors);
      await timelock.deployed();
    
      // Grant roles to timelock where applicable
      // e.g., riskParams.grantRole(RISK_ADMIN_ROLE, timelock.address);
    }
    main().catch(console.error);
    

实践2:稳健的预言机集成(Chainlink + 数据有效性/时效性/对齐)

  • 核心价值:避免使用过期、未完成或异常价格导致错误清算和不当计价。
  • 具体措施:
    • 使用Chainlink AggregatorV3Interface,并验证:answer > 0、answeredInRound >= roundId、updatedAt不超过最大允许陈旧期、decimals对齐到统一精度(如1e18)。
    • 针对L2网络(如Optimism/Arbitrum)可选集成Sequencer Uptime Feed,确保链上序列器恢复后等待安全窗口。
    • 设置价格偏差断路器(与次级或延时TWAP源对比),在偏差过大时限制借款/清算。
  • 实施要点:
    • 价格拉取失败或校验失败时返回错误并在上层进入只读模式(允许还款、拒绝新借款与清算)。
    • 对每个资产维护独立最大陈旧期与小数缩放。
  • 代码示例:
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.21;
    
    import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
    
    library PriceOracleLib {
        error StalePrice();
        error IncompleteRound();
        error BadPrice();
        error DecimalsMismatch();
    
        function getPrice18(
            AggregatorV3Interface feed,
            uint256 maxStale,
            uint8 targetDecimals // typically 18
        ) internal view returns (uint256 price18) {
            (
                uint80 roundId,
                int256 answer,
                uint256 startedAt,
                uint256 updatedAt,
                uint80 answeredInRound
            ) = feed.latestRoundData();
    
            if (answer <= 0) revert BadPrice();
            if (answeredInRound < roundId) revert IncompleteRound();
            if (block.timestamp - updatedAt > maxStale) revert StalePrice();
    
            uint8 d = feed.decimals();
            // scale to target decimals
            if (d == targetDecimals) {
                price18 = uint256(answer);
            } else if (d < targetDecimals) {
                price18 = uint256(answer) * (10 ** (targetDecimals - d));
            } else {
                price18 = uint256(answer) / (10 ** (d - targetDecimals));
            }
        }
    }
    
  • 配置说明(Echidna价格属性):
    # echidna.yaml
    test-mode: assertion
    shrink: true
    # Ensure price validation properties are not violated
    
    // Echidna example: assert staleness handled
    contract OracleProps {
        using PriceOracleLib for AggregatorV3Interface;
        AggregatorV3Interface public feed;
        uint256 public maxStale = 3600; // 1h
        function echidna_price_never_zero() external view returns (bool) {
            try PriceOracleLib.getPrice18(feed, maxStale, 18) returns (uint256 p) {
                return p > 0;
            } catch { return true; } // graceful failure acceptable
        }
    }
    

实践3:重入防护与代币交互安全(ReentrancyGuard + SafeERC20 + 余额差分)

  • 核心价值:防止重入攻击、非标准ERC20行为(fee-on-transfer、返回值非布尔)导致资金会计错误。
  • 具体措施:
    • 所有涉及外部转账的核心路径使用nonReentrant,严格遵循Checks-Effects-Interactions。
    • 使用OpenZeppelin SafeERC20进行安全转账;入账采用余额差分计算实际收到金额;出账禁止FOT代币或进行差分检查。
    • 避免无限授权和approve竞态,使用permit或每次授权最小值。
  • 实施要点:
    • 资产白名单并声明是否支持FOT;若不支持则在入账或出账时断言实际金额与期望一致。
    • 在暂停状态下拒绝非偿还类交互。
  • 代码示例(借款/还款片段):
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.21;
    
    import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
    import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
    import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    
    contract LendingCore is ReentrancyGuard {
        using SafeERC20 for IERC20;
    
        IERC20 public immutable asset;
        bool public supportsFeeOnTransfer; // set per-asset
        mapping(address => uint256) public debt;
    
        constructor(IERC20 _asset, bool _supportsFOT) {
            asset = _asset;
            supportsFeeOnTransfer = _supportsFOT;
        }
    
        function _accrueInterest() internal { /* update indexes, reserves */ }
    
        function repay(uint256 amount) external nonReentrant {
            _accrueInterest();
            uint256 beforeBal = asset.balanceOf(address(this));
            asset.safeTransferFrom(msg.sender, address(this), amount);
            uint256 received = asset.balanceOf(address(this)) - beforeBal;
            // Use actual received to reduce debt to avoid accounting mismatch
            debt[msg.sender] = debt[msg.sender] > received ? debt[msg.sender] - received : 0;
        }
    
        function borrow(uint256 amount) external nonReentrant {
            _accrueInterest();
            // Effects
            debt[msg.sender] += amount;
            uint256 beforeBal = asset.balanceOf(address(this));
            // Interactions
            asset.safeTransfer(msg.sender, amount);
            uint256 delta = beforeBal - asset.balanceOf(address(this));
            if (!supportsFeeOnTransfer) {
                require(delta == amount, "FOT not supported");
            } else {
                // if FOT supported, adjust debt by actual delta
                uint256 actualSent = delta;
                // optional: adjust indexes or charge variable fee accordingly
                debt[msg.sender] = debt[msg.sender] + (amount - actualSent);
            }
        }
    }
    
  • 配置说明(Foundry FFI禁用与fuzz):
    # foundry.toml
    [profile.default]
    fuzz_runs = 10000
    ffi = false
    invariant_runs = 500
    

实践4:利率模型与计息精度(单调性、不溢出、Wad/Ray数学)

  • 核心价值:确保利用率-利率函数单调、利率上限明确、计息精度足够并避免溢出/过大时间步导致异常。
  • 具体措施:
    • 采用分段/kink模型:U<=Uk使用base+Uslope1;U>Uk使用base+Ukslope1+(U-Uk)*slope2;所有参数使用Wad(1e18)或Ray(1e27)。
    • 利率上限(如年化<=50%);计息时对Δt设上限(如≤24小时),超限时分段累积或拒绝。
    • 使用OpenZeppelin Math.mulDiv以避免中间溢出,所有cast使用SafeCast。
  • 实施要点:
    • 累计利息更新index时断言index非下降;总负债=Σ账户负债在指数尺度上保持一致。
    • 由治理参数控制base、slope、kink并设边界。
  • 代码示例(Ray数学与单调性):
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.21;
    
    import "@openzeppelin/contracts/utils/math/Math.sol";
    import "@openzeppelin/contracts/utils/math/SafeCast.sol";
    
    library WadRayMath {
        uint256 internal constant WAD = 1e18;
        uint256 internal constant RAY = 1e27;
        function rayMul(uint256 a, uint256 b) internal pure returns (uint256) {
            return Math.mulDiv(a, b, RAY);
        }
        function rayDiv(uint256 a, uint256 b) internal pure returns (uint256) {
            return Math.mulDiv(a, RAY, b);
        }
    }
    
    contract InterestRateModel {
        using WadRayMath for uint256;
        using SafeCast for uint256;
    
        // in ray (1e27)
        uint256 public baseRateRay;      // per-second
        uint256 public slope1Ray;
        uint256 public slope2Ray;
        uint256 public kinkWad;          // utilization in wad (1e18)
        uint256 public maxRateRay;       // cap per-second
    
        function borrowRatePerSecond(uint256 utilizationWad) public view returns (uint256) {
            // Monotonic piecewise linear
            uint256 u = utilizationWad;
            uint256 rateRay;
            if (u <= kinkWad) {
                rateRay = baseRateRay + Math.mulDiv(slope1Ray, u, WadRayMath.WAD);
            } else {
                uint256 part1 = baseRateRay + Math.mulDiv(slope1Ray, kinkWad, WadRayMath.WAD);
                uint256 part2 = Math.mulDiv(slope2Ray, (u - kinkWad), WadRayMath.WAD);
                rateRay = part1 + part2;
            }
            if (rateRay > maxRateRay) rateRay = maxRateRay;
            return rateRay;
        }
    }
    
  • 配置说明(Foundry单调性与上限不变式测试):
    // Foundry invariant example
    contract RateInvariants {
        InterestRateModel model;
    
        function invariant_monotonicity() public {
            uint256 u1 = 3e17; // 30%
            uint256 u2 = 7e17; // 70%
            assert(model.borrowRatePerSecond(u2) >= model.borrowRatePerSecond(u1));
        }
    
        function invariant_capped() public {
            uint256 u = 1e18;
            assert(model.borrowRatePerSecond(u) <= model.maxRateRay());
        }
    }
    

实践5:面向主网的全链路安全测试与静态分析(Slither + Foundry invariants + Echidna properties)

  • 核心价值:在代码层面对重入、未检查返回值、不可达分支、溢出下溢、授权问题等进行自动化检测;用属性与不变式确保协议核心账本安全。
  • 具体措施:
    • Slither静态分析在PR与CI中强制通过,阻断高危问题。
    • Foundry基于invariant的系统测试:资金守恒、健康因子、价格边界、指数非下降。
    • Echidna属性测试:小而关键的属性如“不在可借状态下发生负债减少”、“价格异常时不触发清算”等。
  • 实施要点:
    • 覆盖关键不变量:总资产守恒(cash + borrows - reserves 等于内部账户余额合计)、利率指数非下降、健康因子HF<1才允许清算、暂停时只允许还款/提款。
    • 对含外部交互的路径增加fuzz与不合法输入测试,确保安全回退。
  • 配置说明与示例:
    • Slither配置:
      # slither run
      slither . --exclude-low --print human-summary --solc-remaps @openzeppelin=node_modules/@openzeppelin
      
    • Foundry不变式测试示例:
      contract AccountingInvariants is Test {
          LendingCore core; // assume deployed with mocks
      
          function invariant_total_assets_conserved() public {
              uint256 cash = core.asset().balanceOf(address(core));
              (uint256 totalBorrows, uint256 totalReserves) = core.getTotals();
              uint256 internalSum = core.sumInternalBalances(); // implement to sum account assets
              assertEq(cash + totalBorrows - totalReserves, internalSum);
          }
      
          function invariant_pause_allows_repay_withdraw_only() public {
              vm.prank(core.pauseGuardian());
              core.pause();
              // fuzz try borrow/liq must revert
              vm.expectRevert();
              core.borrow(1e6);
          }
      }
      
    • Echidna属性示例:
      contract LiquidationProps {
          LendingCore core;
      
          // Liquidation only allowed when HF < 1
          function echidna_no_liquidation_when_safe() external returns (bool) {
              (uint256 hf,) = core.healthFactor(msg.sender);
              bool canLiq = core.canLiquidate(msg.sender);
              return (hf >= 1e18) ? (canLiq == false) : true;
          }
      }
      

总结与后续建议

  • 建议将“治理与参数更改”统一通过Timelock + 多签执行,并在前端与链上事件中暴露即将生效的更改。
  • 在主网部署前,完成:
    • 全量Slither报告修复、Foundry不变式测试通过、Echidna属性测试至少数百小时自动运行。
    • 针对关键路径(清算、利率累计、价格拉取)进行形式化断言与gas边界测试。
  • 上线后运行时监控:
    • 价格异常与偏差报警、参数变更告警、借款与清算速率异常。
    • 预言机失败时自动切换到限制模式(暂停新借款与清算,但允许还款与提款)。

通过以上五项实践,在Solidity 0.8.x + OpenZeppelin + Foundry + Hardhat + Slither + Echidna + Chainlink栈上,能以高可操作性和可验证性提升去中心化借贷协议在主网的安全性,显著降低重入、价格异常、参数误用与会计错误等核心风险。

总体安全原则概述

  • 最小化信任与外部依赖:合约在结算和转移资产前先完成状态更新,避免对外部合约回调或任意收款方逻辑产生信任。
  • 资金安全优先:采用提现(Pull Payment)与重入防护,避免在同一交易中直接向用户转账导致的重入/拒绝服务。
  • 可验证与可追踪:所有关键操作(签名铸造、下单/出价、结算/版税分配)必须事件化,便于链上追溯与监控。
  • 明确的权限与应急控制:通过细粒度角色管理、Pausable,应对密钥泄露与异常交易高峰。
  • 覆盖式测试与持续审计:结合Slither静态分析、Foundry模糊/不变性测试与Tenderly仿真和告警,形成闭环。

最佳实践 1:资金流安全与重入防护(Pull Payment + CEI)

  • 核心价值:避免重入攻击、买家退款失败导致的 DoS、版税接收方/卖家回调风险,确保结算可控且可追踪。
  • 具体措施:
    • 采用 Checks-Effects-Interactions(CEI)顺序和 ReentrancyGuard。
    • 所有支付(卖家货款、平台费、版税)进入“待提现”余额,用户主动提取。
    • 使用 safeTransferFrom 转移 NFT,且在状态更新与资金入账之后执行。
    • 限制外部调用面,结算过程中不进行任意外部合约调用(不直接向用户 .call 支付)。
  • 实施要点:
    • 结算入口函数标记 nonReentrant 和 whenNotPaused。
    • 避免在 nonReentrant 函数中再调用标记 nonReentrant 的外部函数。
    • 所有金额以基点(BPS)为单位计算并向下取整,剩余“卡尾”归卖家。

代码示例(Solidity 0.8.x + OpenZeppelin):

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import {Pausable} from "@openzeppelin/contracts/utils/Pausable.sol";

contract EscrowVault is ReentrancyGuard {
    mapping(address => uint256) public balances;

    function _queue(address to, uint256 amount) internal {
        if (amount == 0) return;
        balances[to] += amount;
    }

    function withdraw() external nonReentrant {
        uint256 amt = balances[msg.sender];
        require(amt > 0, "Nothing to withdraw");
        balances[msg.sender] = 0;
        (bool ok, ) = payable(msg.sender).call{value: amt}("");
        require(ok, "ETH transfer failed");
    }

    receive() external payable {}
}

contract Marketplace is ReentrancyGuard, Pausable, EscrowVault {
    uint96 public platformFeeBps; // e.g., 250 = 2.5%
    address public feeRecipient;

    struct Order {
        address seller;
        address nft;
        uint256 tokenId;
        uint256 price;
    }

    mapping(bytes32 => bool) public filled;

    event Trade(address indexed nft, uint256 indexed tokenId, address seller, address buyer, uint256 price);

    constructor(uint96 _feeBps, address _feeRecipient) {
        require(_feeBps <= 10000, "fee too high");
        platformFeeBps = _feeBps;
        feeRecipient = _feeRecipient;
    }

    function buy(Order calldata o) external payable nonReentrant whenNotPaused {
        require(msg.value == o.price, "wrong price");
        bytes32 hash = keccak256(abi.encode(o.seller, o.nft, o.tokenId, o.price));
        require(!filled[hash], "order filled");
        filled[hash] = true; // Effects

        // Fee split (royalty在实践4中计算,这里仅示范平台费与卖家款)
        uint256 fee = (o.price * platformFeeBps) / 10_000; // 向下取整
        uint256 sellerAmt = o.price - fee;

        _queue(feeRecipient, fee);
        _queue(o.seller, sellerAmt);

        // 最后进行NFT转移(Interactions),触发onERC721Received回调也无法重入修改状态
        IERC721(o.nft).safeTransferFrom(o.seller, msg.sender, o.tokenId);

        emit Trade(o.nft, o.tokenId, o.seller, msg.sender, o.price);
    }

    function pause() external /* onlyRole(PAUSER_ROLE) */ {
        _pause();
    }
    function unpause() external /* onlyRole(PAUSER_ROLE) */ {
        _unpause();
    }
}

最佳实践 2:基于 EIP-712 的懒铸造签名校验与防重放

  • 核心价值:在上链前验证签名凭证,确保铸造授权真实、不可抵赖、不可重放,并且绑定价格、截止时间与元数据。
  • 具体措施:
    • 使用 EIP-712 域分隔(name/version/chainId/verifyingContract)和 OpenZeppelin ECDSA/EIP712。
    • 凭证包含:接收者、数量、(可选)内容哈希/URI、最小成交价、截止时间、nonce。
    • 合约跟踪 nonce/签名摘要使用状态,过期/重复直接拒绝。
    • 仅允许具有 SIGNER_ROLE 的后台签名地址签发懒铸凭证。
  • 实施要点:
    • 对 ERC721A 批量铸造时校验总供应上限。
    • 价格在结算时再二次校验(防止用户前端改价)。
    • 绑定链 ID 与 verifyingContract 避免跨链/跨合约重放。

代码示例(核心校验逻辑,省略ERC721A实现细节):

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import {EIP712} from "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";

contract LazyMintVerifier is EIP712, AccessControl {
    bytes32 public constant SIGNER_ROLE = keccak256("SIGNER_ROLE");

    // Lazy mint voucher struct
    struct Voucher {
        address to;
        uint256 quantity;      // ERC721A 批量数量
        bytes32 contentHash;   // 绑定元数据哈希(或空值)
        uint256 minPrice;      // 最低成交价(总价)
        uint256 nonce;
        uint256 deadline;      // 截止时间
    }

    bytes32 private constant VOUCHER_TYPEHASH =
        keccak256("Voucher(address to,uint256 quantity,bytes32 contentHash,uint256 minPrice,uint256 nonce,uint256 deadline)");

    mapping(uint256 => bool) public usedNonces;

    constructor() EIP712("NFT-Marketplace", "1.0.0") {
        _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
        _grantRole(SIGNER_ROLE, msg.sender);
    }

    function verifyVoucher(Voucher calldata v, bytes calldata sig) public view returns (address) {
        bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
            VOUCHER_TYPEHASH,
            v.to,
            v.quantity,
            v.contentHash,
            v.minPrice,
            v.nonce,
            v.deadline
        )));
        return ECDSA.recover(digest, sig);
    }

    function lazyMintAndList(Voucher calldata v, bytes calldata sig) external payable {
        require(block.timestamp <= v.deadline, "expired");
        require(!usedNonces[v.nonce], "nonce used");
        require(msg.value >= v.minPrice, "price too low");

        address signer = verifyVoucher(v, sig);
        require(hasRole(SIGNER_ROLE, signer), "invalid signer");

        usedNonces[v.nonce] = true;

        // 执行 ERC721A 批量铸造给 v.to,绑定 contentHash => tokenURI(base + hash) 等逻辑
        // _safeMint(v.to, v.quantity);

        // 继续执行市场挂单/结算逻辑(与最佳实践1结合:资金进入提现队列)
    }
}

配置说明(前端/服务端签名要点):

  • EIP-712 域:
    • name: "NFT-Marketplace"
    • version: "1.0.0"
    • chainId: 当前网络 chainId
    • verifyingContract: 懒铸合约地址
  • 签名端使用 ethers.js _signTypedData,确保 nonce 唯一且与合约存储一致。

最佳实践 3:安全的拍卖与竞价逻辑(最小加价、反狙击、退款提现)

  • 核心价值:避免前台狙击、时间操纵、退款失败导致的拍卖卡死,确保竞价秩序与可结算性。
  • 具体措施:
    • 采用 English Auction:最小加价幅度(BPS)、保留价、开始/结束时间。
    • 反狙击(Anti-sniping):在接近结束窗口期内出价则自动延长结束时间。
    • 退款采用提现队列,不直接退回上一高价者。
    • 所有状态变更先于外部交互;结算标记只允许一次。
  • 实施要点:
    • 使用 block.timestamp,避免 block.number 作为时间。
    • 严格校验 “>= 上一高价 + 增幅”。
    • 拍卖结束与结算分离,允许任意人触发结算以防卖家弃置。

代码示例:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";

contract AuctionHouse is ReentrancyGuard {
    struct Auction {
        address seller;
        address nft;
        uint256 tokenId;
        uint96 reservePrice;
        uint40 startTime;
        uint40 endTime;
        uint96 minIncrementBps; // e.g., 500 = 5%
        address highestBidder;
        uint256 highestBid;
        bool settled;
    }

    uint40 public constant EXTENSION_WINDOW = 300; // 5分钟反狙击
    mapping(uint256 => Auction) public auctions;
    mapping(address => uint256) public pendingRefunds;

    event BidPlaced(uint256 indexed auctionId, address bidder, uint256 amount, uint40 newEndTime);
    event AuctionSettled(uint256 indexed auctionId, address winner, uint256 amount);

    function bid(uint256 auctionId) external payable nonReentrant {
        Auction storage a = auctions[auctionId];
        require(block.timestamp >= a.startTime && block.timestamp < a.endTime, "not active");
        uint256 minRequired = a.highestBid == 0
            ? a.reservePrice
            : a.highestBid + (a.highestBid * a.minIncrementBps) / 10_000;
        require(msg.value >= minRequired, "bid too low");

        // 退款入队列(不直接转账)
        if (a.highestBidder != address(0)) {
            pendingRefunds[a.highestBidder] += a.highestBid;
        }

        a.highestBidder = msg.sender;
        a.highestBid = msg.value;

        // 反狙击:若在窗口内出价,则延长
        if (a.endTime - block.timestamp <= EXTENSION_WINDOW) {
            a.endTime = uint40(block.timestamp + EXTENSION_WINDOW);
        }

        emit BidPlaced(auctionId, msg.sender, msg.value, a.endTime);
    }

    function withdrawRefund() external nonReentrant {
        uint256 amt = pendingRefunds[msg.sender];
        require(amt > 0, "no refund");
        pendingRefunds[msg.sender] = 0;
        (bool ok, ) = payable(msg.sender).call{value: amt}("");
        require(ok, "refund failed");
    }

    function settle(uint256 auctionId) external nonReentrant {
        Auction storage a = auctions[auctionId];
        require(!a.settled, "settled");
        require(block.timestamp >= a.endTime, "not ended");

        a.settled = true;

        if (a.highestBidder != address(0)) {
            // 结算资金分配与NFT转移(结合最佳实践1和4)
            // _queue(seller,...), _queue(royaltyRecipient,...)
            IERC721(a.nft).safeTransferFrom(a.seller, a.highestBidder, a.tokenId);
            emit AuctionSettled(auctionId, a.highestBidder, a.highestBid);
        } else {
            // 流拍,无需转移
        }
    }
}

配置说明:

  • 建议前端显示“剩余时间<5分钟自动延长”的用户提示,避免误解。
  • reservePrice 可低于起拍价,但首次出价需满足保留价。

最佳实践 4:可验证的版税与费用结算(EIP-2981 集成与上限约束)

  • 核心价值:遵循行业标准(EIP-2981),确保版税准确可追踪;同时对总费率设上限避免过度抽取。
  • 具体措施:
    • 对支持 EIP-2981 的合约,调用 royaltyInfo(tokenId, salePrice) 获取版税接收方与金额。
    • 设置平台费上限(如 <= 5%),总费率(平台费 + 版税)不得超过 10000 BPS。
    • 统一向下取整,剩余归卖家;如版税接收方与卖家一致,合并入卖家款项减少提现次数。
    • 所有分配通过提现队列入账;事件记录版税明细。
  • 实施要点:
    • 对不支持 EIP-2981 的NFT,可选择本地化配置(仅在项目内部合约,由管理员设置),并同样受总费率上限约束。
    • 严格校验 royaltyAmount <= salePrice,防止异常合约返回。

代码示例(结算片段):

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {IERC2981} from "@openzeppelin/contracts/interfaces/IERC2981.sol";

interface IEscrow {
    function _queue(address to, uint256 amount) external;
}

library RoyaltyLib {
    event RoyaltyPaid(address indexed nft, uint256 indexed tokenId, address indexed receiver, uint256 amount);
    event PlatformFeePaid(address indexed to, uint256 amount);

    function settleWithRoyalty(
        address nft,
        uint256 tokenId,
        uint256 salePrice,
        uint96 platformFeeBps,
        address feeRecipient
    ) internal returns (uint256 sellerAmount, address royaltyReceiver, uint256 royaltyAmount, uint256 platformFee) {
        platformFee = (salePrice * platformFeeBps) / 10_000;
        royaltyReceiver = address(0);
        royaltyAmount = 0;

        // EIP-2981 查询
        if (_supports2981(nft)) {
            (address rcv, uint256 amt) = IERC2981(nft).royaltyInfo(tokenId, salePrice);
            if (rcv != address(0) && amt > 0) {
                require(amt <= salePrice, "royalty too high");
                royaltyReceiver = rcv;
                royaltyAmount = amt;
            }
        }

        // 总费率约束(推导出royaltyBps再校验)
        uint256 impliedRoyaltyBps = (royaltyAmount * 10_000) / salePrice;
        require(platformFeeBps + impliedRoyaltyBps <= 10_000, "total fee > 100%");

        sellerAmount = salePrice - platformFee - royaltyAmount;

        // 事件由上层合约负责触发
    }

    function _supports2981(address nft) private view returns (bool) {
        (bool ok, bytes memory data) = nft.staticcall{gas: 30000}(abi.encodeWithSignature("supportsInterface(bytes4)", type(IERC2981).interfaceId));
        return ok && data.length >= 32 && abi.decode(data, (bool));
    }
}

// 在结算函数中:
// (sellerAmt, rcv, rAmt, fee) = RoyaltyLib.settleWithRoyalty(nft, tokenId, price, platformFeeBps, feeRecipient);
// if (fee > 0) _queue(feeRecipient, fee);
// if (rcv != address(0) && rcv != seller) _queue(rcv, rAmt); else sellerAmt += rAmt;
// _queue(seller, sellerAmt);

配置说明:

  • 平台费上限(示例):platformFeeBps <= 500。
  • 事件建议:
    • emit RoyaltyPaid(nft, tokenId, royaltyReceiver, royaltyAmount)
    • emit PlatformFeePaid(feeRecipient, platformFee)
    • 结合 Trade/AuctionSettled 事件实现全链路可追踪。

最佳实践 5:测试、静态分析与链上仿真告警(Foundry + Slither + Tenderly)

  • 核心价值:在上线公测前,用自动化手段发现边界条件与经济安全问题,持续监控关键交易行为。
  • 具体措施:
    • Foundry:单元测试、模糊测试(fuzz)、不变性(invariant)测试;覆盖结算金额守恒、重入防护、退款不丢失。
    • Slither:静态检测可重入点、可见性问题、tx.origin、调用顺序等。
    • Tenderly:主网/测试网仿真,创建大额成交失败/异常率告警。
  • 实施要点:
    • 将 Slither 与 Foundry 集成到 CI,PR 必须通过。
    • 对关键属性编写 invariants:队列余额之和 <= 合约ETH余额;任何时刻不会出现负数溢出;订单一旦成交不可再次成交。

Foundry 配置(forge.toml):

[profile.default]
src = "src"
out = "out"
libs = ["lib"]
optimizer = true
optimizer_runs = 500
fuzz_runs = 256
invariant_runs = 64
solc_version = "0.8.24"
evm_version = "paris"

Foundry 测试示例(资金守恒与重入):

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "forge-std/Test.sol";
import "../src/Marketplace.sol";

contract Reenter is Test {
    Marketplace mkt;
    bool attack;

    receive() external payable {
        if (attack) {
            // 尝试在回调中重入(应被 nonReentrant 阻止)
            vm.expectRevert();
            // 这里若调用 mkt.buy(...) 应失败;示例仅说明意图
        }
    }
}

contract MarketplaceInvariantTest is Test {
    Marketplace mkt;

    function setUp() public {
        mkt = new Marketplace(250, address(0xFEE));
        // 更多初始化...
    }

    function testFuzz_BuySumEqualsEscrow(uint256 price) public {
        price = bound(price, 1e9, 10 ether);
        // 构造订单并买入...
        // 校验:卖家+平台+版税排队金额之和 == msg.value(或余数归卖家)
    }

    function invariant_EscrowNotExceedBalance() public {
        // 校验:所有 balances 的总和 <= address(mkt).balance
    }
}

Slither 基本使用与配置(slither.config.json):

{
  "seed": 123,
  "solc-remaps": [
    "@openzeppelin/=lib/openzeppelin-contracts/",
    "forge-std/=lib/forge-std/src/"
  ],
  "filter-paths": ["lib/", "test/"],
  "disable": ["reentrancy-benign"], 
  "checklist": true
}

命令:

  • slither . —打印完整报告
  • slither . --print human-summary
  • 将报告纳入 CI,若发现高危(reentrancy、unchecked-transfer、tx-origin)则阻断合并

Tenderly 基本配置(tenderly.yaml):

project_slug: your-org/your-market
simulator:
  access_key: ${TENDERLY_ACCESS_KEY}
  fork_network_id: "5"   # Goerli 或对应测试网
  block_number: latest
alerts:
  - name: High Revert Rate
    type: tx_reverts_rate
    threshold: 5
    window_minutes: 10

用法建议:

  • 部署前在模拟分叉上重放高并发买入/结算/出价操作。
  • 创建大额交易失败率告警、异常 gas 激增告警,及时回滚或暂停合约。

总结与后续建议

  • 本指南围绕资金安全、签名懒铸、拍卖机制、版税结算与测试监控给出系统化落地方案,契合 Solidity 0.8.x + ERC721A + OpenZeppelin + Hardhat/Foundry/Slither/Tenderly 技术栈与中等级别安全要求。
  • 上线公测前务必:
    1. 完成全覆盖单元/模糊/不变性测试与Slither零高危输出;
    2. 在Tenderly进行高并发与极端参数仿真;
    3. 配置Pausable与权限密钥分离(HSM/多签);
    4. 明确平台费与版税上限策略并链上强制;
    5. 开启公开漏洞赏金(测试网阶段),收集社区反馈。

如后续计划引入可升级合约,请使用OpenZeppelin Upgradeable组件并遵循存储布局稳定与初始化函数一次性调用的规范,配合多签管控升级权限与延时执行,降低运维风险。

示例详情

📖 如何使用

30秒出活:复制 → 粘贴 → 搞定
与其花几十分钟和AI聊天、试错,不如直接复制这些经过千人验证的模板,修改几个 {{变量}} 就能立刻获得专业级输出。省下来的时间,足够你轻松享受两杯咖啡!
加载中...
💬 不会填参数?让 AI 反过来问你
不确定变量该填什么?一键转为对话模式,AI 会像资深顾问一样逐步引导你,问几个问题就能自动生成完美匹配你需求的定制结果。零门槛,开口就行。
转为对话模式
🚀 告别复制粘贴,Chat 里直接调用
无需切换,输入 / 唤醒 8000+ 专家级提示词。 插件将全站提示词库深度集成于 Chat 输入框。基于当前对话语境,系统智能推荐最契合的 Prompt 并自动完成参数化,让海量资源触手可及,从此彻底告别"手动搬运"。
即将推出
🔌 接口一调,提示词自己会进化
手动跑一次还行,跑一百次呢?通过 API 接口动态注入变量,接入批量评价引擎,让程序自动迭代出更高质量的提示词方案。Prompt 会自己进化,你只管收结果。
发布 API
🤖 一键变成你的专属 Agent 应用
不想每次都配参数?把这条提示词直接发布成独立 Agent,内嵌图片生成、参数优化等工具,分享链接就能用。给团队或客户一个"开箱即用"的完整方案。
创建 Agent

✅ 特性总结

一键生成针对项目场景的安全实践方案,结合合约类型与技术栈,落地即可执行。
自动识别常见风险点并给出修复路径,减少审计遗漏,降低上线前返工成本。
提供可直接粘贴的措施与配置模板,缩短评审流程,帮助团队快速对齐规范。
结合DeFi与NFT等业务场景输出专属建议,避免清单化套路,保障关键特性不被忽略。
支持代码审计、权限设计与风控策略联动输出,让安全与产品迭代节奏同频推进。
以步骤化实施要点与检查清单辅助分工,便于追踪整改进度,确保结果可复验可交付。
结构化文档适配培训与复盘,沉淀标准作业流程,帮助团队形成长期安全基线。
参数化输入控制建议力度与深度,适配不同安全等级与预算,兼顾速度与质量。
附带示例片段与常见误用对照,帮助新人避坑,资深工程师高效回归与复查。

🎯 解决的问题

用一条可复用的“安全实践生成器”提示词,帮助Web3团队在立项、开发、审计前与上线后,快速产出针对自身场景的5项智能合约安全最佳实践清单。重点解决“不知道从哪开始、缺标准、落地难”的痛点,形成可执行的安全流程:从风险识别到权限设计,从代码审查到安全测试,从发布管控到应急预案。通过对开发场景、合约类型、技术栈与安全等级的自定义输入,自动匹配适合你的做法与检查点,减少返工、缩短审计准备周期,降低线上事故风险,并沉淀为团队规范与培训材料,促进从试用到采购团队版模板库与持续更新服务的转化。

🕒 版本历史

当前版本
v2.1 2024-01-15
优化输出结构,增强情节连贯性
  • ✨ 新增章节节奏控制参数
  • 🔧 优化人物关系描述逻辑
  • 📝 改进主题深化引导语
  • 🎯 增强情节转折点设计
v2.0 2023-12-20
重构提示词架构,提升生成质量
  • 🚀 全新的提示词结构设计
  • 📊 增加输出格式化选项
  • 💡 优化角色塑造引导
v1.5 2023-11-10
修复已知问题,提升稳定性
  • 🐛 修复长文本处理bug
  • ⚡ 提升响应速度
v1.0 2023-10-01
首次发布
  • 🎉 初始版本上线
COMING SOON
版本历史追踪,即将启航
记录每一次提示词的进化与升级,敬请期待。

💬 用户评价

4.8
⭐⭐⭐⭐⭐
基于 28 条评价
5星
85%
4星
12%
3星
3%
👤
电商运营 - 张先生
⭐⭐⭐⭐⭐ 2025-01-15
双十一用这个提示词生成了20多张海报,效果非常好!点击率提升了35%,节省了大量设计时间。参数调整很灵活,能快速适配不同节日。
效果好 节省时间
👤
品牌设计师 - 李女士
⭐⭐⭐⭐⭐ 2025-01-10
作为设计师,这个提示词帮我快速生成创意方向,大大提升了工作效率。生成的海报氛围感很强,稍作调整就能直接使用。
创意好 专业
COMING SOON
用户评价与反馈系统,即将上线
倾听真实反馈,在这里留下您的使用心得,敬请期待。
加载中...
📋
提示词复制
在当前页面填写参数后直接复制: