Skip to content

refactor(xpnts): unify all debt accounting in aPNTs#200

Open
jhfnetboy wants to merge 4 commits into
mainfrom
refactor/unified-apnts-accounting
Open

refactor(xpnts): unify all debt accounting in aPNTs#200
jhfnetboy wants to merge 4 commits into
mainfrom
refactor/unified-apnts-accounting

Conversation

@jhfnetboy
Copy link
Copy Markdown
Member

Summary

All internal protocol debt is now stored in aPNTs (not xPNTs). xPNTs conversions only happen at user-facing boundaries (burn / repay).

Key changes

xPNTsToken.sol

  • burnFromWithOpHash(amountAPNTs): converts aPNTs → xPNTs = ceil(aPNTs × rate / 1e18) before burning
  • recordDebt / recordDebtWithOpHash: accept aPNTs directly (no conversion)
  • repayDebt(amountXPNTs): converts xPNTs → aPNTs = floor(xPNTs × 1e18 / rate) before reducing debt
  • _update auto-repay: converts minted xPNTs → aPNTs (floor), reduces debt, burns ceil(repayAPNTs × rate / 1e18) xPNTs
  • maxSingleTxLimit is now in aPNTs; transferFrom limit check converts xPNTs → aPNTs before comparing
  • Version: XPNTs-3.4.0

SuperPaymaster.sol

  • configureOperator: drops stale exchangeRate param; reads live rate from xPNTsToken.exchangeRate()
  • validatePaymasterUserOp / dryRunValidation: rate-commitment check uses live rate instead of config.exchangeRate
  • postOp: passes finalCharge aPNTs directly to _recordDebt (removes xPNTs conversion)
  • getAvailableCredit: getDebt() returns aPNTs directly (no conversion needed)
  • Version: SuperPaymaster-5.3.3

ISuperPaymaster.sol / IxPNTsToken.sol: interface updates matching the above

Bug fixes included

Bug Description Fix
A (Critical) _update floor-rounding zero-burn: rate=3e17, debt=3, mint=1 → debt cleared but 0 xPNTs burned Changed repayXPNTs to ceil; invariant ceil(floor(v/r)×r) ≤ v proven safe
H transferFrom single-tx limit compared xPNTs against aPNTs denominated limit Convert value to aPNTs before comparing

Rounding convention

Path Direction Reason
burnFromWithOpHash aPNTs→xPNTs ceil Protocol never under-collects
repayDebt xPNTs→aPNTs floor User never over-repays
_update mint→aPNTs floor Conservative debt relief
_update repayAPNTs→xPNTs ceil Non-zero burn + ≤ value invariant

Test plan

  • forge test: 924/924 pass (was 923/923 before adding regression test)
  • test_AutoRepay_ZeroBurn_Regression: proves ceil fix burns 1 xPNT not 0 for the PoC case
  • All rate-limiting, blocklist, and security tests pass with live rate path

Follow-up (deferred, not in this PR)

  • Bug E (ERC-7562): exchangeRate() STATICCALL in validatePaymasterUserOp — document as permissive-bundler-only or add a cached rate
  • Bug G (rate commitment): live rate re-read in postOp could diverge from validated maxRate — pre-existing risk, address in separate PR
  • After this merges: rebase feature/v5.4-credit-purchase (removes xPNTs conversion from credit purchase path)

jhfnetboy added 4 commits May 13, 2026 21:44
…ings (Section 8)

Records all findings from Codex + local adversarial dual-track review after PR #195 merge:
2 Critical, 7 High, 8 Medium, 6 Low/Info with UUPS-upgradeable vs redeploy fix paths.
Deferred to v5.4 per user decision — no code changes in this commit.
- GTokenAuthorization (EIP-3009 gasless transfers) replaces plain GToken
- deploy-core: add --non-interactive flag for Sepolia/live envs to prevent
  forge 1.4+ broadcast prompt blocking in non-TTY environments
- verify-all.sh: update GToken verification path/args for GTokenAuthorization
  (constructor now takes cap + factory address)
- config.sepolia.json: updated with new contract addresses (2026-05-13)
- verify.sepolia.contracts-5-13.md: 12/12 contracts verified on Etherscan

New Sepolia addresses:
  GToken (GTokenAuthorization): 0xbC17B6C319561bcA805981fC2846e4678f9114Cb
  Registry proxy:                0x3dfeBE636eDA211E0a783308Cf0CB31892686d67
  SuperPaymaster proxy:          0x506962D17AEA6E7A15fd3479D8c4E2ABBBF91112
All internal protocol debt is now recorded and settled in aPNTs.
xPNTs conversions are pushed to the boundaries (burn/repay functions
inside xPNTsToken itself) rather than computed at each call site.

Key changes:
- xPNTsToken.debts: unit changed from xPNTs → aPNTs
- burnFromWithOpHash(amountAPNTs): converts to xPNTs internally (ceil)
- recordDebt / recordDebtWithOpHash: accept aPNTs directly
- repayDebt(amountXPNTs): converts to aPNTs before reducing debt
- _update auto-repay: converts minted xPNTs to aPNTs before comparing
- SuperPaymaster.configureOperator: drops exchangeRate param (rate read
  live from xPNTsToken.exchangeRate() at validation time)
- OperatorConfig.exchangeRate field removed; ISuperPaymaster updated
- validatePaymasterUserOp / dryRunValidation: rate commitment check uses
  IxPNTsToken(token).exchangeRate() instead of stale OperatorConfig copy
- postOp: passes finalCharge (aPNTs) directly to _recordDebt (renamed)
- getAvailableCredit: no longer needs rate conversion (debt is aPNTs)
- Version bumps: SuperPaymaster-5.3.3, XPNTs-3.4.0

Fixes the dual-exchangeRate divergence (OperatorConfig.exchangeRate vs
xPNTsToken.exchangeRate()) that could silently cause stale-rate debt
recording after a community updates their token rate.

Tests: 923/923 pass (0 failures)
- _update auto-repay: floor → ceil so repayXPNTs ≥ 1 when repayAPNTs > 0;
  invariant ceil(floor(v/r)×r) ≤ v proven safe (no over-burn)
- transferFrom single-tx limit: convert xPNTs → aPNTs before comparing
  against maxSingleTxLimit (which is now denominated in aPNTs after refactor)
- Regression test: rate=3e17, debt=3, mint=1 → debt cleared + 1 xPNT burned
  (old floor code burned 0, leaving protocol with cleared debt but no collateral)
@jhfnetboy jhfnetboy requested a review from fanhousanbu as a code owner May 27, 2026 11:09
@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant