Skip to content

Commit ac8b822

Browse files
authored
Update RWA (#214)
* Update RWA * fix lint * up * update comment
1 parent 3da562e commit ac8b822

File tree

3 files changed

+32
-70
lines changed

3 files changed

+32
-70
lines changed

contracts/token/ERC7984/extensions/ERC7984Freezable.sol

Lines changed: 10 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -48,50 +48,18 @@ abstract contract ERC7984Freezable is ERC7984 {
4848
}
4949

5050
/**
51-
* @dev See {ERC7984-_update}. The `from` account must have sufficient unfrozen balance,
51+
* @dev See {ERC7984-_update}.
52+
*
53+
* The `from` account must have sufficient unfrozen balance,
5254
* otherwise 0 tokens are transferred.
53-
* The default freezing behaviour can be changed (for a pass-through for instance) by overriding
54-
* {_checkSenderAmountNotFrozenBeforeUpdate} and/or {_syncSenderFrozenAfterUpdate}.
55+
* The default freezing behavior can be changed (for a pass-through for instance) by overriding
56+
* {confidentialAvailable}.
5557
*/
56-
function _update(
57-
address from,
58-
address to,
59-
euint64 encryptedAmount
60-
) internal virtual override returns (euint64 transferred) {
61-
encryptedAmount = _checkSenderAmountNotFrozenBeforeUpdate(from, encryptedAmount);
62-
transferred = super._update(from, to, encryptedAmount);
63-
_syncSenderFrozenAfterUpdate(from);
64-
}
65-
66-
/**
67-
* @dev Internal function which returns the amount to be updated if that amount is not exceeding
68-
* the frozen balance of the `from` account. Otherwise if it is exceeding it returns 0.
69-
* Used in {_update} function.
70-
*/
71-
function _checkSenderAmountNotFrozenBeforeUpdate(
72-
address account,
73-
euint64 requestedAmount
74-
) internal virtual returns (euint64) {
75-
if (account != address(0)) {
76-
return
77-
FHE.select(FHE.le(requestedAmount, confidentialAvailable(account)), requestedAmount, FHE.asEuint64(0));
78-
}
79-
return requestedAmount;
80-
}
81-
82-
/**
83-
* @dev Internal function which resets frozen of the `from` account to its balance after a transfer.
84-
* Used in {_update} function.
85-
*/
86-
function _syncSenderFrozenAfterUpdate(address account) internal virtual {
87-
if (account != address(0)) {
88-
euint64 frozen = confidentialFrozen(account);
89-
if (!FHE.isInitialized(frozen)) {
90-
return;
91-
}
92-
euint64 balance = confidentialBalanceOf(account);
93-
// Reset frozen to balance if transferred more than available
94-
_setConfidentialFrozen(account, FHE.select(FHE.gt(frozen, balance), balance, frozen));
58+
function _update(address from, address to, euint64 encryptedAmount) internal virtual override returns (euint64) {
59+
if (from != address(0)) {
60+
euint64 unfrozen = confidentialAvailable(from);
61+
encryptedAmount = FHE.select(FHE.le(encryptedAmount, unfrozen), encryptedAmount, FHE.asEuint64(0));
9562
}
63+
return super._update(from, to, encryptedAmount);
9664
}
9765
}

contracts/token/ERC7984/extensions/ERC7984Rwa.sol

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@
22

33
pragma solidity ^0.8.27;
44

5-
import {FHE, ebool, externalEuint64, euint64} from "@fhevm/solidity/lib/FHE.sol";
5+
import {FHE, externalEuint64, euint64} from "@fhevm/solidity/lib/FHE.sol";
66
import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
77
import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol";
88
import {Multicall} from "@openzeppelin/contracts/utils/Multicall.sol";
99
import {Pausable} from "@openzeppelin/contracts/utils/Pausable.sol";
1010
import {IERC7984} from "./../../../interfaces/IERC7984.sol";
1111
import {IERC7984Rwa} from "./../../../interfaces/IERC7984Rwa.sol";
12+
import {FHESafeMath} from "./../../../utils/FHESafeMath.sol";
1213
import {ERC7984} from "./../ERC7984.sol";
1314
import {ERC7984Freezable} from "./ERC7984Freezable.sol";
1415
import {ERC7984Restricted} from "./ERC7984Restricted.sol";
@@ -17,15 +18,7 @@ import {ERC7984Restricted} from "./ERC7984Restricted.sol";
1718
* @dev Extension of {ERC7984} that supports confidential Real World Assets (RWAs).
1819
* This interface provides compliance checks, transfer controls and enforcement actions.
1920
*/
20-
abstract contract ERC7984Rwa is
21-
ERC7984,
22-
ERC7984Freezable,
23-
ERC7984Restricted,
24-
Pausable,
25-
Multicall,
26-
ERC165,
27-
AccessControl
28-
{
21+
abstract contract ERC7984Rwa is ERC7984Freezable, ERC7984Restricted, Pausable, Multicall, ERC165, AccessControl {
2922
bytes32 public constant AGENT_ROLE = keccak256("AGENT_ROLE");
3023
// bytes4(keccak256("forceConfidentialTransferFrom(address,address,bytes32)"))
3124
bytes4 private constant FORCE_CONFIDENTIAL_TRANSFER_FROM_SIG = 0x6c9c3c85;
@@ -183,15 +176,21 @@ abstract contract ERC7984Rwa is
183176
address from,
184177
address to,
185178
euint64 encryptedAmount
186-
) internal override(ERC7984Freezable, ERC7984Restricted, ERC7984) whenNotPaused returns (euint64) {
179+
) internal override(ERC7984Freezable, ERC7984Restricted) whenNotPaused returns (euint64) {
187180
// frozen and restriction checks performed through inheritance
188181
return super._update(from, to, encryptedAmount);
189182
}
190183

191184
/// @dev Internal function which forces transfer of confidential amount of tokens from account to account by skipping compliance checks.
192185
function _forceUpdate(address from, address to, euint64 encryptedAmount) internal virtual returns (euint64) {
186+
euint64 senderFrozenAmount = confidentialFrozen(from);
187+
if (FHE.isInitialized(senderFrozenAmount)) {
188+
(, euint64 newFrozen) = FHESafeMath.tryDecrease(senderFrozenAmount, encryptedAmount);
189+
_setConfidentialFrozen(from, newFrozen);
190+
}
191+
193192
// bypassing `from` restriction check with {_checkSenderRestriction}
194-
// bypassing `from` frozen check with {_checkSenderAmountNotFrozenBeforeUpdate}
193+
// bypassing `from` frozen check with {confidentialAvailable}
195194
return super._update(from, to, encryptedAmount); // still performing `to` restriction check
196195
}
197196

@@ -205,17 +204,12 @@ abstract contract ERC7984Rwa is
205204
super._checkSenderRestriction(account);
206205
}
207206

208-
/**
209-
* @dev Bypasses the frozen check of the `from` account when performing a {forceConfidentialTransferFrom}.
210-
*/
211-
function _checkSenderAmountNotFrozenBeforeUpdate(
212-
address account,
213-
euint64 encryptedAmount
214-
) internal override returns (euint64) {
207+
function confidentialAvailable(address account) public virtual override returns (euint64) {
215208
if (_isForceTransfer()) {
216-
return encryptedAmount;
209+
return confidentialBalanceOf(account);
210+
} else {
211+
return super.confidentialAvailable(account);
217212
}
218-
return super._checkSenderAmountNotFrozenBeforeUpdate(account, encryptedAmount);
219213
}
220214

221215
/// @dev Private function which checks if the called function is a {forceConfidentialTransferFrom}.

test/token/ERC7984/extensions/ERC7984Rwa.test.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,7 @@ describe('ERC7984Rwa', function () {
451451
await token.connect(agent1).getHandleAllowance(frozenHandle, agent1, true);
452452
await expect(
453453
fhevm.userDecryptEuint(FhevmType.euint64, frozenHandle, await token.getAddress(), agent1),
454-
).to.eventually.equal(50); // frozen is left unchanged
454+
).to.eventually.equal(25); // is decreased by transfer amount
455455
});
456456
}
457457

@@ -514,18 +514,18 @@ describe('ERC7984Rwa', function () {
514514
);
515515
expect(account).equal(recipient.address);
516516
await expect(
517-
fhevm.userDecryptEuint(FhevmType.euint64, frozenAmountHandle, await token.getAddress(), recipient),
518-
).to.eventually.equal(75);
517+
fhevm.userDecryptEuint(FhevmType.euint64, frozenAmountHandle, token.target, recipient),
518+
).to.eventually.equal(55);
519519
const balanceHandle = await token.confidentialBalanceOf(recipient);
520520
await token.connect(agent1).getHandleAllowance(balanceHandle, agent1, true);
521521
await expect(
522-
fhevm.userDecryptEuint(FhevmType.euint64, balanceHandle, await token.getAddress(), agent1),
522+
fhevm.userDecryptEuint(FhevmType.euint64, balanceHandle, token.target, agent1),
523523
).to.eventually.equal(75);
524524
const frozenHandle = await token.confidentialFrozen(recipient);
525525
await token.connect(agent1).getHandleAllowance(frozenHandle, agent1, true);
526-
await expect(
527-
fhevm.userDecryptEuint(FhevmType.euint64, frozenHandle, await token.getAddress(), agent1),
528-
).to.eventually.equal(75); // frozen got reset to balance
526+
await expect(fhevm.userDecryptEuint(FhevmType.euint64, frozenHandle, token.target, agent1)).to.eventually.equal(
527+
55,
528+
);
529529
});
530530
}
531531

0 commit comments

Comments
 (0)