Reject ERC20uRWA forced transfer to self#246
Conversation
forcedTransfer(from, from, amount) moves no tokens, but the frozen-balance adjustment still lowered the frozen amount, effectively unfreezing tokens without the freezer role. This bypassed the intended separation between the enforcer and freezer roles (closes OpenZeppelin#244). Reject the case by reverting with ERC7943CannotTransfer when from == to, and add a regression test.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
Walkthrough
ChangesERC20uRWA self-forced-transfer guard
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Fixes #244
Problem
ERC20uRWA.forcedTransfer(from, to, amount)does not rejectfrom == to. In that case_update(from, to, amount)moves no tokens, but the frozen-balance adjustment runs first:So a self-directed forced transfer leaves the balance unchanged while lowering the frozen amount — an implicit, unauthorized unfreeze. This is most relevant when the enforcer and freezer permissions are held by different entities: an enforcer could reduce frozen balances without holding the freezer role, breaking the intended separation of duties (also noted for the ERC-7943 reference implementation in ethereum/ERCs#1778).
Example:
balanceOf(alice) = 100,frozen(alice) = 100. CallingforcedTransfer(alice, alice, 100)results infrozen(alice) = 0with the balance untouched.Fix
Reject the degenerate self-transfer by reverting with the existing
ERC7943CannotTransfer(from, to, amount)error before any state changes:Scope
This PR addresses point 1 of #244 (the self-forced-transfer edge case), which is a contained correctness/security fix. Points 2–4 in that issue (the
setFrozenTokensbalance cap, thecanSend/canReceivevscanTransactinterface split, andcanTransfersemantics) are interface/semantic decisions and are intentionally left out of this PR for the maintainers to decide separately.PR Checklist
forcedTransfer(holder, holder, ...)reverts and leaves the frozen balance untouched; fullERC20uRWAsuite passes (40 passing).CHANGELOG.mdentry and an explanatory code comment.Summary by CodeRabbit
Bug Fixes
Tests
Documentation