contract ManagedContract {

    /*
    * smart contract that provides information about person that owns given Ethereum address/key
    */
    CryptonomicaVerification public cryptonomicaVerification;

    /*
    * ledger of admins
    */
    mapping(address => bool) isAdmin;

    modifier onlyAdmin() {
        require(isAdmin[msg.sender], "Only admin can do that");
        _;
    }

    /**
    * @param from Old address
    * @param to New address
    * @param by Who made a change
    */
    event CryptonomicaVerificationContractAddressChanged(address from, address to, address indexed by);

    /**
    * @param _newAddress address of new contract to be used to verify identity of new admins
    */
    function changeCryptonomicaVerificationContractAddress(address _newAddress) public onlyAdmin returns (bool success) {

        emit CryptonomicaVerificationContractAddressChanged(address(cryptonomicaVerification), _newAddress, msg.sender);

        cryptonomicaVerification = CryptonomicaVerification(_newAddress);

        return true;
    }

    /**
    * @param added New admin address
    * @param addedBy Who added new admin
    */
    event AdminAdded(
        address indexed added,
        address indexed addedBy
    );

    /**
    * @param _newAdmin Address of new admin
    */
    function addAdmin(address _newAdmin) public onlyAdmin returns (bool success){

        require(
            cryptonomicaVerification.keyCertificateValidUntil(_newAdmin) > now,
            "New admin has to be verified on Cryptonomica.net"
        );

        // revokedOn returns uint256 (unix time), it's 0 if verification is not revoked
        require(
            cryptonomicaVerification.revokedOn(_newAdmin) == 0,
            "Verification for this address was revoked, can not add"
        );

        isAdmin[_newAdmin] = true;

        emit AdminAdded(_newAdmin, msg.sender);

        return true;
    }

    /**
    * @param removed Removed admin address
    * @param removedBy Who removed admin
    */
    event AdminRemoved(
        address indexed removed,
        address indexed removedBy
    );

    /**
    * @param _oldAdmin Address to remove from admins
    */
    function removeAdmin(address _oldAdmin) external onlyAdmin returns (bool){

        require(msg.sender != _oldAdmin, "Admin can not remove himself");

        isAdmin[_oldAdmin] = false;

        emit AdminRemoved(_oldAdmin, msg.sender);

        return true;
    }

    /* --- financial management */

    /*
    * address to send Ether from this contract
    */
    address payable public withdrawalAddress;

    /*
    * withdrawal address can be fixed (protected from changes),
    */
    bool public withdrawalAddressFixed = false;

    /*
    * @param from Old address
    * @param to New address
    * @param changedBy Who made this change
    */
    event WithdrawalAddressChanged(address indexed from, address indexed to, address indexed changedBy);

    /*
    * @param _withdrawalAddress address to which funds from this contract will be sent
    */
    function setWithdrawalAddress(address payable _withdrawalAddress) public onlyAdmin returns (bool success) {

        require(!withdrawalAddressFixed, "Withdrawal address already fixed");
        require(_withdrawalAddress != address(0), "Wrong address: 0x0");
        require(_withdrawalAddress != address(this), "Wrong address: contract itself");

        emit WithdrawalAddressChanged(withdrawalAddress, _withdrawalAddress, msg.sender);

        withdrawalAddress = _withdrawalAddress;

        return true;
    }

    /*
    * @param withdrawalAddressFixedAs Address for withdrawal
    * @param fixedBy Address who made this change (msg.sender)
    *
    * This event can be fired one time only
    */
    event WithdrawalAddressFixed(address indexed withdrawalAddressFixedAs, address indexed fixedBy);

    /**
    * @param _withdrawalAddress Address to which funds from this contract will be sent
    * This function can be called one time only.
    */
    function fixWithdrawalAddress(address _withdrawalAddress) external onlyAdmin returns (bool success) {

        // prevents event if already fixed
        require(!withdrawalAddressFixed, "Can't change, address fixed");

        // check, to prevent fixing wrong address
        require(withdrawalAddress == _withdrawalAddress, "Wrong address in argument");

        withdrawalAddressFixed = true;

        emit WithdrawalAddressFixed(withdrawalAddress, msg.sender);

        return true;
    }

    /**
    * @param to address to which ETH was sent
    * @param sumInWei sum sent (in wei)
    * @param by who made withdrawal (msg.sender)
    * @param success if withdrawal was successful
    */
    event Withdrawal(
        address indexed to,
        uint sumInWei,
        address indexed by,
        bool indexed success
    );

    /**
    * !!! can be called by any user or contract
    * possible warning: check for reentrancy vulnerability http://solidity.readthedocs.io/en/develop/security-considerations.html#re-entrancy
    * >>> since we are making a withdrawal to our own contract/address only there is no possible attack using re-entrancy vulnerability
    */
    function withdrawAllToWithdrawalAddress() external returns (bool success) {

        // http://solidity.readthedocs.io/en/develop/security-considerations.html#sending-and-receiving-ether
        // about <address>.send(uint256 amount) and <address>.transfer(uint256 amount)
        // see: http://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=transfer#address-related
        // https://ethereum.stackexchange.com/questions/19341/address-send-vs-address-transfer-best-practice-usage

        uint sum = address(this).balance;

        if (!withdrawalAddress.send(sum)) {// makes withdrawal and returns true (success) or false

            emit Withdrawal(withdrawalAddress, sum, msg.sender, false);

            return false;
        }

        emit Withdrawal(withdrawalAddress, sum, msg.sender, true);

        return true;
    }

}
/*
 * Universal functions for smart contract management
 */
function addAdmin(address _newAdmin) public onlyAdmin returns (bool success){

    require(
        cryptonomicaVerification.keyCertificateValidUntil(_newAdmin) > now,
        "New admin has to be verified on Cryptonomica.net"
    );

    // revokedOn returns uint256 (unix time), it's 0 if verification is not revoked
    require(
        cryptonomicaVerification.revokedOn(_newAdmin) == 0,
        "Verification for this address was revoked, can not add"
    );

    isAdmin[_newAdmin] = true;

    emit AdminAdded(_newAdmin, msg.sender);

    return true;
}

/**
 * @param _newAdmin Address of new admin
 */