ERC-7943: Universal RWA Interface (uRWA)

setFrozenTokens delta updates

Given that token freezing will be a permissioned function, I don’t think there’s a big concern about race conditions:

  • User has 500 frozen tokens.
  • Admin A wants to freeze 100 extra tokens, so calls an update to 600.
  • Simultaneously, admin B wants to unfreeze 100 tokens, so calls an update to 400.

We can fix that very edge case by having delta updates, but then we lose idempotency.

I believe idempotency, simplicity, and ERC20-like behaviour (approve vs increaseAllowance/decreaseAllowance) are more important.

I also don’t think that the front-running issue can be solved with it.

  • User A has 100 tokens.
  • 7943 admin decides to freeze all 100 tokens of user A and sends a setFrozenTokens transaction to freeze all of 100.
  • User A front-runs that by moving 1 token out to another wallet. Now user A has 99 tokens.
  • The setFrozenTokens transaction goes through and it reverts because of the assumption that the token doesn’t allow freezing more assets than what the user holds.

If the contract reverts trying to freeze a higher token amount than the user holds, it doesn’t make a difference whether updates are absolute or delta based.

A similar problem cannot be solved either: the user front-runs the admin by sending their full balance to another available address or doing a swap. This second issue has higher specificity though, as it’s intrinsically more difficult to execute if whitelists are in place.


Freezing more tokens than available

I believe we want to update the spec for getFrozenTokens if we allow setFrozenTokens to freeze an amount higher than the user’s balance, as we currently do.

Otherwise, one could assume that if a user has an X frozen tokens balance, they must also hold an underlying X tokens balance.

    /// @notice Checks the frozen status/amount.
    /// @param user The address of the user.
    /// @dev It could return an amount higher than the user's balance.
    /// @return amount The amount of tokens currently frozen for `user`.
    function getFrozenTokens(address user) external view returns (uint256 amount);

    /// @notice Checks the frozen status of a specific `tokenId`.
    /// @dev It could return true even if user does not hold the token.
    /// @param user The address of the user.
    /// @param tokenId The ID of the token.
    /// @return frozenStatus Whether `tokenId` is currently frozen for `user`.
    function getFrozenTokens(address user, uint256 tokenId) external view returns (bool frozenStatus);

    /// @notice Checks the frozen status/amount of a specific `tokenId`.
    /// @dev It could return an amount higher than the user's balance.
    /// @param user The address of the user.
    /// @param tokenId The ID of the token.
    /// @return amount The amount of `tokenId` tokens currently frozen for `user`.
    function getFrozenTokens(address user, uint256 tokenId) external view returns (uint256 amount);
  • getFrozenTokens CAN return an amount higher than the user’s balance due to setFrozenTokens allowing to freeze tokens exceeding user holdings. In ERC721 tokens, it CAN return true even if the user does not hold the token.

setFrozenTokens return type

I don’t think there’s any strong functional reason to return true on success since we’re reverting on failure.

Nonetheless, I’m all for consistency.


isUserAllowed rename to isVerified

I’m ok with it. I don’t really have a strong opinion here.