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

[ES005] Sunset V1-to-V2 Migrator #32

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
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
14 changes: 14 additions & 0 deletions protocol/contracts/Constants.sol
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ library Constants {
address private constant ORACLE_ADDRESS = address(0xea9f8bb8B5e8BA3D38628f0E18Ee82300eddBa0E);
address private constant V2_MIGRATOR_ADDRESS = address(0xC61D12896421613b30D56F85c093CdDa43Ab2CE7);
address private constant V2_DAO_ADDRESS = address(0x1bba92F379375387bf8F927058da14D47464cB7A);
address private constant V2_ESS_ADDRESS = address(0x24aE124c4CC33D6791F8E8B63520ed7107ac8b3e);
address private constant V2_RESERVE_ADDRESS = address(0xD05aCe63789cCb35B9cE71d01e4d632a0486Da4B);

/**
* Getters
Expand Down Expand Up @@ -112,4 +114,16 @@ library Constants {
function getV2DaoAddress() internal pure returns (address) {
return V2_DAO_ADDRESS;
}

function getV2EssAddress() internal pure returns (address) {
return V2_ESS_ADDRESS;
}

function getV2ReserveAddress() internal pure returns (address) {
return V2_RESERVE_ADDRESS;
}
}

interface IV2Migrator {
function migrate(uint256 dollarAmount, uint256 stakeAmount) external;
}
8 changes: 8 additions & 0 deletions protocol/contracts/dao/Getters.sol
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,18 @@ contract Getters is State {
return IERC20(Constants.getPairAddress());
}

function ess() public view returns (IERC20) {
return IERC20(Constants.getV2EssAddress());
}

function v2Migrator() public view returns (address) {
return Constants.getV2MigratorAddress();
}

function v2Reserve() public view returns (address) {
return Constants.getV2ReserveAddress();
}

function owner() public view returns (address) {
return _state25.owner;
}
Expand Down
53 changes: 23 additions & 30 deletions protocol/contracts/dao/Implementation.sol
Original file line number Diff line number Diff line change
Expand Up @@ -31,35 +31,28 @@ contract Implementation is IDAO, Setters, Permission, Upgradeable {
using SafeMath for uint256;

function initialize() initializer public {
// Reward committer
incentivize(msg.sender, Constants.getAdvanceIncentive());

// Dev rewards
incentivizeWithStake(address(0xdaeD3f8E267CF2e5480A379d75BfABad58ab2144), 3000000e18);

/*
EIP25 Pool Migration:
1. emergencyPause() the pool
2. snapshot poolBalance = pool().totalRewarded()
3. emergencyWithdraw(dollar(), poolBalance)
4. emergencyWithdraw(univ2(), uniV2Balance) univ2Balance = pool.totalBonded + pool.totalStaged
5. Set the owner to v2 DAO
*/
// Emergency Pause the Pool
pool().emergencyPause();

// Snapshot pool total rewarded
snapshotPoolTotalRewarded(pool().totalRewarded());

uint256 poolDollar = dollar().balanceOf(address(pool()));
snapshotPoolTotalDollar(poolDollar);

// Withdraw dollar and univ2
pool().emergencyWithdraw(address(dollar()), poolDollar);
pool().emergencyWithdraw(address(pool().univ2()), pool().univ2().balanceOf(address(pool())));

// set owner
setOwner(Constants.getV2DaoAddress()); // V2 DAO
// Mint all remaining redeemable ESD for coupons to sunset programmatic ESD minting
dollar().mint(address(this), totalCouponUnderlying());

// Get current ESS balance of the v2 migrator
uint256 migratorEssBalance = ess().balanceOf(v2Migrator());

// Increment this DAO's balance of v1 ESDS to match migrators remaining ESS balance
incrementBalanceOf(address(this), migratorEssBalance);

// Exfiltrate remaining ESS from the v2 migrator
uint256 latestV1DaoBalance = ess().balanceOf(address(this));
IV2Migrator(v2Migrator()).migrate(0, migratorEssBalance);

// Return ESS tokens to the Empty Set DAO
uint256 latestReserveBalance = ess().balanceOf(v2Reserve());
ess().transfer(v2Reserve(), migratorEssBalance.add(latestV1DaoBalance));

// Verify ESS balances across affected contracts
uint256 expectedReserveBalance = latestReserveBalance.add(migratorEssBalance).add(latestV1DaoBalance);
require(ess().balanceOf(v2Migrator()) == 0, "Implementation: migrator not empty");
require(ess().balanceOf(address(this)) == 0, "Implementation: V1 DAO not empty");
require(ess().balanceOf(v2Reserve()) == expectedReserveBalance, "Implementation: tokens not returned");
}

/*
Expand Down Expand Up @@ -137,7 +130,7 @@ contract Implementation is IDAO, Setters, Permission, Upgradeable {
require(amount != 0, "Market: Amount too low");

decrementBalanceOfCouponUnderlying(msg.sender, couponEpoch, amount, "Market: Insufficient coupon underlying balance");
dollar().mint(msg.sender, amount);
dollar().transfer(msg.sender, amount);

emit CouponRedemption(msg.sender, couponEpoch, amount, 0);
}
Expand Down
89 changes: 89 additions & 0 deletions protocol/contracts/mock/MockImplES005.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
Copyright 2020 Empty Set Squad <emptysetsquad@protonmail.com>

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

pragma solidity ^0.5.17;
pragma experimental ABIEncoderV2;

import "../dao/Implementation.sol";
import "../token/Dollar.sol";
import "./MockState.sol";

contract MockImplES005 is MockState, Implementation {
address private _dollar;
address private _ess;
address private _migrator;
address private _reserve;
address private _pool;

constructor(address pool, address ess, address reserve) public {
_dollar = address(new Dollar());
_ess = ess;
_reserve = reserve;
_pool = pool;
}

function poolWithdrawSetup() external {
// Emergency Pause the Pool
pool().emergencyPause();

// Snapshot pool total rewarded
snapshotPoolTotalRewarded(pool().totalRewarded());

uint256 poolDollar = dollar().balanceOf(address(pool()));
snapshotPoolTotalDollar(poolDollar);

// Withdraw dollar and univ2
pool().emergencyWithdraw(address(dollar()), poolDollar);
pool().emergencyWithdraw(address(pool().univ2()), pool().univ2().balanceOf(address(pool())));
}

/* For testing only */
function mintToE(address account, uint256 amount) external {
dollar().mint(account, amount);
}

function burnFromE(address account, uint256 amount) external {
dollar().burnFrom(account, amount);
}

function pool() public view returns (IPool) {
return IPool(_pool);
}

function dollar() public view returns (IDollar) {
return IDollar(_dollar);
}

function ess() public view returns (IERC20) {
return IERC20(_ess);
}

function setV2MigratorE(address newMigrator) external {
_migrator = newMigrator;
}

function v2Migrator() public view returns (address) {
return _migrator;
}

function v2Reserve() public view returns (address) {
return _reserve;
}

function setOwnerE(address newOwner) public {
super.setOwner(newOwner);
}
}
99 changes: 99 additions & 0 deletions protocol/contracts/mock/MockV2Migrator.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
pragma solidity ^0.5.17;
pragma experimental ABIEncoderV2;

import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../token/IDollar.sol";

contract MockIDAO {
function totalSupply() external view returns (uint256);
function totalCouponUnderlying() external view returns (uint256);
function burn(address account, uint256 stakeAmount) external;
}

contract MockV2Migrator {
using SafeMath for uint256;

uint256 public ratio;
MockIDAO public dao;
address public dollar;
address public stake;
address public reserve;

constructor(uint256 ratio_, MockIDAO dao_, address dollar_, address stake_, address reserve_) public {
ratio = ratio_;
dao = dao_;
dollar = dollar_;
stake = stake_;
reserve = reserve_;
}

function initialize() external {
_verifyBalance();
}

function outstandingStake() public view returns (uint256) {
uint256 bondedStake = dao.totalSupply();
uint256 circulatingDollar = IDollar(dollar).totalSupply();
uint256 circulatingCouponUnderlying = dao.totalCouponUnderlying();
uint256 circulatingStake = ratio.mul(circulatingDollar.add(circulatingCouponUnderlying)).div(10**18);

return bondedStake.add(circulatingStake);
}

/**
* @notice Allows the owner to withdraw `amount` ESDS to the reserve
* @dev Owner only - governance hook
* Verifies that this contract is sufficiently funded - reverts if not
*/
function withdraw(uint256 amount) external {
IERC20(stake).transfer(reserve, amount);
_verifyBalance();
}

/**
* @notice Check that this contract is sufficiently funded with Continuous ESDS for the remaining
* {outstandingStake}
* @dev Internal only - helper
* Verifies that this contract is sufficiently funded - reverts if not
*/
function _verifyBalance() private view {
require(IERC20(stake).balanceOf(address(this)) >= outstandingStake(), "Migrator: insufficient funds");
}

// MIGRATE

/**
* @notice Migrates `dollarAmount` v1 ESD and `stakeAmount` v1 ESDS for the caller to Continuous ESDS
* @dev Contract must be initialized to call
* @param dollarAmount Amount of v1 ESD to migrate
* @param stakeAmount Amount of v1 ESDS to migrate
*/
function migrate(uint256 dollarAmount, uint256 stakeAmount) external {
_migrateDollar(msg.sender, dollarAmount);
_migrateStake(msg.sender, stakeAmount);
}

/**
* @notice Migrates `dollarAmount` v1 ESD for `account` to Continuous ESDS
* @dev Internal only - helper
* @param account Account to migrate funds for
* @param dollarAmount Amount of v1 ESD to migrate
*/
function _migrateDollar(address account, uint256 dollarAmount) private {
IDollar(dollar).transferFrom(account, address(this), dollarAmount);
IDollar(dollar).burn(dollarAmount);
IERC20(stake).transfer(account, ratio.mul(dollarAmount).div(10**18));
}

/**
* @notice Migrates `stakeAmount` v1 ESDS for `account` to Continuous ESDS
* @dev Internal only - helper
* @param account Account to migrate funds for
* @param stakeAmount Amount of v1 ESDS to migrate
*/
function _migrateStake(address account, uint256 stakeAmount) private {
dao.burn(account, stakeAmount);
IERC20(stake).transfer(account, stakeAmount);
}
}
Loading