Skip to content

Commit 3469c1b

Browse files
authored
Update ERC-7943: [WIP] Add delta freezes, fix backward compatibility
Merged by EIP-Bot.
1 parent 19869ba commit 3469c1b

File tree

13 files changed

+3260
-404
lines changed

13 files changed

+3260
-404
lines changed

ERCS/erc-7943.md

Lines changed: 128 additions & 404 deletions
Large diffs are not rendered by default.

assets/erc-7943/README.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# ERC-7943 uRWA Minimal Package
2+
3+
## Prerequisites
4+
```bash
5+
curl -L https://foundry.paradigm.xyz | bash
6+
foundryup
7+
```
8+
9+
## Install dependencies
10+
If OpenZeppelin not yet in lib/:
11+
```bash
12+
forge install OpenZeppelin/openzeppelin-contracts
13+
```
14+
15+
## Build
16+
```bash
17+
forge build
18+
```
19+
20+
## Test
21+
```bash
22+
forge test -vv
23+
```
24+
25+
## Gas report
26+
```bash
27+
forge test --gas-report
28+
```
29+
30+
## Coverage
31+
```bash
32+
forge coverage
33+
```
Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.29;
3+
4+
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
5+
6+
/// @notice Interface for ERC-20 based implementations.
7+
interface IERC7943Fungible is IERC165 {
8+
/// @notice Emitted when tokens are taken from one address and transferred to another.
9+
/// @param from The address from which tokens were taken.
10+
/// @param to The address to which seized tokens were transferred.
11+
/// @param amount The amount seized.
12+
event ForcedTransfer(address indexed from, address indexed to, uint256 amount);
13+
14+
/// @notice Emitted when `setFrozenTokens` is called, changing the frozen `amount` of tokens for `account`.
15+
/// @param account The address of the account whose tokens are being frozen.
16+
/// @param amount The amount of tokens frozen after the change.
17+
event Frozen(address indexed account, uint256 amount);
18+
19+
/// @notice Error reverted when an account is not allowed to transact.
20+
/// @param account The address of the account which is not allowed for transfers.
21+
error ERC7943CannotTransact(address account);
22+
23+
/// @notice Error reverted when a transfer is not allowed according to internal rules.
24+
/// @param from The address from which tokens are being sent.
25+
/// @param to The address to which tokens are being sent.
26+
/// @param amount The amount sent.
27+
error ERC7943CannotTransfer(address from, address to, uint256 amount);
28+
29+
/// @notice Error reverted when a transfer is attempted from `account` with an `amount` less than or equal to its balance, but greater than its unfrozen balance.
30+
/// @param account The address holding the tokens.
31+
/// @param amount The amount being transferred.
32+
/// @param unfrozen The amount of tokens that are unfrozen and available to transfer.
33+
error ERC7943InsufficientUnfrozenBalance(address account, uint256 amount, uint256 unfrozen);
34+
35+
/// @notice Takes tokens from one address and transfers them to another.
36+
/// @dev Requires specific authorization. Used for regulatory compliance or recovery scenarios.
37+
/// @param from The address from which `amount` is taken.
38+
/// @param to The address that receives `amount`.
39+
/// @param amount The amount to force transfer.
40+
/// @return result True if the transfer executed correctly, false otherwise.
41+
function forcedTransfer(address from, address to, uint256 amount) external returns(bool result);
42+
43+
/// @notice Changes the frozen status of `amount` tokens belonging to `account`.
44+
/// This overwrites the current value, similar to an `approve` function.
45+
/// @dev Requires specific authorization. Frozen tokens cannot be transferred by the account.
46+
/// @param account The address of the account whose tokens are to be frozen/unfrozen.
47+
/// @param amount The amount of tokens to freeze. It can be greater than account balance.
48+
/// @return result True if the freezing executed correctly, false otherwise.
49+
function setFrozenTokens(address account, uint256 amount) external returns(bool result);
50+
51+
/// @notice Checks if a specific account is allowed to transact according to token rules.
52+
/// @dev This is often used for allowlist/KYC/KYB/AML checks.
53+
/// @param account The address to check.
54+
/// @return allowed True if the account is allowed, false otherwise.
55+
function canTransact(address account) external view returns (bool allowed);
56+
57+
/// @notice Checks the frozen status/amount.
58+
/// @param account The address of the account.
59+
/// @dev It could return an amount higher than the account's balance.
60+
/// @return amount The amount of tokens currently frozen for `account`.
61+
function getFrozenTokens(address account) external view returns (uint256 amount);
62+
63+
/// @notice Checks if a transfer is currently possible according to token rules. It enforces validations on the frozen tokens.
64+
/// @dev This may involve checks like allowlists, blocklists, transfer limits and other policy-defined restrictions.
65+
/// @param from The address sending tokens.
66+
/// @param to The address receiving tokens.
67+
/// @param amount The amount being transferred.
68+
/// @return allowed True if the transfer is allowed, false otherwise.
69+
function canTransfer(address from, address to, uint256 amount) external view returns (bool allowed);
70+
}
71+
72+
/// @notice Interface for ERC-721 based implementations.
73+
interface IERC7943NonFungible is IERC165 {
74+
/// @notice Emitted when `tokenId` is taken from one address and transferred to another.
75+
/// @param from The address from which `tokenId` is taken.
76+
/// @param to The address to which seized `tokenId` is transferred.
77+
/// @param tokenId The ID of the token being transferred.
78+
event ForcedTransfer(address indexed from, address indexed to, uint256 indexed tokenId);
79+
80+
/// @notice Emitted when `setFrozenTokens` is called, changing the frozen status of `tokenId` for `account`.
81+
/// @param account The address of the account whose `tokenId` is subjected to freeze/unfreeze.
82+
/// @param tokenId The ID of the token subjected to freeze/unfreeze.
83+
/// @param frozenStatus Whether `tokenId` has been frozen or unfrozen.
84+
event Frozen(address indexed account, uint256 indexed tokenId, bool indexed frozenStatus);
85+
86+
/// @notice Error reverted when an account is not allowed to transact.
87+
/// @param account The address of the account which is not allowed for transfers.
88+
error ERC7943CannotTransact(address account);
89+
90+
/// @notice Error reverted when a transfer is not allowed according to internal rules.
91+
/// @param from The address from which tokens are being sent.
92+
/// @param to The address to which tokens are being sent.
93+
/// @param tokenId The id of the token being sent.
94+
error ERC7943CannotTransfer(address from, address to, uint256 tokenId);
95+
96+
/// @notice Error reverted when a transfer is attempted from `account` with a `tokenId` which has been previously frozen.
97+
/// @param account The address holding the tokens.
98+
/// @param tokenId The ID of the token being frozen.
99+
error ERC7943FrozenTokenId(address account, uint256 tokenId);
100+
101+
/// @notice Takes `tokenId` from one address and transfers it to another.
102+
/// @dev Requires specific authorization. Used for regulatory compliance or recovery scenarios.
103+
/// @param from The address from which `tokenId` is taken.
104+
/// @param to The address that receives `tokenId`.
105+
/// @param tokenId The ID of the token being transferred.
106+
/// @return result True if the transfer executed correctly, false otherwise.
107+
function forcedTransfer(address from, address to, uint256 tokenId) external returns(bool result);
108+
109+
/// @notice Changes the frozen status of `tokenId` belonging to an `account`.
110+
/// This overwrites the current value, similar to an `approve` function.
111+
/// @dev Requires specific authorization. Frozen tokens cannot be transferred by the account.
112+
/// @param account The address of the account whose tokens are to be frozen/unfrozen.
113+
/// @param tokenId The ID of the token to freeze/unfreeze.
114+
/// @param frozenStatus Whether `tokenId` is being frozen or not.
115+
/// @return result True if the freezing executed correctly, false otherwise.
116+
function setFrozenTokens(address account, uint256 tokenId, bool frozenStatus) external returns(bool result);
117+
118+
/// @notice Checks if a specific account is allowed to transact according to token rules.
119+
/// @dev This is often used for allowlist/KYC/KYB/AML checks.
120+
/// @param account The address to check.
121+
/// @return allowed True if the account is allowed, false otherwise.
122+
function canTransact(address account) external view returns (bool allowed);
123+
124+
/// @notice Checks the frozen status of a specific `tokenId`.
125+
/// @dev It could return true even if account does not hold the token.
126+
/// @param account The address of the account.
127+
/// @param tokenId The ID of the token.
128+
/// @return frozenStatus Whether `tokenId` is currently frozen for `account`.
129+
function getFrozenTokens(address account, uint256 tokenId) external view returns (bool frozenStatus);
130+
131+
/// @notice Checks if a transfer is currently possible according to token rules. It enforces validations on the frozen tokens.
132+
/// @dev This may involve checks like allowlists, blocklists, transfer limits and other policy-defined restrictions.
133+
/// @param from The address sending tokens.
134+
/// @param to The address receiving tokens.
135+
/// @param tokenId The ID of the token being transferred.
136+
/// @return allowed True if the transfer is allowed, false otherwise.
137+
function canTransfer(address from, address to, uint256 tokenId) external view returns (bool allowed);
138+
}
139+
140+
/// @notice Interface for ERC-1155 based implementations.
141+
interface IERC7943MultiToken is IERC165 {
142+
/// @notice Emitted when tokens are taken from one address and transferred to another.
143+
/// @param from The address from which tokens were taken.
144+
/// @param to The address to which seized tokens were transferred.
145+
/// @param tokenId The ID of the token being transferred.
146+
/// @param amount The amount seized.
147+
event ForcedTransfer(address indexed from, address indexed to, uint256 indexed tokenId, uint256 amount);
148+
149+
/// @notice Emitted when `setFrozenTokens` is called, changing the frozen `amount` of `tokenId` tokens for `account`.
150+
/// @param account The address of the account whose tokens are being frozen.
151+
/// @param tokenId The ID of the token being frozen.
152+
/// @param amount The amount of tokens frozen after the change.
153+
event Frozen(address indexed account, uint256 indexed tokenId, uint256 amount);
154+
155+
/// @notice Error reverted when an account is not allowed to transact.
156+
/// @param account The address of the account which is not allowed for transfers.
157+
error ERC7943CannotTransact(address account);
158+
159+
/// @notice Error reverted when a transfer is not allowed according to internal rules.
160+
/// @param from The address from which tokens are being sent.
161+
/// @param to The address to which tokens are being sent.
162+
/// @param tokenId The id of the token being sent.
163+
/// @param amount The amount sent.
164+
error ERC7943CannotTransfer(address from, address to, uint256 tokenId, uint256 amount);
165+
166+
/// @notice Error reverted when a transfer is attempted from `account` with an `amount` of `tokenId` less than or equal to its balance, but greater than its unfrozen balance.
167+
/// @param account The address holding the tokens.
168+
/// @param tokenId The ID of the token being transferred.
169+
/// @param amount The amount being transferred.
170+
/// @param unfrozen The amount of tokens that are unfrozen and available to transfer.
171+
error ERC7943InsufficientUnfrozenBalance(address account, uint256 tokenId, uint256 amount, uint256 unfrozen);
172+
173+
/// @notice Takes tokens from one address and transfers them to another.
174+
/// @dev Requires specific authorization. Used for regulatory compliance or recovery scenarios.
175+
/// @param from The address from which `amount` is taken.
176+
/// @param to The address that receives `amount`.
177+
/// @param tokenId The ID of the token being transferred.
178+
/// @param amount The amount to force transfer.
179+
/// @return result True if the transfer executed correctly, false otherwise.
180+
function forcedTransfer(address from, address to, uint256 tokenId, uint256 amount) external returns(bool result);
181+
182+
/// @notice Changes the frozen status of `amount` of `tokenId` tokens belonging to an `account`.
183+
/// This overwrites the current value, similar to an `approve` function.
184+
/// @dev Requires specific authorization. Frozen tokens cannot be transferred by the account.
185+
/// @param account The address of the account whose tokens are to be frozen/unfrozen.
186+
/// @param tokenId The ID of the token to freeze/unfreeze.
187+
/// @param amount The amount of tokens to freeze.
188+
/// @return result True if the freezing executed correctly, false otherwise.
189+
function setFrozenTokens(address account, uint256 tokenId, uint256 amount) external returns(bool result);
190+
191+
/// @notice Checks if a specific account is allowed to transact according to token rules.
192+
/// @dev This is often used for allowlist/KYC/KYB/AML checks.
193+
/// @param account The address to check.
194+
/// @return allowed True if the account is allowed, false otherwise.
195+
function canTransact(address account) external view returns (bool allowed);
196+
197+
/// @notice Checks the frozen status/amount of a specific `tokenId`.
198+
/// @dev It could return an amount higher than the account's balance.
199+
/// @param account The address of the account.
200+
/// @param tokenId The ID of the token.
201+
/// @return amount The amount of `tokenId` tokens currently frozen for `account`.
202+
function getFrozenTokens(address account, uint256 tokenId) external view returns (uint256 amount);
203+
204+
/// @notice Checks if a transfer is currently possible according to token rules. It enforces validations on the frozen tokens.
205+
/// @dev This may involve checks like allowlists, blocklists, transfer limits and other policy-defined restrictions.
206+
/// @param from The address sending tokens.
207+
/// @param to The address receiving tokens.
208+
/// @param tokenId The ID of the token being transferred.
209+
/// @param amount The amount being transferred.
210+
/// @return allowed True if the transfer is allowed, false otherwise.
211+
function canTransfer(address from, address to, uint256 tokenId, uint256 amount) external view returns (bool allowed);
212+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.29;
3+
import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol";
4+
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
5+
6+
contract MockERC1155Receiver is IERC1155Receiver {
7+
bool public shouldReject = false;
8+
bytes4 public constant ERC1155_RECEIVER_MAGIC = bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"));
9+
bytes4 public constant ERC1155_BATCH_RECEIVER_MAGIC = bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"));
10+
11+
function setShouldReject(bool _reject) external {
12+
shouldReject = _reject;
13+
}
14+
15+
function onERC1155Received(address, address, uint256, uint256, bytes memory) public view override returns (bytes4) {
16+
if (shouldReject) {
17+
return bytes4(0);
18+
} else {
19+
return ERC1155_RECEIVER_MAGIC;
20+
}
21+
}
22+
23+
function onERC1155BatchReceived(address, address, uint256[] memory, uint256[] memory, bytes memory) public view override returns (bytes4) {
24+
if (shouldReject) {
25+
return bytes4(0);
26+
} else {
27+
return ERC1155_BATCH_RECEIVER_MAGIC;
28+
}
29+
}
30+
31+
function supportsInterface(bytes4 interfaceId) external pure override returns (bool) {
32+
return interfaceId == type(IERC1155Receiver).interfaceId || interfaceId == type(IERC165).interfaceId;
33+
}
34+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.29;
3+
import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
4+
5+
contract MockERC721Receiver is IERC721Receiver {
6+
bool public shouldReject = false;
7+
bytes4 public constant ERC721_RECEIVER_MAGIC = bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"));
8+
9+
function setShouldReject(bool _reject) external {
10+
shouldReject = _reject;
11+
}
12+
13+
function onERC721Received(address, address, uint256, bytes memory) public view override returns (bytes4) {
14+
if (shouldReject) {
15+
return bytes4(0);
16+
} else {
17+
return ERC721_RECEIVER_MAGIC;
18+
}
19+
}
20+
}

0 commit comments

Comments
 (0)