I agree with the specificity and probably we should add it as a SHOULD (in case the token doesnāt have specific errors as the ERC20 template by OZ has) not replace the specific errors for insufficient balances (outside the frozen one). However Iām still having some issues with the naming. ānot availableā (āunavailableā) suggests a temporary condition like a frozen amount not allowing you to transfer a determined amount while āinsufficientā suggest more of a general lack of funds. Indeed if you try to transfer more tokens than the unfrozen one, you technically have a āsufficientā balance, but not an āavailableā one to be used. What do you think about this argument ? What about changing to āunavailableā instead ?
I used ERC20InsufficientBalance
to refer to the error as ERC-6093 defines it and OZ implements it, but I meant the underlying error. A phrasing in the ERC-7943 definition could be:
/// @notice Error reverted when a transfer is attempted from `user` with an `amount` less or equal than its balance, but greater than its unfrozen balance.
/// @param user The address holding the tokens.
/// @param tokenId The ID of the token being transferred.
/// @param amount The amount being transferred.
/// @param unfrozenBalance The amount of tokens that are unfrozen and available to transfer.
error ErrorName(address user, uint256 tokenId, uint256 amount, uint256 unfrozenBalance);
In terms of error naming, I believe āinsufficient balanceā and āunavailable amountā are synonyms in this context, although the latter has a more temporal component. I slightly prefer āinsufficient balanceā due to its clarity and ongoing use in ERC-6093, but I see both as valid options.
As I see it, there are 2 ways we can go with this error:
- Define and specify the error to throw when a user tries to transfer more tokens than they have unfrozen. Either
ERC7943InsufficientUnfrozenBalance
or ERC7943FrozenAmount
.
- Define a broad and miscellaneous error to throw when the user tries to transfer more tokens than they have available, either due to being frozen, or locked by custom compliance implementation logic.
ERC7943UnavailableAmount
.
To me, achieving specificity is key, and we should go with the first, keeping additional errors to be defined in extensions, either custom or standardized in other ERCs.
I agree in using isTransferAllowed, we might end up doing the error of forcing a setPaused and have integrators being forced to integrate this to be compliant with this EIP even if they donāt need it.
We might need more context from other industry integrators, but I wouldnāt discard the feature yet. Most security tokens Iāve seen include it.
For forceTransfer
:
I think the standard should still say that the function SHOULD
skip freezing validations ( I believe you proposed a āMUSTā but Iām curious to know why. If the function actually skips that, then I agree with your suggestion of unfreezing first (and emit an event) before doing the transfer, so that the transfer event doesnāt emit an amount of unavailable tokens. However, the integrators might decide to NOT skip freezing validations in a forceTransfer
. I am thinking of use cases where thereās an automated penalty system that forcefully transfer tokens out without skipping the freezing status (that might have a higher priority over the penalty system).
forceTransfer
being a permissioned function, is there a case where you may not want to unfreeze tokens to forcefully transfer them out of a wallet and revert? I donāt visualize it.
One thing that concerns me is that, if we donāt impose that forceTransfer
unfreezes tokens (only if the transfer cannot be fully satisfied with the unfrozen balance), the tokens must be first unfrozen and then forcefully transferred, adding the requirement to execute those calls in batch to avoid being front-run on the forceful transfer by the user.
I see the benefit of imposing the forceTransfer
implementation in that we donāt end up with a method that behaves differently in different tokens with a core functionality such as token freezing. Either we need to extend the ForcedTransfer
event to specify which tokens were frozen and which unfrozen, or we need to impose the implementation (MUST
), if we want integrators to accurately index frozen and baseline balances.
Additional flexibility can be obtained with custom batched actions.
For burn
:
I actually think is dangerous to allow a burn
to unfreeze. The burn
function is meant to be either public (anyone can burn) or restricted. In the former case, burning would actually circumvent the freezing status imposed by an authorized party. This would easily translate into frozen assets being on purpose burnt down to avoid any reusage of those by the freezing authority.
We have a different vision of whoās allowed to call burn
.
I donāt see token holders being able to burn their shares of a company, or other underlying securities. I see token burning always happening from the permissioned side.
If we add permissioned mint
and burn
functions to the standard, Iād go the same way than with the forceTransfer
definition: impose token unfreezing.
If we donāt, I see the benefit of specifying each expected behaviour:
- Burning CAN run validation to prohibit burning more assets than the ones available (difference between balance and frozen amount). For example, in public burning functions.
- Burning CAN bypass freezing validation, in which case it MUST update the freezing status if required, emitting a
Frozen
event before the Transfer
event. For example, in permissioned burning functions.