fix(p0-12b): per-xPNTs community-controlled facilitator whitelist#110
fix(p0-12b): per-xPNTs community-controlled facilitator whitelist#110jhfnetboy wants to merge 5 commits intofix/p0-12a-x402-asset-whitelistfrom
Conversation
P0-12b (D4): even after P0-12a restricts `settleX402PaymentDirect` to
xPNTs assets, a single global facilitator (e.g. AAStar's default) is
still implicitly trusted by every community's xPNTs. A compromise of
that one key would blast across all communities at once. Per D4, each
xPNTs token now carries its own community-controlled
`approvedFacilitators` whitelist.
Defense:
- xPNTsToken.sol:
- new `mapping(address => bool) public approvedFacilitators` storage
slot. Distinct from `autoApprovedSpenders`: the latter is the
ERC20 transferFrom firewall ("no approve needed"), the former
gates `settleX402PaymentDirect` invocation. The two are
intentionally orthogonal — both must hold for a Direct settle to
succeed.
- `addApprovedFacilitator(address)` / `removeApprovedFacilitator
(address)` — `onlyCommunityOwner` (matches the existing
`removeAutoApprovedSpender` access pattern; community owner is
the policy authority).
- new events `FacilitatorApproved` / `FacilitatorRemoved`.
- per D4, factory does NOT auto-add AAStar's facilitator at
deploy. Communities must explicitly add facilitators after
deployment (runbook concern, not contract default). Existing
`deployxPNTsToken` ABI is unchanged — no breaking SDK impact.
- IxPNTsToken adds `approvedFacilitators(address) returns (bool)`.
- SuperPaymaster.settleX402PaymentDirect: after the P0-12a asset
gate passes, also enforce
`IxPNTsToken(asset).approvedFacilitators(msg.sender)`. Reverts
`Unauthorized` (reuses existing error). Without this, a compromised
facilitator that still holds ROLE_PAYMASTER_SUPER could touch
every community's xPNTs even after that community has tried to
blacklist them upstream.
Trust-model summary (per threat-model.md §3.1): facilitator is
BOUNDED TRUST at the SP layer; this whitelist binds the bound to
the per-community policy surface so a facilitator compromise is
contained to communities that have explicitly opted in.
Storage layout: xPNTsToken adds one mapping (clones share storage
layout; `approvedFacilitators` appends after `autoApprovedSpenders`,
which is safe for clones since storage is fresh at clone time).
SuperPaymaster has no new storage. UUPS upgrade safe.
Tests:
- contracts/test/v3/X402Direct_FacilitatorWhitelist.t.sol (new, 9
tests):
- test_SettleDirect_RevertsForUnapprovedFacilitator
- test_SettleDirect_AllowsApprovedFacilitator
- test_SettleDirect_PerCommunityIsolation (A's approval doesn't
leak to B's xPNTs — the core blast-radius property)
- test_AddApprovedFacilitator_OnlyCommunity (SP owner cannot, only
xPNTs communityOwner can)
- test_AddApprovedFacilitator_RevertsZeroAddress
- test_AddApprovedFacilitator_EmitsEvent
- test_RemoveApprovedFacilitator_RevokesAccess (instant revoke;
next settle reverts even with same allowance state)
- test_RemoveApprovedFacilitator_OnlyCommunity
- test_DefaultEmptyApprovedFacilitators (no auto-add at deploy)
- contracts/test/v3/X402Direct_AssetWhitelist.t.sol: helper updated
to also `addApprovedFacilitator(operator)` so its happy path still
exercises Direct end-to-end. All 6 P0-12a tests still green.
- contracts/test/v3/SuperPaymasterV5Features.t.sol: MockDirectToken
now exposes `approvedFacilitators` + `setApprovedFacilitator`;
setUp wires it for operator1. All 36 V5 features tests still green.
Refs: P0-12b in docs/security/2026-04-26-p0-prelaunch.md, D4 in
docs/security/2026-04-26-decision-records.md
代码审查警告 — 三层防御架构方向正确,但存在 CRITICAL 运营风险和 HIGH 代码问题 CRITICAL(运营)— PR 描述提到"community multisig only",但合约层面没有验证
HIGH — require(facilitator != communityOwner, "communityOwner cannot be facilitator");HIGH — MEDIUM — 缺少 |
…st functions Document that communityOwner MUST be a multisig wallet. A compromised single-EOA owner can add arbitrary facilitators enabling unauthorized token extraction. Add the same warning to removeApprovedFacilitator and a brief note to transferCommunityOwnership referencing the security rationale.
|
已在 核心警告: |
communityOwner calling addApprovedFacilitator(communityOwner) would let them act as both administrator and settlement facilitator — a conflict of interest that bypasses separation-of-duties. Add a check that reverts with Unauthorized(facilitator) when facilitator == communityOwner, and cover it with test_AddApprovedFacilitator_RevertsIfCommunityOwner.
HIGH 修复:阻止 communityOwner 自我添加为 facilitator问题(PR 审核意见): 修复内容(commit
测试结果:10/10 PASS(含新增用例)。
|
Option A 实现:addApprovedFacilitator 自动同步 autoApprovedSpenders根据架构决策,已实现 auto-sync 方案(commit 最新推送): 合约变更
测试变更
测试结果:12/12 PASS |
…Spenders are orthogonal
撤销 auto-sync,恢复两个 mapping 独立设计架构纠正: 内部的 调用方是 SuperPaymaster 合约本身(msg.sender = SP),而非 facilitator。工厂部署时已执行 ,因此 facilitator 完全不需要进入 。两个 mapping 的职责是正交的:
合约变更: / 移除 auto-sync 逻辑,只写 ,NatSpec 更新说明正交关系。 测试变更:移除 auto-sync 断言测试,新增 测试结果:11/11 PASS |
fanhousanbu
left a comment
There was a problem hiding this comment.
MEDIUM(CEI 模式): settleX402PaymentDirect() 里,_validateX402AndComputeFee() 内部先写 nonce(x402SettlementNonces[key] = true),之后才做 isXPNTs 和 approvedFacilitators 两个检查。行为上安全(revert 会回滚 nonce 写入),但违反 CEI——未来维护者若在两个检查之间插入 external call 会引入 re-entrancy 风险。请在该段加注释说明 nonce 写入依赖 revert atomicity 而非代码顺序。不阻断。
✅ Approved
Base 是 `fix/p0-12a-x402-asset-whitelist`(PR #N+5)。完整 stack: main → P0-13 → P0-12a → P0-12b。GitHub 只展示这条 commit 的 delta。
P0-12b (D4 新设计)
D4 决策:每个 xPNTs 由社区 multisig 指定 trusted facilitators。当前 `autoApprovedSpenders` 是单一全局映射 —— 跨社区 blast radius 太大。社区 A 不应被强迫 trust 社区 B 的 facilitator。
Defense
`autoApprovedSpenders` 与 `approvedFacilitators` 是不同概念,两者并存 —— 前者是 ERC20 transfer firewall,后者是 settle 调用权限白名单。
D4 用户决策:社区部署默认不自动 add AAStar facilitator —— community 自己显式 add。AAStar 提供一个可选默认。
Tests
新测试(spender 不在白名单 revert / 在白名单接受 / add/remove only community / settle 强制 `isXPNTs && approvedFacilitator`)+ 现有不破坏
Spec
`docs/security/2026-04-26-p0-prelaunch.md` §3 P0-12b
`docs/security/2026-04-26-decision-records.md` D4
`docs/deployment/facilitator-deployment.md`(PR #98)—— 部署后社区 add facilitator 的 runbook