solidity-security

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Solidity Security

Solidity 智能合约安全

Vulnerability prevention, secure patterns, gas-safe optimizations, audit preparation.

漏洞预防、安全编码模式、安全的Gas优化以及审计准备。

Code Style Rules

代码风格规则

No Unicode Separator Comments

禁止使用Unicode分隔符注释

Never use Unicode box-drawing characters (
,
,
, etc.) as comment decorators or section separators in generated code. This includes patterns like:
// ── State ─────────────────────────────────────────
// ══ Errors ═════════════════════════════════════════
These are AI slop. They carry no semantic value, are invisible noise in diffs, and mark generated code as low-quality. Use plain labels or nothing at all:
solidity
// State
mapping(address => uint256) public balances;

// Errors
error InsufficientBalance();

在生成的代码中,切勿使用Unicode方框绘制字符(
等)作为注释装饰符或章节分隔符。这包括以下这类模式:
// ── State ─────────────────────────────────────────
// ══ Errors ═════════════════════════════════════════
这些是AI生成的冗余内容,不具备任何语义价值,在代码差异对比中是无效噪音,会让生成的代码显得质量低下。请使用普通标签或直接省略:
solidity
// State
mapping(address => uint256) public balances;

// Errors
error InsufficientBalance();

Vulnerabilities & Secure Patterns

漏洞与安全模式

1. Reentrancy

1. 重入漏洞

External call before state update lets an attacker re-enter mid-execution.
Vulnerable:
solidity
function withdraw() public {
    uint256 amount = balances[msg.sender];
    (bool ok, ) = msg.sender.call{value: amount}("");
    require(ok);
    balances[msg.sender] = 0; // state update after call
}
Secure - CEI + ReentrancyGuard:
solidity
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";

contract Vault is ReentrancyGuard {
    // Errors
    error InsufficientBalance();
    error TransferFailed();

    function withdraw(uint256 amount) external nonReentrant {
        if (balances[msg.sender] < amount) revert InsufficientBalance();

        balances[msg.sender] -= amount;

        (bool ok, ) = msg.sender.call{value: amount}("");
        if (!ok) revert TransferFailed();
    }
}
Cross-function reentrancy: attacker re-enters a different function that reads stale state. Apply
nonReentrant
to all functions sharing mutable state, not just the one with the external call.
在更新状态前进行外部调用会让攻击者在执行过程中重新进入合约。
存在漏洞的代码:
solidity
function withdraw() public {
    uint256 amount = balances[msg.sender];
    (bool ok, ) = msg.sender.call{value: amount}("");
    require(ok);
    balances[msg.sender] = 0; // state update after call
}
安全实现 - CEI模式 + ReentrancyGuard:
solidity
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";

contract Vault is ReentrancyGuard {
    // Errors
    error InsufficientBalance();
    error TransferFailed();

    function withdraw(uint256 amount) external nonReentrant {
        if (balances[msg.sender] < amount) revert InsufficientBalance();

        balances[msg.sender] -= amount;

        (bool ok, ) = msg.sender.call{value: amount}("");
        if (!ok) revert TransferFailed();
    }
}
跨函数重入:攻击者重新进入另一个读取过期状态的函数。对所有共享可变状态的函数都应应用
nonReentrant
修饰符,而不仅仅是包含外部调用的函数。

2. Access Control

2. 访问控制

Vulnerable:
solidity
function withdraw(uint256 amount) public {
    payable(msg.sender).transfer(amount);
}
Secure:
solidity
import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol";
import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";

// Two-step transfer prevents accidental ownership loss
contract SimpleAccess is Ownable2Step {
    function emergencyWithdraw() external onlyOwner { /* ... */ }
}

// Role-based for multi-actor systems
contract RoleAccess is AccessControl {
    bytes32 public constant OPERATOR = keccak256("OPERATOR");
    function sensitiveOp() external onlyRole(OPERATOR) { /* ... */ }
}
  • Never
    tx.origin
    for auth - only
    msg.sender
  • Ownable2Step
    over
    Ownable
  • Validate
    address(0)
    on all address parameters
存在漏洞的代码:
solidity
function withdraw(uint256 amount) public {
    payable(msg.sender).transfer(amount);
}
安全实现:
solidity
import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol";
import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";

// 两步转移机制可防止意外丢失所有权
contract SimpleAccess is Ownable2Step {
    function emergencyWithdraw() external onlyOwner { /* ... */ }
}

// 基于角色的控制适用于多参与者系统
contract RoleAccess is AccessControl {
    bytes32 public constant OPERATOR = keccak256("OPERATOR");
    function sensitiveOp() external onlyRole(OPERATOR) { /* ... */ }
}
  • 切勿使用
    tx.origin
    进行身份验证,仅使用
    msg.sender
  • 优先使用
    Ownable2Step
    而非
    Ownable
  • 对所有地址参数都要验证是否为
    address(0)

3. Integer Overflow / Underflow

3. 整数溢出/下溢

Solidity >= 0.8.0 has checked arithmetic by default. For
unchecked
blocks, the surrounding logic must prove bounds:
solidity
uint256 len = arr.length;
for (uint256 i; i < len; ) {
    // i < len < type(uint256).max, so ++i cannot overflow
    unchecked { ++i; }
}
Pre-0.8.0: Use
SafeMath
. There is no reason to target < 0.8.0 for new contracts.
Solidity >= 0.8.0版本默认启用算术检查。对于
unchecked
代码块,其外围逻辑必须能证明边界安全:
solidity
uint256 len = arr.length;
for (uint256 i; i < len; ) {
    // i < len < type(uint256).max,因此++i不会溢出
    unchecked { ++i; }
}
0.8.0之前的版本: 使用
SafeMath
。新合约没有理由再针对<0.8.0的版本开发。

4. Front-Running / MEV

4. 抢先交易/MEV

Vulnerable:
solidity
function swap(uint256 amount, uint256 minOutput) public {
    uint256 output = calculateOutput(amount);
    require(output >= minOutput, "Slippage");
}
Secure - Commit-Reveal:
solidity
// State
mapping(bytes32 => uint256) public commitBlock;
uint256 public constant REVEAL_DELAY = 1;

// Errors
error NoCommitment();
error RevealTooEarly();

function commit(bytes32 hash) external {
    commitBlock[hash] = block.number;
}

function reveal(uint256 amount, uint256 minOutput, bytes32 secret) external {
    bytes32 hash = keccak256(abi.encodePacked(msg.sender, amount, minOutput, secret));
    if (commitBlock[hash] == 0) revert NoCommitment();
    if (block.number <= commitBlock[hash] + REVEAL_DELAY) revert RevealTooEarly();
    delete commitBlock[hash];
}
Other mitigations: Flashbots Protect / MEV Blocker, slippage + deadline params, batch auctions (CoW Protocol).
存在漏洞的代码:
solidity
function swap(uint256 amount, uint256 minOutput) public {
    uint256 output = calculateOutput(amount);
    require(output >= minOutput, "Slippage");
}
安全实现 - 提交-披露模式:
solidity
// State
mapping(bytes32 => uint256) public commitBlock;
uint256 public constant REVEAL_DELAY = 1;

// Errors
error NoCommitment();
error RevealTooEarly();

function commit(bytes32 hash) external {
    commitBlock[hash] = block.number;
}

function reveal(uint256 amount, uint256 minOutput, bytes32 secret) external {
    bytes32 hash = keccak256(abi.encodePacked(msg.sender, amount, minOutput, secret));
    if (commitBlock[hash] == 0) revert NoCommitment();
    if (block.number <= commitBlock[hash] + REVEAL_DELAY) revert RevealTooEarly();
    delete commitBlock[hash];
}
其他缓解措施:Flashbots Protect/MEV Blocker、滑点+截止时间参数、批量拍卖(如CoW Protocol)。

5. Unchecked External Calls

5. 未检查的外部调用

Some tokens (USDT) don't return
bool
- raw
.transfer()
silently fails.
solidity
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

contract TokenVault {
    using SafeERC20 for IERC20;

    function send(IERC20 token, address to, uint256 amount) internal {
        token.safeTransfer(to, amount);
    }
}
Always
SafeERC20
for token operations.
部分代币(如USDT)不会返回
bool
值——直接使用
.transfer()
会静默失败。
solidity
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

contract TokenVault {
    using SafeERC20 for IERC20;

    function send(IERC20 token, address to, uint256 amount) internal {
        token.safeTransfer(to, amount);
    }
}
所有代币操作都应始终使用
SafeERC20

6. Oracle Manipulation

6. 预言机操控

RiskMitigation
Spot price manipulationTWAP over multiple blocks
Single oracle failureMultiple independent oracles, median
Stale dataFreshness check on
updatedAt
Flash loan attackChainlink
latestRoundData
+ sanity bounds
solidity
error InvalidPrice();
error StaleOracle();

uint256 public constant MAX_STALENESS = 1 hours;

function getPrice(AggregatorV3Interface feed) internal view returns (uint256) {
    (, int256 price, , uint256 updatedAt, ) = feed.latestRoundData();
    if (price <= 0) revert InvalidPrice();
    if (block.timestamp - updatedAt > MAX_STALENESS) revert StaleOracle();
    return uint256(price);
}
风险缓解措施
现货价格操控多区块时间加权平均价格(TWAP)
单一预言机故障多个独立预言机,取中位数
数据过时检查
updatedAt
的新鲜度
闪电贷攻击Chainlink
latestRoundData
+ 合理性边界
solidity
error InvalidPrice();
error StaleOracle();

uint256 public constant MAX_STALENESS = 1 hours;

function getPrice(AggregatorV3Interface feed) internal view returns (uint256) {
    (, int256 price, , uint256 updatedAt, ) = feed.latestRoundData();
    if (price <= 0) revert InvalidPrice();
    if (block.timestamp - updatedAt > MAX_STALENESS) revert StaleOracle();
    return uint256(price);
}

7. Proxy / Upgrade Pitfalls

7. 代理/升级陷阱

RiskPrevention
Storage collisionEIP-1967 slots, OZ upgrades plugin
Uninitialized proxy
initialize()
in same tx as deploy
Selector clash
TransparentUpgradeableProxy
or UUPS
Re-initialization
_disableInitializers()
in constructor
solidity
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
    _disableInitializers();
}
风险预防措施
存储冲突EIP-1967插槽、OpenZeppelin升级插件
未初始化的代理在部署交易中调用
initialize()
选择器冲突使用
TransparentUpgradeableProxy
或UUPS
重复初始化在构造函数中调用
_disableInitializers()
solidity
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
    _disableInitializers();
}

8. Signature Replay

8. 签名重放

solidity
error InvalidSignature();
error NonceAlreadyUsed();

mapping(bytes32 => bool) public usedNonces;

function executeWithSig(
    address signer, uint256 amount, bytes32 nonce, bytes calldata sig
) external {
    if (usedNonces[nonce]) revert NonceAlreadyUsed();

    bytes32 digest = keccak256(abi.encodePacked(
        "\x19\x01", DOMAIN_SEPARATOR, keccak256(abi.encode(signer, amount, nonce))
    ));

    if (ECDSA.recover(digest, sig) != signer) revert InvalidSignature();

    usedNonces[nonce] = true;
}
Use EIP-712 typed data + nonce +
block.chainid
in the domain separator.

solidity
error InvalidSignature();
error NonceAlreadyUsed();

mapping(bytes32 => bool) public usedNonces;

function executeWithSig(
    address signer, uint256 amount, bytes32 nonce, bytes calldata sig
) external {
    if (usedNonces[nonce]) revert NonceAlreadyUsed();

    bytes32 digest = keccak256(abi.encodePacked(
        "\x19\x01", DOMAIN_SEPARATOR, keccak256(abi.encode(signer, amount, nonce))
    ));

    if (ECDSA.recover(digest, sig) != signer) revert InvalidSignature();

    usedNonces[nonce] = true;
}
在域分隔符中使用EIP-712类型数据 + 随机数(nonce) +
block.chainid

Design Patterns

设计模式

Pull Over Push

拉取模式替代推送模式

solidity
// State
mapping(address => uint256) public pending;

// Errors
error NothingToWithdraw();
error TransferFailed();

function recordPayment(address recipient, uint256 amount) internal {
    pending[recipient] += amount;
}

function withdraw() external {
    uint256 amount = pending[msg.sender];
    if (amount == 0) revert NothingToWithdraw();
    pending[msg.sender] = 0;
    (bool ok, ) = msg.sender.call{value: amount}("");
    if (!ok) revert TransferFailed();
}
solidity
// State
mapping(address => uint256) public pending;

// Errors
error NothingToWithdraw();
error TransferFailed();

function recordPayment(address recipient, uint256 amount) internal {
    pending[recipient] += amount;
}

function withdraw() external {
    uint256 amount = pending[msg.sender];
    if (amount == 0) revert NothingToWithdraw();
    pending[msg.sender] = 0;
    (bool ok, ) = msg.sender.call{value: amount}("");
    if (!ok) revert TransferFailed();
}

Emergency Stop

紧急停止

solidity
import {PausableUpgradeable} from
    "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";

contract Protocol is PausableUpgradeable, OwnableUpgradeable {
    function deposit() external payable whenNotPaused { /* ... */ }
    function pause() external onlyOwner { _pause(); }
    function unpause() external onlyOwner { _unpause(); }
}
solidity
import {PausableUpgradeable} from
    "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";

contract Protocol is PausableUpgradeable, OwnableUpgradeable {
    function deposit() external payable whenNotPaused { /* ... */ }
    function pause() external onlyOwner { _pause(); }
    function unpause() external onlyOwner { _unpause(); }
}

Input Validation

输入验证

solidity
error ZeroAddress();
error ZeroAmount();
error InsufficientBalance(uint256 available, uint256 requested);

function transfer(address to, uint256 amount) external {
    if (to == address(0)) revert ZeroAddress();
    if (amount == 0) revert ZeroAmount();
    if (balances[msg.sender] < amount) {
        revert InsufficientBalance(balances[msg.sender], amount);
    }
    balances[msg.sender] -= amount;
    balances[to] += amount;
}

solidity
error ZeroAddress();
error ZeroAmount();
error InsufficientBalance(uint256 available, uint256 requested);

function transfer(address to, uint256 amount) external {
    if (to == address(0)) revert ZeroAddress();
    if (amount == 0) revert ZeroAmount();
    if (balances[msg.sender] < amount) {
        revert InsufficientBalance(balances[msg.sender], amount);
    }
    balances[msg.sender] -= amount;
    balances[to] += amount;
}

Gas Optimization

Gas优化

Never sacrifice correctness for gas. Every
unchecked
block must have a provable safety invariant.
切勿为了Gas消耗牺牲正确性。每个
unchecked
代码块都必须有可证明的安全不变量。

Storage Packing

存储打包

solidity
// 1 slot (32 bytes)
struct Packed {
    uint128 balance;
    uint64  lastUpdate;
    uint64  nonce;
}
solidity
// 1个插槽(32字节)
struct Packed {
    uint128 balance;
    uint64  lastUpdate;
    uint64  nonce;
}

Calldata Over Memory

使用Calldata而非Memory

solidity
function process(uint256[] calldata data) external pure returns (uint256) {
    return data[0];
}
solidity
function process(uint256[] calldata data) external pure returns (uint256) {
    return data[0];
}

Custom Errors Over Revert Strings

自定义错误替代回退字符串

Custom errors (Solidity >= 0.8.4) are cheaper than string reverts and encode structured data.
solidity
error WithdrawalExceedsBalance(uint256 requested, uint256 available);

function withdraw(uint256 amount) external {
    if (amount > address(this).balance) {
        revert WithdrawalExceedsBalance(amount, address(this).balance);
    }
}
自定义错误(Solidity >= 0.8.4)比字符串回退更便宜,且能编码结构化数据。
solidity
error WithdrawalExceedsBalance(uint256 requested, uint256 available);

function withdraw(uint256 amount) external {
    if (amount > address(this).balance) {
        revert WithdrawalExceedsBalance(amount, address(this).balance);
    }
}

Events for Off-Chain Data

使用事件存储链下数据

solidity
event DataStored(address indexed user, uint256 indexed id, bytes data);

function storeData(uint256 id, bytes calldata data) external {
    emit DataStored(msg.sender, id, data);
}
Only persist to storage what on-chain logic actually reads.

solidity
event DataStored(address indexed user, uint256 indexed id, bytes data);

function storeData(uint256 id, bytes calldata data) external {
    emit DataStored(msg.sender, id, data);
}
仅将链上逻辑实际需要读取的数据持久化到存储中。

Security Tooling

安全工具

CategoryToolPurpose
Static analysisSlitherDetector suite for common vulns
Static analysisAderynRust-based, Foundry-native
FuzzingEchidnaProperty-based Solidity fuzzer
FuzzingMedusaGo-based alternative to Echidna
Formal verificationCertoraProver for critical invariants
Formal verificationHalmosSymbolic execution for Foundry
SMTSMTCheckerBuilt-in bounded model checker
类别工具用途
静态分析Slither常见漏洞检测套件
静态分析Aderyn基于Rust、原生支持Foundry的工具
模糊测试Echidna基于属性的Solidity模糊测试工具
模糊测试Medusa基于Go的Echidna替代工具
形式化验证Certora关键不变量验证器
形式化验证Halmos针对Foundry的符号执行工具
SMT检查SMTChecker内置的有界模型检查器

Minimum CI Pipeline

最小化CI流水线

bash
slither . --filter-paths "node_modules|lib"
forge test --fuzz-runs 10000
forge snapshot --check

bash
slither . --filter-paths "node_modules|lib"
forge test --fuzz-runs 10000
forge snapshot --check

Testing for Security (Foundry)

安全测试(Foundry)

solidity
import "forge-std/Test.sol";

contract SecurityTest is Test {
    Vault vault;
    address attacker = makeAddr("attacker");

    function setUp() public {
        vault = new Vault();
        vm.deal(address(vault), 10 ether);
    }

    function test_RevertWhen_ReentrancyAttempted() public {
        ReentrancyAttacker exploit = new ReentrancyAttacker(address(vault));
        vm.deal(address(exploit), 1 ether);
        vm.expectRevert();
        exploit.attack();
    }

    function test_RevertWhen_UnauthorizedWithdraw() public {
        vm.prank(attacker);
        vm.expectRevert(Vault.Unauthorized.selector);
        vault.emergencyWithdraw();
    }

    function testFuzz_TransferNeverExceedsBalance(uint256 amount) public {
        vm.assume(amount > 0 && amount <= vault.balanceOf(address(this)));
        vault.transfer(attacker, amount);
        assertEq(vault.balanceOf(attacker), amount);
    }
}

solidity
import "forge-std/Test.sol";

contract SecurityTest is Test {
    Vault vault;
    address attacker = makeAddr("attacker");

    function setUp() public {
        vault = new Vault();
        vm.deal(address(vault), 10 ether);
    }

    function test_RevertWhen_ReentrancyAttempted() public {
        ReentrancyAttacker exploit = new ReentrancyAttacker(address(vault));
        vm.deal(address(exploit), 1 ether);
        vm.expectRevert();
        exploit.attack();
    }

    function test_RevertWhen_UnauthorizedWithdraw() public {
        vm.prank(attacker);
        vm.expectRevert(Vault.Unauthorized.selector);
        vault.emergencyWithdraw();
    }

    function testFuzz_TransferNeverExceedsBalance(uint256 amount) public {
        vm.assume(amount > 0 && amount <= vault.balanceOf(address(this)));
        vault.transfer(attacker, amount);
        assertEq(vault.balanceOf(attacker), amount);
    }
}

Audit Preparation

审计准备

Code Quality

代码质量

  • NatSpec on all public/external functions (
    @notice
    ,
    @dev
    ,
    @param
    ,
    @return
    )
  • CEI on every state-changing function with external calls
  • nonReentrant
    on functions sharing mutable state
  • SafeERC20
    for all token operations
  • Custom errors - no revert strings
  • No
    tx.origin
    , no
    block.timestamp
    randomness, no on-chain secrets
  • 所有公开/外部函数添加NatSpec注释(
    @notice
    @dev
    @param
    @return
  • 所有包含外部调用的状态变更函数遵循CEI模式
  • 对所有共享可变状态的函数添加
    nonReentrant
    修饰符
  • 所有代币操作使用
    SafeERC20
  • 使用自定义错误,避免回退字符串
  • 不使用
    tx.origin
    、不依赖
    block.timestamp
    生成随机数、不存储链上密钥

Testing

测试

  • Unit tests: every function, every revert path
  • Fuzz tests: property-based for numeric/state edges
  • Invariant tests: global properties that must always hold
  • Fork tests: integration against mainnet state
  • Static analysis clean (Slither + Aderyn, zero high/medium)
  • 单元测试:覆盖每个函数、每个回退路径
  • 模糊测试:针对数值/状态边界的基于属性测试
  • 不变量测试:验证必须始终成立的全局属性
  • 分叉测试:针对主网状态的集成测试
  • 静态分析无高/中危问题(Slither + Aderyn)

Documentation

文档

  • Architecture overview with contract interaction diagram
  • Threat model: what is trusted, what is adversarial
  • Known limitations and design trade-offs
  • Deployment and upgrade runbook
  • Previous audit reports (if any)
  • 架构概述及合约交互图
  • 威胁模型:信任范围与对抗场景
  • 已知限制与设计权衡
  • 部署与升级手册
  • 过往审计报告(如有)

Deployment

部署

  • Access control verified and documented
  • Upgrade path tested end-to-end (if proxy)
  • Testnet deployment validated
  • Emergency pause mechanism tested
  • Multi-sig or timelock on admin functions

  • 访问控制已验证并记录
  • 升级路径已端到端测试(如使用代理)
  • 测试网部署已验证
  • 紧急暂停机制已测试
  • 管理函数使用多签或时间锁

NatSpec Template

NatSpec模板

solidity
/// @title  Vault - Collateralized lending vault
/// @notice Accepts collateral deposits and issues vault shares.
/// @dev    UUPS-upgradeable. Tiered fee schedule per ADR-018.
contract Vault {
    /// @notice Deposit collateral into the vault.
    /// @param  token   Collateral token address.
    /// @param  amount  Deposit amount (must be > 0).
    /// @return shares  Vault shares minted.
    function deposit(address token, uint256 amount) external returns (uint256 shares) {
        // ...
    }
}

solidity
/// @title  Vault - 抵押借贷金库
/// @notice 接受抵押品存入并发行金库份额。
/// @dev    基于UUPS可升级。遵循ADR-018的分层费率机制。
contract Vault {
    /// @notice 将抵押品存入金库。
    /// @param  token   抵押品代币地址。
    /// @param  amount  存入金额(必须>0)。
    /// @return shares  铸造的金库份额数量。
    function deposit(address token, uint256 amount) external returns (uint256 shares) {
        // ...
    }
}

Quick Reference

快速参考

VulnerabilityFix
ReentrancyCEI +
ReentrancyGuard
Missing access control
Ownable2Step
/
AccessControl
Unchecked ERC20 return
SafeERC20
Oracle manipulationTWAP + freshness check + sanity bounds
FrontrunningCommit-reveal, slippage + deadline params
Proxy storage collisionEIP-1967, OZ upgrades plugin
tx.origin
auth
msg.sender
On-chain randomnessChainlink VRF
Unbounded loop DoSPagination or pull pattern
Signature replayEIP-712 + nonce +
block.chainid
Flash loan price manipulationTWAP, multiple oracles
Push-payment DoSPull-over-push
Delegatecall to untrustedNever; or restrict target via allowlist
漏洞修复方案
重入漏洞CEI模式 +
ReentrancyGuard
缺失访问控制
Ownable2Step
/
AccessControl
未检查ERC20返回值
SafeERC20
预言机操控TWAP + 新鲜度检查 + 合理性边界
抢先交易提交-披露模式、滑点+截止时间参数
代理存储冲突EIP-1967、OpenZeppelin升级插件
tx.origin
身份验证
使用
msg.sender
链上随机数Chainlink VRF
无界循环DoS分页或拉取模式
签名重放EIP-712 + 随机数 +
block.chainid
闪电贷价格操控TWAP、多预言机
推送支付DoS拉取模式替代推送模式
委托调用不可信合约禁止;或通过白名单限制目标地址