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

Auto rep migration support #599

Merged
merged 4 commits into from Apr 4, 2018
Merged
Changes from 3 commits
Commits
File filter...
Filter file types
Jump to…
Jump to file or symbol
Failed to load files and symbols.

Always

Just for now

@@ -0,0 +1,26 @@
pragma solidity 0.4.20;

import 'libraries/ContractExists.sol';
import 'reporting/ReputationToken.sol';


contract TestNetReputationToken is ReputationToken {
using ContractExists for address;

uint256 private constant DEFAULT_FAUCET_AMOUNT = 47 ether;
address private constant FOUNDATION_REP_ADDRESS = address(0xE94327D07Fc17907b4DB788E5aDf2ed424adDff6);

function TestNetReputationToken() public {
// This is to confirm we are not on foundation network
require(!FOUNDATION_REP_ADDRESS.exists());
}

function faucet(uint256 _amount) public whenNotMigratingFromLegacy returns (bool) {
if (_amount == 0) {
_amount = DEFAULT_FAUCET_AMOUNT;
}
require(_amount < 2 ** 128);
mint(msg.sender, _amount);
return true;
}
}
@@ -42,8 +42,7 @@ contract StandardToken is ERC20, BasicToken {
* @param _value The amount of tokens to be spent.
*/
function approve(address _spender, uint256 _value) public returns (bool) {
allowed[msg.sender][_spender] = _value;
Approval(msg.sender, _spender, _value);
approveInternal(msg.sender, _spender, _value);
return true;
}

@@ -65,8 +64,7 @@ contract StandardToken is ERC20, BasicToken {
* @param _addedValue The amount of tokens to increase the allowance by.
*/
function increaseApproval(address _spender, uint _addedValue) public returns (bool) {
allowed[msg.sender][_spender] = allowed[msg.sender][_spender].add(_addedValue);
Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
approveInternal(msg.sender, _spender, allowed[msg.sender][_spender].add(_addedValue));
return true;
}

@@ -80,11 +78,16 @@ contract StandardToken is ERC20, BasicToken {
function decreaseApproval(address _spender, uint _subtractedValue) public returns (bool) {
uint oldValue = allowed[msg.sender][_spender];
if (_subtractedValue > oldValue) {
allowed[msg.sender][_spender] = 0;
approveInternal(msg.sender, _spender, 0);
} else {
allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue);
approveInternal(msg.sender, _spender, oldValue.sub(_subtractedValue));
}
Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
return true;
}

function approveInternal(address _owner, address _spender, uint256 _value) internal returns (bool) {
allowed[_owner][_spender] = _value;
Approval(_owner, _spender, _value);
return true;
}
}
@@ -26,15 +26,39 @@ contract ReputationToken is DelegationTarget, ITyped, Initializable, VariableSup
uint256 private parentTotalTheoreticalSupply;
uint256 private totalTheoreticalSupply;

// Auto migration related state
bool private isMigratingFromLegacy;
uint256 private targetSupply;

/**
* @dev modifier to allow actions only when the contract IS paused
*/
modifier whenMigratingFromLegacy() {
require(isMigratingFromLegacy);
_;
}

/**
* @dev modifier to allow actions only when the contract IS paused
*/
modifier whenNotMigratingFromLegacy() {
require(!isMigratingFromLegacy);
_;
}

function initialize(IUniverse _universe) public onlyInGoodTimes beforeInitialized returns (bool) {
endInitialization();
require(_universe != address(0));
universe = _universe;
updateParentTotalTheoreticalSupply();
ERC20 _legacyRepToken = getLegacyRepToken();
// Initialize migration related state. If this is Genesis universe REP the balances from the Legacy contract must be migrated before we enable usage
isMigratingFromLegacy = _universe.getParentUniverse() == IUniverse(0);
targetSupply = _legacyRepToken.totalSupply();
return true;
}

function migrateOutByPayout(uint256[] _payoutNumerators, bool _invalid, uint256 _attotokens) public onlyInGoodTimes afterInitialized returns (bool) {
function migrateOutByPayout(uint256[] _payoutNumerators, bool _invalid, uint256 _attotokens) public onlyInGoodTimes whenNotMigratingFromLegacy afterInitialized returns (bool) {
require(_attotokens > 0);
IUniverse _destinationUniverse = universe.createChildUniverse(_payoutNumerators, _invalid);
IReputationToken _destination = _destinationUniverse.getReputationToken();
@@ -43,15 +67,15 @@ contract ReputationToken is DelegationTarget, ITyped, Initializable, VariableSup
return true;
}

function migrateOut(IReputationToken _destination, uint256 _attotokens) public onlyInGoodTimes afterInitialized returns (bool) {
function migrateOut(IReputationToken _destination, uint256 _attotokens) public onlyInGoodTimes whenNotMigratingFromLegacy afterInitialized returns (bool) {
require(_attotokens > 0);
assertReputationTokenIsLegitSibling(_destination);
burn(msg.sender, _attotokens);
_destination.migrateIn(msg.sender, _attotokens);
return true;
}

function migrateIn(address _reporter, uint256 _attotokens) public onlyInGoodTimes afterInitialized returns (bool) {
function migrateIn(address _reporter, uint256 _attotokens) public onlyInGoodTimes whenNotMigratingFromLegacy afterInitialized returns (bool) {
IUniverse _parentUniverse = universe.getParentUniverse();
require(ReputationToken(msg.sender) == _parentUniverse.getReputationToken());
mint(_reporter, _attotokens);
@@ -69,15 +93,7 @@ contract ReputationToken is DelegationTarget, ITyped, Initializable, VariableSup
return true;
}

function migrateFromLegacyReputationToken() public onlyInGoodTimes afterInitialized returns (bool) {
ERC20 _legacyRepToken = ERC20(controller.lookup("LegacyReputationToken"));
uint256 _legacyBalance = _legacyRepToken.balanceOf(msg.sender);
require(_legacyRepToken.transferFrom(msg.sender, address(0), _legacyBalance));
mint(msg.sender, _legacyBalance);
return true;
}

function mintForReportingParticipant(uint256 _amountMigrated) public onlyInGoodTimes afterInitialized returns (bool) {
function mintForReportingParticipant(uint256 _amountMigrated) public onlyInGoodTimes whenNotMigratingFromLegacy afterInitialized returns (bool) {
IUniverse _parentUniverse = universe.getParentUniverse();
IReportingParticipant _reportingParticipant = IReportingParticipant(msg.sender);
require(_parentUniverse.isContainerForReportingParticipant(_reportingParticipant));
@@ -87,22 +103,30 @@ contract ReputationToken is DelegationTarget, ITyped, Initializable, VariableSup
return true;
}

function trustedUniverseTransfer(address _source, address _destination, uint256 _attotokens) public onlyInGoodTimes afterInitialized returns (bool) {
function transfer(address _to, uint _value) public whenNotMigratingFromLegacy returns (bool) {
return super.transfer(_to, _value);
}

function transferFrom(address _from, address _to, uint _value) public whenNotMigratingFromLegacy returns (bool) {
return super.transferFrom(_from, _to, _value);
}

function trustedUniverseTransfer(address _source, address _destination, uint256 _attotokens) public onlyInGoodTimes whenNotMigratingFromLegacy afterInitialized returns (bool) {
require(IUniverse(msg.sender) == universe);
return internalTransfer(_source, _destination, _attotokens);
}

function trustedMarketTransfer(address _source, address _destination, uint256 _attotokens) public onlyInGoodTimes afterInitialized returns (bool) {
function trustedMarketTransfer(address _source, address _destination, uint256 _attotokens) public onlyInGoodTimes whenNotMigratingFromLegacy afterInitialized returns (bool) {
require(universe.isContainerForMarket(IMarket(msg.sender)));
return internalTransfer(_source, _destination, _attotokens);
}

function trustedReportingParticipantTransfer(address _source, address _destination, uint256 _attotokens) public onlyInGoodTimes afterInitialized returns (bool) {
function trustedReportingParticipantTransfer(address _source, address _destination, uint256 _attotokens) public onlyInGoodTimes whenNotMigratingFromLegacy afterInitialized returns (bool) {
require(universe.isContainerForReportingParticipant(IReportingParticipant(msg.sender)));
return internalTransfer(_source, _destination, _attotokens);
}

function trustedFeeWindowTransfer(address _source, address _destination, uint256 _attotokens) public onlyInGoodTimes afterInitialized returns (bool) {
function trustedFeeWindowTransfer(address _source, address _destination, uint256 _attotokens) public onlyInGoodTimes whenNotMigratingFromLegacy afterInitialized returns (bool) {
require(universe.isContainerForFeeWindow(IFeeWindow(msg.sender)));
return internalTransfer(_source, _destination, _attotokens);
}
@@ -127,7 +151,11 @@ contract ReputationToken is DelegationTarget, ITyped, Initializable, VariableSup
return totalMigrated;
}

function updateSiblingMigrationTotal(IReputationToken _token) public returns (bool) {
function getLegacyRepToken() public view returns (ERC20) {
return ERC20(controller.lookup("LegacyReputationToken"));
}

function updateSiblingMigrationTotal(IReputationToken _token) public whenNotMigratingFromLegacy returns (bool) {
require(_token != this);
IUniverse _shadyUniverse = _token.getUniverse();
require(_token == universe.getParentUniverse().getChildUniverse(_shadyUniverse.getParentPayoutDistributionHash()).getReputationToken());
@@ -137,7 +165,7 @@ contract ReputationToken is DelegationTarget, ITyped, Initializable, VariableSup
return true;
}

function updateParentTotalTheoreticalSupply() public returns (bool) {
function updateParentTotalTheoreticalSupply() public whenNotMigratingFromLegacy returns (bool) {
IUniverse _parentUniverse = universe.getParentUniverse();
totalTheoreticalSupply -= parentTotalTheoreticalSupply;
if (_parentUniverse == IUniverse(0)) {
@@ -167,4 +195,65 @@ contract ReputationToken is DelegationTarget, ITyped, Initializable, VariableSup
controller.getAugur().logReputationTokenBurned(universe, _target, _amount);
return true;
}

/**
* @dev Copies the balance of a batch of addresses from the legacy contract
* @param _holders Array of addresses to migrate balance
* @return True if operation was completed
*/
function migrateBalancesFromLegacyRep(address[] _holders) public onlyInGoodTimes whenMigratingFromLegacy afterInitialized returns (bool) {
ERC20 _legacyRepToken = getLegacyRepToken();
for (uint256 i = 0; i < _holders.length; i++) {
migrateBalanceFromLegacyRep(_holders[i], _legacyRepToken);
}
return true;
}

/**
* @dev Copies the balance of a single addresses from the legacy contract
* @param _holder Address to migrate balance
* @return True if balance was copied, false if was already copied or address had no balance
*/
function migrateBalanceFromLegacyRep(address _holder, ERC20 _legacyRepToken) private onlyInGoodTimes whenMigratingFromLegacy afterInitialized returns (bool) {
if (balances[_holder] > 0) {
return false; // Already copied, move on
}

uint256 amount = _legacyRepToken.balanceOf(_holder);
if (amount == 0) {
return false; // Has no balance in legacy contract, move on
}

mint(_holder, amount);

if (targetSupply == supply) {
isMigratingFromLegacy = false;
}
return true;
}

/**
* @dev Copies the allowances of a batch of addresses from the legacy contract. This is an optional step which may only be done before the migration is complete but is not required to complete it.
* @param _owners Array of owner addresses to migrate allowances
* @param _spenders Array of spender addresses to migrate allowances
* @return True if operation was completed
*/
function migrateAllowancesFromLegacyRep(address[] _owners, address[] _spenders) public onlyInGoodTimes whenMigratingFromLegacy afterInitialized returns (bool) {
ERC20 _legacyRepToken = getLegacyRepToken();
for (uint256 i = 0; i < _owners.length; i++) {
address _owner = _owners[i];
address _spender = _spenders[i];
uint256 _allowance = _legacyRepToken.allowance(_owner, _spender);
approveInternal(_owner, _spender, _allowance);
}
return true;
}

function getIsMigratingFromLegacy() public view returns (bool) {
return isMigratingFromLegacy;
}

function getTargetSupply() public view returns (uint256) {
return targetSupply;
}
}
@@ -1,3 +1,4 @@
import BN = require('bn.js');
import { hash } from 'crypto-promise';
import { exists, readFile, writeFile } from "async-file";
import { exec } from 'child_process';
@@ -8,7 +9,7 @@ import { CompilerOutput } from "solc";
import { Abi, AbiFunction } from 'ethereum';
import { DeployerConfiguration } from './DeployerConfiguration';
import { Connector } from './Connector';
import { Augur, ContractFactory, Controller, Controlled, Universe } from './ContractInterfaces';
import { Augur, ContractFactory, Controller, Controlled, Universe, ReputationToken, LegacyReputationToken } from './ContractInterfaces';
import { NetworkConfiguration } from './NetworkConfiguration';
import { AccountManager } from './AccountManager';
import { Contracts, Contract } from './Contracts';
@@ -57,7 +58,15 @@ Deploying to: ${networkConfiguration.networkName}
await this.whitelistTradingContracts();

if(this.configuration.createGenesisUniverse) {
if (!this.configuration.isProduction) {
this.initializeLegacyRep();
}

this.universe = await this.createGenesisUniverse();

if (!this.configuration.isProduction) {
this.migrateFromlegacyRep();

This comment has been minimized.

Copy link
@adrake33

adrake33 Apr 3, 2018

Member

I'm not sure I understand how this is intended to work on production. I thought the idea was that holders of legacy REP would all be migrated to regular REP automatically. How will this be done?

This comment has been minimized.

Copy link
@nuevoalex

nuevoalex Apr 3, 2018

Author Member

In a non production environment there is only one Legacy REP holder, the deployer of the contracts. See the part right above thise where in a non production environment we mint 11 million Legacy REP for the default address

This comment has been minimized.

Copy link
@adrake33

adrake33 Apr 3, 2018

Member

Right, but isn't the intent to auto-migrate legacy REP users on production? Will they have to call migrateBalancesFromLegacyRep and migrateAllowancesFromLegacyRep manually, or how will these be called on production? Just trying to make sure I understand the intended behavior.

This comment has been minimized.

Copy link
@nuevoalex

nuevoalex Apr 3, 2018

Author Member

These won't be called in production. The reason we're calling them in dev is so that everything is ready to go for development and on testnets. In production we'll need to run a script that migrates balances and allowances before the system functions

This comment has been minimized.

Copy link
@adrake33

adrake33 Apr 3, 2018

Member

Ok, gotcha. That's the part I was trying to clarify.

}
}

await this.generateUploadBlockNumberFile(blockNumber);
@@ -143,8 +152,10 @@ Deploying to: ${networkConfiguration.networkName}
if (contractName === 'Controller') return;
if (contractName === 'Delegator') return;
if (contractName === 'TimeControlled') return;
if (contractName === 'TestNetReputationToken') return;
if (contractName === 'Augur') return;
if (contractName === 'Time') contract = this.configuration.useNormalTime ? contract: this.contracts.get('TimeControlled');
if (contractName === 'Time') contract = this.configuration.useNormalTime ? contract : this.contracts.get('TimeControlled');
if (contractName === 'ReputationToken') contract = this.configuration.isProduction ? contract : this.contracts.get('TestNetReputationToken');
if (contract.relativeFilePath.startsWith('legacy_reputation/')) return;
if (contractName !== 'Map' && contract.relativeFilePath.startsWith('libraries/')) return;
// Check to see if we have already uploded this version of the contract
@@ -245,6 +256,15 @@ Deploying to: ${networkConfiguration.networkName}
await this.getContract(contractName).setController(this.controller.address);
}

private async initializeLegacyRep(): Promise<void> {
const legacyReputationToken = new LegacyReputationToken(this.connector, this.accountManager, this.getContract('LegacyReputationToken').address, this.connector.gasPrice);
await legacyReputationToken.faucet(new BN(10).pow(new BN(18)).mul(new BN(11000000)));
const legacyBalance = await legacyReputationToken.balanceOf_(this.accountManager.defaultAddress);
if (!legacyBalance || legacyBalance == new BN(0)) {
throw new Error("Faucet call to Legacy REP failed");
}
}

private async createGenesisUniverse(): Promise<Universe> {
console.log('Creating genesis universe...');
const augur = new Augur(this.connector, this.accountManager, this.getContract("Augur").address, this.connector.gasPrice);
@@ -258,9 +278,24 @@ Deploying to: ${networkConfiguration.networkName}
if (await universe.getTypeName_() !== stringTo32ByteHex("Universe")) {
throw new Error("Unable to create genesis universe. Get type name failed");
}

return universe;
}

private async migrateFromlegacyRep(): Promise<void> {

This comment has been minimized.

Copy link
@adrake33

adrake33 Apr 3, 2018

Member

Nit: shouldn't the 'l' in migrateFromlegacyRep be capitalized here?

This comment has been minimized.

Copy link
@nuevoalex

nuevoalex Apr 3, 2018

Author Member

Will fix!

const reputationTokenAddress = await this.universe.getReputationToken_();
const reputationToken = new ReputationToken(this.connector, this.accountManager, reputationTokenAddress, this.connector.gasPrice);
await reputationToken.migrateBalancesFromLegacyRep([this.accountManager.defaultAddress]);
const balance = await reputationToken.balanceOf_(this.accountManager.defaultAddress);
if (!balance || balance == new BN(0)) {
throw new Error("Migration from Legacy REP failed");
}
const migrationOngoing = await reputationToken.getIsMigratingFromLegacy_();
if (migrationOngoing) {
throw new Error("Still migrating from Legacy REP");
}
}

private async generateAddressMapping(): Promise<string> {
type ContractAddressMapping = { [name: string]: string };
type NetworkAddressMapping = { [networkId: string]: ContractAddressMapping };
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.