ERC-7943: Universal RWA Interface (uRWA)

Previous value in Frozen event

Additionally, to better reflect your concern of indexing the correct unfrozen / frozen amounts during forceTransfers and burn, I’ve added a previousValue and newValue in the Frozen event, so that differences can be tracked. Now if a force transfer or a burn changes the freezing status, the Frozen event will emit both old and new value, so that changes in freezing status can be tracked.

I’m not sure emitting the previous value is necessary. If indexers want to keep track of the difference, they can check against their currently indexed value.

ERC20 tokens already behave by not emitting previous values in allowance nor balance changes. I’d argue the ERC-7943 should behave likewise.


Pausability

What makes me to hesitate about it [pausability] is:

  • Do all use cases need this ? If we can name a few that don’t neccessarily require it, I wouldn’t go with it.

I’d say the majority of permissioned tokens, of which securities and RWAs should be a subset of, use it.

Doing a minor research about it, I can find:

  • OZ already has an historical and battle tested, widely adopted, solution for pausable contracts and it’s easy to think about this as one of the multiple potential extensions of an ERC-7943 minimal token.

I agree that there’s no need to standardize it for ease-of-use of the feature, as OZ already has the de-facto standard for pausability.

I’m more concerned about having a clear criteria for what features are included in the baseline ERC. One can argue that token freezing is not necessarily required within the ERC, as it can be enforced through forceTransfer and vaults, but that it’s best to standardize it, and that pausability can be enforced through isTransferAllowed and extensions, but that it’s also best to standardize it.

I’d aim for what we envision will be the requirements for 90% of the tokens into the baseline ERC, and leave the rest to extensions.

That said, I don’t have enough insights on pausability requirements to take a clear stand on its inclusion. To me, although most tokens have it, it feels operative and not a core token functionality, similarly to upgradeability.


Unfreezing in forced transfers

Yes, cases in which the freezing authority has more power (higher priority) in the entity performing a forced transfer. Those might be two separate entities. You can give forceTransfer rights to one or more entities, and setFrozen rights to completely different entities. An example might be perpetual contracts where a continous funding mechanism might leverage the forceTransfer functionality. In this sense, the forceTransfer would be functional to a perpetual contract funding mechanism but might not have the freedom to move more than frozen assets, since those might be seized by goverments or higher level authorities than a smart contract logic for perpetual contracts. What do you thinkg about this ?

I’d argue forced transfers are not the correct action for that use-case. I believe it has the same implications than using native ERC20 approvals, as you still depend on the user not front-running you to collect the fees, and using an all-powerful permissioned function to execute core business logic doesn’t seem right.

Still, the EIP suggests both things now, a forceTransfer and a burning CAN skips the freezing validations, but they also CAN NOT skip those. The most important thing is that if those automatically unfreeze, the must emit a frozen event to reflect that, before the native transfer event.

I agree it’s most important that unfreezing emits the Frozen event before the native Transfer one, and that it should fix indexing concerns, if ForcedTransfer and Transfer can only occur with unfrozen balances.

I believe the decision is a trade-off between:

  1. Forcing unfreezing in forceTransfer and ensuring standard implementation.
  2. Allowing forceTransfer to revert on frozen balances and, therefore, a freezer role to be of higher status than a force transferer role.

I have a preference for option 1, as I don’t see any use-case for option 2 and consider forceTransfer a “last resort” permissioning function.

If we can come up with option 2 use-cases, it would make total sense to keep it unopinionated.

setFrozen probably doesn’t need to be part of the minimal interface. In a similar way as mint/burn, setFrozen is likely only ever going to be called by the contract’s owner/operator. The owner/operator obviously knows how to interact with their own contract, and so these functions don’t need to be part of the standard interface.

Thanks @tinom9 for cominb back. About this:

I’m not sure emitting the previous value is necessary. If indexers want to keep track of the difference, they can check against their currently indexed value. ERC20 tokens already behave by not emitting previous values in allowance nor balance changes. I’d argue the ERC-7943 should behave likewise.

The intention was to solve this point from our previous conversation:

While I agree that your suggestion of using ForcedTransfer to specify which tokens were frozen and which unfrozen, I think that the real place to reflect this is in the Frozen event and not in the ForcedTransfer (merely because of context, if we are specifying freezing/unfreezing amounts then Frozen event seems more accurate to me than ForcedTransfer.

Additionally, emitting previous and new value not only allows for a correct indexing, but can also give flexibility to offchain services to decide what to index (historical values, just the deltas, both, etc…) I agree that it’s not a common pattern for base tokens to do this in events but it’s also true that this token is uncommon and for this it needs a specific interface. Moreover, it is indeed somehow a standard practice when a value is overwritten with a new one.

Finally, doing this modification to the Frozen event would allow to avoid using MUST when talking about enforcing or not the freezing validations during a burn / forceTransfer, gaining in potential more use cases supported.

Connecting “more use cases supported” to our previous point:

If we can come up with option 2 use-cases, it would make total sense to keep it unopinionated.

and

I’d argue forced transfers are not the correct action for that use-case. I believe it has the same implications than using native ERC20 approvals, as you still depend on the user not front-running you to collect the fees, and using an all-powerful permissioned function to execute core business logic doesn’t seem right.

I believe what you say is particularly true if token holders are EOAs, but there’s an endless number of dynamics that can be unlocked when thinking about holders as smart contracts being part of one or more protocols. A part from the one I mentioned I can think of other ways of leveraging a forceTransfer being an operator and not an EOA owner:

  • escrowed payments between parties to settle payments (you don’t need to actually transfer tokens within the escrow contract when using a forceTransfer)
  • forced liquidations on lending protocols using external vaults as collateral
  • slashing on re-staking protocols

I feel like imposing a skip in freezing validation might automatically unable those dynamics and the one I didn’t thought about just yet. The main important thing that the standard says about those functions is that they should be restricted in access since they are powerful in their logic, but who can have access to them is not necessarily (even if likely in many cases) a last resort, high priviliged EOA actor or multisig.


Pausing

I’m more concerned about having a clear criteria for what features are included in the baseline ERC.

Totally agree and I believe this is key.

One can argue that token freezing is not necessarily required within the ERC, as it can be enforced through forceTransfer and vaults, but that it’s best to standardize it

It was my original thought indeed but there’s a fundamental difference between using forceTransfer + vaults compared to using setFrozen which is that assets never leave holder wallet with freezing. From a regulatory standpoint, my personal opinion is that freezing is a primitive which is different from a forced transfer as it is inherently different in terms of balance accounting and assets movements. I’ll try to summarize here the criteria I’ve used so far to include each function in the standard:

Generally speaking, I am confident to say that getters are the main reason to build an ERC and that trying to standardize setters is generally a thing only if you want to standardize also integrations/tooling/offchain services. So leaving aside the isUserAllowed / isTransferAllowed and getFrozen my reasoning for the setters are:

  • setFrozen: It makes sense to me to include it here because we are also standardizing getFrozen and including it in the context within the same ERC for completeness is sound to me. This should answer also your comment @SamWilsn , wdyt ?
  • forceTransfer: It makes sense to me to standardize it mainly because it includes ERC-20/ERC-721 and ERC-1155 at the same time and it’s a widespread feature with tons of different names and variations. I believe there’s a big benefit in standardizing it. Additionally, it can enable many potential and automated use cases that will benefir from a standardized function for it.

About the pausability, I don’t see any clear reason to include a setter for it since it’s already standardized elsewhere and this would leave us with potentially adding only the getter isPaused or paused which I believe overlaps a bit with the isTransferAllowed.

The same reasoning is what still convinces me to not include mint/burn since:

  • Trying to standardize mint/burn for ERC-20 / ERC-721 and ERC-1155 feels a too wide scope for this ERC. If one wants to do that, it should be outside of an ERC for “regulated” assets.
  • Still not convinced that all use cases need it so that it makes sense to enforce the presence of those functions. A token which is just a fixed pre-minted supply and it’s not meant to be burn doesn’t need them.

What do you think about above criteria @tinom9 / @SamWilsn ?