ERC20Migrator.sol 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. pragma solidity ^0.5.7;
  2. import "../token/ERC20/IERC20.sol";
  3. import "../token/ERC20/ERC20Mintable.sol";
  4. import "../token/ERC20/SafeERC20.sol";
  5. import "../math/Math.sol";
  6. /**
  7. * @title ERC20Migrator
  8. * @dev This contract can be used to migrate an ERC20 token from one
  9. * contract to another, where each token holder has to opt-in to the migration.
  10. * To opt-in, users must approve for this contract the number of tokens they
  11. * want to migrate. Once the allowance is set up, anyone can trigger the
  12. * migration to the new token contract. In this way, token holders "turn in"
  13. * their old balance and will be minted an equal amount in the new token.
  14. * The new token contract must be mintable. For the precise interface refer to
  15. * OpenZeppelin's ERC20Mintable, but the only functions that are needed are
  16. * `isMinter(address)` and `mint(address, amount)`. The migrator will check
  17. * that it is a minter for the token.
  18. * The balance from the legacy token will be transferred to the migrator, as it
  19. * is migrated, and remain there forever.
  20. * Although this contract can be used in many different scenarios, the main
  21. * motivation was to provide a way to migrate ERC20 tokens into an upgradeable
  22. * version of it using ZeppelinOS. To read more about how this can be done
  23. * using this implementation, please follow the official documentation site of
  24. * ZeppelinOS: https://docs.zeppelinos.org/docs/erc20_onboarding.html
  25. * Example of usage:
  26. * ```
  27. * const migrator = await ERC20Migrator.new(legacyToken.address);
  28. * await newToken.addMinter(migrator.address);
  29. * await migrator.beginMigration(newToken.address);
  30. * ```
  31. */
  32. contract ERC20Migrator {
  33. using SafeERC20 for IERC20;
  34. /// Address of the old token contract
  35. IERC20 private _legacyToken;
  36. /// Address of the new token contract
  37. ERC20Mintable private _newToken;
  38. /**
  39. * @param legacyToken address of the old token contract
  40. */
  41. constructor (IERC20 legacyToken) public {
  42. require(address(legacyToken) != address(0));
  43. _legacyToken = legacyToken;
  44. }
  45. /**
  46. * @dev Returns the legacy token that is being migrated.
  47. */
  48. function legacyToken() public view returns (IERC20) {
  49. return _legacyToken;
  50. }
  51. /**
  52. * @dev Returns the new token to which we are migrating.
  53. */
  54. function newToken() public view returns (IERC20) {
  55. return _newToken;
  56. }
  57. /**
  58. * @dev Begins the migration by setting which is the new token that will be
  59. * minted. This contract must be a minter for the new token.
  60. * @param newToken_ the token that will be minted
  61. */
  62. function beginMigration(ERC20Mintable newToken_) public {
  63. require(address(_newToken) == address(0));
  64. require(address(newToken_) != address(0));
  65. require(newToken_.isMinter(address(this)));
  66. _newToken = newToken_;
  67. }
  68. /**
  69. * @dev Transfers part of an account's balance in the old token to this
  70. * contract, and mints the same amount of new tokens for that account.
  71. * @param account whose tokens will be migrated
  72. * @param amount amount of tokens to be migrated
  73. */
  74. function migrate(address account, uint256 amount) public {
  75. require(address(_newToken) != address(0));
  76. _legacyToken.safeTransferFrom(account, address(this), amount);
  77. _newToken.mint(account, amount);
  78. }
  79. /**
  80. * @dev Transfers all of an account's allowed balance in the old token to
  81. * this contract, and mints the same amount of new tokens for that account.
  82. * @param account whose tokens will be migrated
  83. */
  84. function migrateAll(address account) public {
  85. uint256 balance = _legacyToken.balanceOf(account);
  86. uint256 allowance = _legacyToken.allowance(account, address(this));
  87. uint256 amount = Math.min(balance, allowance);
  88. migrate(account, amount);
  89. }
  90. }