Skip to content

Commit

Permalink
Make migration contract upgradeable, remove release functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
gretzke committed Sep 1, 2023
1 parent 59876e2 commit f10a0a4
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 100 deletions.
20 changes: 15 additions & 5 deletions script/Deploy.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,17 @@ contract Deploy is Script {
) public {
vm.startBroadcast(deployerPrivateKey);

PolygonMigration migration = new PolygonMigration(matic, governance);
address migrationImplementation = address(new PolygonMigration());

address migrationProxy = address(
new TransparentUpgradeableProxy(
address(migrationImplementation),
governance,
""
)
);

PolygonMigration(migrationProxy).initialize(matic);

address inflationManagerImplementation = address(
new DefaultInflationManager()
Expand All @@ -37,21 +47,21 @@ contract Deploy is Script {
);

Polygon polygonToken = new Polygon(
address(migration),
address(migrationProxy),
address(inflationManagerProxy)
);

DefaultInflationManager(inflationManagerProxy).initialize(
address(polygonToken),
address(migration),
address(migrationProxy),
stakeManager,
treasury,
governance
);

migration.setPolygonToken(address(polygonToken));
PolygonMigration(migrationProxy).setPolygonToken(address(polygonToken));

migration.transferOwnership(governance); // governance needs to accept the ownership transfer
PolygonMigration(migrationProxy).transferOwnership(governance); // governance needs to accept the ownership transfer

vm.stopBroadcast();
}
Expand Down
46 changes: 16 additions & 30 deletions src/PolygonMigration.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,36 @@ pragma solidity 0.8.21;
import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import {IERC20Permit} from "openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Permit.sol";
import {SafeERC20} from "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";
import {Ownable2Step} from "openzeppelin-contracts/contracts/access/Ownable2Step.sol";
import {Ownable2StepUpgradeable} from "openzeppelin-contracts-upgradeable/contracts/access/Ownable2StepUpgradeable.sol";
import {IPolygonMigration} from "./interfaces/IPolygonMigration.sol";

/// @title Polygon Migration
/// @author Polygon Labs (@DhairyaSethi, @gretzke, @qedk)
/// @notice This is the migration contract for Matic <-> Polygon ERC20 token on Ethereum L1
/// @dev The contract allows for a 1-to-1 conversion from $MATIC into $POL and vice-versa
/// @custom:security-contact [email protected]
contract PolygonMigration is Ownable2Step, IPolygonMigration {
contract PolygonMigration is Ownable2StepUpgradeable, IPolygonMigration {
using SafeERC20 for IERC20;
using SafeERC20 for IERC20Permit;

IERC20 public polygon;
IERC20 public immutable matic;
uint256 public releaseTimestamp;
IERC20 public matic;
bool public unmigrationLocked;

modifier onlyUnmigrationUnlocked() {
if (unmigrationLocked) revert UnmigrationLocked();
_;
}

constructor(address matic_, address owner_) {
if (matic_ == address(0) || owner_ == address(0))
revert InvalidAddress();
constructor() {
// so that the implementation contract cannot be initialized
_disableInitializers();
}

function initialize(address matic_) external initializer {
__Ownable_init();
if (matic_ == address(0)) revert InvalidAddress();
matic = IERC20(matic_);
releaseTimestamp = block.timestamp + (365 days * 4); // 4 years
}

/// @notice This function allows owner/governance to set POL token address *only once*
Expand Down Expand Up @@ -98,15 +100,6 @@ contract PolygonMigration is Ownable2Step, IPolygonMigration {
matic.safeTransfer(msg.sender, amount);
}

/// @notice Allows governance to update the release timestamp if required
/// @dev The function does not do any validation since governance can correct the timestamp if required
/// @param timestamp_ New release timestamp
function updateReleaseTimestamp(uint256 timestamp_) external onlyOwner {
if (timestamp_ < block.timestamp) revert InvalidTimestamp();
releaseTimestamp = timestamp_;
emit ReleaseTimestampUpdated(timestamp_);
}

/// @notice Allows governance to lock or unlock the unmigration process
/// @dev The function does not do any validation since governance can update the unmigration process if required
/// @param unmigrationLocked_ New unmigration lock status
Expand All @@ -115,17 +108,10 @@ contract PolygonMigration is Ownable2Step, IPolygonMigration {
emit UnmigrationLockUpdated(unmigrationLocked_);
}

/// @notice Allows governance to release the remaining POL tokens after the migration period has elapsed
/// @dev In case any MATIC was sent out of process, it will be sent to the dead address
function release() external onlyOwner {
if (block.timestamp < releaseTimestamp) revert MigrationNotOver();
uint256 polAmount = polygon.balanceOf(address(this));
uint256 maticAmount = matic.balanceOf(address(this));
polygon.safeTransfer(msg.sender, polAmount);
matic.safeTransfer(
0x000000000000000000000000000000000000dEaD,
maticAmount
);
emit Released(polAmount, maticAmount);
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
}
4 changes: 0 additions & 4 deletions src/interfaces/IPolygonMigration.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,11 @@ pragma solidity 0.8.21;
interface IPolygonMigration {
error UnmigrationLocked();
error InvalidAddressOrAlreadySet();
error InvalidTimestamp();
error MigrationNotOver();
error InvalidAddress();

event Migrated(address indexed account, uint256 amount);
event Unmigrated(address indexed account, uint256 amount);
event UnmigrationLockUpdated(bool lock);
event ReleaseTimestampUpdated(uint256 timestamp);
event Released(uint256 polAmount, uint256 maticAmount);

function migrate(uint256 amount) external;

Expand Down
11 changes: 10 additions & 1 deletion test/DefaultInflationManager.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,16 @@ contract DefaultInflationManagerTest is Test {
)
);
matic = new ERC20PresetMinterPauser("Matic Token", "MATIC");
migration = new PolygonMigration(address(matic), governance);
migration = PolygonMigration(
address(
new TransparentUpgradeableProxy(
address(new PolygonMigration()),
msg.sender,
""
)
)
);
migration.initialize(address(matic));
polygon = new Polygon(address(migration), address(inflationManager));
migration.setPolygonToken(address(polygon)); // deployer sets token
migration.transferOwnership(governance);
Expand Down
81 changes: 21 additions & 60 deletions test/PolygonMigration.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {Polygon} from "src/Polygon.sol";
import {PolygonMigration} from "src/PolygonMigration.sol";
import {IPolygonMigration} from "src/interfaces/IPolygonMigration.sol";
import {ERC20PresetMinterPauser} from "openzeppelin-contracts/contracts/token/ERC20/presets/ERC20PresetMinterPauser.sol";
import {TransparentUpgradeableProxy} from "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
import {SigUtils} from "test/SigUtils.t.sol";
import {Test} from "forge-std/Test.sol";

Expand All @@ -24,7 +25,16 @@ contract PolygonMigrationTest is Test {
inflationManager = makeAddr("inflationManager");
governance = makeAddr("governance");
matic = new ERC20PresetMinterPauser("Matic Token", "MATIC");
migration = new PolygonMigration(address(matic), governance);
migration = PolygonMigration(
address(
new TransparentUpgradeableProxy(
address(new PolygonMigration()),
msg.sender,
""
)
)
);
migration.initialize(address(matic));
polygon = new Polygon(address(migration), address(inflationManager));
sigUtils = new SigUtils(polygon.DOMAIN_SEPARATOR());

Expand All @@ -37,21 +47,21 @@ contract PolygonMigrationTest is Test {
function test_Deployment() external {
assertEq(address(migration.polygon()), address(polygon));
assertEq(address(migration.matic()), address(matic));
assertEq(
migration.releaseTimestamp(),
block.timestamp + (365 days * 4)
);
assertEq(migration.owner(), governance);
}

function test_InvalidDeployment() external {
PolygonMigration temp;
vm.expectRevert(IPolygonMigration.InvalidAddress.selector);
temp = new PolygonMigration(address(matic), address(0));
vm.expectRevert(IPolygonMigration.InvalidAddress.selector);
temp = new PolygonMigration(address(0), governance);
PolygonMigration temp = PolygonMigration(
address(
new TransparentUpgradeableProxy(
address(new PolygonMigration()),
msg.sender,
""
)
)
);
vm.expectRevert(IPolygonMigration.InvalidAddress.selector);
temp = new PolygonMigration(address(0), address(0));
temp.initialize(address(0));
}

function test_Migrate(address user, uint256 amount) external {
Expand Down Expand Up @@ -210,53 +220,4 @@ contract PolygonMigrationTest is Test {
assertEq(matic.balanceOf(address(migration)), amount - amount2);
assertEq(matic.balanceOf(user), amount2);
}

function testRevert_UpdateReleaseTimestampOnlyGovernance(
address user,
uint256 timestamp
) external {
vm.assume(timestamp >= block.timestamp && user != governance);
vm.expectRevert("Ownable: caller is not the owner");
migration.updateReleaseTimestamp(timestamp);
}

function testRevert_UpdateReleaseTimestampTooEarly(
uint256 timestamp
) external {
vm.assume(timestamp < block.timestamp);
vm.startPrank(governance);
vm.expectRevert(IPolygonMigration.InvalidTimestamp.selector);
migration.updateReleaseTimestamp(timestamp);
}

function test_UpdateReleaseTimestamp(uint256 timestamp) external {
vm.assume(timestamp >= block.timestamp);
vm.startPrank(governance);
migration.updateReleaseTimestamp(timestamp);

assertEq(migration.releaseTimestamp(), timestamp);
}

function testRevert_ReleaseOnlyGovernance() external {
vm.expectRevert("Ownable: caller is not the owner");
migration.release();
}

function testRevert_ReleaseTooEarly(uint256 timestamp) external {
vm.assume(timestamp < migration.releaseTimestamp());
vm.startPrank(governance);
vm.expectRevert(IPolygonMigration.MigrationNotOver.selector);
migration.release();
}

function test_Release(uint256 timestamp) external {
vm.assume(timestamp >= migration.releaseTimestamp());
vm.warp(timestamp);
uint256 balance = polygon.balanceOf(address(migration));
vm.startPrank(governance);
migration.release();

assertEq(polygon.balanceOf(address(migration)), 0);
assertEq(polygon.balanceOf(governance), balance);
}
}

0 comments on commit f10a0a4

Please sign in to comment.