From 07344b3944e3cf608ce95b86b23e7f2e39fd647a Mon Sep 17 00:00:00 2001 From: xaler Date: Thu, 1 May 2025 09:16:19 +0200 Subject: [PATCH 01/13] add draft urwa --- ERCS/draft-erc-urwa.md | 126 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 ERCS/draft-erc-urwa.md diff --git a/ERCS/draft-erc-urwa.md b/ERCS/draft-erc-urwa.md new file mode 100644 index 0000000000..da7fc98c04 --- /dev/null +++ b/ERCS/draft-erc-urwa.md @@ -0,0 +1,126 @@ +--- +title: uRWA - Universal Real World Asset Interface +description: A minimal standard interface for regulated assets, targeting the broad spectrum of RWAs. +author: Dario Lo Buglio (@xaler5) +discussions-to: +status: Draft +type: Standards Track +category: ERC +created: 2025-04-27 +requires: EIP-165 +--- + +## Abstract + +This EIP proposes "Universal RWA" (uRWA) standard, a minimal interface for all commond tokens like ERC-20 / ERC-721 or ERC-1155 based tokens, meant to be the primitive for the different classes of Real World Assets. It defines essential functions and events for regulatory compliance and enforcement actions common to RWAs. + +## Motivation + +The tokenization of Real World Assets introduces requirements often absent in purely digital assets, such as regulatory compliance checks, nuanced transfer controls, and potential enforcement actions. Existing token standards, primarily ERC-20, ERC-721 and ERC-1155, lack the inherent structure to address these needs directly within the standard itself. + +Attempts at defining universal RWA standards historically imposed unnecessary complexity and gas overhead for simpler use cases that do not require the full spectrum of features like granular role-based access control, mandatory on-chain whitelisting, specific on-chain identity solutions or metadata handling solutions mandated by the standard. + +The broad spectrum of RWA classes inherently suggests the need to move away from a one-size-fits-all solution. With the purpose in mind of defining an EIP for it, a minimalistic approach, unopinionated features list and maximally compatible design should be kept in mind. + +The uRWA standard seeks a more refined balance by defining an essential interface, establishing a common ground for interaction regarding compliance and control, without dictating the underlying implementation mechanisms. This allows core token implementations (like ERC-20, ERC-721 or ERC-1155) to remain lean while providing standard functions for RWA-specific interactions. + +The final goal is to build composable DeFi around RWAs, providing the same interface when dealing with compliance and regulation. + +## Specification + +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174. + +**uRWA Interface** + +The following defines the standard interface for an uRWA token contract. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.29; + +/// @title Interface for the uRWA Token Interface +interface IuRWA { + /// @notice Emitted when tokens are taken from one address and transferred to another. + /// @param from The address from which tokens were taken. + /// @param to The address to which seized tokens were transferred. + /// @param amount The amount seized. + /// @param tokenId The ID of the token being transferred. + event Recalled(address indexed from, address indexed to, uint256 amount, uint256 tokenId); + + /// @notice Error reverted when a user is not allowed to interact. + /// @param account The address of the user which is not allowed for interactions. + error UserNotAllowed(address account); + + /// @notice Error reverted when a transfer is not allowed due to restrictions in place. + /// @param from The address from which tokens are being transferred. + /// @param to The address to which tokens are being transferred. + /// @param amount The amount being transferred. + /// @param tokenId The ID of the token being transferred. + error TransferNotAllowed(address from, address to, uint256 amount, uint256 tokenId); + + /// @notice Takes tokens from one address and transfers them to another. + /// @dev Requires specific authorization. Used for regulatory compliance or recovery scenarios. + /// @param from The address from which `amount` is taken. + /// @param to The address that receives `amount`. + /// @param amount The amount to recall. + /// @param tokenId The ID of the token being transferred. + function recall(address from, address to, uint256 amount, uint256 tokenId) external; + + /// @notice Checks if a transfer is currently possible according to token rules and registered plugins. + /// @dev This may involve checks like allowlists, blocklists, transfer limits, etc. + /// @param from The address sending tokens. + /// @param to The address receiving tokens. + /// @param amount The amount being transferred. + /// @param tokenId The ID of the token being transferred. + /// @return allowed True if the transfer is allowed, false otherwise. + function isTransferAllowed(address from, address to, uint256 amount, uint256 tokenId) external view returns (bool allowed); + + /// @notice Checks if a specific user is allowed to interact with the token. + /// @dev This is often used for allowlist/KYC checks. + /// @param user The address to check. + /// @return allowed True if the user is allowed, false otherwise. + function isUserAllowed(address user) external view returns (bool allowed); + + /// Derived from ERC-165 + function supportsInterface(bytes4 interfaceID) external view returns (bool); +} +``` +* The `isUserAllowed` and `isTransferAllowed` functions provide views into the implementing contract's compliance and transfer policy logic. The exact implementation of these checks (e.g., internal allowlist, external calls, complex logic) is NOT mandated by this interface standard. However these two functions: + - MUST NOT revert. + - MUST NOT change the storage of the contract. + - MAY depend on context (e.g., current timestamp or block.number). +* The `recall` function provide a standard mechanism for forcing a transfer from a `from` to a `to` address. This is often known as either "confiscation" / "revocation" or even "recovery" which are all names related to the motivation being the feature itself. The name chose tries to abstract away the motivation and keep a general name. This function: + - MUST directly manipulate balances or ownership to transfer the asset from `from` to `to` either by transfering or burning from `from` and minting to `to`. + - MUST perform necessary validation checks (e.g., sufficient balance/ownership of a specific token). + - MUST emit both the standard `Transfer` event (from the base ERC standard) and the `Recalled` event. + - SHOULD bypass standard transfer validation logic, including checks enforced by `isTransferAllowed` and `isUserAllowed`. + +Given the agnostic nature of the standard on the specific base token standard being used (ERC-20, ERC-721, ERC-1155), the implementation SHOULD use `tokenId = 0` for ERC-20 based implementations, and `amount = 1` for ERC-721 based implementations on `Recalled` event, `TransferNotAllowed` error and `recall` / `isTransferAllowed` functions. Integrators MAY decide to not enforce this, however the standard discourages it. This is considered a little tradeoff for having a unique standard interface for different token standards without overlapping syntaxes. + +Implementations of this interface MUST implement the necessary functions of their chosen base standard (e.g., `ERC-20`, `ERC-721`, `ERC-1155` functionalities) and MUST also restrict access to sensitive functions like `recall` using an appropriate access control mechanism (e.g., `onlyOwner`, Role-Based Access Control). The specific mechanism is NOT mandated by this interface standard. + +Integrators MUST ensure their internal transfer logic (e.g., within `_update`, `_transfer`, `_mint`, `_burn`) respects the boolean outcomes of `isUserAllowed` and `isTransferAllowed`. Transfers, mints, or burns MUST NOT proceed and instead MUST revert with `UserNotAllowed` or `TransferNotAllowed` if and only if these checks indicate the action is disallowed according to the contract's specific policy. + +## Rationale + +* **Minimalism:** Defines only the essential functions (`recall`, `isUserAllowed`, `isTransferAllowed`) and associated events/errors needed for common RWA compliance and control patterns, avoiding mandated complexity or opinionated features. +* **Flexibility:** Provides standard view functions (`isUserAllowed`, `isTransferAllowed`) for compliance checks without dictating *how* those checks are implemented internally by the token contract. This allows diverse compliance strategies. +* **Compatibility:** Designed as an interface layer compatible with existing base standards like ERC-20, ERC-721 and ERC-1155. Implementations extend from `IuRWA` alongside their base standard interface. +* **RWA Essential:** Includes `recall` as a standard function, acknowledging its importance for regulatory enforcement in the RWA space, distinct from standard transfers. Mandates access control for this sensitive function. +* **EIP-165:** Ensures implementing contracts can signal support for this interface. + +As an example, a Uniswap v4 pool can integrate with uRWA ERC-20 tokens by calling `isUserAllowed` or `isTransferAllowed` within its before/after hooks to handle these assets in a compliant manner. Users can then expand these tokens with additional features to fit the specific needs of individual asset types, either with on-chain identity systems, historical balances tracking for dividend distributions, semi-fungibility with tokens metadata, etc. + +## Backwards Compatibility + +This EIP defines a new interface standard and does not alter existing ones like ERC-20, ERC-721, ERC-1155. Standard wallets and explorers can interact with the base token functionality of implementing contracts, subject to the rules enforced by that contract's implementation of `isUserAllowed` and `isTransferAllowed`. Full support for the `IuRWA` functions requires explicit integration. + +## Security Considerations + +* **Access Control for `recall`:** The security of the mechanism chosen by the implementer to restrict access to the `recall` function is paramount. Unauthorized access could lead to asset theft. Secure patterns (multisig, timelocks) are highly recommended. +* **Implementation Logic:** The security and correctness of the *implementation* behind `isUserAllowed` and `isTransferAllowed` are critical. Flaws in this logic could bypass intended transfer restrictions or incorrectly block valid transfers. +* **Standard Contract Security:** Implementations MUST adhere to general smart contract security best practices (reentrancy guards where applicable, checks-effects-interactions, etc.). + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). \ No newline at end of file From 44035fa8302132f2cf2bb5fcbacb5e995f9e2aea Mon Sep 17 00:00:00 2001 From: xaler Date: Thu, 1 May 2025 09:27:52 +0200 Subject: [PATCH 02/13] add reference implementation --- ERCS/draft-erc-urwa.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ERCS/draft-erc-urwa.md b/ERCS/draft-erc-urwa.md index da7fc98c04..90aebfe1f9 100644 --- a/ERCS/draft-erc-urwa.md +++ b/ERCS/draft-erc-urwa.md @@ -115,6 +115,10 @@ As an example, a Uniswap v4 pool can integrate with uRWA ERC-20 tokens by callin This EIP defines a new interface standard and does not alter existing ones like ERC-20, ERC-721, ERC-1155. Standard wallets and explorers can interact with the base token functionality of implementing contracts, subject to the rules enforced by that contract's implementation of `isUserAllowed` and `isTransferAllowed`. Full support for the `IuRWA` functions requires explicit integration. +## Reference Implementation + +An example of basic implementation for ERC-20, ERC-721 and ERC-1155 can be found at [uRWA](https://github.com/xaler5/urwa) + ## Security Considerations * **Access Control for `recall`:** The security of the mechanism chosen by the implementer to restrict access to the `recall` function is paramount. Unauthorized access could lead to asset theft. Secure patterns (multisig, timelocks) are highly recommended. From e1c465869612226590c463383ca7989c236b1a6d Mon Sep 17 00:00:00 2001 From: xaler Date: Thu, 1 May 2025 09:29:20 +0200 Subject: [PATCH 03/13] rename file --- ERCS/{draft-erc-urwa.md => erc-draft-urwa.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename ERCS/{draft-erc-urwa.md => erc-draft-urwa.md} (100%) diff --git a/ERCS/draft-erc-urwa.md b/ERCS/erc-draft-urwa.md similarity index 100% rename from ERCS/draft-erc-urwa.md rename to ERCS/erc-draft-urwa.md From f95a6c7cc739714409019a350da4a2a8f69be76b Mon Sep 17 00:00:00 2001 From: xaler Date: Thu, 1 May 2025 10:23:06 +0200 Subject: [PATCH 04/13] link to eip-165 --- ERCS/{erc-draft-urwa.md => erc-x.md} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename ERCS/{erc-draft-urwa.md => erc-x.md} (99%) diff --git a/ERCS/erc-draft-urwa.md b/ERCS/erc-x.md similarity index 99% rename from ERCS/erc-draft-urwa.md rename to ERCS/erc-x.md index 90aebfe1f9..f73e099bf1 100644 --- a/ERCS/erc-draft-urwa.md +++ b/ERCS/erc-x.md @@ -7,7 +7,7 @@ status: Draft type: Standards Track category: ERC created: 2025-04-27 -requires: EIP-165 +requires: [ERC-165](./erc-165.md) --- ## Abstract @@ -107,7 +107,7 @@ Integrators MUST ensure their internal transfer logic (e.g., within `_update`, ` * **Flexibility:** Provides standard view functions (`isUserAllowed`, `isTransferAllowed`) for compliance checks without dictating *how* those checks are implemented internally by the token contract. This allows diverse compliance strategies. * **Compatibility:** Designed as an interface layer compatible with existing base standards like ERC-20, ERC-721 and ERC-1155. Implementations extend from `IuRWA` alongside their base standard interface. * **RWA Essential:** Includes `recall` as a standard function, acknowledging its importance for regulatory enforcement in the RWA space, distinct from standard transfers. Mandates access control for this sensitive function. -* **EIP-165:** Ensures implementing contracts can signal support for this interface. +* **ERC-165:** Ensures implementing contracts can signal support for this interface. As an example, a Uniswap v4 pool can integrate with uRWA ERC-20 tokens by calling `isUserAllowed` or `isTransferAllowed` within its before/after hooks to handle these assets in a compliant manner. Users can then expand these tokens with additional features to fit the specific needs of individual asset types, either with on-chain identity systems, historical balances tracking for dividend distributions, semi-fungibility with tokens metadata, etc. From a087ca3095f304edade97d065b3c1f9881a9d3d8 Mon Sep 17 00:00:00 2001 From: xaler Date: Thu, 1 May 2025 16:09:06 +0200 Subject: [PATCH 05/13] reorder parameters --- ERCS/{erc-x.md => erc-1234.md} | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) rename ERCS/{erc-x.md => erc-1234.md} (95%) diff --git a/ERCS/erc-x.md b/ERCS/erc-1234.md similarity index 95% rename from ERCS/erc-x.md rename to ERCS/erc-1234.md index f73e099bf1..d8190d0b9a 100644 --- a/ERCS/erc-x.md +++ b/ERCS/erc-1234.md @@ -2,17 +2,17 @@ title: uRWA - Universal Real World Asset Interface description: A minimal standard interface for regulated assets, targeting the broad spectrum of RWAs. author: Dario Lo Buglio (@xaler5) -discussions-to: +discussions-to: [Discussion on Ethereum Magicians](https://ethereum-magicians.org/t/erc-universal-rwa-interface/23972) status: Draft type: Standards Track category: ERC -created: 2025-04-27 -requires: [ERC-165](./erc-165.md) +created: 2025-05-01 +requires: 165 --- ## Abstract -This EIP proposes "Universal RWA" (uRWA) standard, a minimal interface for all commond tokens like ERC-20 / ERC-721 or ERC-1155 based tokens, meant to be the primitive for the different classes of Real World Assets. It defines essential functions and events for regulatory compliance and enforcement actions common to RWAs. +This EIP proposes "Universal RWA" (uRWA) standard, a minimal interface for all commond tokens like ERC-20 / ERC-721 or ERC-1155 based tokens, meant to be the primitive for the different classes of Real World Assets. It defines essential functions and events for regulatory compliance and enforcement actions common to RWAs. It also extends from [EIP-165](./erc-165.md) for introspection. ## Motivation @@ -43,9 +43,9 @@ interface IuRWA { /// @notice Emitted when tokens are taken from one address and transferred to another. /// @param from The address from which tokens were taken. /// @param to The address to which seized tokens were transferred. - /// @param amount The amount seized. /// @param tokenId The ID of the token being transferred. - event Recalled(address indexed from, address indexed to, uint256 amount, uint256 tokenId); + /// @param amount The amount seized. + event Recalled(address indexed from, address indexed to, uint256 tokenId, uint256 amount); /// @notice Error reverted when a user is not allowed to interact. /// @param account The address of the user which is not allowed for interactions. @@ -54,26 +54,26 @@ interface IuRWA { /// @notice Error reverted when a transfer is not allowed due to restrictions in place. /// @param from The address from which tokens are being transferred. /// @param to The address to which tokens are being transferred. - /// @param amount The amount being transferred. /// @param tokenId The ID of the token being transferred. - error TransferNotAllowed(address from, address to, uint256 amount, uint256 tokenId); + /// @param amount The amount being transferred. + error TransferNotAllowed(address from, address to, uint256 tokenId, uint256 amount); /// @notice Takes tokens from one address and transfers them to another. /// @dev Requires specific authorization. Used for regulatory compliance or recovery scenarios. /// @param from The address from which `amount` is taken. /// @param to The address that receives `amount`. - /// @param amount The amount to recall. /// @param tokenId The ID of the token being transferred. - function recall(address from, address to, uint256 amount, uint256 tokenId) external; + /// @param amount The amount to recall. + function recall(address from, address to, uint256 tokenId, uint256 amount) external; /// @notice Checks if a transfer is currently possible according to token rules and registered plugins. /// @dev This may involve checks like allowlists, blocklists, transfer limits, etc. /// @param from The address sending tokens. /// @param to The address receiving tokens. - /// @param amount The amount being transferred. /// @param tokenId The ID of the token being transferred. + /// @param amount The amount being transferred. /// @return allowed True if the transfer is allowed, false otherwise. - function isTransferAllowed(address from, address to, uint256 amount, uint256 tokenId) external view returns (bool allowed); + function isTransferAllowed(address from, address to, uint256 tokenId, uint256 amount) external view returns (bool allowed); /// @notice Checks if a specific user is allowed to interact with the token. /// @dev This is often used for allowlist/KYC checks. @@ -81,10 +81,11 @@ interface IuRWA { /// @return allowed True if the user is allowed, false otherwise. function isUserAllowed(address user) external view returns (bool allowed); - /// Derived from ERC-165 + /// Derived from EIP-165 function supportsInterface(bytes4 interfaceID) external view returns (bool); } ``` + * The `isUserAllowed` and `isTransferAllowed` functions provide views into the implementing contract's compliance and transfer policy logic. The exact implementation of these checks (e.g., internal allowlist, external calls, complex logic) is NOT mandated by this interface standard. However these two functions: - MUST NOT revert. - MUST NOT change the storage of the contract. From 0a0eced1c4eae42f88879e2c978600b94a99ac94 Mon Sep 17 00:00:00 2001 From: xaler Date: Thu, 1 May 2025 16:09:34 +0200 Subject: [PATCH 06/13] put direct link to discussion --- ERCS/erc-1234.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ERCS/erc-1234.md b/ERCS/erc-1234.md index d8190d0b9a..457ef49563 100644 --- a/ERCS/erc-1234.md +++ b/ERCS/erc-1234.md @@ -2,7 +2,7 @@ title: uRWA - Universal Real World Asset Interface description: A minimal standard interface for regulated assets, targeting the broad spectrum of RWAs. author: Dario Lo Buglio (@xaler5) -discussions-to: [Discussion on Ethereum Magicians](https://ethereum-magicians.org/t/erc-universal-rwa-interface/23972) +discussions-to: https://ethereum-magicians.org/t/erc-universal-rwa-interface/23972 status: Draft type: Standards Track category: ERC From c097ac2027dcc6db138d23d7a29780ecf983d070 Mon Sep 17 00:00:00 2001 From: xaler Date: Thu, 1 May 2025 16:19:47 +0200 Subject: [PATCH 07/13] formatting --- ERCS/erc-1234.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ERCS/erc-1234.md b/ERCS/erc-1234.md index 457ef49563..c6e8d68cda 100644 --- a/ERCS/erc-1234.md +++ b/ERCS/erc-1234.md @@ -1,6 +1,7 @@ --- +eip: 1234 title: uRWA - Universal Real World Asset Interface -description: A minimal standard interface for regulated assets, targeting the broad spectrum of RWAs. +description: A minimal interface for regulated assets, targeting the broad spectrum of RWAs. author: Dario Lo Buglio (@xaler5) discussions-to: https://ethereum-magicians.org/t/erc-universal-rwa-interface/23972 status: Draft @@ -12,7 +13,7 @@ requires: 165 ## Abstract -This EIP proposes "Universal RWA" (uRWA) standard, a minimal interface for all commond tokens like ERC-20 / ERC-721 or ERC-1155 based tokens, meant to be the primitive for the different classes of Real World Assets. It defines essential functions and events for regulatory compliance and enforcement actions common to RWAs. It also extends from [EIP-165](./erc-165.md) for introspection. +This EIP proposes "Universal RWA" (uRWA) standard, a minimal interface for all commond tokens like [ERC-20](./erc-20.md) / [ERC-721](./erc-721.md) or [ERC-1155](./erc-1155.md) based tokens, meant to be the primitive for the different classes of Real World Assets. It defines essential functions and events for regulatory compliance and enforcement actions common to RWAs. It also extends from [EIP-165](./erc-165.md) for introspection. ## Motivation @@ -98,7 +99,7 @@ interface IuRWA { Given the agnostic nature of the standard on the specific base token standard being used (ERC-20, ERC-721, ERC-1155), the implementation SHOULD use `tokenId = 0` for ERC-20 based implementations, and `amount = 1` for ERC-721 based implementations on `Recalled` event, `TransferNotAllowed` error and `recall` / `isTransferAllowed` functions. Integrators MAY decide to not enforce this, however the standard discourages it. This is considered a little tradeoff for having a unique standard interface for different token standards without overlapping syntaxes. -Implementations of this interface MUST implement the necessary functions of their chosen base standard (e.g., `ERC-20`, `ERC-721`, `ERC-1155` functionalities) and MUST also restrict access to sensitive functions like `recall` using an appropriate access control mechanism (e.g., `onlyOwner`, Role-Based Access Control). The specific mechanism is NOT mandated by this interface standard. +Implementations of this interface MUST implement the necessary functions of their chosen base standard (e.g., ERC-20, ERC-721, ERC-1155 functionalities) and MUST also restrict access to sensitive functions like `recall` using an appropriate access control mechanism (e.g., `onlyOwner`, Role-Based Access Control). The specific mechanism is NOT mandated by this interface standard. Integrators MUST ensure their internal transfer logic (e.g., within `_update`, `_transfer`, `_mint`, `_burn`) respects the boolean outcomes of `isUserAllowed` and `isTransferAllowed`. Transfers, mints, or burns MUST NOT proceed and instead MUST revert with `UserNotAllowed` or `TransferNotAllowed` if and only if these checks indicate the action is disallowed according to the contract's specific policy. @@ -128,4 +129,4 @@ An example of basic implementation for ERC-20, ERC-721 and ERC-1155 can be found ## Copyright -Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). \ No newline at end of file +Copyright and related rights waived via [CC0](../LICENSE.md). \ No newline at end of file From 421024ae9aeedad6ac1b20ec6886af3158d63ee6 Mon Sep 17 00:00:00 2001 From: xaler Date: Thu, 1 May 2025 16:23:25 +0200 Subject: [PATCH 08/13] typo --- ERCS/erc-1234.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ERCS/erc-1234.md b/ERCS/erc-1234.md index c6e8d68cda..6177433012 100644 --- a/ERCS/erc-1234.md +++ b/ERCS/erc-1234.md @@ -13,7 +13,7 @@ requires: 165 ## Abstract -This EIP proposes "Universal RWA" (uRWA) standard, a minimal interface for all commond tokens like [ERC-20](./erc-20.md) / [ERC-721](./erc-721.md) or [ERC-1155](./erc-1155.md) based tokens, meant to be the primitive for the different classes of Real World Assets. It defines essential functions and events for regulatory compliance and enforcement actions common to RWAs. It also extends from [EIP-165](./erc-165.md) for introspection. +This EIP proposes "Universal RWA" (uRWA) standard, a minimal interface for all common tokens like [ERC-20](./erc-20.md) / [ERC-721](./erc-721.md) or [ERC-1155](./erc-1155.md) based tokens, meant to be the primitive for the different classes of Real World Assets. It defines essential functions and events for regulatory compliance and enforcement actions common to RWAs. It also extends from [EIP-165](./erc-165.md) for introspection. ## Motivation From 937e8649e99c03602fab26fca896773e147c245a Mon Sep 17 00:00:00 2001 From: xaler Date: Thu, 1 May 2025 16:54:44 +0200 Subject: [PATCH 09/13] remove referenced implementation, add snippets --- ERCS/erc-1234.md | 208 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 197 insertions(+), 11 deletions(-) diff --git a/ERCS/erc-1234.md b/ERCS/erc-1234.md index 6177433012..9c2b863313 100644 --- a/ERCS/erc-1234.md +++ b/ERCS/erc-1234.md @@ -13,17 +13,17 @@ requires: 165 ## Abstract -This EIP proposes "Universal RWA" (uRWA) standard, a minimal interface for all common tokens like [ERC-20](./erc-20.md) / [ERC-721](./erc-721.md) or [ERC-1155](./erc-1155.md) based tokens, meant to be the primitive for the different classes of Real World Assets. It defines essential functions and events for regulatory compliance and enforcement actions common to RWAs. It also extends from [EIP-165](./erc-165.md) for introspection. +This EIP proposes "Universal RWA" (uRWA) standard, a minimal interface for all common tokens like [ERC-20](./erc-20.md), [ERC-721](./erc-721.md) or [ERC-1155](./erc-1155.md) based tokens, meant to be the primitive for the different classes of Real World Assets. It defines essential functions and events for regulatory compliance and enforcement actions common to RWAs. It also extends from [ERC-165](./erc-165.md) for introspection. ## Motivation -The tokenization of Real World Assets introduces requirements often absent in purely digital assets, such as regulatory compliance checks, nuanced transfer controls, and potential enforcement actions. Existing token standards, primarily ERC-20, ERC-721 and ERC-1155, lack the inherent structure to address these needs directly within the standard itself. +The tokenization of Real World Assets introduces requirements often absent in purely digital assets, such as regulatory compliance checks, nuanced transfer controls, and potential enforcement actions. Existing token standards, primarily [ERC-20](./erc-20.md), [ERC-721](./erc-721.md) and [ERC-1155](./erc-1155.md), lack the inherent structure to address these needs directly within the standard itself. Attempts at defining universal RWA standards historically imposed unnecessary complexity and gas overhead for simpler use cases that do not require the full spectrum of features like granular role-based access control, mandatory on-chain whitelisting, specific on-chain identity solutions or metadata handling solutions mandated by the standard. The broad spectrum of RWA classes inherently suggests the need to move away from a one-size-fits-all solution. With the purpose in mind of defining an EIP for it, a minimalistic approach, unopinionated features list and maximally compatible design should be kept in mind. -The uRWA standard seeks a more refined balance by defining an essential interface, establishing a common ground for interaction regarding compliance and control, without dictating the underlying implementation mechanisms. This allows core token implementations (like ERC-20, ERC-721 or ERC-1155) to remain lean while providing standard functions for RWA-specific interactions. +The uRWA standard seeks a more refined balance by defining an essential interface, establishing a common ground for interaction regarding compliance and control, without dictating the underlying implementation mechanisms. This allows core token implementations to remain lean while providing standard functions for RWA-specific interactions. The final goal is to build composable DeFi around RWAs, providing the same interface when dealing with compliance and regulation. @@ -94,12 +94,12 @@ interface IuRWA { * The `recall` function provide a standard mechanism for forcing a transfer from a `from` to a `to` address. This is often known as either "confiscation" / "revocation" or even "recovery" which are all names related to the motivation being the feature itself. The name chose tries to abstract away the motivation and keep a general name. This function: - MUST directly manipulate balances or ownership to transfer the asset from `from` to `to` either by transfering or burning from `from` and minting to `to`. - MUST perform necessary validation checks (e.g., sufficient balance/ownership of a specific token). - - MUST emit both the standard `Transfer` event (from the base ERC standard) and the `Recalled` event. + - MUST emit both the standard `Transfer` event (from the base standard) and the `Recalled` event. - SHOULD bypass standard transfer validation logic, including checks enforced by `isTransferAllowed` and `isUserAllowed`. -Given the agnostic nature of the standard on the specific base token standard being used (ERC-20, ERC-721, ERC-1155), the implementation SHOULD use `tokenId = 0` for ERC-20 based implementations, and `amount = 1` for ERC-721 based implementations on `Recalled` event, `TransferNotAllowed` error and `recall` / `isTransferAllowed` functions. Integrators MAY decide to not enforce this, however the standard discourages it. This is considered a little tradeoff for having a unique standard interface for different token standards without overlapping syntaxes. +Given the agnostic nature of the standard on the specific base token standard being used the implementation SHOULD use `tokenId = 0` for [ERC-20](./erc-20.md) based implementations, and `amount = 1` for [ERC-721](./erc-721.md) based implementations on `Recalled` event, `TransferNotAllowed` error and `recall` / `isTransferAllowed` functions. Integrators MAY decide to not enforce this, however the standard discourages it. This is considered a little tradeoff for having a unique standard interface for different token standards without overlapping syntaxes. -Implementations of this interface MUST implement the necessary functions of their chosen base standard (e.g., ERC-20, ERC-721, ERC-1155 functionalities) and MUST also restrict access to sensitive functions like `recall` using an appropriate access control mechanism (e.g., `onlyOwner`, Role-Based Access Control). The specific mechanism is NOT mandated by this interface standard. +Implementations of this interface MUST implement the necessary functions of their chosen base standard (e.g., [ERC-20](./erc-20.md), [ERC-721](./erc-721.md) and [ERC-1155](./erc-1155.md) functionalities) and MUST also restrict access to sensitive functions like `recall` using an appropriate access control mechanism (e.g., `onlyOwner`, Role-Based Access Control). The specific mechanism is NOT mandated by this interface standard. Integrators MUST ensure their internal transfer logic (e.g., within `_update`, `_transfer`, `_mint`, `_burn`) respects the boolean outcomes of `isUserAllowed` and `isTransferAllowed`. Transfers, mints, or burns MUST NOT proceed and instead MUST revert with `UserNotAllowed` or `TransferNotAllowed` if and only if these checks indicate the action is disallowed according to the contract's specific policy. @@ -107,19 +107,205 @@ Integrators MUST ensure their internal transfer logic (e.g., within `_update`, ` * **Minimalism:** Defines only the essential functions (`recall`, `isUserAllowed`, `isTransferAllowed`) and associated events/errors needed for common RWA compliance and control patterns, avoiding mandated complexity or opinionated features. * **Flexibility:** Provides standard view functions (`isUserAllowed`, `isTransferAllowed`) for compliance checks without dictating *how* those checks are implemented internally by the token contract. This allows diverse compliance strategies. -* **Compatibility:** Designed as an interface layer compatible with existing base standards like ERC-20, ERC-721 and ERC-1155. Implementations extend from `IuRWA` alongside their base standard interface. +* **Compatibility:** Designed as an interface layer compatible with existing base standards like [ERC-20](./erc-20.md), [ERC-721](./erc-721.md) and [ERC-1155](./erc-1155.md). Implementations extend from `IuRWA` alongside their base standard interface. * **RWA Essential:** Includes `recall` as a standard function, acknowledging its importance for regulatory enforcement in the RWA space, distinct from standard transfers. Mandates access control for this sensitive function. -* **ERC-165:** Ensures implementing contracts can signal support for this interface. +* **[ERC-165](./erc-165.md):** Ensures implementing contracts can signal support for this interface. -As an example, a Uniswap v4 pool can integrate with uRWA ERC-20 tokens by calling `isUserAllowed` or `isTransferAllowed` within its before/after hooks to handle these assets in a compliant manner. Users can then expand these tokens with additional features to fit the specific needs of individual asset types, either with on-chain identity systems, historical balances tracking for dividend distributions, semi-fungibility with tokens metadata, etc. +As an example, a Uniswap v4 pool can integrate with uRWA [ERC-20](./erc-20.md) tokens by calling `isUserAllowed` or `isTransferAllowed` within its before/after hooks to handle these assets in a compliant manner. Users can then expand these tokens with additional features to fit the specific needs of individual asset types, either with on-chain identity systems, historical balances tracking for dividend distributions, semi-fungibility with tokens metadata, etc. ## Backwards Compatibility -This EIP defines a new interface standard and does not alter existing ones like ERC-20, ERC-721, ERC-1155. Standard wallets and explorers can interact with the base token functionality of implementing contracts, subject to the rules enforced by that contract's implementation of `isUserAllowed` and `isTransferAllowed`. Full support for the `IuRWA` functions requires explicit integration. +This EIP defines a new interface standard and does not alter existing ones like [ERC-20](./erc-20.md), [ERC-721](./erc-721.md) and [ERC-1155](./erc-1155.md). Standard wallets and explorers can interact with the base token functionality of implementing contracts, subject to the rules enforced by that contract's implementation of `isUserAllowed` and `isTransferAllowed`. Full support for the `IuRWA` functions requires explicit integration. ## Reference Implementation -An example of basic implementation for ERC-20, ERC-721 and ERC-1155 can be found at [uRWA](https://github.com/xaler5/urwa) +Examples of basic implementation for [ERC-20](./erc-20.md), [ERC-721](./erc-721.md) and [ERC-1155](./erc-1155.md) which includes a basic whitelist for users and an enumerable role based access control: + +### [ERC-20](./erc-20.md) Example + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.29; + +/* required imports ... */ + +contract uRWA20 is Context, ERC20, AccessControlEnumerable, IuRWA { + bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); + bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE"); + bytes32 public constant RECALL_ROLE = keccak256("RECALL_ROLE"); + bytes32 public constant WHITELIST_ROLE = keccak256("WHITELIST_ROLE"); + + mapping(address user => bool whitelisted) public isWhitelisted; + + event Whitelisted(address indexed account, bool status); + error NotZeroAddress(); + + constructor(string memory name, string memory symbol, address initialAdmin) ERC20(name, symbol) { + /* give initialAdmin necessary roles ...*/ + } + + function changeWhitelist(address account, bool status) external onlyRole(WHITELIST_ROLE) { + require(account != address(0), NotZeroAddress()); + isWhitelisted[account] = status; + emit Whitelisted(account, status); + } + + /* standard mint and burn functions with access control ...*/ + + function recall(address from, address to, uint256, uint256 amount) public onlyRole(RECALL_ROLE) { + require(isUserAllowed(to), UserNotAllowed(to)); + // Directly update balances, bypassing overridden _update + super._update(from, to, amount); + emit Recalled(from, to, 0, amount); + } + + function isTransferAllowed(address from, address to, uint256, uint256 amount) public virtual view returns (bool allowed) { + if (balanceOf(from) < amount) return false; + if (!isUserAllowed(from) || !isUserAllowed(to)) return false; + + return true; + } + + function isUserAllowed(address user) public virtual view returns (bool allowed) { + if (!isWhitelisted[user]) return false; + + return true; + } + + function _update(address from, address to, uint256 value) internal virtual override { + if (from != address(0) && to != address(0)) { // Transfer + require(isTransferAllowed(from, to, 0, value), TransferNotAllowed(from, to, 0, value)); + } else if (from == address(0)) { // Mint + require(isUserAllowed(to), UserNotAllowed(to)); + } else { // Burn + require(isUserAllowed(from), UserNotAllowed(from)); + } + + super._update(from, to, value); + } + + function supportsInterface(bytes4 interfaceId) public view virtual override(AccessControlEnumerable, IERC165) returns (bool) { + return interfaceId == type(IuRWA).interfaceId || + interfaceId == type(IERC20).interfaceId || + super.supportsInterface(interfaceId); + } +} +``` + +### [ERC-721](./erc-721.md) Example + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.29; + +/* required imports ... */ + +contract uRWA721 is Context, ERC721, AccessControlEnumerable, IuRWA { + /* same roles definitions, constructor and changeWhitelist function as before ...*/ + + /* standard mint and burn functions with access control ...*/ + + function recall(address from, address to, uint256 tokenId, uint256) public virtual override onlyRole(RECALL_ROLE) { + require(to != address(0), ERC721InvalidReceiver(address(0))); + address previousOwner = super._update(to, tokenId, address(0)); // Skip _update override + require(previousOwner != address(0), ERC721NonexistentToken(tokenId)); + require(previousOwner == from, ERC721IncorrectOwner(from, tokenId, previousOwner)); + + ERC721Utils.checkOnERC721Received(_msgSender(), from, to, tokenId, ""); + emit Recalled(from, to, tokenId, 1); + } + + function isUserAllowed(address user) public view virtual override returns (bool allowed) { + return isWhitelisted[user]; + } + + function isTransferAllowed(address from, address to, uint256 tokenId, uint256) public view virtual override returns (bool allowed) { + if (_ownerOf(tokenId) != from || _ownerOf(tokenId) == address(0)) return false; // Use internal function to avoid reverting for non existing tokenIds + if (!isUserAllowed(from) || !isUserAllowed(to)) return false; + return true; + } + + function _update(address to, uint256 value, address auth) internal virtual override returns(address) { + address from = _ownerOf(value); + + if (auth != address(0)) { + _checkAuthorized(from, auth, value); + } + + if (from != address(0) && to != address(0)) { // Transfer + require(isTransferAllowed(from, to, value, 1), TransferNotAllowed(from, to, value, 1)); + } else if (from == address(0)) { // Mint + require(isUserAllowed(to), UserNotAllowed(to)); + } else { // Burn + require(isUserAllowed(from), UserNotAllowed(from)); + } + + return super._update(to, value, auth); + } + + function supportsInterface(bytes4 interfaceId) public view virtual override(AccessControlEnumerable, ERC721, IERC165) returns (bool) { + return interfaceId == type(IuRWA).interfaceId || + super.supportsInterface(interfaceId); + } +} +``` + +### [ERC-1155](./erc-1155.md) Example + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.29; + +/* required imports ... */ + +contract uRWA1155 is Context, ERC1155, AccessControlEnumerable, IuRWA { + /* same roles definitions, constructor and changeWhitelist function as before ...*/ + + /* standard mint and burn functions with access control ...*/ + + function recall(address from, address to, uint256 tokenId, uint256 amount) public onlyRole(RECALL_ROLE) { + require(isUserAllowed(to), UserNotAllowed(to)); + _safeTransferFrom(from, to, tokenId, amount, ""); + emit Recalled(from, to, tokenId, amount); + } + + function isTransferAllowed(address from, address to, uint256 tokenId, uint256 amount) public view virtual override returns (bool allowed) { + if (balanceOf(from, tokenId) < amount) return false; + if (!isUserAllowed(from) || !isUserAllowed(to)) return false; + + return true; + } + + function isUserAllowed(address user) public view virtual override returns (bool allowed) { + return isWhitelisted[user]; + } + + function _update(address from, address to, uint256[] memory ids, uint256[] memory values) internal virtual override { + if (ids.length != values.length) { + revert ERC1155InvalidArrayLength(ids.length, values.length); + } + + for (uint256 i = 0; i < ids.length; ++i) { + if (from != address(0) && to != address(0)) { // Transfer + require(isTransferAllowed(from, to, ids[i], values[i]), TransferNotAllowed(from, to, ids[i], values[i])); + } + } + + if (from == address(0)) { // Mint + require(isUserAllowed(to), UserNotAllowed(to)); + } else if (to == address(0)) { // Burn + require(isUserAllowed(from), UserNotAllowed(from)); + } + + super._update(from, to, ids, values); + } + + function supportsInterface(bytes4 interfaceId) public view virtual override(AccessControlEnumerable, ERC1155, IERC165) returns (bool) { + return interfaceId == type(IuRWA).interfaceId || + super.supportsInterface(interfaceId); + } +} +``` ## Security Considerations From 449aacfb4c0b4888c28ccc313730909505355d6c Mon Sep 17 00:00:00 2001 From: xaler Date: Thu, 1 May 2025 17:13:37 +0200 Subject: [PATCH 10/13] change erc to eip --- ERCS/erc-1234.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/ERCS/erc-1234.md b/ERCS/erc-1234.md index 9c2b863313..fa4c770721 100644 --- a/ERCS/erc-1234.md +++ b/ERCS/erc-1234.md @@ -13,11 +13,11 @@ requires: 165 ## Abstract -This EIP proposes "Universal RWA" (uRWA) standard, a minimal interface for all common tokens like [ERC-20](./erc-20.md), [ERC-721](./erc-721.md) or [ERC-1155](./erc-1155.md) based tokens, meant to be the primitive for the different classes of Real World Assets. It defines essential functions and events for regulatory compliance and enforcement actions common to RWAs. It also extends from [ERC-165](./erc-165.md) for introspection. +This EIP proposes "Universal RWA" (uRWA) standard, a minimal interface for all common tokens like [ERC-20](./eip-20.md), [ERC-721](./eip-721.md) or [ERC-1155](./eip-1155.md) based tokens, meant to be the primitive for the different classes of Real World Assets. It defines essential functions and events for regulatory compliance and enforcement actions common to RWAs. It also extends from [ERC-165](./eip-165.md) for introspection. ## Motivation -The tokenization of Real World Assets introduces requirements often absent in purely digital assets, such as regulatory compliance checks, nuanced transfer controls, and potential enforcement actions. Existing token standards, primarily [ERC-20](./erc-20.md), [ERC-721](./erc-721.md) and [ERC-1155](./erc-1155.md), lack the inherent structure to address these needs directly within the standard itself. +The tokenization of Real World Assets introduces requirements often absent in purely digital assets, such as regulatory compliance checks, nuanced transfer controls, and potential enforcement actions. Existing token standards, primarily [ERC-20](./eip-20.md), [ERC-721](./eip-721.md) and [ERC-1155](./eip-1155.md), lack the inherent structure to address these needs directly within the standard itself. Attempts at defining universal RWA standards historically imposed unnecessary complexity and gas overhead for simpler use cases that do not require the full spectrum of features like granular role-based access control, mandatory on-chain whitelisting, specific on-chain identity solutions or metadata handling solutions mandated by the standard. @@ -97,9 +97,9 @@ interface IuRWA { - MUST emit both the standard `Transfer` event (from the base standard) and the `Recalled` event. - SHOULD bypass standard transfer validation logic, including checks enforced by `isTransferAllowed` and `isUserAllowed`. -Given the agnostic nature of the standard on the specific base token standard being used the implementation SHOULD use `tokenId = 0` for [ERC-20](./erc-20.md) based implementations, and `amount = 1` for [ERC-721](./erc-721.md) based implementations on `Recalled` event, `TransferNotAllowed` error and `recall` / `isTransferAllowed` functions. Integrators MAY decide to not enforce this, however the standard discourages it. This is considered a little tradeoff for having a unique standard interface for different token standards without overlapping syntaxes. +Given the agnostic nature of the standard on the specific base token standard being used the implementation SHOULD use `tokenId = 0` for [ERC-20](./eip-20.md) based implementations, and `amount = 1` for [ERC-721](./eip-721.md) based implementations on `Recalled` event, `TransferNotAllowed` error and `recall` / `isTransferAllowed` functions. Integrators MAY decide to not enforce this, however the standard discourages it. This is considered a little tradeoff for having a unique standard interface for different token standards without overlapping syntaxes. -Implementations of this interface MUST implement the necessary functions of their chosen base standard (e.g., [ERC-20](./erc-20.md), [ERC-721](./erc-721.md) and [ERC-1155](./erc-1155.md) functionalities) and MUST also restrict access to sensitive functions like `recall` using an appropriate access control mechanism (e.g., `onlyOwner`, Role-Based Access Control). The specific mechanism is NOT mandated by this interface standard. +Implementations of this interface MUST implement the necessary functions of their chosen base standard (e.g., [ERC-20](./eip-20.md), [ERC-721](./eip-721.md) and [ERC-1155](./eip-1155.md) functionalities) and MUST also restrict access to sensitive functions like `recall` using an appropriate access control mechanism (e.g., `onlyOwner`, Role-Based Access Control). The specific mechanism is NOT mandated by this interface standard. Integrators MUST ensure their internal transfer logic (e.g., within `_update`, `_transfer`, `_mint`, `_burn`) respects the boolean outcomes of `isUserAllowed` and `isTransferAllowed`. Transfers, mints, or burns MUST NOT proceed and instead MUST revert with `UserNotAllowed` or `TransferNotAllowed` if and only if these checks indicate the action is disallowed according to the contract's specific policy. @@ -107,21 +107,21 @@ Integrators MUST ensure their internal transfer logic (e.g., within `_update`, ` * **Minimalism:** Defines only the essential functions (`recall`, `isUserAllowed`, `isTransferAllowed`) and associated events/errors needed for common RWA compliance and control patterns, avoiding mandated complexity or opinionated features. * **Flexibility:** Provides standard view functions (`isUserAllowed`, `isTransferAllowed`) for compliance checks without dictating *how* those checks are implemented internally by the token contract. This allows diverse compliance strategies. -* **Compatibility:** Designed as an interface layer compatible with existing base standards like [ERC-20](./erc-20.md), [ERC-721](./erc-721.md) and [ERC-1155](./erc-1155.md). Implementations extend from `IuRWA` alongside their base standard interface. +* **Compatibility:** Designed as an interface layer compatible with existing base standards like [ERC-20](./eip-20.md), [ERC-721](./eip-721.md) and [ERC-1155](./eip-1155.md). Implementations extend from `IuRWA` alongside their base standard interface. * **RWA Essential:** Includes `recall` as a standard function, acknowledging its importance for regulatory enforcement in the RWA space, distinct from standard transfers. Mandates access control for this sensitive function. -* **[ERC-165](./erc-165.md):** Ensures implementing contracts can signal support for this interface. +* **[ERC-165](./eip-165.md):** Ensures implementing contracts can signal support for this interface. -As an example, a Uniswap v4 pool can integrate with uRWA [ERC-20](./erc-20.md) tokens by calling `isUserAllowed` or `isTransferAllowed` within its before/after hooks to handle these assets in a compliant manner. Users can then expand these tokens with additional features to fit the specific needs of individual asset types, either with on-chain identity systems, historical balances tracking for dividend distributions, semi-fungibility with tokens metadata, etc. +As an example, a Uniswap v4 pool can integrate with uRWA [ERC-20](./eip-20.md) tokens by calling `isUserAllowed` or `isTransferAllowed` within its before/after hooks to handle these assets in a compliant manner. Users can then expand these tokens with additional features to fit the specific needs of individual asset types, either with on-chain identity systems, historical balances tracking for dividend distributions, semi-fungibility with tokens metadata, etc. ## Backwards Compatibility -This EIP defines a new interface standard and does not alter existing ones like [ERC-20](./erc-20.md), [ERC-721](./erc-721.md) and [ERC-1155](./erc-1155.md). Standard wallets and explorers can interact with the base token functionality of implementing contracts, subject to the rules enforced by that contract's implementation of `isUserAllowed` and `isTransferAllowed`. Full support for the `IuRWA` functions requires explicit integration. +This EIP defines a new interface standard and does not alter existing ones like [ERC-20](./eip-20.md), [ERC-721](./eip-721.md) and [ERC-1155](./eip-1155.md). Standard wallets and explorers can interact with the base token functionality of implementing contracts, subject to the rules enforced by that contract's implementation of `isUserAllowed` and `isTransferAllowed`. Full support for the `IuRWA` functions requires explicit integration. ## Reference Implementation -Examples of basic implementation for [ERC-20](./erc-20.md), [ERC-721](./erc-721.md) and [ERC-1155](./erc-1155.md) which includes a basic whitelist for users and an enumerable role based access control: +Examples of basic implementation for [ERC-20](./eip-20.md), [ERC-721](./eip-721.md) and [ERC-1155](./eip-1155.md) which includes a basic whitelist for users and an enumerable role based access control: -### [ERC-20](./erc-20.md) Example +### [ERC-20](./eip-20.md) Example ```solidity // SPDX-License-Identifier: MIT @@ -192,7 +192,7 @@ contract uRWA20 is Context, ERC20, AccessControlEnumerable, IuRWA { } ``` -### [ERC-721](./erc-721.md) Example +### [ERC-721](./eip-721.md) Example ```solidity // SPDX-License-Identifier: MIT @@ -250,7 +250,7 @@ contract uRWA721 is Context, ERC721, AccessControlEnumerable, IuRWA { } ``` -### [ERC-1155](./erc-1155.md) Example +### [ERC-1155](./eip-1155.md) Example ```solidity // SPDX-License-Identifier: MIT From b97f9bd9b2ec38e75855dfc83f6b82f3a12a6528 Mon Sep 17 00:00:00 2001 From: xaler Date: Fri, 2 May 2025 11:27:07 +0200 Subject: [PATCH 11/13] minor edits --- ERCS/erc-1234.md | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/ERCS/erc-1234.md b/ERCS/erc-1234.md index fa4c770721..a110e36786 100644 --- a/ERCS/erc-1234.md +++ b/ERCS/erc-1234.md @@ -13,7 +13,7 @@ requires: 165 ## Abstract -This EIP proposes "Universal RWA" (uRWA) standard, a minimal interface for all common tokens like [ERC-20](./eip-20.md), [ERC-721](./eip-721.md) or [ERC-1155](./eip-1155.md) based tokens, meant to be the primitive for the different classes of Real World Assets. It defines essential functions and events for regulatory compliance and enforcement actions common to RWAs. It also extends from [ERC-165](./eip-165.md) for introspection. +This EIP proposes "Universal RWA" (uRWA) standard, a minimal interface for all common tokens like [ERC-20](./eip-20.md), [ERC-721](./eip-721.md) or [ERC-1155](./eip-1155.md) based tokens, meant to be the primitive for the different classes of Real World Assets. It defines essential functions and events for regulatory compliance and enforcement actions common to RWAs. It also adopts [ERC-165](./eip-165.md) for introspection. ## Motivation @@ -40,7 +40,7 @@ The following defines the standard interface for an uRWA token contract. pragma solidity ^0.8.29; /// @title Interface for the uRWA Token Interface -interface IuRWA { +interface IuRWA /* is ERC165 */ { /// @notice Emitted when tokens are taken from one address and transferred to another. /// @param from The address from which tokens were taken. /// @param to The address to which seized tokens were transferred. @@ -81,18 +81,16 @@ interface IuRWA { /// @param user The address to check. /// @return allowed True if the user is allowed, false otherwise. function isUserAllowed(address user) external view returns (bool allowed); - - /// Derived from EIP-165 - function supportsInterface(bytes4 interfaceID) external view returns (bool); } ``` +* The contract MUST implement the [ERC-165](./eip-165.md) `supportsInterface` function and MUST return true for the `bytes4` value `0x4099785e` being it the `interfaceID` of the `uRWA`. * The `isUserAllowed` and `isTransferAllowed` functions provide views into the implementing contract's compliance and transfer policy logic. The exact implementation of these checks (e.g., internal allowlist, external calls, complex logic) is NOT mandated by this interface standard. However these two functions: - MUST NOT revert. - MUST NOT change the storage of the contract. - MAY depend on context (e.g., current timestamp or block.number). -* The `recall` function provide a standard mechanism for forcing a transfer from a `from` to a `to` address. This is often known as either "confiscation" / "revocation" or even "recovery" which are all names related to the motivation being the feature itself. The name chose tries to abstract away the motivation and keep a general name. This function: - - MUST directly manipulate balances or ownership to transfer the asset from `from` to `to` either by transfering or burning from `from` and minting to `to`. +* The `recall` function provides a standard mechanism for forcing a transfer from a `from` to a `to` address. This is often known as either "confiscation" / "revocation" or even "recovery" which are all names related to the motivation being the feature itself. The name chosen tries to abstract away the motivation and keep a general name. This function: + - MUST directly manipulate balances or ownership to transfer the asset from `from` to `to` either by transferring or burning from `from` and minting to `to`. - MUST perform necessary validation checks (e.g., sufficient balance/ownership of a specific token). - MUST emit both the standard `Transfer` event (from the base standard) and the `Recalled` event. - SHOULD bypass standard transfer validation logic, including checks enforced by `isTransferAllowed` and `isUserAllowed`. @@ -207,6 +205,7 @@ contract uRWA721 is Context, ERC721, AccessControlEnumerable, IuRWA { function recall(address from, address to, uint256 tokenId, uint256) public virtual override onlyRole(RECALL_ROLE) { require(to != address(0), ERC721InvalidReceiver(address(0))); + require(isUserAllowed(to), UserNotAllowed(to)); address previousOwner = super._update(to, tokenId, address(0)); // Skip _update override require(previousOwner != address(0), ERC721NonexistentToken(tokenId)); require(previousOwner == from, ERC721IncorrectOwner(from, tokenId, previousOwner)); @@ -216,7 +215,9 @@ contract uRWA721 is Context, ERC721, AccessControlEnumerable, IuRWA { } function isUserAllowed(address user) public view virtual override returns (bool allowed) { - return isWhitelisted[user]; + if (!isWhitelisted[user]) return false; + + return true; } function isTransferAllowed(address from, address to, uint256 tokenId, uint256) public view virtual override returns (bool allowed) { @@ -277,7 +278,9 @@ contract uRWA1155 is Context, ERC1155, AccessControlEnumerable, IuRWA { } function isUserAllowed(address user) public view virtual override returns (bool allowed) { - return isWhitelisted[user]; + if (!isWhitelisted[user]) return false; + + return true; } function _update(address from, address to, uint256[] memory ids, uint256[] memory values) internal virtual override { From 0c4dd4e18c869931adbf3c761cf4833bd9fe7666 Mon Sep 17 00:00:00 2001 From: xaler Date: Mon, 5 May 2025 11:10:22 +0200 Subject: [PATCH 12/13] erc-6093, recall rename --- ERCS/erc-1234.md | 67 ++++++++++++++++++++++++------------------------ 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/ERCS/erc-1234.md b/ERCS/erc-1234.md index a110e36786..3e359c2a93 100644 --- a/ERCS/erc-1234.md +++ b/ERCS/erc-1234.md @@ -46,26 +46,26 @@ interface IuRWA /* is ERC165 */ { /// @param to The address to which seized tokens were transferred. /// @param tokenId The ID of the token being transferred. /// @param amount The amount seized. - event Recalled(address indexed from, address indexed to, uint256 tokenId, uint256 amount); + event ForcedTransfer(address indexed from, address indexed to, uint256 tokenId, uint256 amount); /// @notice Error reverted when a user is not allowed to interact. /// @param account The address of the user which is not allowed for interactions. - error UserNotAllowed(address account); + error ERC1234NotAllowedUser(address account); /// @notice Error reverted when a transfer is not allowed due to restrictions in place. /// @param from The address from which tokens are being transferred. /// @param to The address to which tokens are being transferred. /// @param tokenId The ID of the token being transferred. /// @param amount The amount being transferred. - error TransferNotAllowed(address from, address to, uint256 tokenId, uint256 amount); + error ERC1234NotAllowedTransfer(address from, address to, uint256 tokenId, uint256 amount); /// @notice Takes tokens from one address and transfers them to another. /// @dev Requires specific authorization. Used for regulatory compliance or recovery scenarios. /// @param from The address from which `amount` is taken. /// @param to The address that receives `amount`. /// @param tokenId The ID of the token being transferred. - /// @param amount The amount to recall. - function recall(address from, address to, uint256 tokenId, uint256 amount) external; + /// @param amount The amount to forcefully transfer. + function forceTransfer(address from, address to, uint256 tokenId, uint256 amount) external; /// @notice Checks if a transfer is currently possible according to token rules and registered plugins. /// @dev This may involve checks like allowlists, blocklists, transfer limits, etc. @@ -89,24 +89,28 @@ interface IuRWA /* is ERC165 */ { - MUST NOT revert. - MUST NOT change the storage of the contract. - MAY depend on context (e.g., current timestamp or block.number). -* The `recall` function provides a standard mechanism for forcing a transfer from a `from` to a `to` address. This is often known as either "confiscation" / "revocation" or even "recovery" which are all names related to the motivation being the feature itself. The name chosen tries to abstract away the motivation and keep a general name. This function: +* The `forceTransfer` function provides a standard mechanism for forcing a transfer from a `from` to a `to` address. This is often known as either "confiscation" / "revocation" or even "recovery" which are all names related to the motivation being the feature itself. The name chosen tries to abstract away the motivation and keep a general name. This function: - MUST directly manipulate balances or ownership to transfer the asset from `from` to `to` either by transferring or burning from `from` and minting to `to`. - MUST perform necessary validation checks (e.g., sufficient balance/ownership of a specific token). - - MUST emit both the standard `Transfer` event (from the base standard) and the `Recalled` event. - - SHOULD bypass standard transfer validation logic, including checks enforced by `isTransferAllowed` and `isUserAllowed`. + - MUST emit both the standard `Transfer` event (from the base standard) and the `ForcedTransfer` event. + - MUST bypass checks enforced by `isTransferAllowed`. + - MUST perform `isUserAllowed` check in the `recipient` parameter. -Given the agnostic nature of the standard on the specific base token standard being used the implementation SHOULD use `tokenId = 0` for [ERC-20](./eip-20.md) based implementations, and `amount = 1` for [ERC-721](./eip-721.md) based implementations on `Recalled` event, `TransferNotAllowed` error and `recall` / `isTransferAllowed` functions. Integrators MAY decide to not enforce this, however the standard discourages it. This is considered a little tradeoff for having a unique standard interface for different token standards without overlapping syntaxes. +Given the agnostic nature of the standard on the specific base token standard being used the implementation SHOULD use `tokenId = 0` for [ERC-20](./eip-20.md) based implementations, and `amount = 1` for [ERC-721](./eip-721.md) based implementations on `ForcedTransfer` event, `ERC1234NotAllowedTransfer` error and `forceTransfer` / `isTransferAllowed` functions. Integrators MAY decide to not enforce this, however the standard discourages it. This is considered a little tradeoff for having a unique standard interface for different token standards without overlapping syntaxes. -Implementations of this interface MUST implement the necessary functions of their chosen base standard (e.g., [ERC-20](./eip-20.md), [ERC-721](./eip-721.md) and [ERC-1155](./eip-1155.md) functionalities) and MUST also restrict access to sensitive functions like `recall` using an appropriate access control mechanism (e.g., `onlyOwner`, Role-Based Access Control). The specific mechanism is NOT mandated by this interface standard. +Implementations of this interface MUST implement the necessary functions of their chosen base standard (e.g., [ERC-20](./eip-20.md), [ERC-721](./eip-721.md) and [ERC-1155](./eip-1155.md) functionalities) and MUST also restrict access to sensitive functions like `forceTransfer` using an appropriate access control mechanism (e.g., `onlyOwner`, Role-Based Access Control). The specific mechanism is NOT mandated by this interface standard. -Integrators MUST ensure their internal transfer logic (e.g., within `_update`, `_transfer`, `_mint`, `_burn`) respects the boolean outcomes of `isUserAllowed` and `isTransferAllowed`. Transfers, mints, or burns MUST NOT proceed and instead MUST revert with `UserNotAllowed` or `TransferNotAllowed` if and only if these checks indicate the action is disallowed according to the contract's specific policy. +Integrators MUST ensure their transfer methods (`transfer`, `transferFrom`, `mint`, `burn`) respect the following validation logic, meaning that they MUST revert if any of the following validation logics return `false`: +- Public transfers (`transfer`, `transferFrom`, `safeTransferFrom`) MUST run `isTransferAllowed` and `isUserAllowed` on origin and destination addresses. +- Minting MUST run `isUserAllowed` on destination address. +- Burning MUST NOT run `isTransferAllowed` nor `isUserAllowed` on the wallet holding the tokens to burn. ## Rationale -* **Minimalism:** Defines only the essential functions (`recall`, `isUserAllowed`, `isTransferAllowed`) and associated events/errors needed for common RWA compliance and control patterns, avoiding mandated complexity or opinionated features. +* **Minimalism:** Defines only the essential functions (`forceTransfer`, `isUserAllowed`, `isTransferAllowed`) and associated events/errors needed for common RWA compliance and control patterns, avoiding mandated complexity or opinionated features. * **Flexibility:** Provides standard view functions (`isUserAllowed`, `isTransferAllowed`) for compliance checks without dictating *how* those checks are implemented internally by the token contract. This allows diverse compliance strategies. * **Compatibility:** Designed as an interface layer compatible with existing base standards like [ERC-20](./eip-20.md), [ERC-721](./eip-721.md) and [ERC-1155](./eip-1155.md). Implementations extend from `IuRWA` alongside their base standard interface. -* **RWA Essential:** Includes `recall` as a standard function, acknowledging its importance for regulatory enforcement in the RWA space, distinct from standard transfers. Mandates access control for this sensitive function. +* **RWA Essential:** Includes `forceTransfer` as a standard function, acknowledging its importance for regulatory enforcement in the RWA space, distinct from standard transfers. Mandates access control for this sensitive function. * **[ERC-165](./eip-165.md):** Ensures implementing contracts can signal support for this interface. As an example, a Uniswap v4 pool can integrate with uRWA [ERC-20](./eip-20.md) tokens by calling `isUserAllowed` or `isTransferAllowed` within its before/after hooks to handle these assets in a compliant manner. Users can then expand these tokens with additional features to fit the specific needs of individual asset types, either with on-chain identity systems, historical balances tracking for dividend distributions, semi-fungibility with tokens metadata, etc. @@ -130,7 +134,7 @@ pragma solidity ^0.8.29; contract uRWA20 is Context, ERC20, AccessControlEnumerable, IuRWA { bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE"); - bytes32 public constant RECALL_ROLE = keccak256("RECALL_ROLE"); + bytes32 public constant FORCE_TRANSFER_ROLE = keccak256("FORCE_TRANSFER_ROLE"); bytes32 public constant WHITELIST_ROLE = keccak256("WHITELIST_ROLE"); mapping(address user => bool whitelisted) public isWhitelisted; @@ -150,11 +154,11 @@ contract uRWA20 is Context, ERC20, AccessControlEnumerable, IuRWA { /* standard mint and burn functions with access control ...*/ - function recall(address from, address to, uint256, uint256 amount) public onlyRole(RECALL_ROLE) { - require(isUserAllowed(to), UserNotAllowed(to)); + function forceTransfer(address from, address to, uint256, uint256 amount) public onlyRole(FORCE_TRANSFER_ROLE) { + require(isUserAllowed(to), ERC1234NotAllowedUser(to)); // Directly update balances, bypassing overridden _update super._update(from, to, amount); - emit Recalled(from, to, 0, amount); + emit ForcedTransfer(from, to, 0, amount); } function isTransferAllowed(address from, address to, uint256, uint256 amount) public virtual view returns (bool allowed) { @@ -172,11 +176,10 @@ contract uRWA20 is Context, ERC20, AccessControlEnumerable, IuRWA { function _update(address from, address to, uint256 value) internal virtual override { if (from != address(0) && to != address(0)) { // Transfer - require(isTransferAllowed(from, to, 0, value), TransferNotAllowed(from, to, 0, value)); + require(isTransferAllowed(from, to, 0, value), ERC1234NotAllowedTransfer(from, to, 0, value)); } else if (from == address(0)) { // Mint - require(isUserAllowed(to), UserNotAllowed(to)); + require(isUserAllowed(to), ERC1234NotAllowedUser(to)); } else { // Burn - require(isUserAllowed(from), UserNotAllowed(from)); } super._update(from, to, value); @@ -203,15 +206,15 @@ contract uRWA721 is Context, ERC721, AccessControlEnumerable, IuRWA { /* standard mint and burn functions with access control ...*/ - function recall(address from, address to, uint256 tokenId, uint256) public virtual override onlyRole(RECALL_ROLE) { + function forceTransfer(address from, address to, uint256 tokenId, uint256) public virtual override onlyRole(FORCE_TRANSFER_ROLE) { require(to != address(0), ERC721InvalidReceiver(address(0))); - require(isUserAllowed(to), UserNotAllowed(to)); + require(isUserAllowed(to), ERC1234NotAllowedUser(to)); address previousOwner = super._update(to, tokenId, address(0)); // Skip _update override require(previousOwner != address(0), ERC721NonexistentToken(tokenId)); require(previousOwner == from, ERC721IncorrectOwner(from, tokenId, previousOwner)); ERC721Utils.checkOnERC721Received(_msgSender(), from, to, tokenId, ""); - emit Recalled(from, to, tokenId, 1); + emit ForcedTransfer(from, to, tokenId, 1); } function isUserAllowed(address user) public view virtual override returns (bool allowed) { @@ -234,11 +237,10 @@ contract uRWA721 is Context, ERC721, AccessControlEnumerable, IuRWA { } if (from != address(0) && to != address(0)) { // Transfer - require(isTransferAllowed(from, to, value, 1), TransferNotAllowed(from, to, value, 1)); + require(isTransferAllowed(from, to, value, 1), ERC1234NotAllowedTransfer(from, to, value, 1)); } else if (from == address(0)) { // Mint - require(isUserAllowed(to), UserNotAllowed(to)); + require(isUserAllowed(to), ERC1234NotAllowedUser(to)); } else { // Burn - require(isUserAllowed(from), UserNotAllowed(from)); } return super._update(to, value, auth); @@ -264,10 +266,10 @@ contract uRWA1155 is Context, ERC1155, AccessControlEnumerable, IuRWA { /* standard mint and burn functions with access control ...*/ - function recall(address from, address to, uint256 tokenId, uint256 amount) public onlyRole(RECALL_ROLE) { - require(isUserAllowed(to), UserNotAllowed(to)); + function forceTransfer(address from, address to, uint256 tokenId, uint256 amount) public onlyRole(FORCE_TRANSFER_ROLE) { + require(isUserAllowed(to), ERC1234NotAllowedUser(to)); _safeTransferFrom(from, to, tokenId, amount, ""); - emit Recalled(from, to, tokenId, amount); + emit ForcedTransfer(from, to, tokenId, amount); } function isTransferAllowed(address from, address to, uint256 tokenId, uint256 amount) public view virtual override returns (bool allowed) { @@ -290,14 +292,13 @@ contract uRWA1155 is Context, ERC1155, AccessControlEnumerable, IuRWA { for (uint256 i = 0; i < ids.length; ++i) { if (from != address(0) && to != address(0)) { // Transfer - require(isTransferAllowed(from, to, ids[i], values[i]), TransferNotAllowed(from, to, ids[i], values[i])); + require(isTransferAllowed(from, to, ids[i], values[i]), ERC1234NotAllowedTransfer(from, to, ids[i], values[i])); } } if (from == address(0)) { // Mint - require(isUserAllowed(to), UserNotAllowed(to)); + require(isUserAllowed(to), ERC1234NotAllowedUser(to)); } else if (to == address(0)) { // Burn - require(isUserAllowed(from), UserNotAllowed(from)); } super._update(from, to, ids, values); @@ -312,7 +313,7 @@ contract uRWA1155 is Context, ERC1155, AccessControlEnumerable, IuRWA { ## Security Considerations -* **Access Control for `recall`:** The security of the mechanism chosen by the implementer to restrict access to the `recall` function is paramount. Unauthorized access could lead to asset theft. Secure patterns (multisig, timelocks) are highly recommended. +* **Access Control for `forceTransfer`:** The security of the mechanism chosen by the implementer to restrict access to the `forceTransfer` function is paramount. Unauthorized access could lead to asset theft. Secure patterns (multisig, timelocks) are highly recommended. * **Implementation Logic:** The security and correctness of the *implementation* behind `isUserAllowed` and `isTransferAllowed` are critical. Flaws in this logic could bypass intended transfer restrictions or incorrectly block valid transfers. * **Standard Contract Security:** Implementations MUST adhere to general smart contract security best practices (reentrancy guards where applicable, checks-effects-interactions, etc.). From 51f6b4b22cc8fd3c1081b965843ac4c04c98c94d Mon Sep 17 00:00:00 2001 From: xaler Date: Mon, 5 May 2025 11:21:36 +0200 Subject: [PATCH 13/13] add checks on forceTransfer examples --- ERCS/erc-1234.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ERCS/erc-1234.md b/ERCS/erc-1234.md index 3e359c2a93..55aa96bef9 100644 --- a/ERCS/erc-1234.md +++ b/ERCS/erc-1234.md @@ -155,6 +155,7 @@ contract uRWA20 is Context, ERC20, AccessControlEnumerable, IuRWA { /* standard mint and burn functions with access control ...*/ function forceTransfer(address from, address to, uint256, uint256 amount) public onlyRole(FORCE_TRANSFER_ROLE) { + require(from != address(0) && to != address(0), NotZeroAddress()); require(isUserAllowed(to), ERC1234NotAllowedUser(to)); // Directly update balances, bypassing overridden _update super._update(from, to, amount); @@ -202,11 +203,12 @@ pragma solidity ^0.8.29; /* required imports ... */ contract uRWA721 is Context, ERC721, AccessControlEnumerable, IuRWA { - /* same roles definitions, constructor and changeWhitelist function as before ...*/ + /* same definitions, constructor and changeWhitelist function as before ...*/ /* standard mint and burn functions with access control ...*/ function forceTransfer(address from, address to, uint256 tokenId, uint256) public virtual override onlyRole(FORCE_TRANSFER_ROLE) { + require(from != address(0), NotZeroAddress()); require(to != address(0), ERC721InvalidReceiver(address(0))); require(isUserAllowed(to), ERC1234NotAllowedUser(to)); address previousOwner = super._update(to, tokenId, address(0)); // Skip _update override @@ -262,11 +264,12 @@ pragma solidity ^0.8.29; /* required imports ... */ contract uRWA1155 is Context, ERC1155, AccessControlEnumerable, IuRWA { - /* same roles definitions, constructor and changeWhitelist function as before ...*/ + /* same definitions, constructor and changeWhitelist function as before ...*/ /* standard mint and burn functions with access control ...*/ function forceTransfer(address from, address to, uint256 tokenId, uint256 amount) public onlyRole(FORCE_TRANSFER_ROLE) { + require(from != address(0), NotZeroAddress()); require(isUserAllowed(to), ERC1234NotAllowedUser(to)); _safeTransferFrom(from, to, tokenId, amount, ""); emit ForcedTransfer(from, to, tokenId, amount);