Currently, there has been some discussion about reversible transactions in the crypto space due to the many exploits and hacks. The immutability of blockchain transactions is a double-edged sword. On one hand, no single individual or party can tamper with transactions to act dishonestly. On the other hand, hackers can exploit various protocols and steal crypto without the worry of having their crypto taken back. There has been billions of dollars in crypto stolen over the years, and many people are trying to create solutions to mitigate these exploits. The community has been discussing and developing their own security measures to mitigate such hacks, but one idea caught my attention: reversible transactions. This article shed some light on a new type of token standard called "ERC-20R" with the ability of reversible transactions. This concept was very intriguing to me and made me research more about how reversible transactions can be implemented. Additionally, a 2018 tweet from Vitalik Buterin also mentioned reversible transactions with the idea of "Reversible Ether". The idea of "Reversible Ether" has been experimented with in this article/tutorial which is where I got my inspiration to try and create a wrapped ERC-20 token (not just Ether) with the ability of reversible transactions. However, before talking about how a reversible wrapped ERC-20 token works, I must give some background on the design of this implementation as compared with other designs.
When coming up with the design and logic of this smart contract, I initially thought of how reversible transactions work in traditional finance. When it comes to traditional banks and credit cards, transactions are not immediately finalized at the time of the occurrence. There is a pending transaction duration in which the bank takes N days to finalize that certain transaction. This is due to the transaction not fully being processed by the merchant (merchant needs to take the funds from your account).
This way of categorizing transactions into two categories, pending and finalized, made me think of how this can be implemented into an ERC-20 token without creating a brand new token standard. Although creating a new token standard would allow for more customized functionality, the downside would be that the vast amount of protocols would need to implement the new standard to deal with these tokens which can lead to complexity problems. I believe that protocols need to have a way to implement tokens in an easy way as they already have various problems to solve. Hence, another way to look at this problem would be to create a wrapped ERC-20 token that is backed 1:1 with the actual token but with reversible transaction capability as mentioned in Vitalik's tweet. In this way, protocols won't need to implement a completely new token standard (maybe some front-end changes).
Starting from the beginning, the constructor
takes two parameters: an ERC-20 token address and the pending duration for a transfer. The contract also has an arbitrator
which can be a DAO that has the ability to reverse transactions if they deem it dishonest. This allows for no single individual to have to power to reverse transactions and instead leave the power in the hands of a trusted group of individuals.
In addition to the standard ERC-20 token data, this contract also has data for pending transactions. First, there is a mapping called pendingBalanceOf
. This is essentially equivalent to the standard ERC-20 balanceOf
mapping but just tracks a user's pending token balance that has not been finalized (balanceOf
would track a user's token balance that has been finalized). Additionally, the contract contains a custom data structure for a pending transfer, a global array that contains all pending transfers, and another mapping to keep track of each users' pending transfers.
This contract is ERC-20 compatible so it does have all the standard methods such as approve
, allowance
, and transfer
. However, the logic in the transferFrom
method is different from the original. Instead of increasing the address _to
token balance (balanceOf
) by _value
, their pending token balance (pendingBalanceOf
) is increased. This is similar to how traditional credit cards work in the real world as a charge is considered pending rather than immediately finalized. Additionally, a user's pending transfer gets added into the global state array of all pending transfers and tracked in a mapping that stores the index location of that pending transfer in an array that is specific for each user.
Furthermore, this contract also contains methods specific to reversible transactions. First, deposit
and withdraw
are the main methods to mint and reclaim the reversible wrapped ERC-20 tokens in exchange for the original ERC-20 tokens since they are backed 1:1. To finalize a transaction, a user (most likely the recipient) would need to call the finalize
method which takes one parameter: the index of the pending transaction in the global state array of all pending transactions. If the transaction pending duration has passed, then the transaction can be considered to be finalized and the recipient's balanceOf
increases by the pending amount while pendingBalanceOf
decreases by the pending amount. Then, the global state array of pending transfers and the mapping of the user's pending transfers are cleaned up. The reverse
method is similar to the finalize
method except that only the arbitrator can call it. Also, the recipient's pendingBalanceOf
is decreased by the pending amount while the initiator's balanceOf
is increased by the pending amount to refund the initiator.
This is a proof of concept for a reversible wrapped ERC-20 token. One thing to note is that pending transactions can only be finalized one transaction at a time which is not efficient for users who receive multiple incoming token transfers (can edit function to handle multiple transactions). Another thing to note would be that front-ends (external wallets and etc.) would need to keep track of a user's pending balance in addition to their normal token balance so they can see the correct amount of tokens they can possibly have. I chose to go with a wrapped version of an ERC-20 token rather than a completely new ERC-20 token standard due to fewer complexities and protocols not having to implement a new token standard to interact with.