-
Notifications
You must be signed in to change notification settings - Fork 30
Add ERC7984Freezable
#151
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
james-toussaint
merged 23 commits into
master
from
feature/confidential-freezable-token
Aug 25, 2025
Merged
Add ERC7984Freezable
#151
Changes from 21 commits
Commits
Show all changes
23 commits
Select commit
Hold shift + click to select a range
b4ce8d9
Add `ERC7984Freezable` extension.
james-toussaint f04162b
Update pragma
james-toussaint ec16cdf
Merge remote-tracking branch 'origin/master' into feature/confidentia…
james-toussaint ff2a74f
Add freezable test
james-toussaint f485ec0
Remove unused functions in freezable mock
james-toussaint a961e33
Bring back previous HandleAccessManager
james-toussaint 3c759fb
Move allow available to access function in mock
james-toussaint cd81483
Swap event & error order
james-toussaint 3bb3f2e
Update inline documentation
james-toussaint ac9ae06
Remove useless var
james-toussaint 4095083
Merge branch 'master' into feature/confidential-freezable-token
arr00 0286f57
Apply suggestions from review
james-toussaint c790dc5
Move freezable test file to extensions dir
james-toussaint 754ecec
update tests
arr00 8e08500
Apply suggestions
james-toussaint 7e45893
Remove account from `_validateHandleAllowance` (#178)
arr00 ed13e2f
Call internal when setting frozen with proof
james-toussaint 9ae4074
Update doc
james-toussaint 6a52710
Update pragma & remove import in freezable mock
james-toussaint bc71379
Base freezable mock on ERC7984Mock
james-toussaint 4466341
Merge branch 'master' into feature/confidential-freezable-token
arr00 7f9b646
Apply suggestions
james-toussaint 9d8aa7d
Check transfer amount is zero if transferring more than available
james-toussaint File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| 'openzeppelin-confidential-contracts': minor | ||
| --- | ||
|
|
||
| `ERC7984Freezable`: Add an extension to `ERC7984`, which allows an account granted the "freezer" role to freeze and unfreeze a confidential amount of tokens for holders. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| // SPDX-License-Identifier: MIT | ||
|
|
||
| pragma solidity ^0.8.27; | ||
|
|
||
| import {SepoliaConfig} from "@fhevm/solidity/config/ZamaConfig.sol"; | ||
| import {FHE, euint64, externalEuint64} from "@fhevm/solidity/lib/FHE.sol"; | ||
| import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol"; | ||
| import {ERC7984} from "../../token/ERC7984/ERC7984.sol"; | ||
| import {ERC7984Freezable} from "../../token/ERC7984/extensions/ERC7984Freezable.sol"; | ||
| import {HandleAccessManager} from "../../utils/HandleAccessManager.sol"; | ||
| import {ERC7984Mock} from "./ERC7984Mock.sol"; | ||
|
|
||
| // solhint-disable func-name-mixedcase | ||
james-toussaint marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
james-toussaint marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| contract ERC7984FreezableMock is ERC7984Mock, ERC7984Freezable, AccessControl, HandleAccessManager { | ||
| bytes32 public constant FREEZER_ROLE = keccak256("FREEZER_ROLE"); | ||
|
|
||
| error UnallowedHandleAccess(bytes32 handle, address account); | ||
|
|
||
| constructor( | ||
| string memory name, | ||
| string memory symbol, | ||
| string memory tokenUri, | ||
| address freezer | ||
| ) ERC7984Mock(name, symbol, tokenUri) { | ||
| _grantRole(FREEZER_ROLE, freezer); | ||
| } | ||
|
|
||
| function _update( | ||
| address from, | ||
| address to, | ||
| euint64 amount | ||
| ) internal virtual override(ERC7984Mock, ERC7984Freezable) returns (euint64) { | ||
| return super._update(from, to, amount); | ||
| } | ||
|
|
||
| function createEncryptedAmount(uint64 amount) public returns (euint64 encryptedAmount) { | ||
| FHE.allowThis(encryptedAmount = FHE.asEuint64(amount)); | ||
| FHE.allow(encryptedAmount, msg.sender); | ||
| } | ||
|
|
||
| function confidentialAvailableAccess(address account) public { | ||
| euint64 available = confidentialAvailable(account); | ||
| FHE.allowThis(available); | ||
arr00 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| getHandleAllowance(euint64.unwrap(available), account, true); | ||
| } | ||
|
|
||
| function _validateHandleAllowance(bytes32 handle) internal view override {} | ||
|
|
||
| function _checkFreezer() internal override onlyRole(FREEZER_ROLE) {} | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| // SPDX-License-Identifier: MIT | ||
|
|
||
| pragma solidity ^0.8.27; | ||
|
|
||
| import {FHE, ebool, euint64, externalEuint64} from "@fhevm/solidity/lib/FHE.sol"; | ||
| import {FHESafeMath} from "../../../utils/FHESafeMath.sol"; | ||
| import {ERC7984} from "../ERC7984.sol"; | ||
|
|
||
| /** | ||
| * @dev Extension of {ERC7984} that allows to implement a confidential | ||
james-toussaint marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| * freezing mechanism that can be managed by an authorized account with | ||
| * {setConfidentialFrozen} functions. | ||
| * | ||
| * The freezing mechanism provides the guarantee to the contract owner | ||
| * (e.g. a DAO or a well-configured multisig) that a specific confidential | ||
| * amount of tokens held by an account won't be transferable until those | ||
| * tokens are unfrozen. | ||
| * | ||
| * Inspired by https://github.com/OpenZeppelin/openzeppelin-community-contracts/blob/master/contracts/token/ERC20/extensions/ERC20Freezable.sol | ||
| */ | ||
| abstract contract ERC7984Freezable is ERC7984 { | ||
| /// @dev Confidential frozen amount of tokens per address. | ||
| mapping(address account => euint64 encryptedAmount) private _frozenBalances; | ||
|
|
||
| /// @dev Emitted when a confidential amount of token is frozen for an account | ||
| event TokensFrozen(address indexed account, euint64 encryptedAmount); | ||
|
|
||
| /// @dev Returns the confidential frozen balance of an account. | ||
| function confidentialFrozen(address account) public view virtual returns (euint64) { | ||
| return _frozenBalances[account]; | ||
| } | ||
|
|
||
| /// @dev Returns the confidential available (unfrozen) balance of an account. Up to {confidentialBalanceOf}. | ||
| function confidentialAvailable(address account) public virtual returns (euint64) { | ||
| (ebool success, euint64 unfrozen) = FHESafeMath.tryDecrease( | ||
| confidentialBalanceOf(account), | ||
| confidentialFrozen(account) | ||
| ); | ||
| return FHE.select(success, unfrozen, FHE.asEuint64(0)); | ||
| } | ||
|
|
||
| /// @dev Freezes a confidential amount of tokens for an account with a proof. | ||
| function setConfidentialFrozen( | ||
| address account, | ||
| externalEuint64 encryptedAmount, | ||
| bytes calldata inputProof | ||
| ) public virtual { | ||
| _setConfidentialFrozen(account, FHE.fromExternal(encryptedAmount, inputProof)); | ||
| } | ||
|
|
||
| /// @dev Freezes a confidential amount of tokens for an account. | ||
| function setConfidentialFrozen(address account, euint64 encryptedAmount) public virtual { | ||
| require( | ||
| FHE.isAllowed(encryptedAmount, msg.sender), | ||
| ERC7984UnauthorizedUseOfEncryptedAmount(encryptedAmount, msg.sender) | ||
| ); | ||
| _setConfidentialFrozen(account, encryptedAmount); | ||
| } | ||
|
|
||
| /// @dev Internal function to freeze a confidential amount of tokens for an account. | ||
| function _setConfidentialFrozen(address account, euint64 encryptedAmount) internal virtual { | ||
| _checkFreezer(); | ||
| FHE.allowThis(encryptedAmount); | ||
| FHE.allow(encryptedAmount, account); | ||
| _frozenBalances[account] = encryptedAmount; | ||
| emit TokensFrozen(account, encryptedAmount); | ||
| } | ||
|
|
||
| /// @dev Unimplemented function that must revert if `msg.sender` is not authorized as a freezer. | ||
| function _checkFreezer() internal virtual; | ||
|
|
||
| /** | ||
| * @dev See {ERC7984-_update}. The `from` account must have sufficient unfrozen balance, | ||
| * otherwise 0 tokens are transferred. | ||
| */ | ||
| function _update(address from, address to, euint64 encryptedAmount) internal virtual override returns (euint64) { | ||
| if (from != address(0)) { | ||
| euint64 unfrozen = confidentialAvailable(from); | ||
| encryptedAmount = FHE.select(FHE.le(encryptedAmount, unfrozen), encryptedAmount, FHE.asEuint64(0)); | ||
| } | ||
| return super._update(from, to, encryptedAmount); | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.