Skip to content

Conversation

@arr00
Copy link
Contributor

@arr00 arr00 commented Dec 31, 2025

This PR introduces the ERC-20 encumber extension (ERC-7246). ERC-7246 specifies a way to create encumbrances on tokens, which is a stronger version of an approval. If an encumbrance is granted to a given account, only that account can transfer the tokens. The balance of the account granting the encumbrance does not decrease until the tokens are actually transferred out by the account granted the encumbrance.

PR Checklist

  • Tests
  • Documentation
  • Changeset entry (run npx changeset add)

@arr00 arr00 requested a review from a team as a code owner December 31, 2025 19:38
@changeset-bot
Copy link

changeset-bot bot commented Dec 31, 2025

⚠️ No Changeset found

Latest commit: a06c4c6

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@socket-security
Copy link

socket-security bot commented Dec 31, 2025

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Updatednpm/​@​openzeppelin/​docs-utils@​0.1.5 ⏵ 0.1.6751008783 -2100

View full report

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 31, 2025

Walkthrough

A new ERC7246 standard interface and implementation are introduced, adding encumbrance functionality to ERC20 tokens. The interface defines methods to track and manage encumbered balances alongside available balances, along with encumber, encumberFrom, and release operations. The implementation extends ERC20 with internal mappings to track encumbrances per owner-spender pair and validates availability before transfers. Documentation is updated across interface and extension READMEs, and a comprehensive test suite validates the encumbrance mechanics and interactions with standard ERC20 operations. A minor dependency version bump is also included.

Suggested labels

ignore-changeset

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: introducing the ERC-7246 encumber extension for ERC-20 tokens, which is the primary purpose of the PR.
Description check ✅ Passed The description clearly explains the ERC-7246 feature, its purpose as an encumbrance mechanism, and how it functions relative to approvals, directly matching the changeset content.
✨ Finishing touches
  • 📝 Generate docstrings

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (3)
test/token/ERC20/extensions/draft-ERC7246.test.js (3)

5-9: Remove unused imports or enable the behavior tests.

The static analysis correctly identifies that shouldBehaveLikeERC20, shouldBehaveLikeERC20Transfer, and shouldBehaveLikeERC20Approve are imported but never used. This aligns with the commented-out line 193.

Either remove these imports or uncomment the behavior test to verify ERC20 compatibility is preserved.

🔎 Option 1: Remove unused imports
-const {
-  shouldBehaveLikeERC20,
-  shouldBehaveLikeERC20Transfer,
-  shouldBehaveLikeERC20Approve,
-} = require('../ERC20.behavior.js');
🔎 Option 2: Enable behavior tests
-  // shouldBehaveLikeERC20(value);
+  shouldBehaveLikeERC20(value);

88-92: Use this.encumberAmount instead of hardcoded 400n.

For consistency and maintainability, reference the variable defined in the beforeEach block.

🔎 Proposed fix
     it('should allow transfer within available balance', async function () {
-      await expect(this.token.connect(this.holder).transfer(this.other, value - 400n))
+      await expect(this.token.connect(this.holder).transfer(this.other, value - this.encumberAmount))
         .to.emit(this.token, 'Transfer')
-        .withArgs(this.holder.address, this.other.address, value - 400n);
+        .withArgs(this.holder.address, this.other.address, value - this.encumberAmount);
     });

37-93: Consider adding a test for Encumber event emission in the encumber describe block.

The encumberFrom tests verify the Encumber event is emitted (line 107), but the encumber tests don't have an equivalent assertion.

🔎 Suggested test to add
    it('should emit Encumber event', async function () {
      await expect(this.token.connect(this.holder).encumber(this.other, 100n))
        .to.emit(this.token, 'Encumber')
        .withArgs(this.holder.address, this.other.address, 100n);
    });
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a83d9aa and bfc9090.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (8)
  • contracts/interfaces/README.adoc
  • contracts/interfaces/draft-IERC7246.sol
  • contracts/token/ERC20/ERC20.sol
  • contracts/token/ERC20/IERC20.sol
  • contracts/token/ERC20/README.adoc
  • contracts/token/ERC20/extensions/draft-ERC7246.sol
  • package.json
  • test/token/ERC20/extensions/draft-ERC7246.test.js
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-09-04T09:13:21.278Z
Learnt from: Amxx
Repo: OpenZeppelin/openzeppelin-contracts PR: 5904
File: contracts/crosschain/ERC7786Recipient.sol:3-3
Timestamp: 2025-09-04T09:13:21.278Z
Learning: In OpenZeppelin contracts, hardhat.config.js uses a sophisticated yargs-based configuration where the Solidity compiler version is set via argv.compiler (line 77) with a default of '0.8.27' defined in the yargs options (line 21), allowing flexible command-line overrides while maintaining a consistent default.

Applied to files:

  • package.json
📚 Learning: 2025-09-04T09:13:21.278Z
Learnt from: Amxx
Repo: OpenZeppelin/openzeppelin-contracts PR: 5904
File: contracts/crosschain/ERC7786Recipient.sol:3-3
Timestamp: 2025-09-04T09:13:21.278Z
Learning: In OpenZeppelin contracts, hardhat.config.js uses yargs to configure the Solidity compiler version dynamically via command line arguments, with a default of '0.8.27' set on line 21 and the solidity configuration using argv.compile around lines 76-87, rather than hardcoded version strings.

Applied to files:

  • package.json
🧬 Code graph analysis (1)
test/token/ERC20/extensions/draft-ERC7246.test.js (1)
test/utils/Address.test.js (1)
  • recipient (12-12)
🪛 GitHub Check: lint
test/token/ERC20/extensions/draft-ERC7246.test.js

[failure] 8-8:
'shouldBehaveLikeERC20Approve' is assigned a value but never used


[failure] 7-7:
'shouldBehaveLikeERC20Transfer' is assigned a value but never used


[failure] 6-6:
'shouldBehaveLikeERC20' is assigned a value but never used

🪛 Gitleaks (8.30.0)
test/token/ERC20/extensions/draft-ERC7246.test.js

[high] 69-69: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 75-75: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 84-84: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 160-160: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: Redirect rules - solidity-contracts
  • GitHub Check: Header rules - solidity-contracts
  • GitHub Check: Pages changed - solidity-contracts
  • GitHub Check: halmos
  • GitHub Check: tests-foundry
  • GitHub Check: tests-upgradeable
  • GitHub Check: tests
  • GitHub Check: coverage
  • GitHub Check: slither
🔇 Additional comments (11)
contracts/token/ERC20/IERC20.sol (1)

39-39: LGTM: Documentation standardization.

The explicit event placeholders {IERC20-Transfer} and {IERC20-Approval} improve cross-referencing clarity in the documentation without affecting contract functionality.

Also applies to: 65-65, 76-76

contracts/token/ERC20/README.adoc (1)

30-30: LGTM: ERC7246 documentation added.

The ERC7246 references are properly placed in the extensions list and documentation sections, consistent with the formatting of other extensions.

Also applies to: 78-79

contracts/interfaces/README.adoc (1)

49-49: LGTM: IERC7246 interface documented.

The interface is correctly positioned (numerically before IERC7579) in both the list and Detailed ABI sections, following the established documentation pattern.

Also applies to: 117-118

contracts/token/ERC20/ERC20.sol (1)

155-155: LGTM: Enhanced event documentation placeholders.

The explicit event placeholders improve documentation generation quality by providing fully qualified references ({Transfer-address-address-uint256}, {IERC20-Transfer}, {IERC20-Approval}), consistent with the standardization applied to IERC20.sol.

Also applies to: 174-174, 210-210, 242-242

package.json (1)

64-64: No action required — dependency version is valid and secure.

The version @openzeppelin/[email protected] exists on npm, is the latest available, carries an MIT license, and has no known security vulnerabilities or deprecation warnings.

contracts/interfaces/draft-IERC7246.sol (1)

7-63: LGTM: Interface structure is well-designed.

The interface correctly extends IERC20, defines appropriate events, and provides clear separation between view functions and state-modifying operations. The encumbrance semantics are well-documented.

contracts/token/ERC20/extensions/draft-ERC7246.sol (5)

70-76: LGTM: Unchecked block is safe.

The comment correctly justifies safety: since availableBalanceOf is checked first and equals balanceOf(owner) - encumberedBalanceOf(owner), the additions cannot overflow. The invariant _encumbrances[owner][spender] <= _encumberedBalances[owner] <= balanceOf(owner) holds.


119-127: Resolve TODO before merging.

The TODO raises a valid design question: when a user has insufficient balance (not just insufficient available balance), should the error be ERC7246InsufficientAvailableBalance or the standard ERC20 insufficient balance error?

Current behavior: A user with 100 tokens (0 encumbered) trying to transfer 200 will get ERC7246InsufficientAvailableBalance(100, 200) instead of the standard ERC20 error.

Consider whether this is the intended UX, and remove the TODO once decided.


103-116: LGTM: Encumbrance-first consumption is correctly implemented.

The logic correctly consumes encumbrances before allowances. The Math.min ensures we don't release more than available, and the unchecked subtraction is safe because encumberedToUse <= amount.


50-53: LGTM: Forward encumbrance pattern enabled.

The implementation correctly allows encumbrance forwarding by consuming the caller's encumbrance (via _spendAllowance) before creating a new encumbrance to spender. This is well-tested in the test file's "can forward encumbrance" case.


87-97: LGTM: Release logic is correct and safe.

The require check on line 89 ensures the unchecked subtractions cannot underflow. The invariant that _encumbrances[owner][spender] <= _encumberedBalances[owner] is maintained.

@Amxx Amxx added this to the 5.7 milestone Jan 7, 2026
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.

2 participants