本次审计为“静态+文档一致性”快速评审,聚焦核心合约逻辑、自洽性、稳定性、gas 效率、部署流程与测试覆盖说明。未运行测试、未做链上复核、未执行静态分析工具。
已查看的关键文件包括:
contracts/src/paymasters/v4/PaymasterBase.solcontracts/src/paymasters/v4/Paymaster.solcontracts/src/paymasters/v4/core/PaymasterFactory.solcontracts/src/paymasters/superpaymaster/v3/SuperPaymaster.solcontracts/src/tokens/xPNTsToken.solcontracts/src/modules/validators/BLSValidator.solREADME.md、CLAUDE.md、docs/中与部署/测试/安全相关的说明
整体架构在“注册制+托管式支付”的主逻辑上较自洽,V4 走 deposit-only 模式、V3 走信用与信誉模型,二者风险边界清晰。主要风险来自配置可变性与价格路径的精度/溢出控制,以及部分信任假设未被文档明确约束或自动化测试覆盖。
- 严重/高危:0
- 中危:3
- 低危/提示:5
-
价格计算潜在溢出导致拒绝服务 证据:
contracts/src/paymasters/v4/PaymasterBase.sol中_calculateTokenCost先做gasCostWei * ethUsdPrice * totalRate * (10 ** tDecimals)再Math.mulDiv。在高 gas 上限、较高价格或异常tokenDecimals下可能溢出并 revert,导致验证或结算阶段拒绝服务。建议将乘法分段并使用Math.mulDiv串联,或限制tokenDecimals上限并对maxGasCostCap进行合理约束。 -
Token decimals 未限制可能触发
10 ** tDecimals溢出 证据:contracts/src/paymasters/v4/PaymasterBase.sol中setTokenPrice读取 decimals 后直接存入tokenDecimals,在_calculateTokenCost中使用10 ** tDecimals。若为异常 token(decimals 极大),会溢出并导致 paymaster 全面不可用。建议加入require(decimals <= 18 || <= 24)或安全指数计算。 -
V3 postOp 对外部 token 调用无容错 证据:
contracts/src/paymasters/superpaymaster/v3/SuperPaymaster.sol的postOp直接调用IxPNTsToken.recordDebt。若 token 未配置、被暂停或异常 revert,将导致postOp失败并引发 paymaster 赔付风险。建议try/catch包裹并记录告警,或在validatePaymasterUserOp中预先检查 token 依赖是否可用。
-
Oracle 响应校验不完整 证据:
contracts/src/paymasters/v4/PaymasterBase.sol的updatePrice()仅校验price > 0,未校验answeredInRound或updatedAt != 0。建议加入标准 Chainlink 校验,减少极端情况下的错误价格进入缓存。 -
关键配置可将系统置于“免费支付”状态 证据:
contracts/src/paymasters/v4/PaymasterBase.sol的maxGasCostCap无最小值校验,若被设置为 0,则cappedMaxCost为 0,用户无需余额即可通过验证,paymaster 直接承担费用。建议设置下限或在validatePaymasterUserOp中拒绝 0 cap。 -
自动授权 spender 的信任假设未在文档中显式约束 证据:
contracts/src/tokens/xPNTsToken.sol中allowance()对autoApprovedSpenders直接返回无限额度,且burn仅禁止 SuperPaymaster,但允许其他 autoApproved 地址烧毁用户资产。建议在文档中明确允许名单的安全边界,或限制其 burn 能力。 -
价格缓存与有效期的稳定性依赖外部更新流程 证据:V4 通过
cachedPrice.updatedAt + priceStalenessThreshold设置validUntil,若 Keeper 未更新,EntryPoint 会拒绝。建议在部署/运维文档中明确 Keeper SLA 与监控告警策略。 -
版本字符串分散且对齐不清晰 证据:
PaymasterBase返回PaymasterV4-4.3.0,Paymaster返回PMV4-Deposit-4.3.0,SuperPaymaster返回SuperPaymaster-4.0.0。建议在docs/CHANGELOG中建立版本映射表,避免与部署脚本/文档描述失配。
- 部署流程围绕
./deploy-core <env>与contracts/script/脚本清晰,但需明确foundry.toml的script=script与实际contracts/script/的差异,以防新成员误用(已有部分说明,建议集中到单一部署指南)。 - 文档中存在多个“测试通过数量/最新日期”的静态描述,但缺少自动化生成来源,易与实际状态偏离。建议将测试统计与版本变更通过脚本自动化生成并写入单一位置。
- 目前仅看到
check-secrets工作流,无 CI 自动运行forge test、forge coverage或 Echidna/Slither。测试与覆盖率更多依赖文档与手工执行。 docs/内存在覆盖率与安全工具的说明,但缺乏统一“必须通过”的门槛定义。建议建立最小门槛(例如:关键路径测试必须通过、核心合约覆盖率达到某阈值、至少一次静态分析通过)。
- V4 通过缓存与
calculateCost降低验证阶段读取成本,但this.calculateCost与this.updatePrice使用外部调用增加了 gas 与复杂度,可考虑内联逻辑或将部分调用改为 internal 函数减少开销。 nonReentrant在validatePaymasterUserOp与postOp上提供安全性,但在 4337 模式下重入路径有限,可评估是否可移除以节省 gas(需保证不会引入跨合约 reentrancy)。
- 修复 V4 价格计算溢出风险(分段
mulDiv或限制 decimals)。 - 对
updatePrice()加入 Chainlink 标准校验。 - 在 V3
postOp中对recordDebt加容错并上报异常。 - 增加最小配置校验:
maxGasCostCap不得为 0。 - 建立一个统一的“版本映射+测试状态”文档入口,并由脚本生成。
本报告基于静态阅读与文档核对,未执行任何自动化测试或链上验证。若需要正式审计级别结论,建议补充:
forge test全量回归与覆盖率报告forge coverage --ir-minimum关键模块覆盖- Echidna/Slither/Aderyn 报告输出与归档
- 针对部署脚本的真实网络演练与回滚验证
forge test:失败(Foundry 崩溃,报错:Attempted to create a NULL object,发生在system-configuration读取系统代理阶段)。- 已尝试:
FOUNDRY_DISABLE_EVM_TRACE_IDENTIFIERS=1、FOUNDRY_TRACE_IDENTIFIERS=none、清空HTTP_PROXY/HTTPS_PROXY/ALL_PROXY,均无效。 - 结论:当前环境下 Foundry 测试不可执行,需升级/降级 Foundry 或在无系统代理依赖的环境中运行。
- 已尝试:
forge coverage --ir-minimum --report summary:编译阶段耗时过长且无结果输出,已中止。- 结论:覆盖率未生成。
- 建议:修复 Foundry 运行环境后再执行覆盖率命令,并将 summary 粘贴到本报告。
./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)。
- 建议:为 Echidna 指定具体的测试合约/部署合约(
npm audit:失败(缺少 lockfile,且日志目录无权限)。- 建议:若需依赖漏洞扫描,先生成 lockfile(或在真实项目根有 package.json/lockfile 的位置执行)。
-
V4 价格计算溢出风险
- 将多项乘法拆分为多次
Math.mulDiv,避免中间值溢出。 - 或对
tokenDecimals、maxGasCostCap设置上限与安全区间。
- 将多项乘法拆分为多次
-
tokenDecimals上限校验setTokenPrice()中增加:require(decimals <= 18 || decimals <= 24)(按你们支持的 token 范围选定)。
-
V3
postOp对外部调用容错- 对
recordDebt()包裹try/catch,失败时写入事件并进行应急处理(避免postOp失败导致赔付风险)。
- 对
由于 Foundry 在当前环境崩溃且覆盖率执行被阻断,本次无法补充“实际测试通过率与覆盖率数值”。建议你确认运行环境后,我可以继续完成:
forge test完整回归forge coverage --report summary- Slither/Aderyn/Echidna 全量报告
- Foundry 测试结果:
- 37 个 test suites,318 tests passed,0 failed,0 skipped
- 示例输出:
Suite result: ok. 8 passed; 0 failed; 0 skipped; ...
说明:以上结果来自你的本地环境执行输出,我未在当前环境中复现(当前环境 Foundry 会在启动阶段崩溃)。
目标:避免 _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");目标:避免 10 ** tDecimals 溢出。
建议位置:PaymasterBase.setTokenPrice()
uint8 decimals = IERC20Metadata(token).decimals();
require(decimals <= 24, "Token decimals too large");目标:避免 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# 只扫 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-dependenciesaderyn . --output security-report.md# 例:只跑 GTokenStaking 的 invariant 合约
# 需要你们提供或确认测试合约名
EchidnaConfig=echidna.yaml
# echidna-test . --contract GTokenStakingInvariants --config $EchidnaConfigmyth analyze contracts/src/paymasters/v4/PaymasterBase.sol# 需要 package-lock.json 或 pnpm-lock.yaml
npm audit建议在 docs/CHANGELOG 或 docs/DEPLOYMENT_* 新增:
版本映射:
- PaymasterBase: PaymasterV4-4.3.0
- Paymaster (proxy): PMV4-Deposit-4.3.0
- SuperPaymaster: SuperPaymaster-4.0.0
说明:版本字符串用于运行时标识,部署版本以 deploy-core 与 deployment config 为准。
建议在部署文档增加:
Keeper 责任:
- 需按 priceStalenessThreshold 定期更新缓存价格
- 若 Keeper 停止更新,EntryPoint 会拒绝 UserOp
- 建议部署监控告警(价格更新失败/延迟)
建议在 xPNTsToken 相关文档增加:
安全提示:
- autoApprovedSpenders 拥有无限 allowance
- 仅允许可信合约进入名单
- 建议治理层提供上链审批或 timelock
建议新增一处统一入口(例如 docs/TEST_STATUS.md):
- 最新测试通过数
- 覆盖率 summary
- Slither/Aderyn/Echidna 报告链接
- 版本与日期