How to migrate a non-upgradeable ERC20 token to a new version?

Pius asked on Telegram

If someone delpoys a contract (e.g. ERC20 token contract) which is not upgradeable and he has users on it, how can he migrate the data to a new comtract?

Please ideas and links to resource materiails will be welcomed

The options appear to be:

  1. Create a new token and mint amounts for token holders based on their balance at a pre-agreed block.

    This option would work best where a token has a small number of holders and is not being used or exchanged.

  2. Create a new token and an exchange contract where token holders can exchange the original token for new ones at a specified rate. This could be 1:1 or some other rate. The old tokens could be burnt, transferred to an address without a known private key or locked in a contract.

2 Likes

Hi, @abcoathup
Regarding option1, Could you please tell me some examples and corresponding code?
BTW, are there any other methods to achieve the goal? such as attaching a label for a token which is used for migration and can’t be used for migrate again even if the marked original token was transferred to other accounts.

1 Like

Hi @uua,

I don’t have an example of option 1, this would be where there were only a small number of holders and you could look at community consensus to migrate to a new token.

You may want to look at how some projects did token swaps after recent hacks:

1 Like

Hi, @abcoathup
I’ve found the other way to figure it out, but thanks for replying.

1 Like

Hi @uua,

Would be interested to hear how you plan to do it.

Sure, it’s my pleasure.

First of all, there are some requests for exchange.

  • no need to burn the old token
  • The old tokens are staked in the contract and users can withdraw their old token anytime
  • the new tokens are deposited in the contract in advance not mining when users exchange their old token.

So, I’ve just done a little modification of your option 2.

  • the holders of the old token can exchange the new token in 1:1.
  • withdraw old token any time by the safe transfer function.

The main code is below:

function exchange(uint256 amount) external nonReentrant  {
    require(amount > 0, "Cannot deposit 0 to exchange");
    require(amount < from.balanceOf(msg.sender), "not enough token");
    _totalSupply = _totalSupply.add(amount);
    _balances[msg.sender] = _balances[msg.sender].add(amount);
    from.safeTransferFrom(msg.sender, address(this), amount);
    to.safeTransfer(msg.sender, amount*10**18);
}

Would you mind giving some suggestions on security?

2 Likes

Hi @uua,

I am a Community Manager and not an auditor :smile:

I recommend appropriately testing and appropriately auditing any solution.

If you are doing a 1:1 exchange and not minting, I wasn't sure why you were tracking _totalSupply or _balances, nor why the from token had an apparent decimals of zero but the to token had decimals of 18.

1 Like

Tracking _totalSupply or _balances is for showing information publicly. Such as exchanged token individually or exchanged token in original totalsupply. Regarding the decimals, why it is different is because the original token design is different from the new one. So If I want to transfer token by one input amount, I need to add 10**18.

1 Like

Hi https://forum.openzeppelin.com/t/how-to-verify-a-contract-deployed-by-a-factory-contract/4131 about contract verify,That problem has been closed .Can you answer me in the email? How did you solve it ? kwinin@163.com , please