Revert when wrapper is full#268
Conversation
✅ Deploy Preview for confidential-tokens ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the WalkthroughRefactored minting logic in ERC7984ERC20Wrapper by re-introducing public accessors (decimals, rate, underlying), adding totalSupply() and internal _mint() functions with overflow checks, and simplifying type conversions by removing FHE wrapping in mint paths. Added tests validating maximum representable amounts and overflow behavior. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes
Suggested reviewers
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
contracts/token/ERC7984/extensions/ERC7984ERC20Wrapper.sol (1)
161-164: Consider expanding the comment to clarify the invariant.The overflow check works because both call sites (
wrapandonTransferReceived) transfer the underlying tokens before calling this function. A more descriptive comment could help future maintainers understand this invariant.function _mint(address to, uint64 amount) internal virtual { - totalSupply(); // check that total supply doesn't overflow + // Revert if new total supply would overflow uint64. + // Requires: underlying tokens already transferred before this call. + totalSupply(); _mint(to, FHE.asEuint64(amount)); }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
contracts/token/ERC7984/extensions/ERC7984ERC20Wrapper.sol(4 hunks)test/token/ERC7984/extensions/ERC7984Wrapper.test.ts(1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-09-22T09:21:34.470Z
Learnt from: james-toussaint
Repo: OpenZeppelin/openzeppelin-confidential-contracts PR: 160
File: test/token/ERC7984/extensions/ERC7984Rwa.test.ts:474-479
Timestamp: 2025-09-22T09:21:34.470Z
Learning: In ERC7984Freezable force transfers, the frozen balance is reset to the new balance only when the transferred amount exceeds the available balance (balance - frozen). If the transferred amount is within the available balance, the frozen amount remains unchanged. This is implemented via FHE.select(FHE.gt(encryptedAmount, confidentialAvailable(account)), confidentialBalanceOf(account), frozen).
Applied to files:
contracts/token/ERC7984/extensions/ERC7984ERC20Wrapper.sol
📚 Learning: 2025-09-15T14:43:25.644Z
Learnt from: arr00
Repo: OpenZeppelin/openzeppelin-confidential-contracts PR: 186
File: contracts/token/ERC7984/extensions/ERC7984Omnibus.sol:140-167
Timestamp: 2025-09-15T14:43:25.644Z
Learning: In ERC7984Omnibus callback functions like confidentialTransferFromAndCallOmnibus, the encrypted sender and recipient addresses are not passed to the callback recipient - only the standard transfer parameters (omnibusFrom, omnibusTo, amount, data) are passed. The ACL grants for the encrypted addresses are for omnibus event emission and future access, not for callback usage.
Applied to files:
contracts/token/ERC7984/extensions/ERC7984ERC20Wrapper.sol
⏰ 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). (4)
- GitHub Check: Redirect rules - confidential-tokens
- GitHub Check: tests
- GitHub Check: coverage
- GitHub Check: slither
🔇 Additional comments (5)
contracts/token/ERC7984/extensions/ERC7984ERC20Wrapper.sol (3)
53-72: LGTM!The updated flow correctly handles the minting after excess refund. The
SafeCast.toUint64ensures the wrapped amount fits withinuint64bounds, and the subsequenttotalSupply()check in_mintvalidates the post-transfer state since tokens are already in the contract when this callback executes.
79-85: LGTM!The ordering is correct: underlying tokens are transferred first (line 81), so when
_mintcallstotalSupply(), the balance already reflects the incoming tokens. This ensures the overflow check validates the post-mint state.
152-159: LGTM!The implementation correctly uses
SafeCast.toUint64which provides the overflow protection mechanism. The NOTE appropriately documents that direct transfers can inflate this value.test/token/ERC7984/extensions/ERC7984Wrapper.test.ts (2)
85-104: LGTM!The max amount calculation is correct:
(2^64 - 1) / 10^6yields the maximum value that, when scaled to 18 decimals and divided by the rate, still fits inuint64. The test properly verifies both callback and direct wrap paths.
106-119: LGTM!The test correctly validates that wrapping
maxAmount + 1triggers an overflow. TheSafeCastOverflowedUintDowncasterror is the expected revert fromSafeCast.toUint64when the value exceedsuint64.max.
45bcb75 to
270850c
Compare
Co-authored-by: Hadrien Croubois <hadrien.croubois@gmail.com>
| * Reductions will lag compared to {confidentialTotalSupply} since it is updated on {unwrap} while this function updates | ||
| * on {finalizeUnwrap}. | ||
| */ | ||
| function totalSupply() public view virtual returns (uint256) { |
There was a problem hiding this comment.
Same question than here https://github.com/OpenZeppelin/openzeppelin-confidential-contracts/pull/268/files#r2603361771. If kept I would (a) choose a different name (totalSupplyEstimation?) with same code or (b) call it totalUnderlyingSupply with return underlying().balanceOf(address(this)).
There was a problem hiding this comment.
#268 (comment) used in tandem with maxTotalSupply to understand if the wrap request will be successful.
There was a problem hiding this comment.
Renaming the function is an interesting idea. @Amxx what do you think? It may be worthwhile to ensure that people don't use it without looking into the additional notes around it.
There was a problem hiding this comment.
I don't have a strong attachment to the name.
* Revert when wrapper full * up * simplify and add tests * update docs * cei * add doc * up * add custom error and fix tests * comment clarification * Rename `_checkTotalSupply` to `_checkConfidentialTotalSupply` * add changeset * Update contracts/token/ERC7984/extensions/ERC7984ERC20Wrapper.sol Co-authored-by: Hadrien Croubois <hadrien.croubois@gmail.com> * docs --------- Co-authored-by: Hadrien Croubois <hadrien.croubois@gmail.com>
* Revert when wrapper full * up * simplify and add tests * update docs * cei * add doc * up * add custom error and fix tests * comment clarification * Rename `_checkTotalSupply` to `_checkConfidentialTotalSupply` * add changeset * Update contracts/token/ERC7984/extensions/ERC7984ERC20Wrapper.sol * docs --------- Co-authored-by: Hadrien Croubois <hadrien.croubois@gmail.com>
* Start release candidate * Release v0.3.0 (rc) (#221) * Release v0.3.0 (rc) * Update changelog --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: James Toussaint <33313130+james-toussaint@users.noreply.github.com> * Update `checkOnTransferReceived` doc (#235) * Versioned Docs (#236) * generate versioned docs * publish docs even on pre-release * N-04: remove unused import in `ERC7984Rwa` (#243) * N-01: reset user instead of allowing user in `unblockUser` (#244) * N-05: Named mapping var in `ERC7984ObserverAccess` (#251) * N-05: Named mapping var in `ERC7984ObserverAccess` * Update contracts/token/ERC7984/extensions/ERC7984ObserverAccess.sol Co-authored-by: James Toussaint <33313130+james-toussaint@users.noreply.github.com> --------- Co-authored-by: James Toussaint <33313130+james-toussaint@users.noreply.github.com> * N-08: constant names are screaming camel case (#247) * N-08: constant names are screaming camel case * fix lint * N-02: reorder allowances omnibus (#250) * Support ERC-165 interface detection on ERC-7984 (#246) * Support ERC-165 interface detection on ERC-7984 * update link format * Add ERC7984 impl changeset * Update changeset --------- Co-authored-by: Arr00 <13561405+arr00@users.noreply.github.com> * M-03: grant allowances to agent in `ERC7984Rwa` (#242) * M-03: grant allowances to agent in `ERC7984Rwa` * up * N-12: update docs in `ERC7984Restricted` (#245) * Upgrade to use fhevm contracts v0.9.0 (#248) * chore: fhevm-v9 * chore: port all tests for fhevm v9 * Merge pull request #1 from OpenZeppelin/chore/update-disclose-flow update disclose flow * Update wrapper contract (#2) * Update wrapper contract * fix tests * fix mock * update docs * add changeset * request id unnecessary * Update contracts/token/ERC7984/extensions/ERC7984ERC20Wrapper.sol Co-authored-by: James Toussaint <33313130+james-toussaint@users.noreply.github.com> * remove unused params * Update test/token/ERC7984/ERC7984.test.ts Co-authored-by: James Toussaint <33313130+james-toussaint@users.noreply.github.com> * `cts` -> `handles` * `cleartext` -> `cleartextAmount` * Update test/token/ERC7984/extensions/ERC7984Wrapper.test.ts Co-authored-by: James Toussaint <33313130+james-toussaint@users.noreply.github.com> * nit --------- Co-authored-by: 0xalexbel <alexandre.belhoste@zama.ai> Co-authored-by: James Toussaint <33313130+james-toussaint@users.noreply.github.com> * N-[9,11]: fix `ERC7984Rwa` docs (#249) * M-11: fix `ERC7984Rwa` docs * add docs * Update contracts/token/ERC7984/extensions/ERC7984Rwa.sol * L-05: Grant allowances in `confidentialAvailable` (#252) * L-05: Grant allowances in `confidentialAvailable` * fix doc * L-01: `tryDecrease` return initialized value if delta is initialized (#241) * L-01: `tryDecrease` return initialized value if delta is initialized * add comment * Add changeset * Upgrade to use fhevm contracts v0.9.1 (#254) * Upgrade to use fhevm contracts v0.9.1 * bump sub package as well * Update `ERC7984Rwa` docs (#255) * Exit pre-release (#258) * Release v0.3.0 (#253) * Release v0.3.0 * Update changelog (#259) * Update changelog * Update CHANGELOG.md Co-authored-by: James Toussaint <33313130+james-toussaint@users.noreply.github.com> --------- Co-authored-by: James Toussaint <33313130+james-toussaint@users.noreply.github.com> * remove duplicate entry --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Arr00 <13561405+arr00@users.noreply.github.com> Co-authored-by: James Toussaint <33313130+james-toussaint@users.noreply.github.com> * Revert when wrapper is full (#268) (#271) * Revert when wrapper full * up * simplify and add tests * update docs * cei * add doc * up * add custom error and fix tests * comment clarification * Rename `_checkTotalSupply` to `_checkConfidentialTotalSupply` * add changeset * Update contracts/token/ERC7984/extensions/ERC7984ERC20Wrapper.sol * docs --------- Co-authored-by: Hadrien Croubois <hadrien.croubois@gmail.com> * Add `ERC7984ERC20Wrapper` missing docs (#280) Add missing docs * Rename `ERC7984ERC20Wrapper` `totalSupply()` to `inferredTotalSuppy()` (#284) Rename `totalSupply()` to `inferredTotalSuppy()` * Release v0.3.1 (#272) * Release v0.3.1 * update changelog --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Arr00 <13561405+arr00@users.noreply.github.com> --------- Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: James Toussaint <33313130+james-toussaint@users.noreply.github.com> Co-authored-by: Arr00 <13561405+arr00@users.noreply.github.com> Co-authored-by: 0xalexbel <alexandre.belhoste@zama.ai> Co-authored-by: Hadrien Croubois <hadrien.croubois@gmail.com>
This PR fixes an issue where wrapping assumes that the ERC7984
_mintfunction is successful without any checks. This is not a safe assumption and can result in loss of funds if the_mintfails (would happen if the call results in overflow).This issue is fixed by checking plaintext state, which allows the contract to infer an upper bound on the confidential total supply. If the confidential total supply may overflow, the wrap transaction is reverted.
Summary by CodeRabbit
Bug Fixes
Tests
✏️ Tip: You can customize this high-level summary in your review settings.