Yesterday during the Ledger&Tokenization Working Group we discussed ERC-20 the
approve/transferFrom flow and its issues. Only 16 people participated in the meeting so I thought it could be useful to have a conversation about the topic here on the forum.
For context, on Friday we asked people whether ERC-20 like
approve/transferFrom should be added to the ICRC-1 Token Standard and half of the people voted against it. If we had to make a decision right now we would not include
approve/transferFrom in the standard. I actually would like to give it a second go to be sure this is what we want and that’s why I’m writing this post.
From what I understand, the main reasons for not wanting ERC-20 like
approve/transferFrom are security concerns and issues with cycle draining.
approve/transferFrom have been highly criticized for their issues. There is a famous attack vector using them. Another issue is with phishing. Many phishing attacks on Ethereum involve tricking users into signing approvals. Phishing attacks exist everywhere but
approve can make them trivial to do. On one hand, users get used to
approve third parties without fully understanding the implications and lower their defenses regarding this approach. This is exacerbated by the fact that there is no immediate effect in approving somebody. On the other hand, third-parties often abuse
approve and the trust of their users by getting approval for an unlimited amount of tokens. The combination of these two aspects together with simple phishing techniques is highly damaging.
Note that the ERC-20 standard implicitly relies on wallet UIs to help users understand what they are signing. To date wallets are poor at fulfilling this task.
I would like to know what you guys think about those security concerns.
We don’t have to have exactly the same
approve/transferFrom ERC-20 provides. We could improve over Ethereum’s standard now that we know it has issues. For instance, a way to avoid the attack vector on ERC-20’s
approve/transferFrom is to change the way
approve/transferFrom works to mimic how cheques work in real life. In essence,
approve/transferFrom would be a 2 steps transfer. To better reflect this, let’s rename
approveTransfer would be equivalent to signing a check with an amount of tokens. The cheque can be destroyed by the issuer or can be cashed by the beneficiary via
approveTransfers would sign multiple cheques meaning that the attack vector would not exist anymore. The interface would look like this
approveTransfer( from_subaccount: opt Subaccount, to_principal: Principal, to_subaccount: opt Subaccount, amount: Tokens, ) -> ApprovalId; commitTransfer( from_principal: Principal, from_subaccount: opt Subaccount, to_principal: Principal, to_subaccount: opt Subaccount, approvalId: ApprovalId, ) -> CommitTransferResult; revokeApproval(approvalId: ApprovalId) -> (); allowance(approvalId: ApprovalId) -> Tokens;
Now this solves only partially the issue with ERC-20’s
approve/transferFrom but it’s a significant improvement over the original standard in terms of security.
Other solutions could include having a max allowance and having expiration for the allowance and forcing the user to renew the allowance. Now the problem with these two solutions is what is the right max amount and expiration time. This can be quite tricky to decide but better than allowing unlimited values or infinite time.
Another issue with
approve/transferFrom that was raised is the cycle draining issue for service canisters. Approvals don’t really give any guarantee to a service that
transferFrom will succeed. A user can approve a service and then notify the service about the approval. The service will then use some cycles to attempt a
transferFrom. The user can control how many tokens it has in its own account so it can make
transferFrom fail. At that point the service can only retry, ask the user to retry or black list the user. None of them really take care of the problem. Retries means using more cycles by doing an operation that only the user knows if it will fail or not. Blacklisting users work but only if you require users to register to your service first.
Another solution is that approvals can be done against a “service invoice” that will be issued by the service only after a creation fee has been paid by the user.
I hope this gives food for thought to everybody involved with the discussion. Looking forward to the answers and the next working group next Tuesday!
- 14 Jul 22 11:01 add