ERC-7144 An ERC-20 token that goes through a validation step (ERC-20 with transaction validation step.)
Review · Tokens · created 2023-05-07
An extension that holds ERC-20 transfer and approve calls in a pending validation state instead of executing them immediately. Because actual execution requires a separate validation step, tokens do not leave a wallet immediately even if its keys are stolen.
Why it is needed
On Ethereum, whoever holds the private key is the owner of the asset. The power of blockchain comes from placing full responsibility for data in the user's hands, but that is also a weakness. If a key leaks, a thief can drain all the tokens in a wallet with a single transfer or approve call, and there are in fact many cases of stolen tokens. Existing countermeasures, such as moving tokens to a cold wallet disconnected from the internet, make tokens inconvenient to use. By placing a validation step before every transfer and approve, a thief would need to obtain both the user's address and the validator's address at the same time, making for a much safer system. This standard defines a common foundation for building that kind of anti-theft system.
What to implement
event ValidateTransfer(address indexed from, address indexed to, uint256 amount, uint256 indexed transferValidationId);: An event emitted when a new transfer validation request is created. It contains the sender address, recipient address, amount, and the validation ID.event ValidateApproval(address indexed owner, address indexed spender, uint256 amount, uint256 indexed approvalValidationId);: An event emitted when a new approval validation request is created. It contains the owner, the spender address, the amount, and the validation ID.function isValidatorContract() external view returns (bool);: Returns true if this contract is an ERC-20 with validation functionality.function transferValidation(uint256 transferId) external view returns (TransferValidation memory);: Returns the contents of the transfer validation request for the given transfer ID.function approvalValidation(uint256 approvalId) external view returns (ApprovalValidation memory);: Returns the contents of the approval validation request for the given approval ID.function totalTransferValidations() external view returns (uint256);: Returns the total number of transfer validation requests created so far.function totalApprovalValidations() external view returns (uint256);: Returns the total number of approval validation requests created so far.
In depth
Transfers and approvals are not executed immediately
Every action that changes token ownership, i.e. transfer or transferFrom, must create a pending TransferValidation and emit a ValidateTransfer event instead of moving the token, and SHALL NOT move the token. Likewise, approve, which grants token management authority, must create an ApprovalValidation and emit a ValidateApproval event, and must not turn on the approval immediately. A TransferValidation struct contains the sender address from, the recipient address to, the amount amount, and the validation flag valid, while an ApprovalValidation contains owner, spender, amount, and valid.
Transfers by an approved account are an exception
If transfer is called not by the owner but by an account that already has approval, it MUST execute immediately without validation. This rule exists to accommodate existing projects that assume approve is used to move tokens directly. However, since the approval itself must go through the validation step before it is turned on, this path cannot be used with an approval the user has not validated. In the reference implementation, inside _transfer, if the caller (_msgSender()) is the same as from, a pending validation request is created; otherwise the parent contract's _transfer is executed as-is, distinguishing the two cases.
Rules for the validation action
When validating a TransferValidation or ApprovalValidation, the valid field MUST be set to true, and the same request MUST NOT be validated again. The action that validates a TransferValidation actually changes token ownership, and the action that validates an ApprovalValidation turns on the approval. In the reference implementation, _validateTransfer and _validateApproval revert if the request is already valid, and otherwise execute the parent contract's _transfer or _approve and then record valid as true. Both functions are defined as internal, leaving it up to the implementer to decide who may call them and under what authority.
Interface and visibility requirements
The standard interface IERC7144 consists of functions for querying pending validation requests. transferValidation(transferId) and approvalValidation(approvalId) return the contents of each validation request by ID, while totalTransferValidations() and totalApprovalValidations() return the total number of validation requests created so far. isValidatorContract() is a function that signals that this contract is a validating ERC-20, and MUST be implemented as public. The two lookup functions may be implemented as public or external, and the two count functions may be implemented as pure or view.
The standard does not dictate the validation method
The standard defines only the validation functionality itself and does not dictate who validates or how. Validation is defined as internal, leaving the management method up to the user. One example the original text gives is having a validator address linked to a dApp so the user manages validation, and this validator could be used for all tokens or only for some users. It is also possible to build a wrapper contract around an existing ERC-20 that exchanges it one-to-one with the original token. Third-party protocols are free to define how they want to call these functions.
Compatibility with existing ERC-20
This standard is an extension of ERC-20 and is compatible with every action except transfer, transferFrom, and approve. Only these three actions are overridden to create a validation request instead of moving tokens or turning on an approval. In other words, the existing ERC-20 interface is kept intact while only the behavior of transfers and approvals changes into a two-step process. Contracts that follow ERC-20 MAY optionally implement this standard.
A proposal still under review
This proposal was created in May 2023 and is currently in Review status. Since it is still under review, its content may change or progress may stall, so the latest original text should be checked before actual implementation. It requires ERC-20 as a prerequisite (requires: 20), and the core rules of the spec, such as creating pending validation requests, immediate transfers by approved accounts, and the prohibition on re-validation, are defined using RFC 2119 terms such as MUST and SHALL.
Things to watch out for
- The validation action must be protected as strongly as possible: Since transfer and approve only create pending validation requests while the validation action handles actual execution, the action that validates a TransferValidation or ApprovalValidation must be protected with the maximum level of security required by the system that applies it.
- Unauthorized validation must be impossible: No address should be able to execute validation without authorization from the chosen system. This is the core of the security model. The original text cites as correct examples a system where a single validator address validates transactions, or a system where each user chooses their own validator address.