contract NewUpgradeAgent is SafeMath {

    // flag to determine if address is for a real contract or not
    bool public isUpgradeAgent = false;

    // Contract information
    address public owner;

    // Upgrade information
    bool public upgradeHasBegun = false;

    bool public finalizedUpgrade = false;

    OldToken public oldToken;

    address public decentBetMultisig;

    NewDecentBetToken public newToken;

    uint256 public originalSupply; // the original total supply of old tokens

    uint256 public correctOriginalSupply; // Correct original supply accounting for 30% minted at finalizeCrowdfunding

    uint256 public mintedPercentOfTokens = 30; // Amount of tokens that're minted at finalizeCrowdfunding

    uint256 public crowdfundPercentOfTokens = 70;

    uint256 public mintedTokens;

    event NewTokenSet(address token);

    event UpgradeHasBegun();

    event InvariantCheckFailed(uint oldTokenSupply, uint newTokenSupply, uint originalSupply, uint value);

    event InvariantCheckPassed(uint oldTokenSupply, uint newTokenSupply, uint originalSupply, uint value);

    function NewUpgradeAgent(address _oldToken) {
        owner = msg.sender;
        isUpgradeAgent = true;
        oldToken = OldToken(_oldToken);
        if (!oldToken.isDecentBetToken()) revert();
        decentBetMultisig = oldToken.decentBetMultisig();
        originalSupply = oldToken.totalSupply();
        mintedTokens = safeDiv(safeMul(originalSupply, mintedPercentOfTokens), crowdfundPercentOfTokens);
        correctOriginalSupply = safeAdd(originalSupply, mintedTokens);
    }

    /// @notice Check to make sure that the current sum of old and
    /// new version tokens is still equal to the original number of old version
    /// tokens
    /// @param _value The number of DBETs to upgrade
    function safetyInvariantCheck(uint256 _value) public {
        if (!newToken.isNewToken()) revert();
        // Abort if new token contract has not been set
        uint oldSupply = oldToken.totalSupply();
        uint newSupply = newToken.totalSupply();
        if (safeAdd(oldSupply, newSupply) != safeSub(correctOriginalSupply, _value)) {
            InvariantCheckFailed(oldSupply, newSupply, correctOriginalSupply, _value);
        } else {
            InvariantCheckPassed(oldSupply, newSupply, correctOriginalSupply, _value);
        }
    }

    /// @notice Sets the new token contract address
    /// @param _newToken The address of the new token contract
    function setNewToken(address _newToken) external {
        if (msg.sender != owner) revert();
        if (_newToken == 0x0) revert();
        if (upgradeHasBegun) revert();
        // Cannot change token after upgrade has begun

        newToken = NewDecentBetToken(_newToken);
        if (!newToken.isNewToken()) revert();
        NewTokenSet(newToken);
    }

    /// @notice Sets flag to prevent changing newToken after upgrade
    function setUpgradeHasBegun() internal {
        if (!upgradeHasBegun) {
            upgradeHasBegun = true;
            UpgradeHasBegun();
        }
    }

    /// @notice Creates new version tokens from the new token
    /// contract
    /// @param _from The address of the token upgrader
    /// @param _value The number of tokens to upgrade
    function upgradeFrom(address _from, uint256 _value) public {
        if(finalizedUpgrade) revert();
        if (msg.sender != address(oldToken)) revert();
        // Multisig can't upgrade since tokens are minted for it in new token constructor as it isn't part
        // of totalSupply of oldToken.
        if (_from == decentBetMultisig) revert();
        // only upgrade from oldToken
        if (!newToken.isNewToken()) revert();
        // need a real newToken!

        setUpgradeHasBegun();
        // Right here oldToken has already been updated, but corresponding
        // DBETs have not been created in the newToken contract yet
        safetyInvariantCheck(_value);

        newToken.createToken(_from, _value);

        //Right here totalSupply invariant must hold
        safetyInvariantCheck(0);
    }

    // Initializes original supply from old token total supply
    function setOriginalSupply() public {
        if (msg.sender != address(oldToken)) revert();
        originalSupply = oldToken.totalSupply();
    }

    function finalizeUpgrade() public {
        if (msg.sender != address(oldToken)) revert();
        finalizedUpgrade = true;
    }

    /// @dev Fallback function disallows depositing ether.
    function() {revert();}

}
//Test the whole process against this: https://www.kingoftheether.com/contract-safety-checklist.html
function setUpgradeHasBegun() internal {
    if (!upgradeHasBegun) {
        upgradeHasBegun = true;
        UpgradeHasBegun();
    }
}

/// @notice Sets flag to prevent changing newToken after upgrade