diff --git a/contracts/Migrator.sol b/contracts/Migrator.sol index 43e3706..a12f450 100644 --- a/contracts/Migrator.sol +++ b/contracts/Migrator.sol @@ -3,7 +3,7 @@ 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"; @@ -11,12 +11,17 @@ contract Migrator is IMigrator { uint256 public immutable override TOKEN_SPLIT_SCALAR; + address public immutable override globals; address public immutable override newToken; address public immutable override oldToken; - constructor(address oldToken_, address newToken_, uint256 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_; @@ -28,10 +33,21 @@ contract Migrator is IMigrator { } function migrate(address owner_, uint256 amount_) public override { - require(amount_ != uint256(0), "M:M:ZERO_AMOUNT"); + 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_ * TOKEN_SPLIT_SCALAR), "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 2fb2158..f570613 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 149d982..fb9b1cc 100644 --- a/tests/Migrator.t.sol +++ b/tests/Migrator.t.sol @@ -7,44 +7,119 @@ 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_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.globals(), address(globals)); assertEq(migrator.oldToken(), address(oldToken)); assertEq(migrator.newToken(), address(newToken)); } } +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); + } + +} + contract MigratorTest is Test { uint256 internal constant SCALAR = 10; 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; + 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); @@ -69,6 +144,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); @@ -255,15 +338,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 { @@ -273,7 +362,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_; + } + +}