Skip to content

Fix ERC20uRWA canTransfer/forcedTransfer restriction checks#249

Open
mdimran29 wants to merge 1 commit into
OpenZeppelin:masterfrom
mdimran29:fix/erc20urwa-send-receive-checks
Open

Fix ERC20uRWA canTransfer/forcedTransfer restriction checks#249
mdimran29 wants to merge 1 commit into
OpenZeppelin:masterfrom
mdimran29:fix/erc20urwa-send-receive-checks

Conversation

@mdimran29

@mdimran29 mdimran29 commented Jul 16, 2026

Copy link
Copy Markdown

Summary

Fixes #244 (points 3 & 4).

  • Add virtual canSend(address) and canReceive(address), both defaulting to canTransact(), so send-side and receive-side restrictions can be overridden independently.
  • canTransfer(): remove the amount <= available(from) balance check (a canTransfer availability query shouldn't also encode balance sufficiency) and check canSend(from) && canReceive(to) instead of canTransact(from) && canTransact(to).
  • forcedTransfer(): gate the recipient with canReceive(to) instead of canTransact(to), consistent with canTransfer.

No changes to setFrozenTokens, the forcedTransfer from == to handling, or any other files.

Test plan

  • forge build passes
  • forge test passes (41/41, including 11 new tests in test/token/ERC20/extensions/ERC20uRWA.t.sol)
  • New tests cover:
    • canTransfer returns true even when amount exceeds available/frozen balance
    • canTransfer returns false when canSend or canReceive is false
    • forcedTransfer reverts when canReceive is false
    • Accounts that can receive but not send, and vice versa

@mdimran29
mdimran29 requested a review from a team as a code owner July 16, 2026 15:05
@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: dfd4bda9-7dc8-4c13-b437-74b10604138d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

ERC20uRWA now exposes separate send and receive permission checks, uses them for transfer validation, and applies receive checks to forced transfers. A configurable mock and tests cover default, asymmetric, balance-independent, and forced-transfer behaviors.

Changes

ERC20uRWA permissions

Layer / File(s) Summary
Send and receive permission API
contracts/token/ERC20/extensions/ERC20uRWA.sol
Adds overridable canSend and canReceive checks, updates canTransfer to ignore amount availability, and validates forced-transfer recipients with canReceive.
Permission-controlled test token
test/token/ERC20/extensions/ERC20uRWAMock.t.sol
Adds configurable send/receive overrides, minting, role setup, state-transition helpers, and interface support for testing.
Permission and forced-transfer tests
test/token/ERC20/extensions/ERC20uRWA.t.sol
Tests amount-independent transfer checks, default and asymmetric permissions, and forced-transfer recipient enforcement.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Poem

I’m a rabbit with permissions to hop,
Send one way, receive at the stop.
Frozen coins no longer sway,
Forced paths check who may stay.
Tests bloom bright in the token patch.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes implement separate canSend/canReceive checks and remove balance-based canTransfer gating, matching #244's relevant objectives.
Out of Scope Changes check ✅ Passed All code changes are directly related to the ERC20uRWA transfer restriction fix and its tests.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: updated ERC20uRWA transfer restriction checks in canTransfer and forcedTransfer.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

canTransfer incorrectly bundled a balance check into an availability
query, and forcedTransfer gated recipients with canTransact instead of
a receive-specific check. Add virtual canSend/canReceive (defaulting
to canTransact) so send- and receive-side restrictions can be
overridden independently, use them in canTransfer, and gate
forcedTransfer's recipient with canReceive.
@mdimran29
mdimran29 force-pushed the fix/erc20urwa-send-receive-checks branch from 265c1d4 to 924ec70 Compare July 16, 2026 15:08

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
contracts/token/ERC20/extensions/ERC20uRWA.sol (1)

117-127: 🔒 Security & Privacy | 🔴 Critical | 🏗️ Heavy lift

Enforce canSend and canReceive during actual transfers and verify with tests.

The PR introduces independent canSend and canReceive permissions but does not enforce them during the actual token transfer lifecycle. Currently, relying on the inherited _update chain only evaluates canTransact(from) and canTransact(to). If a derived contract overrides canSend or canReceive, external queries to canTransfer will correctly reflect these overrides, but actual token transfers will completely bypass them and succeed.

  • contracts/token/ERC20/extensions/ERC20uRWA.sol#L117-L127: Ensure _update explicitly evaluates canSend(from) (if not minting) and canReceive(to) (if not burning) instead of relying solely on ERC20Restricted's generic canTransact checks. Note that forcedTransfer will also need a mechanism (like a transient bypass flag) to skip these new checks since it currently bypasses restrictions by modifying canTransact state, which would fail to bypass an overridden canSend.
  • test/token/ERC20/extensions/ERC20uRWA.t.sol#L90-L112: Add assertions using vm.expectRevert() to execute actual transfers (token.transfer or token.transferFrom) and verify that the mocked send/receive restrictions are genuinely enforced at runtime, preventing the movement of tokens.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@contracts/token/ERC20/extensions/ERC20uRWA.sol` around lines 117 - 127,
Update ERC20uRWA._update to enforce canSend(from) for non-mints and
canReceive(to) for non-burns, while preserving forcedTransfer’s ability to
bypass these checks through an appropriate transient bypass mechanism; retain
the inherited restrictions where applicable. In
test/token/ERC20/extensions/ERC20uRWA.t.sol lines 90-112, add vm.expectRevert()
cases that perform actual transfer or transferFrom calls and verify mocked send
and receive restrictions prevent token movement.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@contracts/token/ERC20/extensions/ERC20uRWA.sol`:
- Around line 117-127: Update ERC20uRWA._update to enforce canSend(from) for
non-mints and canReceive(to) for non-burns, while preserving forcedTransfer’s
ability to bypass these checks through an appropriate transient bypass
mechanism; retain the inherited restrictions where applicable. In
test/token/ERC20/extensions/ERC20uRWA.t.sol lines 90-112, add vm.expectRevert()
cases that perform actual transfer or transferFrom calls and verify mocked send
and receive restrictions prevent token movement.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d805fd4e-03ae-46dd-a58b-a52d0911760d

📥 Commits

Reviewing files that changed from the base of the PR and between f7e5f08 and 265c1d4.

📒 Files selected for processing (3)
  • contracts/token/ERC20/extensions/ERC20uRWA.sol
  • test/token/ERC20/extensions/ERC20uRWA.t.sol
  • test/token/ERC20/extensions/ERC20uRWAMock.t.sol

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.

ERC20uRWA: potential ERC-7943 compliance issues and self-forced-transfer edge case

1 participant