Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Added migrator contract and tests (SC-4769) #1

Merged
merged 8 commits into from
Feb 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 14 additions & 10 deletions .github/workflows/pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,19 @@ jobs:
run-ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v2

- name: Install Foundry
uses: onbjerg/foundry-toolchain@v1
with:
version: nightly
- name: Install Foundry
uses: onbjerg/foundry-toolchain@v1
with:
version: nightly

- name: Pull library deps
run: forge update

- name: Run forge tests
run: ./test.sh -r 1000
- name: Checkout submodules
shell: bash
run: |
git config --global url."https://github.com/".insteadOf "[email protected]:"
forge update
- name: Run forge tests
env:
ETH_RPC_URL: ${{ secrets.ETH_RPC_URL }}
run: ./test.sh -r 10000
26 changes: 15 additions & 11 deletions .github/workflows/push-to-main.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: 50k fuzz run test on push to main
name: SC Tests

on:
push:
Expand All @@ -9,15 +9,19 @@ jobs:
run-ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v2

- name: Install Foundry
uses: onbjerg/foundry-toolchain@v1
with:
version: nightly
- name: Install Foundry
uses: onbjerg/foundry-toolchain@v1
with:
version: nightly

- name: Pull library deps
run: forge update

- name: Run forge tests
run: ./test.sh -r 50000
- name: Checkout submodules
shell: bash
run: |
git config --global url."https://github.com/".insteadOf "[email protected]:"
forge update
- name: Run forge tests
env:
ETH_RPC_URL: ${{ secrets.ETH_RPC_URL }}
run: ./test.sh -r 1000000
12 changes: 6 additions & 6 deletions .github/workflows/slither.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,25 +30,25 @@ jobs:

- name: Summary of static analysis
run: |
slither src --print human-summary
slither contracts --print human-summary

- name: Contract summary of static analysis
run: |
slither src --print contract-summary
slither contracts --print contract-summary

- name: Function summary
run: |
slither src --print function-summary
slither contracts --print function-summary

- name: Inheritance
run: |
slither src --print inheritance
slither contracts --print inheritance

- name: Data dependency
run: |
slither src --print data-dependency
slither contracts --print data-dependency

- name: Static Analysis
run: |
slither src
slither contracts
continue-on-error: true
9 changes: 9 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[submodule "modules/erc20"]
path = modules/erc20
url = [email protected]:maple-labs/erc20.git
[submodule "modules/erc20-helper"]
path = modules/erc20-helper
url = [email protected]:maple-labs/erc20-helper.git
[submodule "modules/contract-test-utils"]
path = modules/contract-test-utils
url = [email protected]:maple-labs/contract-test-utils.git
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
# MPLMigration
# MPL Migration

![Foundry CI](https://github.com/maple-labs/loan/actions/workflows/push-to-main.yml/badge.svg) [![License: AGPL v3](https://img.shields.io/badge/License-AGPL%20v3-blue.svg)](https://www.gnu.org/licenses/agpl-3.0)

**DISCLAIMER: This code has NOT been externally audited and is actively being developed. Please do not use in production without taking the appropriate steps to ensure maximum security.**

This repo contains a set of contracts to facilitate on-chain distribution of vesting earnings on an aggregated schedule. This allows for multiple deposits to be made to the same contract on a recurring basis with custom vesting parameters.
This repo contains a set of contracts to facilitate MPL token migration.

## Testing and Development
#### Setup
```sh
git clone [email protected]:maple-labs/revenue-distribution-token.git
cd revenue-distribution-token
git clone [email protected]:maple-labs/mpl-migration.git
cd mpl-migration
forge update
```
#### Running Tests
Expand Down
23 changes: 23 additions & 0 deletions contracts/Migrator.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.7;

import { ERC20Helper } from "../modules/erc20-helper/src/ERC20Helper.sol";

contract Migrator {

address immutable oldToken;
address immutable newToken;

constructor(address oldToken_, address newToken_) {
oldToken = oldToken_;
newToken = newToken_;
}

function migrate(uint256 amount_) external {
require(amount_ > 0, "M:M:ZERO_AMOUNT");

require(ERC20Helper.transferFrom(oldToken, msg.sender, address(this), amount_), "M:M:TRANSFER_FROM_FAILED");
require(ERC20Helper.transfer(newToken, msg.sender, amount_), "M:M:TRANSFER_FAILED");
}

}
178 changes: 178 additions & 0 deletions contracts/test/Migrator.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.7;

import { TestUtils } from "../../modules/contract-test-utils/contracts/test.sol";

import { MockERC20 } from "../../modules/erc20/src/test/mocks/MockERC20.sol";

import { Migrator } from "../Migrator.sol";

contract MigratorTest is TestUtils {

uint256 public constant OLD_SUPPLY = 10_000_000 ether;

Migrator migrator;
MockERC20 oldToken;
MockERC20 newToken;

function setUp() external {
oldToken = new MockERC20("Old Token", "OT", 18);
newToken = new MockERC20("New Token", "NT", 18);

migrator = new Migrator(address(oldToken), address(newToken));

// Mint new token to migrator
newToken.mint(address(migrator), OLD_SUPPLY);
}

function test_migration(uint256 amount_) external {
amount_ = constrictToRange(amount_, 1, OLD_SUPPLY);

// Mint amount of old token
oldToken.mint(address(this), amount_);

// Approve
oldToken.approve(address(migrator), amount_);

assertEq(oldToken.balanceOf(address(this)), amount_);
assertEq(oldToken.balanceOf(address(migrator)), 0);
assertEq(oldToken.allowance(address(this), address(migrator)), amount_);
assertEq(newToken.balanceOf(address(this)), 0);
assertEq(newToken.balanceOf(address(migrator)), OLD_SUPPLY);

migrator.migrate(amount_);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should include all below assertions above this to reflect "before" state


assertEq(oldToken.balanceOf(address(this)), 0);
assertEq(oldToken.balanceOf(address(migrator)), amount_);
assertEq(oldToken.allowance(address(this), address(migrator)), 0);
assertEq(newToken.balanceOf(address(this)), amount_);
assertEq(newToken.balanceOf(address(migrator)), OLD_SUPPLY - amount_);
}

function test_partialMigration(uint256 amount_, uint256 partialAmount_) external {
amount_ = constrictToRange(amount_, 2, OLD_SUPPLY);
partialAmount_ = constrictToRange(partialAmount_, 1, amount_ - 1);

// Mint amount of old token
oldToken.mint(address(this), amount_);

// Approve partial
oldToken.approve(address(migrator), partialAmount_);

assertEq(oldToken.balanceOf(address(this)), amount_);
assertEq(oldToken.balanceOf(address(migrator)), 0);
assertEq(oldToken.allowance(address(this), address(migrator)), partialAmount_);
assertEq(newToken.balanceOf(address(this)), 0);
assertEq(newToken.balanceOf(address(migrator)), OLD_SUPPLY);

migrator.migrate(partialAmount_);

assertEq(oldToken.balanceOf(address(this)), amount_ - partialAmount_);
assertEq(oldToken.balanceOf(address(migrator)), partialAmount_);
assertEq(oldToken.allowance(address(this), address(migrator)), 0);
assertEq(newToken.balanceOf(address(this)), partialAmount_);
assertEq(newToken.balanceOf(address(migrator)), OLD_SUPPLY - partialAmount_);

uint256 remaining = amount_ - partialAmount_;

oldToken.approve(address(migrator), remaining);

migrator.migrate(remaining);

assertEq(oldToken.balanceOf(address(this)), 0);
assertEq(oldToken.balanceOf(address(migrator)), amount_);
assertEq(oldToken.allowance(address(this), address(migrator)), 0);
assertEq(newToken.balanceOf(address(this)), amount_);
assertEq(newToken.balanceOf(address(migrator)), OLD_SUPPLY - amount_);
}

function test_zeroAmount() external {
uint256 amount_ = 0;

vm.expectRevert("M:M:ZERO_AMOUNT");
migrator.migrate(amount_);

amount_ = 1;

oldToken.mint(address(this), amount_);
oldToken.approve(address(migrator), amount_);

migrator.migrate(amount_);

assertEq(oldToken.balanceOf(address(this)), 0);
assertEq(oldToken.balanceOf(address(migrator)), amount_);
assertEq(oldToken.allowance(address(this), address(migrator)), 0);
assertEq(newToken.balanceOf(address(this)), amount_);
assertEq(newToken.balanceOf(address(migrator)), OLD_SUPPLY - amount_);
}

function test_failWithoutApprove(uint256 amount_) external {
amount_ = constrictToRange(amount_, 1, OLD_SUPPLY);

// Mint amount of old token
oldToken.mint(address(this), amount_);

vm.expectRevert("M:M:TRANSFER_FROM_FAILED");
migrator.migrate(amount_);

// Approve
oldToken.approve(address(migrator), amount_);

migrator.migrate(amount_);

assertEq(oldToken.balanceOf(address(this)), 0);
assertEq(oldToken.balanceOf(address(migrator)), amount_);
assertEq(oldToken.allowance(address(this), address(migrator)), 0);
assertEq(newToken.balanceOf(address(this)), amount_);
assertEq(newToken.balanceOf(address(migrator)), OLD_SUPPLY - amount_);
}

function test_failWithoutBalance(uint256 amount_) external {
amount_ = constrictToRange(amount_, 1, OLD_SUPPLY);

oldToken.mint(address(this), amount_ - 1);

oldToken.approve(address(migrator), amount_);

vm.expectRevert("M:M:TRANSFER_FROM_FAILED");
migrator.migrate(amount_);

// Mint
oldToken.mint(address(this), 1);

migrator.migrate(amount_);

assertEq(oldToken.balanceOf(address(this)), 0);
assertEq(oldToken.balanceOf(address(migrator)), amount_);
assertEq(oldToken.allowance(address(this), address(migrator)), 0);
assertEq(newToken.balanceOf(address(this)), amount_);
assertEq(newToken.balanceOf(address(migrator)), OLD_SUPPLY - amount_);
}

function test_failWithoutNewToken(uint256 amount_) external {
amount_ = constrictToRange(amount_, 1, OLD_SUPPLY);

// Burn new supply that was added in setUp
newToken.burn(address(migrator), OLD_SUPPLY - amount_ + 1);

// Mint amount of old token
oldToken.mint(address(this), amount_);

// Approve
oldToken.approve(address(migrator), amount_);

vm.expectRevert("M:M:TRANSFER_FAILED");
migrator.migrate(amount_);

newToken.mint(address(migrator), 1);

migrator.migrate(amount_);

assertEq(oldToken.balanceOf(address(this)), 0);
assertEq(oldToken.balanceOf(address(migrator)), amount_);
assertEq(oldToken.allowance(address(this), address(migrator)), 0);
assertEq(newToken.balanceOf(address(this)), amount_);
assertEq(newToken.balanceOf(address(migrator)), 0);
}

}
1 change: 1 addition & 0 deletions modules/contract-test-utils
Submodule contract-test-utils added at f00859
1 change: 1 addition & 0 deletions modules/erc20
Submodule erc20 added at c3d599
1 change: 1 addition & 0 deletions modules/erc20-helper
Submodule erc20-helper added at d674c6
5 changes: 4 additions & 1 deletion test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@ done
runs=$([ -z "$runs" ] && echo "10" || echo "$runs")

export DAPP_SOLC_VERSION=0.8.7
export DAPP_SRC="contracts"
export DAPP_LIB="modules"
export PROPTEST_CASES=$runs
export RUST_BACKTRACE=full

if [ -z "$test" ]; then match="[src/test/*.t.sol]"; else match=$test; fi
if [ -z "$test" ]; then match="[contracts/test/*.t.sol]"; else match=$test; fi

forge test --match "$match" -vvv