OpenZeppelin/openzeppelin-contracts

ERC20 Extension: "ERC20Deferred"

alexbabits opened this issue · 2 comments

Description

Extension of ERC20.sol with an opt-in deferred mechanism for all received tokens. If a user chooses to set their manual claim status to ON, then all incoming tokens from transfers AND mints are re-directed to the _pendingBalance mapping. An accurate description of this mapping is the user's "inert balance until revived". Until the user calls either claimAll() or claimSpecificAmount(), the token's remain dead.

The default for all users manual claim status is 0 (UNINITIALIZED), which means users that do not enable their manual claim status receive tokens just like normal.

Code

Details

Design Note

  • Minting that happens within the context of the claimAll() and claimSpecificAmount() are considered the "real" _mint(). This is flagged as the sole special case where a user can revive their dead tokens through a "real" _mint() call. All other minting from ANY other source works as expected (if manual claim is not enabled, mints are normal. If manual claim is enabled, mints are forwarded to pending balance.)

Benefits

  • Tokens found in the _pendingBalances mapping are inert and cannot be transferred or burned. Only upon calling a claim function are they revived from the dead. This may be advantageous for users in certain tax jurisdictions around the world, which might serve as a potential tax deferred instrument similar to traditional finance funds.
  • Some extra surface level privacy that makes a users overall status more opaque to frontends that display balances. Because the _pendingBalances mapping is separate from the _balances mapping, many displays will not understand that a user has dead tokens that might one day be revived if the user is able to call a claim function. However, anyone who checks the blockchain and token contract in detail will be able to view the users "living" and "dead" balance.
  • Stable coins & meme coins can brag about this feature. This extension could attract the attention of mainstream users.

Drawbacks

  • The total supply is temporarily decreased during any transfer to recipients with manual claim enabled. The total supply is increased back to par once the recipient claims the dead tokens. This may cause conflicts with other extensions like ERC20Capped.sol.
  • This extension is probably not adaptable or applicable with Rebase/FoT tokens.
  • Total Supply Griefing: Users that hold pending tokens can wait to "revive" them at any moment, which will change the total supply. This attack vector may be conquerable if a design change is made, where rather than burning tokens during a transfer, tokens are sent to an inert intermediary single global vault that holds all pending tokens, so the total supply stays constant. When a user wants to claim pending tokens, the vault burns the tokens and mints tokens to the user.
  • Donation Griefing: Someone could frontrun a claimAll() call and transfer some tokens to the user who is reviving all their dead tokens. This causes the user to revive more dead tokens than expected. Results in total loss for griefer but should be noted.

Options & Expansions

  • A timelock mechanism could be implemented ERC20DeferredTimelock.sol where a user can choose a minimum locked time once they receive tokens to their pending balances, where they cannot claim the pending tokens to their real balance until the timelock has expired. This is advantageous because the timelock would be inherent to the token itself rather than any outside protocol.
  • It would be possible to change this contract so only transfers are deferred (and not mints). The default is set to deferring all transfers and mints to the users pending balance if the user has opted in to the manual claim status.
  • It may be possible for this idea to be executed on ERC721 contracts, or any token-like smart contract.

Thought Experiment

Someone wants to give you $1,000,000 but you refuse to claim it. The person insists that this donation is something they must do. You once again remind the person that you are NOT currently the type of person to accept ANY donation of {insert fiat name} from ANYONE. In this universe, the person cannot force you to hold the cash in your physical hands, or personal savings account for that matter. Despite your refusal of the donation, the person does the next best thing they can imagine, and forcefully donates $1,000,000 in cash to your local banks vault on behalf of your name. The bank sends you a notice saying, "If you call our number, this cash will be instantly added to your personal savings account."

You go visit your friends working at a fine dining establishment and attempt to purchase a sandwich composed of three buns, 2 patties, and a delicious secret sauce. Because your personal savings account has $1.32 your card gets declined and your friends laugh at you to mask the stench of their own poverty. Walking out of the establishment with an empty stomach, you briefly consider giving the bank a call, but remain steadfast in your morals to pave your own path in life without any donations or help.

Time goes by and you forget about the donation. You lost the banks number and thus ability to call the bank and claim those funds. Even if you changed your mind and heart, you would have no way of retrieving the money. Unless someone reminded you about the donation fiasco and your consciousness and senses were coherent enough to understand the situation, the money remains indefinitely inert inside the vault.

Amxx commented

Hello @alexbabits and that you for raising that issue.

I feel like this idea would require a community discussion and a standard interface. This should be achieved through the ERC process.

There are likelly multiple implementation of this, both with their own spcificities. A consequence of this "fragmentation" is that wallets don't support these workflow nativelly. Additionnally, conflicts with the ERC20 standard are not resolves (how should the transfer event be handled so that movement are correctly reported?)

We don't want to be one more non-standard implementation of this workflow. We strongly encourage you to review existing solution, discuss that with project that would use this pattern and with wallet providers that would have to interract with it, and submit an ERC. If/when community support crystallizes on a standard interface, than we would implement it.

I appreciate you taking the time to look at this and give feedback and advice!! @Amxx

The deferred idea natively built into tokens might be a desired feature, but it does seem to span too far beyond a smooth ERC20 extension right now for the reasons you mentioned. If wallets and communities are excited about it then an ERC process could be imagined over time, rather than for example trying to immediately force it into ERC20 standard which might be impossible/too clunky/not a good fit.

A consequence of this "fragmentation" is that wallets don't support these workflow natively.

There are zero wallets that currently support displaying a pending balance or custom events for ERC20... That's a big user friendly obstacle. (Although project front ends would probably be happy to, I'm guessing wallets like MM would never implement displaying pending balances unless it became very mainstream).

conflicts with the ERC20 standard are not resolved (how should the transfer event be handled so that movement are correctly reported?)

If it were to adhere to ERC20 all _update() actions should still emit the transfer event in all cases of token movement, but within this idea that would be misleading and inappropriate because some actions are not traditional transfers, so this is a problem as well for adhering to ERC20 standard.

There are likely multiple implementation of this

Right... Pending balances could be updated for just non-mint transfers, or just mints, or transfers and mints... Along with many other things. So those options would all be on the table. And other oddities of how it might interact with other common customized ERC20s/protocols/things/etc.