热门角色不仅是灵感来源,更是你的效率助手。通过精挑细选的角色提示词,你可以快速生成高质量内容、提升创作灵感,并找到最契合你需求的解决方案。让创作更轻松,让价值更直接!
我们根据不同用户需求,持续更新角色库,让你总能找到合适的灵感入口。
本提示词专为区块链开发者设计,提供系统化的智能合约安全开发最佳实践指导。通过角色扮演区块链开发专家,结合具体开发场景,输出5项核心安全实践方案,涵盖代码审计、权限控制、漏洞防护等关键领域。提示词采用技术文档写作风格,确保内容精确、清晰且具有实操性,帮助开发者构建更安全的去中心化应用。
总体安全原则概述
5项最佳实践
实践1:角色治理与参数安全(AccessControl + Timelock + Pausable)
// 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 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 + 数据有效性/时效性/对齐)
// 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.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 + 余额差分)
// 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.toml
[profile.default]
fuzz_runs = 10000
ffi = false
invariant_runs = 500
实践4:利率模型与计息精度(单调性、不溢出、Wad/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 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 run
slither . --exclude-low --print human-summary --solc-remaps @openzeppelin=node_modules/@openzeppelin
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);
}
}
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;
}
}
总结与后续建议
通过以上五项实践,在Solidity 0.8.x + OpenZeppelin + Foundry + Hardhat + Slither + Echidna + Chainlink栈上,能以高可操作性和可验证性提升去中心化借贷协议在主网的安全性,显著降低重入、价格异常、参数误用与会计错误等核心风险。
代码示例(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();
}
}
代码示例(核心校验逻辑,省略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结合:资金进入提现队列)
}
}
配置说明(前端/服务端签名要点):
代码示例:
// 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 {
// 流拍,无需转移
}
}
}
配置说明:
代码示例(结算片段):
// 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);
配置说明:
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
}
命令:
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
用法建议:
如后续计划引入可升级合约,请使用OpenZeppelin Upgradeable组件并遵循存储布局稳定与初始化函数一次性调用的规范,配合多签管控升级权限与延时执行,降低运维风险。
用一条可复用的“安全实践生成器”提示词,帮助Web3团队在立项、开发、审计前与上线后,快速产出针对自身场景的5项智能合约安全最佳实践清单。重点解决“不知道从哪开始、缺标准、落地难”的痛点,形成可执行的安全流程:从风险识别到权限设计,从代码审查到安全测试,从发布管控到应急预案。通过对开发场景、合约类型、技术栈与安全等级的自定义输入,自动匹配适合你的做法与检查点,减少返工、缩短审计准备周期,降低线上事故风险,并沉淀为团队规范与培训材料,促进从试用到采购团队版模板库与持续更新服务的转化。
在编码前一键生成五项关键实践,按步骤完成自测与评审,降低返工率,加速从开发到上线的完整交付。
作为审计前预筛工具,快速形成检查要点与高风险清单,搭建整改建议框架,提升审计沟通与交付效率。
快速制定团队级安全规范与优先级,拆解冲刺任务与验收标准,为融资与第三方审计准备合规材料,控制成本。
将模板生成的提示词复制粘贴到您常用的 Chat 应用(如 ChatGPT、Claude 等),即可直接对话使用,无需额外开发。适合个人快速体验和轻量使用场景。
把提示词模板转化为 API,您的程序可任意修改模板参数,通过接口直接调用,轻松实现自动化与批量处理。适合开发者集成与业务系统嵌入。
在 MCP client 中配置对应的 server 地址,让您的 AI 应用自动调用提示词模板。适合高级用户和团队协作,让提示词在不同 AI 工具间无缝衔接。
半价获取高级提示词-优惠即将到期