ERC-7943: Universal RWA Interface (uRWA)

Hello everyone,

This proposal clearly states that EIP-7943 deliberately omits metadata handling and focuses more on compliance and enforcement. Great work keeping the interface minimal.

I chatted with @davrilio about the document management aspect (outside of this thread).

Although I understand how this document storage can be viewed as metadata handling or something optional, it can be a core token component, linking the real-world asset with the token itself. While this is more common for security tokens, it could be valuable for Asset-Referenced Tokens (ART) regarding MiCA compliance (I’m not a legal expert). It can be used to prove to investors that the prospectus hasn’t changed and that it’s the same one that regulators approved.

The question is whether we want to implement this as part of the original standard or as an extension/independent modular component on top of EIP-7943.

ERC20 can be extended with ERC1046 to provide the tokenURI() extension similar to ERC721/ERC1155, but this approach might be opinionated.

I believe EIP1643 already solves this problem. Even though it hasn’t been officially adopted as a standard, it has been battle-tested by CMTAT and other ERC1400 implementations, so I don’t see why we need to reinvent the wheel.

From a technical standpoint, with EIP1643, you can attach multiple documents because it stores a bytes32 document hash (e.g., a Merkle root aggregating IPFS CIDs), so one hash covers many docs.

If immutability or upgradeability is a concern, you can always make it immutable or extend EIP1643 with some lockable/governance mechanism. I think this is why the original authors of EIP1643 didn’t implement this, leaving it to implementors to decide based on the use case.

So, maybe I’d keep EIP-7943 untouched and reference EIP-1643 (or an evolution of it) in a separate module, leaving it customizable as required.

Good point about EIP-1643. The handling of a Hash (associated to a document or as a root of a Merkle tree being associated to many documents) is one part of the problem. I believe a link to a RWA that can be added or removed is not enough though as there is no logical update of the document, not even a way to check whether the hashes are versions of the same document or completely disconnected things. In a sense, this is ok because EIP-1643 doesn’t convey any semantics on the hashes, on the other side for compliance you may want to maintain audit trails, etc. In any case the point is that the logic for implementing all these things should be IMO outside EIP-7943, and my point was that the computational element that manages this logic should be a smart contract on-chain and not a URI or a hash. In that way the AssetReference related to an EIP-7943 token is also on-chain so for traceability reasons one can follow what processing and logic is behind the AssetReference.

Thanks for this proposal guys. I’m not a big fan of adding freezing mechanisms in a standard, I’m more of delegating such dirty actions to other actors/modules as the implementation of Distributed Labs demonstrates

1 Like

Hello @ivanmmurcia and thanks for your comment.

Can you provide more arguments of why you prefer one way or the other ? Is there any important legit use case that really don’t need that feature ? As of my understanding, in regulated environments, it’s a pretty much needed feature indeed, justifying why it should stay in the standard.

Additionally I gave a quick look at your implementation, I’m not really sure but as far as I see:

  • It uses a diamond like structure
  • Modules are exclusively read-only features ? If not, how is storage changes handled ? Are modules directly called with an external call or using delegate ones ?

I have been exploring the same exact architecture and while I believe is a powerful one, it present some limitations which are not easy to handle with:

  • Diamond is not the most simple (and not yet widely supported) design solution, it solves the size limits but it really complicates flows and manteinance, not really suitable for simple use cases. Additionally, while diamond inherently enables features-extension, it does not extend the interface itself as a normal inheritance design would do, and this might present some tradeoffs with offchain integrations.
  • Storage changes through modules, having only read-only modules is kind of limiting, while having modules to change the state present the need to decide which context should be used to store the changes (I see you adopted a namespaced storage pattern, that would imply that delegatecalls to modules can safely change the state of the calling contract but bringing even delegate call into the design brings also some security considerations).

Either way, this might be off topic. In summary:

I believe there are strong reasons to believe that freezing should stay in the main interface, but I’m happy to listen to any additional argument beoynd the implementation itself of the feature.

Again thanks for the time taken to review this so far :pray:

Thanks @davrilio and @mihaic195 for the interesting conversations about the metadata handling.

I agree with both:

  • This to me is something orthogonal to 7943 and it’s definetely compatible through inclusion. As you mentioned, you can go even beyond that and enforces specific metadata rules to be applied in functions like isTransferAllowed or isUserAllowed).

  • The idea of EIP-1643 sounds nice. On the “mutability” issue mentioned and the need for a more robust solution to ensure untampered metadata I’ve been recently reading about new patterns emerging as @mihaic195 mentioned (hash-chained root of the whole state of metadata, merkle tree solutions, and I wouldn’t be surprised to start seeing more zero knowledge related work to better fit into privacy conserving contexts).

Hello folks !

I’ve been thinking a bit more and doing some extra research and I ended up finding two additional EIPs which are interesting for the current scope:

At this point I have several thoughts:

  • isTransferAllowed can be renamed to canTransfer to align with 3643, 7751 and 7518.
  • It appear that pausability is present in almost all of similar other EIPs. Still I’m in not fully confident in adding it to the EIP because it would come with more challenges like what paused token means ? all transfers / mint / burn are paused or only one of them ? What about approvals or other features the token might have (yield, rebalances, voting … ) ? Additionally, there’s still a bit of overlap with isTransferAllowed and isUserAllowed where pausability can be implemented.
  • About metadata handling, it also seems to be a recurrent topic accross several EIPs but as stated before I’m in favor of creating an example plugin for 7943 in the reference implementation repository.
  • freeze/unfreeze seems preferred in other EIPs, but we already addressed why we use setFrozen / getFrozen in the “Notes on naming” paragraph. Do you think it is sufficient ?
  • Other EIPs implement an account-based freezing (different from the current balance-based freezing mechanism) where accounts are frozen by their address and not by their assets. While I believe this is interesting, I think it overlaps with isUserAllowed and that there’s no need for a specific extra feature to cover for it. What are your thoughts ?

Also, based on feedback received on a potential implementation for OpenZeppelin community contracts library (Add ERC20Freezable, ERC20Restricted and ERC20uRWA by ernestognw ¡ Pull Request #186 ¡ OpenZeppelin/openzeppelin-community-contracts ¡ GitHub)

  • ERC7943NotAllowedTransfer seems to be useless in terms of implementation since it’s not specific and most of the time a more explicit error will preceed. The EIP defines already the optionality of this error, but wondering if it’s worth at all at this point.

cc @ernestognw in case you want to provide more feedback :slight_smile:

1 Like

I’ve also added some examples in the reference implementation on how to integrate:

  • Multicall
  • Pausability
  • EIP-1643 like metadata handling

Congrats on your effort; AI analyzed the proposal and said this:

## 1. Summary

ERC-7943 defines a minimal interface to introduce on-chain compliance controls—freezing, forced transfers, and allowlist checks—on top of any base token (ERC-20/721/1155) while preserving composability via ERC-165 introspection.


## 2. Strengths

1. Minimal, Non-Opinionated Design

  • Only five new functions plus matching events/errors, avoiding the “kitchen-sink” approach of prior RWA EIPs.
  • Leaves implementation details (whitelisting, role management, identity backends) entirely to integrators, reducing gas overhead for simple use-cases.

2. Unified Across Standards

  • One interface covers fungible, non-fungible, and semi-fungible assets, with clear guidance on using tokenId = 0 or amount = 1 for ERC-20/721 respectively.
  • Leverages ERC-165 so wallets and tooling can detect RWA-capable tokens at runtime.

3. Explicit Error Semantics

  • Introduces three bespoke errors (ERC7943NotAllowedUser, …NotAllowedTransfer, …InsufficientUnfrozenBalance), improving on generic “revert” messages and helping integrators handle compliance failures programmatically.

4. Enforcement Hook

  • forceTransfer provides a standardized way to implement on-chain asset recovery or regulatory seizure, complete with event ordering rules to aid indexers.

## 3. Weaknesses & Open Questions

1. Access Control Unspecified

  • While deliberate, the lack of any recommendation (e.g. using OpenZeppelin’s AccessControl) means some implementations may insecurely gate forceTransfer/setFrozen. A “recommended practice” section could help.

3. Batch Operations for 1155

  • Freezing and forced-transfer APIs are single-token only. High-volume 1155 use-cases (e.g. real-world commodity baskets) might need batch versions to avoid repetitive gas costs.

4. No On-chain Auditing Hooks

  • Aside from the two events, there’s no mechanism to record why an address was disallowed or frozen. An optional “reason” string or a getFreezeReason(user, tokenId) view could aid transparency.

Interplay with ERC-1404/1400

  • Prior EIPs (e.g. the security-token ERC-1404) also tackled allowlists and transfer checks. A brief “Compatibility” section comparing uRWA to those could help implementers choose the right standard.

## 4. Suggestions

Add a “Recommended Access Control” Annex:

Propose a minimal RBAC pattern (e.g. an ENFORCER_ROLE) so newcomers avoid mis-configuring permissions.

Introduce Optional Batch APIs:

Two additional functions:

function setFrozenBatch(address user, uint256[] tokenIds, uint256[] amounts) external;
function forceTransferBatch(address from, address to, uint256[] tokenIds, uint256[] amounts) external;

These can be “MAY” rather than “MUST,” preserving minimalism.

Define “Reason Codes”:

Extend the Frozen event (or add a companion) to carry a short bytes32 reasonCode so off-chain systems can track compliance workflows.

Clarify Edge Cases:

For example, should forceTransfer on a non-existent ERC-721 token revert with the same errors as a standard transfer? A bullet in “Security Considerations” would tighten the spec.


## 5. Conclusion

ERC-7943 strikes a solid balance between “just enough” compliance tooling and base-token simplicity. It’s flexible enough to cover most RWA scenarios, yet lightweight relative to full security-token frameworks. With a few minor enhancements around access control guidance, batch operations, and auditability, I believe it can become the go-to standard for tokenized real-world assets.

Hey @xaler,

Thanks for the update!

Although we see many similarities in functionality regarding ERC-7551, I think the motivation was to have something without the ONCHAINID component and aligned with the German electronic securities law (eWpG). However, I haven’t seen major developments since it was opened in 2023.

I hadn’t encountered ERC-7518 before. It looks like an extension to ERC-1155, specifically regarding partitions. ERC-1400 already popularized the concept of tranches/partitions. ERC-7943 can already extend ERC-1155, as seen in your examples repo.

Terminology-wise, renaming isTransferAllowed to canTransfer makes sense to better align with what already exists.

@davrilio and I had a fruitful conversation about handling metadata and exchanged ideas. We often see opinionated versions of this and implementations specific to each use case. Decoupling it from the token contract and simply providing a reference could make sense. Hopefully, @davrilio and I can collaborate on something soon.

Thinking about pausability, I’m looking at OZ’s ERC20Pausable NatSpec comments:


/**

* @dev ERC-20 token with pausable token transfers, minting and burning.

*

* Useful for scenarios such as preventing trades until the end of an evaluation

* period, or having an emergency switch for freezing all token transfers in the

* event of a large bug.

*

It overrides the _update function, which is common to all of these functionalities (transfer/mint/burn). Since it’s an “emergency switch,” I think it’s irrelevant for the other functionalities you mentioned, only for critical ones that explicitly change balances/supply. The different functionalities (approvals, yield, rebalances, voting, etc.) are highly coupled with transfers, so pausing those is redundant in theory.

Maybe this will help you better decide whether to include it as part of the EIP.

One useful improvement I could suggest is in regards to the event in the interface:

Change from


event ForcedTransfer(address indexed from, address indexed to, uint256 tokenId, uint256 amount);

to


event ForcedTransfer(address indexed from, address indexed to, uint256 indexed tokenId, uint256 amount);

In the context of NFT-style assets, regulators, custodians, or UIs usually care much more about “show me the forced transfers for token #1234”. You already index it on the Frozen event.

Hi @xaler! Some thoughts in response:

  • isTransferAllowed can be renamed to canTransfer to align with 3643, 7751 and 7518.

I would 100% agree with this. I don’t think changing the name of this function is a deal breaker and it’s a good opportunity to align interface-wise. There are projects that want certain level of compatibility with 3643 and imo this is in the right direction.

It appear that pausability is present in almost all of similar other EIPs.

I wouldn’t include it. I see little benefit in adding pausability to a standard. At the end, this is a feature that allows the team behind a token to react to security incidents, so under “normal operation” pausability shouldn’t be even perceived by the end user.

I would say protocol security teams would decide how (and to what degree) pause their systems, and the uRWA spec is not the place to enforce that. Also, it raises weird questions about isTransferAllowed and isUserAllowed as you pointed out. Imo, the best we can do to specify what those functions should do is speculate until we see a couple of pausable RWA in the wild.

Do you think it is sufficient ?

I think it’s sufficient. Perhaps I would mention increaseAllowance and decreaseAllowance in ERC20s that was never really used although it theoretically fixed a frontrunning issue. See this issue

ERC7943NotAllowedTransfer seems to be useless in terms of implementation since it’s not specific and most of the time a more explicit error will preceed.

I ended up removing it from the community contracts implementation. Here’s the rationale: Add ERC20Freezable, ERC20Restricted and ERC20uRWA by ernestognw · Pull Request #186 · OpenZeppelin/openzeppelin-community-contracts · GitHub

I would consider removing the error altogether. The main reason is that it’s not specific and there seems to be always a more specific replacement for it.

2 Likes

gm, great work @xaler!

I noticed @tinom9’s thoughtful discussion on the forceTransfer behavior, especially regarding the handling of a token’s potential frozen state. One point that seems crucial but currently missing from the discussion is explicitly addressing whether tokens should remain frozen or become unfrozen after a forced transfer.

I believe it’s essential that a token maintains its original frozen/unfrozen state after being forcefully transferred, independent of the transfer itself.

To illustrate this, consider a legal scenario: a court could mandate transferring tokens from party A to party B due to ongoing litigation, but simultaneously restrict party B’s ability to use or dispose of the tokens until the case is resolved (effectively allowing use but not possession). Automatically unfreezing tokens upon forced transfer would bypass this critical legal step and potentially undermine compliance.

While I’m not a lawyer and there are likely many nuanced cases, intuitively, it seems safer and more legally sound to separate these two actions clearly. Unfreezing a token should ideally be a distinct subsequent step, explicitly authorized and handled separately, rather than implicitly assumed.

Curious to hear your thoughts on this!

After some talks with @ernestognw I agree that for the sake of consistency the following changes are going to be applied:

  • Renamed isTransferAllowed → canTransfer (matches ERC-3643, ERC-7518, ERC-7751)
  • Renamed forceTransfer → forcedTransfer (matches ERC-3643)
  • Renamed getFrozen → getFrozenTokens (matches ERC-3643)

Additionally the generic ERC7943NotAllowedTransfer error is removed to encourage specific error handling.

The pull request has been already opened by @ernestognw


@ariutokintumi your use case is interesting indeed. I have a couple of comments:

(effectively allowing use but not possession)

What is “use” in this case ? What would you expect to do with frozen tokens that you can’t transfer ?

Additionally, by having Multicall by inheritance, a call can be batched that does two operations within the same tx:

  1. Force transfer
  2. Freeze on the recipient of the force transfer

I understand this is a workaround but looking forward to know your thoughts

2 Likes

@ernestognw I also agree with this:

I wouldn’t include it. I see little benefit in adding pausability to a standard. At the end, this is a feature that allows the team behind a token to react to security incidents, so under “normal operation” pausability shouldn’t be even perceived by the end user.

In the end, I see this interface to serve the needs of external integrators, but product developers can include pausing at their discretion.

1 Like

Hello @xaler, thanks for the quick follow-up and for pointing out the Multicall option, that certainly works as a workaround. My concern is mainly about keeping contract semantics intuitive and minimizing accidental side-effects.

What is “use” in this case? What would you expect to do with frozen tokens that you can’t transfer?

By “use” I mean situations where the token holder may need to reference or prove ownership of the asset (e.g., receive income, vote, accrue interest, meet regulatory‐capital ratios) even though they are not yet free to dispose of it. Think of it as beneficial ownership without full possession.

More broadly, I’m proposing that forced movement of title (transfer) and change of mobility status (freeze/unfreeze) remain two independent primitives. Merging them assumes that every compulsory transfer must also unlock the asset, yet in practice regulators and courts often separate those steps. If an implementation wants to auto-unfreeze it can still do so explicitly, but making it implicit removes flexibility.

Illustrative scenarios where “frozen-on-arrival” matters, maybe some are wrong, but can express the idea.

  1. Court-ordered interim measure
    A judge orders tokens moved from A to B while litigation is pending. B may collect dividends or exercise voting rights under supervision, but cannot sell until the final verdict. The tokens must arrive still frozen.
  2. Collateral reassignment
    A non-performing loan is sold from Bank X to Debt-Fund Y. The on-chain collateral is transferred to Y, but remains frozen until the borrower signs new terms. Automatic unfreeze would prematurely release the lien.
  3. Estate settlement with multiple heirs
    An executor moves tokens into a holding wallet representing the estate. Assets stay frozen until probate finishes and each heir’s share is determined, preventing any single heir from selling before distribution.
  4. Regulatory seizure pending investigation
    A financial regulator seizes assets and moves them to a treasury address while investigating sanctions violations. Tokens must stay frozen until the investigation concludes.
  5. Cross-jurisdiction escrow
    Two parties in different jurisdictions agree that tokens move to a neutral escrow address immediately, but unfreezing only happens once off-chain obligations (e.g., physical delivery of goods) are certified.

Keeping forcedTransfer and freeze/unfreeze orthogonal lets all of these workflows be expressed cleanly, while a batched Multicall can still combine them when desired.

Happy to hear your thoughts, thanks again for this opportunity to contribute!

1 Like

100% agreed, if the goal is to create a minimal interface for RWAs, the real value lies much more on the integrator side than the admin side.

Where ERC-7943 could bring genuine value is by making it crystal clear to integrators which getter functions to call for pre-checks before executing a transaction. That way, an application can detect in advance which transfers will be blocked by the token’s permissioning system, understand why, and, where possible, guide the user on how to resolve the issue. (In some cases, of course, certain users will simply never be eligible for specific tokens, and the pre-check should make that obvious.)

If ERC-7943 were to precisely define and standardize:

  • The minimal set of read-only compliance functions,
  • The meaning of their return values, and
  • The standardized errors they can throw,

then integrators wouldn’t have to dig through large, opinionated architectures (as in most other standards) just to find the few functions they actually need.

A lean, predictable compliance interface like this, focused purely on integration points, could be extremely valuable for wallets, custodians, and DeFi protocols that want to support RWAs without getting lost in the noise of full token architecture definitions.

We would be very happy to help if that is the direction you want to take with the ERC, we started to develop something to do that on the ERC-3643 but it is aligned purely on the ERC-3643 smart contracts, not on other permissioned tokens, it would be valuable to have something standard and minimal.

Adding write functions on the ERC-7943 interfaces can make sense too if integrators are supposed to use these interfaces i guess, but if it is only for the token issuer/agents it could make sense to even remove them from the interface to follow the minimalist approach that this ERC is aiming for.

3 Likes

The distinction between ownership and mobility is a fundamental legal and technical principle that should be preserved at the protocol level. In many legal systems, title to an asset and the ability to dispose of it are separate rights. Regulatory, judicial, and contractual frameworks often require that ownership can be transferred while the asset remains immobilized, whether due to litigation, insolvency, sanctions, probate, or escrow conditions.

Automatically unfreezing on transfer conflates these two rights and risks making the standard incompatible with such scenarios. From a technical standpoint, coupling forceTransfer and unfreeze hardcodes a compliance assumption that is not universal and undermines modularity. By keeping these functions independent, the standard remains jurisdiction-agnostic and adaptable, allowing issuers and service providers to design workflows that reflect their specific legal and regulatory requirements. Implementations can still combine transfer and unfreeze when appropriate, but the choice should remain with the implementer, but in my opinion and following what @ariutokintumi brought to light, it should not be dictated at the standard level.

5 Likes

Hi guys, very happy to see more discussion and progress, congrats!


Forced transfer token unfreezing

I believe it’s essential that a token maintains its original frozen/unfrozen state after being forcefully transferred, independent of the transfer itself.

@ariutokintumi, @EdwinMata, there are other complex scenarios in which the forceTransfer function would not be technically sufficient on its own, such as the case in which a user has both frozen and unfrozen tokens, and a court ordered transfer of either of those. For example, an extra parameter would be needed to specify it, and 2 calls instead of 1 if both frozen and unfrozen tokens wanted to be transferred.

I’d specify a baseline behaviour, and implement specific flows with multicall atomic transactions (outside the ERC), as @xaler proposed. To me, the baseline behaviour is unfreezing when forcefully transferring tokens if needed.


Renaming

  • Renamed isTransferAllowed → canTransfer (matches ERC-3643, ERC-7518, ERC-7751)
  • Renamed forceTransfer → forcedTransfer (matches ERC-3643)
  • Renamed getFrozen → getFrozenTokens (matches ERC-3643)

Given the current direction of the ERC, I’m not onboard with the renaming.

Synergies with ERC-3643

There are important differences in the definition of ERC-3643 and ERC-7943 that would make reusing the interface layer difficult.

ERC-7943 will have to get its own implementation by integrators, regardless of existing ERC-3643 support.

And then, there is different expected behaviour from the analogous functions listed above between ERC-3643 and ERC-7943 that would either need to be changed or well documented to minimize confusion, apart from the ones that would not be changed (setFrozen).

Furthermore, @Joachim-Lebrun response to my initial proposion of a minimal ERC-3643 standard stated that the small changes would already break existing integrations.

Synergies with ERC-7518 and ERC-7551

I would say there’s also not much homogeneity between ERC-3643, ERC-7518, ERC-7551, and the current ERC-7943 definition.

ERC-7518 has:

  • lockedBalanceOf

  • canTransfer

  • forceTransfer

  • freezeAddress

  • unFreeze

ERC-7551 has:

  • frozenBalanceOf

  • canTransfer

  • canTransferFrom

  • forceTransferFrom

  • freezeTokens

  • unfreezeTokens

Furthermore, they’re in draft/proposal stages.

Heterogeneous naming

This is a nitpick, but I find the resulting naming of functions much more confusing:

  • canTransfer and isUserAllowed instead of isTransferAllowed and isUserAllowed.

  • getFrozenTokens in ERC-721 would not make sense.

  • forcedTransfer (past participle or adjective and verb/noun) is less clear to me than forceTransfer (verb and noun).

ERC scope

I’ll refloat my first comment in the thread, as I think it still stands:

I then believe there are two possible paths for this standard:

  • Universal token transfer check, non-opinionated on the admin or functionality standpoint. Additional ERCs can be built on top to standardize whitelisting, pausing/unpausing, access-control, or other compliance mechanisms.
  • Universal RWA token, with core token functionality embedded: forceTransfer/recall, freezeTokens, frozenBalance. Compliance functionality should be built on top, similarly to the previous option.

I believe a security/RWA/permissioned token standard should be opinionated on the core token functionality.

I believe the value of the ERC lies in the core functionality and it being shared across major token standards (ERC20, ERC721, ERC1155), and that it’s more than sufficient to justify interface divergences.


isTransferAllowed to canTransfer

If the renaming takes place, note that ERC7943.isTransferAllowed is currently defined as checking ERC7943.isUserAllowed, from the sender and recipient. ERC3643.canTransfer does not make this check.

I believe either ERC7943.isTransferAllowed behaviour is changed to match ERC3643.canTransfer or the difference should be documented in the ERC.


Pausability

I wouldn’t include it. I see little benefit in adding pausability to a standard. At the end, this is a feature that allows the team behind a token to react to security incidents, so under “normal operation” pausability shouldn’t be even perceived by the end user.

I would say protocol security teams would decide how (and to what degree) pause their systems, and the uRWA spec is not the place to enforce that. Also, it raises weird questions about isTransferAllowed and isUserAllowed as you pointed out. Imo, the best we can do to specify what those functions should do is speculate until we see a couple of pausable RWA in the wild.

@ernestognw, my point from previous comments stands.

Additionally, I do see some benefit in the user-facing part of pausability, that is specification on whether the token is paused, above the baseline check from isTransferAllowed. The same way that isUserAllowed is nested in practice within isTransferAllowed.

Appreciate the thoughtful pushback. I agree with the goal of clarity and I think your own point about heterogeneous admin ABIs across ERC‑7518/7551/3643 (and we could add ERC‑1400) actually argues for keeping ERC‑7943 focused on a single, standard‑agnostic, read‑only surface that integrators can rely on without creating yet another admin naming set.

Rather than re-litigate admin verbs (“force/forced”, “freeze/frozen”, etc.), my proposal is to make ERC‑7943 the universal RWA interface for integrators, i.e. a tiny layer that any RWA token (ERC‑3643, ERC‑1400, 7518/7551 variants, or any permissioned 20/721/1155) can implement on top of their existing contracts.

Two key principles:

  1. Standard‑agnostic: don’t clash with existing admin ABIs; sit beside them.
  2. Predictable UX for wallets/custodians/venues: a small set of read‑only calls that answer “can this work?” and “why/what’s needed?” in a machine‑readable way.

To make that intent explicit and avoid collisions, I suggest namespaced integrator calls prefixed with uRWA*. Concretely:

// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.20;

/// @title IERC7943
/// @notice Minimal, integrator-first, read-only surface for permissioned RWAs.
///         Works across ERC-20/721/1155 and across standards (3643, 1400, 7518/7551, custom).
interface IERC7943 /* is IERC165 */ {
    /// @notice Amount currently transferable by `user` for `tokenId`.
    /// @dev tokenId=0 for ERC-20; amount semantics follow the underlying token.
    function uRWATransferableBalance(address user, uint256 tokenId)
        external
        view
        returns (uint256 amount);

    /// @notice Preflight: is `user` eligible to hold/receive under current policy?
    /// @param data Optional context (attestations, zk proofs, metadata…).
    /// @return ok True if eligible; reasonCode 0=OK, nonzero=standard/vendor code.
    function uRWAUserValidityCheck(address user, bytes calldata data)
        external
        view
        returns (bool ok, uint256 reasonCode);

    /// @notice Preflight: would a transfer be allowed right now?
    /// @dev tokenId=0 for fungible; amount=1 for ERC-721; real values for ERC-1155.
   /// @param data Optional context (attestations, zk proofs, metadata…).
    function uRWATransferValidityCheck(
        address from,
        address to,
        uint256 amount,
        uint256 tokenId,
        bytes calldata data
    ) external view returns (bool ok, uint256 reasonCode);

    /// @notice Detailed, machine-readable explanation of user eligibility.
    /// @dev `schema` identifies how to decode `details` (keccak256 of ABI or URI).
    ///      Implementations MAY publish their schema ABI/URI off-chain.
    function uRWAUserValidityDetails(address user, bytes calldata data)
        external
        view
        returns (bytes32 schema, bytes memory details);

    /// @notice Detailed, machine-readable explanation of transfer eligibility.
    function uRWATransferValidityDetails(
        address from,
        address to,
        uint256 amount,
        uint256 tokenId,
        bytes calldata data
    ) external view returns (bytes32 schema, bytes memory details);
}

Why this shape?

  • Avoids admin bikeshedding. We don’t rename or redefine freeze/force/recall/paused. Those remain the domain of each standard (3643/1400/7518/7551…) and their governance. ERC‑7943 becomes the universal read layer everyone can add without breaking anything.
  • Namespacing = zero collisions. uRWA* makes it obvious to indexers and integrators that these are universal hooks, not issuer‑specific or standard‑specific admin calls.
  • Reason codes + details. Fast UX uses (ok, reasonCode); deeper UX can call the Details methods and decode via schema (e.g., for ERC‑3643 this could enumerate which claim topics/modules pass/fail; for ERC‑1400 it could map partition/allowlist checks). We get consistency and flexibility.
  • Cross‑standard, cross‑token by construction. One call surface for 20/721/1155 with the usual tokenId/amount conventions; one interface that any permissioning model can satisfy.
  • Feature discovery via ERC‑165. Keep IERC7943 as the minimal required interface for all RWAs

On renaming (canTransfer vs isTransferAllowed, force vs forced, etc.): by keeping those in their standards and having uRWA’s read-only methods separate, we sidestep the inconsistency problem entirely. Integrators implement one path; token teams keep their established admin semantics.

On pausability: it naturally folds under uRWATransferValidityCheck (blocked → reason code). If projects want a separate paused flag for UX, they can include it in uRWATransferValidityDetails under their schema.

This keeps ERC‑7943 truly universal and solves the integrator problem you highlighted: one minimal, predictable surface. No fragmentation, no forced admin convergence, and portable across today’s RWA standards.

Hey @ariutokintumi

I think I see your arguments. The standard currently says the following about the forceTransfer:

  • It CAN bypass the freezing validations and update the freezing status accordingly. Only if this happens, it MUST unfreeze the assets first and emit a Frozen event before the underlying base token transfer event reflecting the change. Having the unfrozen amount changed before the actual transfer is critical for tokens that might be susceptible to reentrancy attacks doing external checks on recipients as it is the case for ERC-721 and ERC-1155 tokens.

While it suggests to unfreeze first, it also says that this behaviour is an option and the standard doesn’t mandate that. Force transfers can either be failing if trying to transfer more than unfrozen amount or unfreeze first and transfer next. With the proposed multicall workaround one would be able to do a force transfer and a subsequent freeze to achieve your mentioned functionality, do you think is worth adding it to the considerations of the eip itself ? How would you phrase it if so ?

I’m personally onboard with @tinom9 arguments on this, meaning that a baseline is proposed and other functionalities can be built on top but let’s consider whether the standard description itself can be more clear or if the description is enough already.