Skip to content

Commit

Permalink
feat: Added migrator contract and tests (SC-4769) (#1)
Browse files Browse the repository at this point in the history
* feat: added migrator contract and tests

* fix: adjust slither target

* fix: PR ci

* fix: increase runs for push to main

* fix: PR comments

* fix readme

* fix readme

* feat: update README, test.sh, add newlines

Co-authored-by: Lucas Manuel <[email protected]>
  • Loading branch information
JGcarv and lucas-manuel authored Feb 17, 2022
1 parent e44e029 commit 9f8566a
Show file tree
Hide file tree
Showing 11 changed files with 256 additions and 32 deletions.
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_);

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

0 comments on commit 9f8566a

Please sign in to comment.