Skip to content

Latest commit

 

History

History
262 lines (205 loc) · 13 KB

File metadata and controls

262 lines (205 loc) · 13 KB

codeex 2026-03-20 合约与文档审计报告

范围与方法

本次审计为“静态+文档一致性”快速评审,聚焦核心合约逻辑、自洽性、稳定性、gas 效率、部署流程与测试覆盖说明。未运行测试、未做链上复核、未执行静态分析工具。

已查看的关键文件包括:

  • contracts/src/paymasters/v4/PaymasterBase.sol
  • contracts/src/paymasters/v4/Paymaster.sol
  • contracts/src/paymasters/v4/core/PaymasterFactory.sol
  • contracts/src/paymasters/superpaymaster/v3/SuperPaymaster.sol
  • contracts/src/tokens/xPNTsToken.sol
  • contracts/src/modules/validators/BLSValidator.sol
  • README.mdCLAUDE.mddocs/ 中与部署/测试/安全相关的说明

总结

整体架构在“注册制+托管式支付”的主逻辑上较自洽,V4 走 deposit-only 模式、V3 走信用与信誉模型,二者风险边界清晰。主要风险来自配置可变性与价格路径的精度/溢出控制,以及部分信任假设未被文档明确约束或自动化测试覆盖。

发现概览

  • 严重/高危:0
  • 中危:3
  • 低危/提示:5

中危发现

  1. 价格计算潜在溢出导致拒绝服务 证据:contracts/src/paymasters/v4/PaymasterBase.sol_calculateTokenCost 先做 gasCostWei * ethUsdPrice * totalRate * (10 ** tDecimals)Math.mulDiv。在高 gas 上限、较高价格或异常 tokenDecimals 下可能溢出并 revert,导致验证或结算阶段拒绝服务。建议将乘法分段并使用 Math.mulDiv 串联,或限制 tokenDecimals 上限并对 maxGasCostCap 进行合理约束。

  2. Token decimals 未限制可能触发 10 ** tDecimals 溢出 证据:contracts/src/paymasters/v4/PaymasterBase.solsetTokenPrice 读取 decimals 后直接存入 tokenDecimals,在 _calculateTokenCost 中使用 10 ** tDecimals。若为异常 token(decimals 极大),会溢出并导致 paymaster 全面不可用。建议加入 require(decimals <= 18 || <= 24) 或安全指数计算。

  3. V3 postOp 对外部 token 调用无容错 证据:contracts/src/paymasters/superpaymaster/v3/SuperPaymaster.solpostOp 直接调用 IxPNTsToken.recordDebt。若 token 未配置、被暂停或异常 revert,将导致 postOp 失败并引发 paymaster 赔付风险。建议 try/catch 包裹并记录告警,或在 validatePaymasterUserOp 中预先检查 token 依赖是否可用。

低危/提示

  1. Oracle 响应校验不完整 证据:contracts/src/paymasters/v4/PaymasterBase.solupdatePrice() 仅校验 price > 0,未校验 answeredInRoundupdatedAt != 0。建议加入标准 Chainlink 校验,减少极端情况下的错误价格进入缓存。

  2. 关键配置可将系统置于“免费支付”状态 证据:contracts/src/paymasters/v4/PaymasterBase.solmaxGasCostCap 无最小值校验,若被设置为 0,则 cappedMaxCost 为 0,用户无需余额即可通过验证,paymaster 直接承担费用。建议设置下限或在 validatePaymasterUserOp 中拒绝 0 cap。

  3. 自动授权 spender 的信任假设未在文档中显式约束 证据:contracts/src/tokens/xPNTsToken.solallowance()autoApprovedSpenders 直接返回无限额度,且 burn 仅禁止 SuperPaymaster,但允许其他 autoApproved 地址烧毁用户资产。建议在文档中明确允许名单的安全边界,或限制其 burn 能力。

  4. 价格缓存与有效期的稳定性依赖外部更新流程 证据:V4 通过 cachedPrice.updatedAt + priceStalenessThreshold 设置 validUntil,若 Keeper 未更新,EntryPoint 会拒绝。建议在部署/运维文档中明确 Keeper SLA 与监控告警策略。

  5. 版本字符串分散且对齐不清晰 证据:PaymasterBase 返回 PaymasterV4-4.3.0Paymaster 返回 PMV4-Deposit-4.3.0SuperPaymaster 返回 SuperPaymaster-4.0.0。建议在 docs/CHANGELOG 中建立版本映射表,避免与部署脚本/文档描述失配。

文档一致性与部署评审

  • 部署流程围绕 ./deploy-core <env>contracts/script/ 脚本清晰,但需明确 foundry.tomlscript=script 与实际 contracts/script/ 的差异,以防新成员误用(已有部分说明,建议集中到单一部署指南)。
  • 文档中存在多个“测试通过数量/最新日期”的静态描述,但缺少自动化生成来源,易与实际状态偏离。建议将测试统计与版本变更通过脚本自动化生成并写入单一位置。

测试与覆盖率

  • 目前仅看到 check-secrets 工作流,无 CI 自动运行 forge testforge coverage 或 Echidna/Slither。测试与覆盖率更多依赖文档与手工执行。
  • docs/ 内存在覆盖率与安全工具的说明,但缺乏统一“必须通过”的门槛定义。建议建立最小门槛(例如:关键路径测试必须通过、核心合约覆盖率达到某阈值、至少一次静态分析通过)。

Gas 效率与稳定性观察

  • V4 通过缓存与 calculateCost 降低验证阶段读取成本,但 this.calculateCostthis.updatePrice 使用外部调用增加了 gas 与复杂度,可考虑内联逻辑或将部分调用改为 internal 函数减少开销。
  • nonReentrantvalidatePaymasterUserOppostOp 上提供安全性,但在 4337 模式下重入路径有限,可评估是否可移除以节省 gas(需保证不会引入跨合约 reentrancy)。

建议清单(短期可执行)

  1. 修复 V4 价格计算溢出风险(分段 mulDiv 或限制 decimals)。
  2. updatePrice() 加入 Chainlink 标准校验。
  3. 在 V3 postOp 中对 recordDebt 加容错并上报异常。
  4. 增加最小配置校验:maxGasCostCap 不得为 0。
  5. 建立一个统一的“版本映射+测试状态”文档入口,并由脚本生成。

审计限制

本报告基于静态阅读与文档核对,未执行任何自动化测试或链上验证。若需要正式审计级别结论,建议补充:

  • forge test 全量回归与覆盖率报告
  • forge coverage --ir-minimum 关键模块覆盖
  • Echidna/Slither/Aderyn 报告输出与归档
  • 针对部署脚本的真实网络演练与回滚验证

测试与安全工具执行记录(2026-03-20)

单元测试

  • forge test:失败(Foundry 崩溃,报错:Attempted to create a NULL object,发生在 system-configuration 读取系统代理阶段)。
    • 已尝试:FOUNDRY_DISABLE_EVM_TRACE_IDENTIFIERS=1FOUNDRY_TRACE_IDENTIFIERS=none、清空 HTTP_PROXY/HTTPS_PROXY/ALL_PROXY,均无效。
    • 结论:当前环境下 Foundry 测试不可执行,需升级/降级 Foundry 或在无系统代理依赖的环境中运行。

覆盖率

  • forge coverage --ir-minimum --report summary:编译阶段耗时过长且无结果输出,已中止。
    • 结论:覆盖率未生成。
    • 建议:修复 Foundry 运行环境后再执行覆盖率命令,并将 summary 粘贴到本报告。

E2E / 集成测试

  • ./script/gasless-tests/run-all-tests.sh:失败(缺少配置文件 /Volumes/UltraDisk/Dev2/aastar/env/.env)。
    • 建议:提供正确的 .env 路径或修改脚本读取的配置路径,然后重试。

自动化安全工具

  • slither . --exclude-dependencies:失败(Contract BaseSimpleAccount.SimpleAccount not found)。
    • 建议:使用更精确的目标路径或过滤规则(例如只扫描 contracts/src,或提供可解析的 remappings/依赖)。
  • aderyn . --output security-report.md:失败(未安装)。
  • myth analyze contracts/src/paymasters/v4/PaymasterBase.sol:失败(无法下载 solc + 本地权限不足 ~/.solcx)。
  • echidna . --config echidna.yaml:失败(多合约时默认分析第一个接口合约,无 bytecode)。
    • 建议:为 Echidna 指定具体的测试合约/部署合约(deployContracts--contract)。
  • npm audit:失败(缺少 lockfile,且日志目录无权限)。
    • 建议:若需依赖漏洞扫描,先生成 lockfile(或在真实项目根有 package.json/lockfile 的位置执行)。

中危建议的代码级修复思路(不直接改代码)

  1. V4 价格计算溢出风险

    • 将多项乘法拆分为多次 Math.mulDiv,避免中间值溢出。
    • 或对 tokenDecimalsmaxGasCostCap 设置上限与安全区间。
  2. tokenDecimals 上限校验

    • setTokenPrice() 中增加:require(decimals <= 18 || decimals <= 24)(按你们支持的 token 范围选定)。
  3. V3 postOp 对外部调用容错

    • recordDebt() 包裹 try/catch,失败时写入事件并进行应急处理(避免 postOp 失败导致赔付风险)。

当前结论

由于 Foundry 在当前环境崩溃且覆盖率执行被阻断,本次无法补充“实际测试通过率与覆盖率数值”。建议你确认运行环境后,我可以继续完成:

  • forge test 完整回归
  • forge coverage --report summary
  • Slither/Aderyn/Echidna 全量报告

用户本地测试结果(由用户提供,2026-03-20)

  • Foundry 测试结果:
    • 37 个 test suites,318 tests passed,0 failed,0 skipped
    • 示例输出:Suite result: ok. 8 passed; 0 failed; 0 skipped; ...

说明:以上结果来自你的本地环境执行输出,我未在当前环境中复现(当前环境 Foundry 会在启动阶段崩溃)。

中危修复指引(伪代码级,不改代码)

1) V4 价格计算溢出

目标:避免 _calculateTokenCost 中间乘法溢出导致拒绝服务。

建议方案 A(分段 mulDiv):

// Step 1: gasCostWei * ethUsdPrice (保持 256 位安全)
uint256 costEthUsd = Math.mulDiv(gasCostWei, uint256(ethUsdPrice), 10 ** ethDecimals);

// Step 2: 叠加费率(BPS)
uint256 costWithFee = Math.mulDiv(costEthUsd, totalRate, BPS_DENOMINATOR);

// Step 3: 转 token 单位(再乘 10^tokenDecimals 再除 tokenPrice)
uint256 tokenAmount = Math.mulDiv(costWithFee, 10 ** tDecimals, tokenPriceUSD);

建议方案 B(约束输入上限):

require(tDecimals <= 18 || tDecimals <= 24, "Token decimals too large");
require(maxGasCostCap <= SAFE_MAX_GAS_CAP, "Gas cap too large");

2) Token decimals 上限校验

目标:避免 10 ** tDecimals 溢出。

建议位置:PaymasterBase.setTokenPrice()

uint8 decimals = IERC20Metadata(token).decimals();
require(decimals <= 24, "Token decimals too large");

3) V3 postOp 外部调用容错

目标:避免 recordDebt() revert 造成 postOp 失败和赔付风险。

建议位置:SuperPaymaster.postOp()

try IxPNTsToken(token).recordDebt(user, finalXPNTsDebt) {
    emit TransactionSponsored(operator, user, finalCharge, finalXPNTsDebt);
} catch {
    emit DebtRecordFailed(operator, user, token, finalXPNTsDebt);
    // 可选:将债务暂存到待补偿队列
}

安全工具命令清单(不执行,仅给出可跑命令)

单元测试与覆盖率

forge test -vv
forge coverage --report summary

Slither(建议限定范围)

# 只扫 contracts/src,避免依赖解析问题
slither contracts/src \
  --solc-remaps "@openzeppelin/contracts/=contracts/lib/openzeppelin-contracts/contracts/" \
  --solc-remaps "@openzeppelin-v5.0.2/=singleton-paymaster/lib/openzeppelin-contracts-v5.0.2/" \
  --solc-remaps "@account-abstraction-v7/=singleton-paymaster/lib/account-abstraction-v7/contracts/" \
  --solc-remaps "@chainlink/contracts/=contracts/lib/chainlink-brownie-contracts/contracts/" \
  --solc-remaps "solady/=contracts/lib/solady/src/" \
  --solc-remaps "src/=contracts/src/" \
  --exclude-dependencies

Aderyn(若已安装)

aderyn . --output security-report.md

Echidna(指定测试合约)

# 例:只跑 GTokenStaking 的 invariant 合约
# 需要你们提供或确认测试合约名
EchidnaConfig=echidna.yaml
# echidna-test . --contract GTokenStakingInvariants --config $EchidnaConfig

Mythril(建议仅在联网且 solc 可用环境)

myth analyze contracts/src/paymasters/v4/PaymasterBase.sol

依赖漏洞扫描

# 需要 package-lock.json 或 pnpm-lock.yaml
npm audit

文档修正文案(草案)

1) 版本映射统一说明

建议在 docs/CHANGELOGdocs/DEPLOYMENT_* 新增:

版本映射:
- PaymasterBase: PaymasterV4-4.3.0
- Paymaster (proxy): PMV4-Deposit-4.3.0
- SuperPaymaster: SuperPaymaster-4.0.0
说明:版本字符串用于运行时标识,部署版本以 deploy-core 与 deployment config 为准。

2) Oracle/Keeper 运维要求

建议在部署文档增加:

Keeper 责任:
- 需按 priceStalenessThreshold 定期更新缓存价格
- 若 Keeper 停止更新,EntryPoint 会拒绝 UserOp
- 建议部署监控告警(价格更新失败/延迟)

3) Auto-approved Spender 风险提示

建议在 xPNTsToken 相关文档增加:

安全提示:
- autoApprovedSpenders 拥有无限 allowance
- 仅允许可信合约进入名单
- 建议治理层提供上链审批或 timelock

4) 测试与覆盖率结果集中化

建议新增一处统一入口(例如 docs/TEST_STATUS.md):

- 最新测试通过数
- 覆盖率 summary
- Slither/Aderyn/Echidna 报告链接
- 版本与日期