From a529d9def9ce86d9b501541c88e2c7535488b655 Mon Sep 17 00:00:00 2001 From: JG Carvalho Date: Tue, 4 Jun 2024 21:59:53 -0400 Subject: [PATCH 1/7] feat: add scaler and update repo --- .gitmodules | 6 +- contracts/Migrator.sol | 7 ++- contracts/test/Migrator.t.sol | 109 ++++++++++++++++++---------------- foundry.toml | 10 ++++ modules/contract-test-utils | 1 - modules/forge-std | 1 + 6 files changed, 79 insertions(+), 55 deletions(-) create mode 100644 foundry.toml delete mode 160000 modules/contract-test-utils create mode 160000 modules/forge-std diff --git a/.gitmodules b/.gitmodules index 9de8505..4f2865f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,6 +4,6 @@ [submodule "modules/erc20-helper"] path = modules/erc20-helper url = git@github.com:maple-labs/erc20-helper.git -[submodule "modules/contract-test-utils"] - path = modules/contract-test-utils - url = git@github.com:maple-labs/contract-test-utils.git +[submodule "modules/forge-std"] + path = modules/forge-std + url = git@github.com:foundry-rs/forge-std.git diff --git a/contracts/Migrator.sol b/contracts/Migrator.sol index 5177fda..b1cfff5 100644 --- a/contracts/Migrator.sol +++ b/contracts/Migrator.sol @@ -7,6 +7,8 @@ import { IERC20Like } from "./interfaces/Interfaces.sol"; contract Migrator { + uint256 public constant SCALER = 10; + address public immutable newToken; address public immutable oldToken; @@ -23,8 +25,11 @@ contract Migrator { function migrate(address owner_, uint256 amount_) public { require(amount_ != uint256(0), "M:M:ZERO_AMOUNT"); + + uint256 outputAmount = amount_ * SCALER; + require(ERC20Helper.transferFrom(oldToken, owner_, address(this), amount_), "M:M:TRANSFER_FROM_FAILED"); - require(ERC20Helper.transfer(newToken, owner_, amount_), "M:M:TRANSFER_FAILED"); + require(ERC20Helper.transfer(newToken, owner_, outputAmount), "M:M:TRANSFER_FAILED"); } } diff --git a/contracts/test/Migrator.t.sol b/contracts/test/Migrator.t.sol index 4921438..b7d57ff 100644 --- a/contracts/test/Migrator.t.sol +++ b/contracts/test/Migrator.t.sol @@ -1,22 +1,14 @@ // SPDX-License-Identifier: AGPL-3.0-or-later pragma solidity 0.8.7; -import { IERC20 } from "../../modules/erc20/contracts/interfaces/IERC20.sol"; +import { Test } from "../../modules/forge-std/src/Test.sol"; -import { TestUtils } from "../../modules/contract-test-utils/contracts/test.sol"; +import { IERC20 } from "../../modules/erc20/contracts/interfaces/IERC20.sol"; import { MockERC20 } from "../../modules/erc20/contracts/test/mocks/MockERC20.sol"; import { Migrator } from "../Migrator.sol"; -contract SomeAccount { - - function approve(address token_, address spender_, uint256 amount_) external { - IERC20(token_).approve(spender_, amount_); - } - -} - -contract MigratorConstructorTest is TestUtils { +contract MigratorConstructorTest is Test { function test_constructor_mismatch_decimals() external { MockERC20 oldToken = new MockERC20("Old Token", "OT", 18); @@ -38,10 +30,13 @@ contract MigratorConstructorTest is TestUtils { } -contract MigratorTest is TestUtils { +contract MigratorTest is Test { + uint256 internal constant SCALER = 10; uint256 internal constant OLD_SUPPLY = 10_000_000 ether; + address account = makeAddr("account"); + Migrator internal _migrator; MockERC20 internal _oldToken; MockERC20 internal _newToken; @@ -53,7 +48,7 @@ contract MigratorTest is TestUtils { _migrator = new Migrator(address(_oldToken), address(_newToken)); // Mint new token to migrator - _newToken.mint(address(_migrator), OLD_SUPPLY); + _newToken.mint(address(_migrator), OLD_SUPPLY * SCALER); } function test_migrate_zeroAmount() external { @@ -65,16 +60,20 @@ contract MigratorTest is TestUtils { _migrator.migrate(1); + uint256 newAmount = 1 * SCALER; + assertEq(_oldToken.allowance(address(this), address(_migrator)), 0); assertEq(_oldToken.balanceOf(address(this)), 0); assertEq(_oldToken.balanceOf(address(_migrator)), 1); - assertEq(_newToken.balanceOf(address(this)), 1); - assertEq(_newToken.balanceOf(address(_migrator)), OLD_SUPPLY - 1); + assertEq(_newToken.balanceOf(address(this)), newAmount); + assertEq(_newToken.balanceOf(address(_migrator)), (OLD_SUPPLY * SCALER)- newAmount); } function testFuzz_migrate_insufficientApproval(uint256 amount_) external { - amount_ = constrictToRange(amount_, 1, OLD_SUPPLY); + amount_ = bound(amount_, 1, OLD_SUPPLY); + + uint256 newTokenAmount = amount_ * SCALER; // Mint amount of old token _oldToken.mint(address(this), amount_); @@ -93,12 +92,12 @@ contract MigratorTest is TestUtils { assertEq(_oldToken.balanceOf(address(this)), 0); assertEq(_oldToken.balanceOf(address(_migrator)), amount_); - assertEq(_newToken.balanceOf(address(this)), amount_); - assertEq(_newToken.balanceOf(address(_migrator)), OLD_SUPPLY - amount_); + assertEq(_newToken.balanceOf(address(this)), newTokenAmount); + assertEq(_newToken.balanceOf(address(_migrator)), (OLD_SUPPLY * SCALER) - newTokenAmount); } function testFuzz_migrate_insufficientBalance(uint256 amount_) external { - amount_ = constrictToRange(amount_, 1, OLD_SUPPLY); + amount_ = bound(amount_, 1, OLD_SUPPLY); _oldToken.mint(address(this), amount_ - 1); @@ -112,19 +111,23 @@ contract MigratorTest is TestUtils { _migrator.migrate(amount_); + uint256 newAmount = amount_ * SCALER; + assertEq(_oldToken.allowance(address(this), address(_migrator)), 0); assertEq(_oldToken.balanceOf(address(this)), 0); assertEq(_oldToken.balanceOf(address(_migrator)), amount_); - assertEq(_newToken.balanceOf(address(this)), amount_); - assertEq(_newToken.balanceOf(address(_migrator)), OLD_SUPPLY - amount_); + assertEq(_newToken.balanceOf(address(this)), newAmount); + assertEq(_newToken.balanceOf(address(_migrator)), (OLD_SUPPLY * SCALER) - newAmount); } function testFuzz_migrate_newTokenInsufficientBalance(uint256 amount_) external { - amount_ = constrictToRange(amount_, 1, OLD_SUPPLY); + amount_ = bound(amount_, 1, OLD_SUPPLY); + + uint256 newAmount = amount_ * SCALER; // Burn new supply that was added in setUp - _newToken.burn(address(_migrator), OLD_SUPPLY - amount_ + 1); + _newToken.burn(address(_migrator), (OLD_SUPPLY * SCALER) - newAmount + 1); // Mint amount of old token _oldToken.mint(address(this), amount_); @@ -143,12 +146,14 @@ contract MigratorTest is TestUtils { assertEq(_oldToken.balanceOf(address(this)), 0); assertEq(_oldToken.balanceOf(address(_migrator)), amount_); - assertEq(_newToken.balanceOf(address(this)), amount_); + assertEq(_newToken.balanceOf(address(this)), newAmount); assertEq(_newToken.balanceOf(address(_migrator)), 0); } function testFuzz_migrate_success(uint256 amount_) external { - amount_ = constrictToRange(amount_, 1, OLD_SUPPLY); + amount_ = bound(amount_, 1, OLD_SUPPLY); + + uint256 newAmount = amount_ * SCALER; // Mint amount of old token _oldToken.mint(address(this), amount_); @@ -161,7 +166,7 @@ contract MigratorTest is TestUtils { assertEq(_oldToken.balanceOf(address(this)), amount_); assertEq(_oldToken.balanceOf(address(_migrator)), 0); assertEq(_newToken.balanceOf(address(this)), 0); - assertEq(_newToken.balanceOf(address(_migrator)), OLD_SUPPLY); + assertEq(_newToken.balanceOf(address(_migrator)), OLD_SUPPLY * SCALER); _migrator.migrate(amount_); @@ -169,41 +174,45 @@ contract MigratorTest is TestUtils { assertEq(_oldToken.balanceOf(address(this)), 0); assertEq(_oldToken.balanceOf(address(_migrator)), amount_); - assertEq(_newToken.balanceOf(address(this)), amount_); - assertEq(_newToken.balanceOf(address(_migrator)), OLD_SUPPLY - amount_); + assertEq(_newToken.balanceOf(address(this)), newAmount); + assertEq(_newToken.balanceOf(address(_migrator)), (OLD_SUPPLY * SCALER) - newAmount); } function testFuzz_migration_specifiedOwner(uint256 amount_) external { - amount_ = constrictToRange(amount_, 1, OLD_SUPPLY); + amount_ = bound(amount_, 1, OLD_SUPPLY); - SomeAccount someAccount = new SomeAccount(); + uint256 newAmount = amount_ * SCALER; // Mint amount of old token - _oldToken.mint(address(someAccount), amount_); + _oldToken.mint(address(account), amount_); // Approve - someAccount.approve(address(_oldToken), address(_migrator), amount_); + vm.prank(account); + _oldToken.approve(address(_migrator), amount_); - assertEq(_oldToken.allowance(address(someAccount), address(_migrator)), amount_); + assertEq(_oldToken.allowance(address(account), address(_migrator)), amount_); - assertEq(_oldToken.balanceOf(address(someAccount)), amount_); - assertEq(_oldToken.balanceOf(address(_migrator)), 0); - assertEq(_newToken.balanceOf(address(someAccount)), 0); - assertEq(_newToken.balanceOf(address(_migrator)), OLD_SUPPLY); + assertEq(_oldToken.balanceOf(address(account)), amount_); + assertEq(_oldToken.balanceOf(address(_migrator)), 0); + assertEq(_newToken.balanceOf(address(account)), 0); + assertEq(_newToken.balanceOf(address(_migrator)), OLD_SUPPLY * SCALER); - _migrator.migrate(address(someAccount), amount_); + _migrator.migrate(address(account), amount_); - assertEq(_oldToken.allowance(address(someAccount), address(_migrator)), 0); + assertEq(_oldToken.allowance(address(account), address(_migrator)), 0); - assertEq(_oldToken.balanceOf(address(someAccount)), 0); - assertEq(_oldToken.balanceOf(address(_migrator)), amount_); - assertEq(_newToken.balanceOf(address(someAccount)), amount_); - assertEq(_newToken.balanceOf(address(_migrator)), OLD_SUPPLY - amount_); + assertEq(_oldToken.balanceOf(address(account)), 0); + assertEq(_oldToken.balanceOf(address(_migrator)), amount_); + assertEq(_newToken.balanceOf(address(account)), newAmount); + assertEq(_newToken.balanceOf(address(_migrator)), (OLD_SUPPLY * SCALER) - newAmount); } function testFuzz_migrate_partialMigration(uint256 amount_, uint256 partialAmount_) external { - amount_ = constrictToRange(amount_, 2, OLD_SUPPLY); - partialAmount_ = constrictToRange(partialAmount_, 1, amount_ - 1); + amount_ = bound(amount_, 2, OLD_SUPPLY); + partialAmount_ = bound(partialAmount_, 1, amount_ - 1); + + uint256 newAmount = amount_ * SCALER; + uint256 newPartialAmount = partialAmount_ * SCALER; // Mint amount of old token _oldToken.mint(address(this), amount_); @@ -216,7 +225,7 @@ contract MigratorTest is TestUtils { assertEq(_oldToken.balanceOf(address(this)), amount_); assertEq(_oldToken.balanceOf(address(_migrator)), 0); assertEq(_newToken.balanceOf(address(this)), 0); - assertEq(_newToken.balanceOf(address(_migrator)), OLD_SUPPLY); + assertEq(_newToken.balanceOf(address(_migrator)), OLD_SUPPLY * SCALER); _migrator.migrate(partialAmount_); @@ -224,8 +233,8 @@ contract MigratorTest is TestUtils { assertEq(_oldToken.balanceOf(address(this)), amount_ - partialAmount_); assertEq(_oldToken.balanceOf(address(_migrator)), partialAmount_); - assertEq(_newToken.balanceOf(address(this)), partialAmount_); - assertEq(_newToken.balanceOf(address(_migrator)), OLD_SUPPLY - partialAmount_); + assertEq(_newToken.balanceOf(address(this)), newPartialAmount); + assertEq(_newToken.balanceOf(address(_migrator)), (OLD_SUPPLY * SCALER) - newPartialAmount); uint256 remaining = amount_ - partialAmount_; @@ -237,8 +246,8 @@ contract MigratorTest is TestUtils { assertEq(_oldToken.balanceOf(address(this)), 0); assertEq(_oldToken.balanceOf(address(_migrator)), amount_); - assertEq(_newToken.balanceOf(address(this)), amount_); - assertEq(_newToken.balanceOf(address(_migrator)), OLD_SUPPLY - amount_); + assertEq(_newToken.balanceOf(address(this)), newAmount); + assertEq(_newToken.balanceOf(address(_migrator)), (OLD_SUPPLY * SCALER)- newAmount); } } diff --git a/foundry.toml b/foundry.toml new file mode 100644 index 0000000..7b5cb65 --- /dev/null +++ b/foundry.toml @@ -0,0 +1,10 @@ +[profile.default] +contracts = 'contracts' # The source directory +test = 'tests' # The test directory +libs = ['modules'] # A list of library directories +solc_version = '0.8.7' # Override for the solc version (setting this ignores `auto_detect_solc`) +optimizer = false # Enable or disable the solc optimizer +optimizer_runs = 200 # The number of optimizer runs +verbosity = 3 # The verbosity of tests +block_timestamp = 1_670_692_487 # Timestamp for tests (non-zero) +gas_limit = "18446744073709551615" # Gas limit for tests diff --git a/modules/contract-test-utils b/modules/contract-test-utils deleted file mode 160000 index d42c3fa..0000000 --- a/modules/contract-test-utils +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d42c3fa7605ccce09384ae455f3fe7b2caf60a95 diff --git a/modules/forge-std b/modules/forge-std new file mode 160000 index 0000000..52715a2 --- /dev/null +++ b/modules/forge-std @@ -0,0 +1 @@ +Subproject commit 52715a217dc51d0de15877878ab8213f6cbbbab5 From 52d2107cbc93ff8238eb312a637b2ed7bb2fe5c9 Mon Sep 17 00:00:00 2001 From: JG Carvalho Date: Wed, 5 Jun 2024 12:50:05 -0400 Subject: [PATCH 2/7] fix: PR comments --- contracts/Migrator.sol | 24 ++++++----- contracts/interfaces/IMigrator.sol | 6 +++ {contracts/test => tests}/Migrator.t.sol | 55 ++++++++++++------------ 3 files changed, 46 insertions(+), 39 deletions(-) rename {contracts/test => tests}/Migrator.t.sol (87%) diff --git a/contracts/Migrator.sol b/contracts/Migrator.sol index b1cfff5..43e3706 100644 --- a/contracts/Migrator.sol +++ b/contracts/Migrator.sol @@ -5,31 +5,33 @@ import { ERC20Helper } from "../modules/erc20-helper/src/ERC20Helper.sol"; import { IERC20Like } from "./interfaces/Interfaces.sol"; -contract Migrator { +import { IMigrator } from "./interfaces/IMigrator.sol"; - uint256 public constant SCALER = 10; +contract Migrator is IMigrator { - address public immutable newToken; - address public immutable oldToken; + uint256 public immutable override TOKEN_SPLIT_SCALAR; - constructor(address oldToken_, address newToken_) { + address public immutable override newToken; + address public immutable override oldToken; + + constructor(address oldToken_, address newToken_, uint256 scalar_) { require(IERC20Like(newToken_).decimals() == IERC20Like(oldToken_).decimals(), "M:C:DECIMAL_MISMATCH"); oldToken = oldToken_; newToken = newToken_; + + TOKEN_SPLIT_SCALAR = scalar_; } - function migrate(uint256 amount_) external { + function migrate(uint256 amount_) external override { migrate(msg.sender, amount_); } - function migrate(address owner_, uint256 amount_) public { + function migrate(address owner_, uint256 amount_) public override { require(amount_ != uint256(0), "M:M:ZERO_AMOUNT"); - uint256 outputAmount = amount_ * SCALER; - - require(ERC20Helper.transferFrom(oldToken, owner_, address(this), amount_), "M:M:TRANSFER_FROM_FAILED"); - require(ERC20Helper.transfer(newToken, owner_, outputAmount), "M:M:TRANSFER_FAILED"); + require(ERC20Helper.transferFrom(oldToken, owner_, address(this), amount_), "M:M:TRANSFER_FROM_FAILED"); + require(ERC20Helper.transfer(newToken, owner_, amount_ * TOKEN_SPLIT_SCALAR), "M:M:TRANSFER_FAILED"); } } diff --git a/contracts/interfaces/IMigrator.sol b/contracts/interfaces/IMigrator.sol index 959de0e..2fb2158 100644 --- a/contracts/interfaces/IMigrator.sol +++ b/contracts/interfaces/IMigrator.sol @@ -28,4 +28,10 @@ interface IMigrator { */ function migrate(address owner_, uint256 amount_) external; + /** + * @dev Get the scalar value for token split. + * @return TOKEN_SPLIT_SCALAR_ The scalar value for token split. + */ + function TOKEN_SPLIT_SCALAR() external view returns (uint256 TOKEN_SPLIT_SCALAR_); + } diff --git a/contracts/test/Migrator.t.sol b/tests/Migrator.t.sol similarity index 87% rename from contracts/test/Migrator.t.sol rename to tests/Migrator.t.sol index b7d57ff..c622908 100644 --- a/contracts/test/Migrator.t.sol +++ b/tests/Migrator.t.sol @@ -1,12 +1,11 @@ // SPDX-License-Identifier: AGPL-3.0-or-later pragma solidity 0.8.7; -import { Test } from "../../modules/forge-std/src/Test.sol"; +import { Test } from "../modules/forge-std/src/Test.sol"; -import { IERC20 } from "../../modules/erc20/contracts/interfaces/IERC20.sol"; -import { MockERC20 } from "../../modules/erc20/contracts/test/mocks/MockERC20.sol"; +import { MockERC20 } from "../modules/erc20/contracts/test/mocks/MockERC20.sol"; -import { Migrator } from "../Migrator.sol"; +import { Migrator } from "../contracts/Migrator.sol"; contract MigratorConstructorTest is Test { @@ -15,14 +14,14 @@ contract MigratorConstructorTest is Test { MockERC20 newToken = new MockERC20("New Token", "NT", 17); vm.expectRevert("M:C:DECIMAL_MISMATCH"); - new Migrator(address(oldToken), address(newToken)); + new Migrator(address(oldToken), address(newToken), 1); } function test_constructor() external { MockERC20 oldToken = new MockERC20("Old Token", "OT", 18); MockERC20 newToken = new MockERC20("New Token", "NT", 18); - Migrator migrator = new Migrator(address(oldToken), address(newToken)); + Migrator migrator = new Migrator(address(oldToken), address(newToken), 1); assertEq(migrator.oldToken(), address(oldToken)); assertEq(migrator.newToken(), address(newToken)); @@ -32,7 +31,7 @@ contract MigratorConstructorTest is Test { contract MigratorTest is Test { - uint256 internal constant SCALER = 10; + uint256 internal constant SCALAR = 10; uint256 internal constant OLD_SUPPLY = 10_000_000 ether; address account = makeAddr("account"); @@ -45,10 +44,10 @@ contract MigratorTest is Test { _oldToken = new MockERC20("Old Token", "OT", 18); _newToken = new MockERC20("New Token", "NT", 18); - _migrator = new Migrator(address(_oldToken), address(_newToken)); + _migrator = new Migrator(address(_oldToken), address(_newToken), SCALAR); // Mint new token to migrator - _newToken.mint(address(_migrator), OLD_SUPPLY * SCALER); + _newToken.mint(address(_migrator), OLD_SUPPLY * SCALAR); } function test_migrate_zeroAmount() external { @@ -60,20 +59,20 @@ contract MigratorTest is Test { _migrator.migrate(1); - uint256 newAmount = 1 * SCALER; + uint256 newAmount = 1 * SCALAR; assertEq(_oldToken.allowance(address(this), address(_migrator)), 0); assertEq(_oldToken.balanceOf(address(this)), 0); assertEq(_oldToken.balanceOf(address(_migrator)), 1); assertEq(_newToken.balanceOf(address(this)), newAmount); - assertEq(_newToken.balanceOf(address(_migrator)), (OLD_SUPPLY * SCALER)- newAmount); + assertEq(_newToken.balanceOf(address(_migrator)), (OLD_SUPPLY * SCALAR)- newAmount); } function testFuzz_migrate_insufficientApproval(uint256 amount_) external { amount_ = bound(amount_, 1, OLD_SUPPLY); - uint256 newTokenAmount = amount_ * SCALER; + uint256 newTokenAmount = amount_ * SCALAR; // Mint amount of old token _oldToken.mint(address(this), amount_); @@ -93,7 +92,7 @@ contract MigratorTest is Test { assertEq(_oldToken.balanceOf(address(this)), 0); assertEq(_oldToken.balanceOf(address(_migrator)), amount_); assertEq(_newToken.balanceOf(address(this)), newTokenAmount); - assertEq(_newToken.balanceOf(address(_migrator)), (OLD_SUPPLY * SCALER) - newTokenAmount); + assertEq(_newToken.balanceOf(address(_migrator)), (OLD_SUPPLY * SCALAR) - newTokenAmount); } function testFuzz_migrate_insufficientBalance(uint256 amount_) external { @@ -111,23 +110,23 @@ contract MigratorTest is Test { _migrator.migrate(amount_); - uint256 newAmount = amount_ * SCALER; + uint256 newAmount = amount_ * SCALAR; assertEq(_oldToken.allowance(address(this), address(_migrator)), 0); assertEq(_oldToken.balanceOf(address(this)), 0); assertEq(_oldToken.balanceOf(address(_migrator)), amount_); assertEq(_newToken.balanceOf(address(this)), newAmount); - assertEq(_newToken.balanceOf(address(_migrator)), (OLD_SUPPLY * SCALER) - newAmount); + assertEq(_newToken.balanceOf(address(_migrator)), (OLD_SUPPLY * SCALAR) - newAmount); } function testFuzz_migrate_newTokenInsufficientBalance(uint256 amount_) external { amount_ = bound(amount_, 1, OLD_SUPPLY); - uint256 newAmount = amount_ * SCALER; + uint256 newAmount = amount_ * SCALAR; // Burn new supply that was added in setUp - _newToken.burn(address(_migrator), (OLD_SUPPLY * SCALER) - newAmount + 1); + _newToken.burn(address(_migrator), (OLD_SUPPLY * SCALAR) - newAmount + 1); // Mint amount of old token _oldToken.mint(address(this), amount_); @@ -153,7 +152,7 @@ contract MigratorTest is Test { function testFuzz_migrate_success(uint256 amount_) external { amount_ = bound(amount_, 1, OLD_SUPPLY); - uint256 newAmount = amount_ * SCALER; + uint256 newAmount = amount_ * SCALAR; // Mint amount of old token _oldToken.mint(address(this), amount_); @@ -166,7 +165,7 @@ contract MigratorTest is Test { assertEq(_oldToken.balanceOf(address(this)), amount_); assertEq(_oldToken.balanceOf(address(_migrator)), 0); assertEq(_newToken.balanceOf(address(this)), 0); - assertEq(_newToken.balanceOf(address(_migrator)), OLD_SUPPLY * SCALER); + assertEq(_newToken.balanceOf(address(_migrator)), OLD_SUPPLY * SCALAR); _migrator.migrate(amount_); @@ -175,13 +174,13 @@ contract MigratorTest is Test { assertEq(_oldToken.balanceOf(address(this)), 0); assertEq(_oldToken.balanceOf(address(_migrator)), amount_); assertEq(_newToken.balanceOf(address(this)), newAmount); - assertEq(_newToken.balanceOf(address(_migrator)), (OLD_SUPPLY * SCALER) - newAmount); + assertEq(_newToken.balanceOf(address(_migrator)), (OLD_SUPPLY * SCALAR) - newAmount); } function testFuzz_migration_specifiedOwner(uint256 amount_) external { amount_ = bound(amount_, 1, OLD_SUPPLY); - uint256 newAmount = amount_ * SCALER; + uint256 newAmount = amount_ * SCALAR; // Mint amount of old token _oldToken.mint(address(account), amount_); @@ -195,7 +194,7 @@ contract MigratorTest is Test { assertEq(_oldToken.balanceOf(address(account)), amount_); assertEq(_oldToken.balanceOf(address(_migrator)), 0); assertEq(_newToken.balanceOf(address(account)), 0); - assertEq(_newToken.balanceOf(address(_migrator)), OLD_SUPPLY * SCALER); + assertEq(_newToken.balanceOf(address(_migrator)), OLD_SUPPLY * SCALAR); _migrator.migrate(address(account), amount_); @@ -204,15 +203,15 @@ contract MigratorTest is Test { assertEq(_oldToken.balanceOf(address(account)), 0); assertEq(_oldToken.balanceOf(address(_migrator)), amount_); assertEq(_newToken.balanceOf(address(account)), newAmount); - assertEq(_newToken.balanceOf(address(_migrator)), (OLD_SUPPLY * SCALER) - newAmount); + assertEq(_newToken.balanceOf(address(_migrator)), (OLD_SUPPLY * SCALAR) - newAmount); } function testFuzz_migrate_partialMigration(uint256 amount_, uint256 partialAmount_) external { amount_ = bound(amount_, 2, OLD_SUPPLY); partialAmount_ = bound(partialAmount_, 1, amount_ - 1); - uint256 newAmount = amount_ * SCALER; - uint256 newPartialAmount = partialAmount_ * SCALER; + uint256 newAmount = amount_ * SCALAR; + uint256 newPartialAmount = partialAmount_ * SCALAR; // Mint amount of old token _oldToken.mint(address(this), amount_); @@ -225,7 +224,7 @@ contract MigratorTest is Test { assertEq(_oldToken.balanceOf(address(this)), amount_); assertEq(_oldToken.balanceOf(address(_migrator)), 0); assertEq(_newToken.balanceOf(address(this)), 0); - assertEq(_newToken.balanceOf(address(_migrator)), OLD_SUPPLY * SCALER); + assertEq(_newToken.balanceOf(address(_migrator)), OLD_SUPPLY * SCALAR); _migrator.migrate(partialAmount_); @@ -234,7 +233,7 @@ contract MigratorTest is Test { assertEq(_oldToken.balanceOf(address(this)), amount_ - partialAmount_); assertEq(_oldToken.balanceOf(address(_migrator)), partialAmount_); assertEq(_newToken.balanceOf(address(this)), newPartialAmount); - assertEq(_newToken.balanceOf(address(_migrator)), (OLD_SUPPLY * SCALER) - newPartialAmount); + assertEq(_newToken.balanceOf(address(_migrator)), (OLD_SUPPLY * SCALAR) - newPartialAmount); uint256 remaining = amount_ - partialAmount_; @@ -247,7 +246,7 @@ contract MigratorTest is Test { assertEq(_oldToken.balanceOf(address(this)), 0); assertEq(_oldToken.balanceOf(address(_migrator)), amount_); assertEq(_newToken.balanceOf(address(this)), newAmount); - assertEq(_newToken.balanceOf(address(_migrator)), (OLD_SUPPLY * SCALER)- newAmount); + assertEq(_newToken.balanceOf(address(_migrator)), (OLD_SUPPLY * SCALAR)- newAmount); } } From 17470e76fe99ac9f6f29fba78470fa54a4d3e31d Mon Sep 17 00:00:00 2001 From: JG Carvalho Date: Wed, 5 Jun 2024 14:03:20 -0400 Subject: [PATCH 3/7] feat: add test for fuzzed scalar --- tests/Migrator.t.sol | 49 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/tests/Migrator.t.sol b/tests/Migrator.t.sol index c622908..149d982 100644 --- a/tests/Migrator.t.sol +++ b/tests/Migrator.t.sol @@ -250,3 +250,52 @@ contract MigratorTest is Test { } } + +contract TokenSplitScalars is Test { + + uint256 internal constant OLD_SUPPLY = 10_000_000 ether; + + address account = makeAddr("account"); + + Migrator internal _migrator; + MockERC20 internal _oldToken; + MockERC20 internal _newToken; + + function setUp() external { + _oldToken = new MockERC20("Old Token", "OT", 18); + _newToken = new MockERC20("New Token", "NT", 18); + } + + function testFuzz_tokenSplitScalar(uint256 amount_, uint16 scalar_) external { + vm.assume(scalar_ > 0); + + amount_ = bound(amount_, 1, OLD_SUPPLY); + + uint256 newAmount = amount_ * scalar_; + + _migrator = new Migrator(address(_oldToken), address(_newToken), scalar_); + + _newToken.mint(address(_migrator), OLD_SUPPLY * scalar_); + + _oldToken.mint(address(this), amount_); + + // Approve + _oldToken.approve(address(_migrator), amount_); + + assertEq(_oldToken.allowance(address(this), address(_migrator)), amount_); + + assertEq(_oldToken.balanceOf(address(this)), amount_); + assertEq(_oldToken.balanceOf(address(_migrator)), 0); + assertEq(_newToken.balanceOf(address(this)), 0); + assertEq(_newToken.balanceOf(address(_migrator)), OLD_SUPPLY * scalar_); + + _migrator.migrate(amount_); + + assertEq(_oldToken.allowance(address(this), address(_migrator)), 0); + + assertEq(_oldToken.balanceOf(address(this)), 0); + assertEq(_oldToken.balanceOf(address(_migrator)), amount_); + assertEq(_newToken.balanceOf(address(this)), newAmount); + assertEq(_newToken.balanceOf(address(_migrator)), (OLD_SUPPLY * scalar_) - newAmount); + } +} From e712bd2082ed42cc2ef08c0da80450cb279c59cd Mon Sep 17 00:00:00 2001 From: JG Carvalho Date: Thu, 6 Jun 2024 10:53:08 -0400 Subject: [PATCH 4/7] fix: adjust PR comments --- contracts/Migrator.sol | 13 +++++++------ contracts/interfaces/IMigrator.sol | 4 ++-- tests/Migrator.t.sol | 12 +++++++++--- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/contracts/Migrator.sol b/contracts/Migrator.sol index 43e3706..c0c7614 100644 --- a/contracts/Migrator.sol +++ b/contracts/Migrator.sol @@ -9,18 +9,19 @@ import { IMigrator } from "./interfaces/IMigrator.sol"; contract Migrator is IMigrator { - uint256 public immutable override TOKEN_SPLIT_SCALAR; - address public immutable override newToken; address public immutable override oldToken; + uint256 public immutable override tokenSplitScalar; + constructor(address oldToken_, address newToken_, uint256 scalar_) { + require(scalar_ > 0, "M:C:ZERO_SCALAR"); require(IERC20Like(newToken_).decimals() == IERC20Like(oldToken_).decimals(), "M:C:DECIMAL_MISMATCH"); oldToken = oldToken_; newToken = newToken_; - TOKEN_SPLIT_SCALAR = scalar_; + tokenSplitScalar = scalar_; } function migrate(uint256 amount_) external override { @@ -28,10 +29,10 @@ contract Migrator is IMigrator { } function migrate(address owner_, uint256 amount_) public override { - require(amount_ != uint256(0), "M:M:ZERO_AMOUNT"); + require(amount_ != uint256(0), "M:M:ZERO_AMOUNT"); - require(ERC20Helper.transferFrom(oldToken, owner_, address(this), amount_), "M:M:TRANSFER_FROM_FAILED"); - require(ERC20Helper.transfer(newToken, owner_, amount_ * TOKEN_SPLIT_SCALAR), "M:M:TRANSFER_FAILED"); + require(ERC20Helper.transferFrom(oldToken, owner_, address(this), amount_), "M:M:TRANSFER_FROM_FAILED"); + require(ERC20Helper.transfer(newToken, owner_, amount_ * tokenSplitScalar), "M:M:TRANSFER_FAILED"); } } diff --git a/contracts/interfaces/IMigrator.sol b/contracts/interfaces/IMigrator.sol index 2fb2158..6bf081f 100644 --- a/contracts/interfaces/IMigrator.sol +++ b/contracts/interfaces/IMigrator.sol @@ -30,8 +30,8 @@ interface IMigrator { /** * @dev Get the scalar value for token split. - * @return TOKEN_SPLIT_SCALAR_ The scalar value for token split. + * @return tokenSplitScalar_ The scalar value for token split. */ - function TOKEN_SPLIT_SCALAR() external view returns (uint256 TOKEN_SPLIT_SCALAR_); + function tokenSplitScalar() external view returns (uint256 tokenSplitScalar_); } diff --git a/tests/Migrator.t.sol b/tests/Migrator.t.sol index 149d982..8510fc7 100644 --- a/tests/Migrator.t.sol +++ b/tests/Migrator.t.sol @@ -9,6 +9,11 @@ import { Migrator } from "../contracts/Migrator.sol"; contract MigratorConstructorTest is Test { + function test_constructor_zeroScalar() external { + vm.expectRevert("M:C:ZERO_SCALAR"); + new Migrator(address(0), address(0), 0); + } + function test_constructor_mismatch_decimals() external { MockERC20 oldToken = new MockERC20("Old Token", "OT", 18); MockERC20 newToken = new MockERC20("New Token", "NT", 17); @@ -23,8 +28,9 @@ contract MigratorConstructorTest is Test { Migrator migrator = new Migrator(address(oldToken), address(newToken), 1); - assertEq(migrator.oldToken(), address(oldToken)); - assertEq(migrator.newToken(), address(newToken)); + assertEq(migrator.tokenSplitScalar(), 1); + assertEq(migrator.oldToken(), address(oldToken)); + assertEq(migrator.newToken(), address(newToken)); } } @@ -32,7 +38,7 @@ contract MigratorConstructorTest is Test { contract MigratorTest is Test { uint256 internal constant SCALAR = 10; - uint256 internal constant OLD_SUPPLY = 10_000_000 ether; + uint256 internal constant OLD_SUPPLY = 10_000_000e18; address account = makeAddr("account"); From b30ccceefad08e8eae2d9bc4d6dfe6221fb0baf3 Mon Sep 17 00:00:00 2001 From: JG Carvalho Date: Wed, 5 Jun 2024 17:25:11 -0400 Subject: [PATCH 5/7] feat: add boolean to toggle migration --- contracts/Migrator.sol | 21 ++++- contracts/interfaces/IMigrator.sol | 12 +++ contracts/interfaces/Interfaces.sol | 8 ++ tests/Migrator.t.sol | 116 +++++++++++++++++++++++++--- tests/mocks/Mocks.sol | 17 ++++ 5 files changed, 159 insertions(+), 15 deletions(-) create mode 100644 tests/mocks/Mocks.sol diff --git a/contracts/Migrator.sol b/contracts/Migrator.sol index c0c7614..950203c 100644 --- a/contracts/Migrator.sol +++ b/contracts/Migrator.sol @@ -3,21 +3,25 @@ pragma solidity 0.8.7; import { ERC20Helper } from "../modules/erc20-helper/src/ERC20Helper.sol"; -import { IERC20Like } from "./interfaces/Interfaces.sol"; +import { IERC20Like, IGlobalsLike } from "./interfaces/Interfaces.sol"; import { IMigrator } from "./interfaces/IMigrator.sol"; contract Migrator is IMigrator { + address public immutable override globals; address public immutable override newToken; address public immutable override oldToken; uint256 public immutable override tokenSplitScalar; - constructor(address oldToken_, address newToken_, uint256 scalar_) { - require(scalar_ > 0, "M:C:ZERO_SCALAR"); + bool public override active; + + constructor(address globals_, address oldToken_, address newToken_, uint256 scalar_) { require(IERC20Like(newToken_).decimals() == IERC20Like(oldToken_).decimals(), "M:C:DECIMAL_MISMATCH"); + globals = globals_; + oldToken = oldToken_; newToken = newToken_; @@ -29,10 +33,21 @@ contract Migrator is IMigrator { } function migrate(address owner_, uint256 amount_) public override { + require(active, "M:M:INACTIVE"); require(amount_ != uint256(0), "M:M:ZERO_AMOUNT"); require(ERC20Helper.transferFrom(oldToken, owner_, address(this), amount_), "M:M:TRANSFER_FROM_FAILED"); require(ERC20Helper.transfer(newToken, owner_, amount_ * tokenSplitScalar), "M:M:TRANSFER_FAILED"); } + function setActive(bool active_) external { + require( + msg.sender == IGlobalsLike(globals).governor() || + msg.sender == IGlobalsLike(globals).operationalAdmin(), + "M:SA:NOT_PROTOCOL_ADMIN" + ); + + active = active_; + } + } diff --git a/contracts/interfaces/IMigrator.sol b/contracts/interfaces/IMigrator.sol index 6bf081f..60124e4 100644 --- a/contracts/interfaces/IMigrator.sol +++ b/contracts/interfaces/IMigrator.sol @@ -3,6 +3,18 @@ pragma solidity ^0.8.7; interface IMigrator { + /** + * @dev Get the status of the migrator. + * @return active_ True if migrations are active. + */ + function active() external view returns (bool active_); + + /** + * @dev Gets the Maple Globals address + * @param globals_ The address of the Maple globals. + */ + function globals() external view returns (address globals_); + /** * @dev Get address of newToken. * @return newToken_ The address of new token. diff --git a/contracts/interfaces/Interfaces.sol b/contracts/interfaces/Interfaces.sol index bbec88f..832f3c2 100644 --- a/contracts/interfaces/Interfaces.sol +++ b/contracts/interfaces/Interfaces.sol @@ -6,3 +6,11 @@ interface IERC20Like { function decimals() external view returns (uint8 decimals_); } + +interface IGlobalsLike { + + function governor() external view returns (address governor_); + + function operationalAdmin() external view returns (address operationalAdmin_); + +} diff --git a/tests/Migrator.t.sol b/tests/Migrator.t.sol index 8510fc7..4abca5b 100644 --- a/tests/Migrator.t.sol +++ b/tests/Migrator.t.sol @@ -7,6 +7,8 @@ import { MockERC20 } from "../modules/erc20/contracts/test/mocks/MockERC20.sol"; import { Migrator } from "../contracts/Migrator.sol"; +import { MockGlobals } from "./mocks/Mocks.sol"; + contract MigratorConstructorTest is Test { function test_constructor_zeroScalar() external { @@ -15,22 +17,86 @@ contract MigratorConstructorTest is Test { } function test_constructor_mismatch_decimals() external { + MockGlobals globals = new MockGlobals(); MockERC20 oldToken = new MockERC20("Old Token", "OT", 18); MockERC20 newToken = new MockERC20("New Token", "NT", 17); vm.expectRevert("M:C:DECIMAL_MISMATCH"); - new Migrator(address(oldToken), address(newToken), 1); + new Migrator(address(globals), address(oldToken), address(newToken), 1); } function test_constructor() external { - MockERC20 oldToken = new MockERC20("Old Token", "OT", 18); - MockERC20 newToken = new MockERC20("New Token", "NT", 18); + MockGlobals globals = new MockGlobals(); + MockERC20 oldToken = new MockERC20("Old Token", "OT", 18); + MockERC20 newToken = new MockERC20("New Token", "NT", 18); - Migrator migrator = new Migrator(address(oldToken), address(newToken), 1); + Migrator migrator = new Migrator(address(globals), address(oldToken), address(newToken), 1); - assertEq(migrator.tokenSplitScalar(), 1); + assertEq(migrator.globals(), address(globals)); assertEq(migrator.oldToken(), address(oldToken)); assertEq(migrator.newToken(), address(newToken)); + assertEq(migrator.tokenSplitScalar(), 1); + } + +} + +contract SetActiveTests is Test { + + uint256 internal constant SCALAR = 10; + uint256 internal constant OLD_SUPPLY = 10_000_000 ether; + + address operationalAdmin = makeAddr("operationalAdmin"); + address governor = makeAddr("governor"); + address account = makeAddr("account"); + + Migrator internal _migrator; + MockERC20 internal _oldToken; + MockERC20 internal _newToken; + MockGlobals internal _globals; + + function setUp() external { + _globals = new MockGlobals(); + + _globals.__setGovernor(governor); + _globals.__setOperationalAdmin(operationalAdmin); + + _oldToken = new MockERC20("Old Token", "OT", 18); + _newToken = new MockERC20("New Token", "NT", 18); + + _migrator = new Migrator(address(_globals), address(_oldToken), address(_newToken), SCALAR); + } + + function test_setActive_notProtocolAdmin() external { + vm.expectRevert("M:SA:NOT_PROTOCOL_ADMIN"); + _migrator.setActive(false); + } + + function test_setActive_withGovernor() external { + assertEq(_migrator.active(), false); + + vm.prank(governor); + _migrator.setActive(true); + + assertEq(_migrator.active(), true); + + vm.prank(governor); + _migrator.setActive(false); + + assertEq(_migrator.active(), false); + } + + function test_setActive_withOperationalAdmin() external { + assertEq(_migrator.active(), false); + + vm.prank(operationalAdmin); + _migrator.setActive(true); + + assertEq(_migrator.active(), true); + + vm.prank(operationalAdmin); + _migrator.setActive(false); + + assertEq(_migrator.active(), false); } } @@ -40,17 +106,26 @@ contract MigratorTest is Test { uint256 internal constant SCALAR = 10; uint256 internal constant OLD_SUPPLY = 10_000_000e18; - address account = makeAddr("account"); + address operationalAdmin = makeAddr("operationalAdmin"); + address account = makeAddr("account"); - Migrator internal _migrator; - MockERC20 internal _oldToken; - MockERC20 internal _newToken; + Migrator internal _migrator; + MockERC20 internal _oldToken; + MockERC20 internal _newToken; + MockGlobals internal _globals; function setUp() external { + _globals = new MockGlobals(); + _oldToken = new MockERC20("Old Token", "OT", 18); _newToken = new MockERC20("New Token", "NT", 18); - _migrator = new Migrator(address(_oldToken), address(_newToken), SCALAR); + _migrator = new Migrator(address(_globals), address(_oldToken), address(_newToken), SCALAR); + + _globals.__setOperationalAdmin(operationalAdmin); + + vm.prank(operationalAdmin); + _migrator.setActive(true); // Mint new token to migrator _newToken.mint(address(_migrator), OLD_SUPPLY * SCALAR); @@ -75,6 +150,14 @@ contract MigratorTest is Test { assertEq(_newToken.balanceOf(address(_migrator)), (OLD_SUPPLY * SCALAR)- newAmount); } + function test_migrate_inactive() external { + vm.prank(operationalAdmin); + _migrator.setActive(false); + + vm.expectRevert("M:M:INACTIVE"); + _migrator.migrate(1); + } + function testFuzz_migrate_insufficientApproval(uint256 amount_) external { amount_ = bound(amount_, 1, OLD_SUPPLY); @@ -261,15 +344,21 @@ contract TokenSplitScalars is Test { uint256 internal constant OLD_SUPPLY = 10_000_000 ether; - address account = makeAddr("account"); + address operationalAdmin = makeAddr("operationalAdmin"); + address account = makeAddr("account"); Migrator internal _migrator; MockERC20 internal _oldToken; MockERC20 internal _newToken; + MockGlobals internal _globals; function setUp() external { + _globals = new MockGlobals(); + _oldToken = new MockERC20("Old Token", "OT", 18); _newToken = new MockERC20("New Token", "NT", 18); + + _globals.__setOperationalAdmin(operationalAdmin); } function testFuzz_tokenSplitScalar(uint256 amount_, uint16 scalar_) external { @@ -279,7 +368,10 @@ contract TokenSplitScalars is Test { uint256 newAmount = amount_ * scalar_; - _migrator = new Migrator(address(_oldToken), address(_newToken), scalar_); + _migrator = new Migrator(address(_globals), address(_oldToken), address(_newToken), scalar_); + + vm.prank(operationalAdmin); + _migrator.setActive(true); _newToken.mint(address(_migrator), OLD_SUPPLY * scalar_); diff --git a/tests/mocks/Mocks.sol b/tests/mocks/Mocks.sol new file mode 100644 index 0000000..2955578 --- /dev/null +++ b/tests/mocks/Mocks.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later +pragma solidity 0.8.7; + +contract MockGlobals { + + address public governor; + address public operationalAdmin; + + function __setGovernor(address governor_) external { + governor = governor_; + } + + function __setOperationalAdmin(address operationalAdmin_) external { + operationalAdmin = operationalAdmin_; + } + +} From 89bc643fb643b82b07f77df7234e5f80c93a7bf4 Mon Sep 17 00:00:00 2001 From: JG Carvalho Date: Thu, 6 Jun 2024 11:03:24 -0400 Subject: [PATCH 6/7] fix: PR comments --- contracts/Migrator.sol | 5 +++-- contracts/interfaces/IMigrator.sol | 2 +- tests/Migrator.t.sol | 6 +++--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/contracts/Migrator.sol b/contracts/Migrator.sol index 950203c..a0a20f7 100644 --- a/contracts/Migrator.sol +++ b/contracts/Migrator.sol @@ -18,10 +18,11 @@ contract Migrator is IMigrator { bool public override active; constructor(address globals_, address oldToken_, address newToken_, uint256 scalar_) { - require(IERC20Like(newToken_).decimals() == IERC20Like(oldToken_).decimals(), "M:C:DECIMAL_MISMATCH"); + require(scalar_ > 0, "M:C:ZERO_SCALAR"); - globals = globals_; + require(IERC20Like(newToken_).decimals() == IERC20Like(oldToken_).decimals(), "M:C:DECIMAL_MISMATCH"); + globals = globals_; oldToken = oldToken_; newToken = newToken_; diff --git a/contracts/interfaces/IMigrator.sol b/contracts/interfaces/IMigrator.sol index 60124e4..b543a0a 100644 --- a/contracts/interfaces/IMigrator.sol +++ b/contracts/interfaces/IMigrator.sol @@ -10,7 +10,7 @@ interface IMigrator { function active() external view returns (bool active_); /** - * @dev Gets the Maple Globals address + * @dev Gets the Maple Globals address. * @param globals_ The address of the Maple globals. */ function globals() external view returns (address globals_); diff --git a/tests/Migrator.t.sol b/tests/Migrator.t.sol index 4abca5b..a24a5f2 100644 --- a/tests/Migrator.t.sol +++ b/tests/Migrator.t.sol @@ -13,7 +13,7 @@ contract MigratorConstructorTest is Test { function test_constructor_zeroScalar() external { vm.expectRevert("M:C:ZERO_SCALAR"); - new Migrator(address(0), address(0), 0); + new Migrator(address(0), address(0), address(0), 0); } function test_constructor_mismatch_decimals() external { @@ -43,7 +43,7 @@ contract MigratorConstructorTest is Test { contract SetActiveTests is Test { uint256 internal constant SCALAR = 10; - uint256 internal constant OLD_SUPPLY = 10_000_000 ether; + uint256 internal constant OLD_SUPPLY = 10_000_000e18; address operationalAdmin = makeAddr("operationalAdmin"); address governor = makeAddr("governor"); @@ -342,7 +342,7 @@ contract MigratorTest is Test { contract TokenSplitScalars is Test { - uint256 internal constant OLD_SUPPLY = 10_000_000 ether; + uint256 internal constant OLD_SUPPLY = 10_000_000e18; address operationalAdmin = makeAddr("operationalAdmin"); address account = makeAddr("account"); From e672439acb6bd6499d4602c5a85c1c1be1492a68 Mon Sep 17 00:00:00 2001 From: JG Carvalho Date: Fri, 7 Jun 2024 15:19:24 -0400 Subject: [PATCH 7/7] feat: include setActive on interface --- contracts/Migrator.sol | 2 +- contracts/interfaces/IMigrator.sol | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/contracts/Migrator.sol b/contracts/Migrator.sol index a0a20f7..7d57740 100644 --- a/contracts/Migrator.sol +++ b/contracts/Migrator.sol @@ -41,7 +41,7 @@ contract Migrator is IMigrator { require(ERC20Helper.transfer(newToken, owner_, amount_ * tokenSplitScalar), "M:M:TRANSFER_FAILED"); } - function setActive(bool active_) external { + function setActive(bool active_) external override { require( msg.sender == IGlobalsLike(globals).governor() || msg.sender == IGlobalsLike(globals).operationalAdmin(), diff --git a/contracts/interfaces/IMigrator.sol b/contracts/interfaces/IMigrator.sol index b543a0a..d1dc709 100644 --- a/contracts/interfaces/IMigrator.sol +++ b/contracts/interfaces/IMigrator.sol @@ -40,6 +40,12 @@ interface IMigrator { */ function migrate(address owner_, uint256 amount_) external; + /** + * @dev Set the migrator to active or inactive. + * @param active_ True if migrations are active. + */ + function setActive(bool active_) external; + /** * @dev Get the scalar value for token split. * @return tokenSplitScalar_ The scalar value for token split.