From ffcdc3f3f28cea7db022cb3af3bb5b64d3c511e1 Mon Sep 17 00:00:00 2001 From: valentin Date: Mon, 27 Sep 2021 18:16:37 +0200 Subject: [PATCH] add token migration contract Change-Id: I2bc3fb4d99f5a08452bc2defd3597ec7ad300523 --- ethereum/contracts/bridge/utils/Migrator.sol | 68 +++++++++++++++ ethereum/test/tokenmigrator.js | 87 ++++++++++++++++++++ 2 files changed, 155 insertions(+) create mode 100644 ethereum/contracts/bridge/utils/Migrator.sol create mode 100644 ethereum/test/tokenmigrator.js diff --git a/ethereum/contracts/bridge/utils/Migrator.sol b/ethereum/contracts/bridge/utils/Migrator.sol new file mode 100644 index 000000000..17995f3c9 --- /dev/null +++ b/ethereum/contracts/bridge/utils/Migrator.sol @@ -0,0 +1,68 @@ +// contracts/Messages.sol +// SPDX-License-Identifier: Apache 2 + +pragma solidity ^0.8.0; +pragma experimental ABIEncoderV2; + +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; + +contract Migrator is ERC20 { + IERC20 public fromAsset; + IERC20 public toAsset; + uint public fromDecimals; + uint public toDecimals; + + constructor ( + address _fromAsset, + address _toAsset + ) + // LP shares track the underlying toToken amount + ERC20("Token Migration Pool", "Migrator-LP") { + fromAsset = IERC20(_fromAsset); + toAsset = IERC20(_toAsset); + fromDecimals = ERC20(_fromAsset).decimals(); + toDecimals = ERC20(_toAsset).decimals(); + } + + // _amount denominated in toAsset + function add(uint _amount) external { + // deposit toAsset + SafeERC20.safeTransferFrom(toAsset, msg.sender, address(this), _amount); + // mint LP shares + _mint(msg.sender, _amount); + } + + // _amount denominated in LP shares + function remove(uint _amount) external { + // burn LP shares + _burn(msg.sender, _amount); + // send out toAsset + SafeERC20.safeTransfer(toAsset, msg.sender, _amount); + } + + // _amount denominated in LP shares + function claim(uint _amount) external { + // burn LP shares + _burn(msg.sender, _amount); + // send out fromAsset + SafeERC20.safeTransfer(fromAsset, msg.sender, adjustDecimals(toDecimals, fromDecimals, _amount)); + } + + // _amount denominated in fromToken + function migrate(uint _amount) external { + // deposit fromAsset + SafeERC20.safeTransferFrom(fromAsset, msg.sender, address(this), _amount); + // send out toAsset + SafeERC20.safeTransfer(toAsset, msg.sender, adjustDecimals(fromDecimals, toDecimals, _amount)); + } + + function adjustDecimals(uint _fromDecimals, uint _toDecimals, uint _amount) internal pure returns (uint) { + if (_fromDecimals > _toDecimals){ + _amount /= 10 ** (_fromDecimals - _toDecimals); + } else if (_fromDecimals < _toDecimals) { + _amount *= 10 ** (_toDecimals - _fromDecimals); + } + return _amount; + } +} diff --git a/ethereum/test/tokenmigrator.js b/ethereum/test/tokenmigrator.js new file mode 100644 index 000000000..f676706bb --- /dev/null +++ b/ethereum/test/tokenmigrator.js @@ -0,0 +1,87 @@ +const jsonfile = require('jsonfile'); +const BigNumber = require('bignumber.js'); + +const Migrator = artifacts.require("Migrator"); +const TokenImplementation = artifacts.require("TokenImplementation"); + +contract("Migrator", function (accounts) { + var migrator, + fromToken, + toToken, + fromDecimals = 8, + toDecimals = 18; + + it("should deploy with the correct values", async function () { + fromToken = await TokenImplementation.new(); + await fromToken.initialize( + "TestFrom", + "FROM", + fromDecimals, + 0, + accounts[0], + 0, + "0x00" + ) + toToken = await TokenImplementation.new(); + await toToken.initialize( + "TestTo", + "TO", + toDecimals, + 0, + accounts[0], + 0, + "0x00" + ) + + migrator = await Migrator.new( + fromToken.address, + toToken.address, + ); + + assert.equal(await migrator.fromAsset(), fromToken.address) + assert.equal(await migrator.toAsset(), toToken.address) + assert.equal((await migrator.fromDecimals()).toNumber(), fromDecimals) + assert.equal((await migrator.toDecimals()).toNumber(), toDecimals) + }) + + it("should give out LP tokens 1:1 for a toToken deposit", async function () { + await toToken.mint(accounts[0], "1000000000000000000") + await toToken.approve(migrator.address, "1000000000000000000") + await migrator.add("1000000000000000000") + + + assert.equal((await toToken.balanceOf(migrator.address)).toString(), "1000000000000000000") + assert.equal((await migrator.balanceOf(accounts[0])).toString(), "1000000000000000000") + }) + + it("should refund toToken for LP tokens", async function () { + await migrator.remove("500000000000000000") + + assert.equal((await toToken.balanceOf(migrator.address)).toString(), "500000000000000000") + assert.equal((await toToken.balanceOf(accounts[0])).toString(), "500000000000000000") + assert.equal((await migrator.balanceOf(accounts[0])).toString(), "500000000000000000") + }) + + it("should redeem fromToken to toToken adjusting for decimals", async function () { + await fromToken.mint(accounts[1], "50000000") + await fromToken.approve(migrator.address, "50000000", { + from : accounts[1] + }) + await migrator.migrate("50000000", { + from : accounts[1] + }) + + assert.equal((await toToken.balanceOf(accounts[1])).toString(), "500000000000000000") + assert.equal((await fromToken.balanceOf(accounts[1])).toString(), "0") + assert.equal((await fromToken.balanceOf(migrator.address)).toString(), "50000000") + assert.equal((await toToken.balanceOf(migrator.address)).toString(), "0") + }) + + it("fromToken should be claimable for LP tokens, adjusting for decimals", async function () { + await migrator.claim("500000000000000000") + + assert.equal((await fromToken.balanceOf(migrator.address)).toString(), "0") + assert.equal((await fromToken.balanceOf(accounts[0])).toString(), "50000000") + assert.equal((await migrator.balanceOf(accounts[0])).toString(), "0") + }) +}) \ No newline at end of file