ETH Price: $3,162.78 (-6.22%)

Contract

0xCc8D82f6ba952966E63001c7B320EEF2Ae729099
 

Overview

ETH Balance

0.000000002084599699 ETH

Eth Value

Less Than $0.01 (@ $3,162.78/ETH)

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Withdraw207860662024-09-19 17:14:5954 days ago1726766099IN
nahmii: Client Fund
0 ETH0.0086313823.22307391
Set Deployer207855552024-09-19 15:31:5954 days ago1726759919IN
nahmii: Client Fund
0 ETH0.0006729322.11334838
Set Operator207855532024-09-19 15:31:3554 days ago1726759895IN
nahmii: Client Fund
0 ETH0.0006810522.31811222
Withdraw207809572024-09-19 0:08:1155 days ago1726704491IN
nahmii: Client Fund
0 ETH0.002075899.32649069
Withdraw200888122024-06-14 8:20:35151 days ago1718353235IN
nahmii: Client Fund
0 ETH0.0045840811.08742943
Withdraw200886072024-06-14 7:39:11152 days ago1718350751IN
nahmii: Client Fund
0 ETH0.0068723416.62151148
Withdraw200881132024-06-14 5:59:59152 days ago1718344799IN
nahmii: Client Fund
0 ETH0.003758469.09026075
Withdraw199885342024-05-31 8:10:11165 days ago1717143011IN
nahmii: Client Fund
0 ETH0.002629126.8814343
Withdraw155174892022-09-11 23:01:56793 days ago1662937316IN
nahmii: Client Fund
0 ETH0.0047131811.4
Withdraw153805532022-08-20 23:36:53815 days ago1661038613IN
nahmii: Client Fund
0 ETH0.003307598
Withdraw153777472022-08-20 13:01:44815 days ago1661000504IN
nahmii: Client Fund
0 ETH0.0036496110
Withdraw153777012022-08-20 12:51:52815 days ago1660999912IN
nahmii: Client Fund
0 ETH0.0053746813
Withdraw153776902022-08-20 12:48:21815 days ago1660999701IN
nahmii: Client Fund
0 ETH0.0057884514
Withdraw153776772022-08-20 12:44:37815 days ago1660999477IN
nahmii: Client Fund
0 ETH0.0040145711
Withdraw153776702022-08-20 12:43:06815 days ago1660999386IN
nahmii: Client Fund
0 ETH0.0047444913
Withdraw153776472022-08-20 12:35:47815 days ago1660998947IN
nahmii: Client Fund
0 ETH0.0054744115
Withdraw153775452022-08-20 12:06:01815 days ago1660997161IN
nahmii: Client Fund
0 ETH0.002189766
Withdraw153775082022-08-20 11:59:50815 days ago1660996790IN
nahmii: Client Fund
0 ETH0.002919688
Withdraw153774662022-08-20 11:51:57815 days ago1660996317IN
nahmii: Client Fund
0 ETH0.002919688
Withdraw150791542022-07-05 0:02:56862 days ago1656979376IN
nahmii: Client Fund
0 ETH0.0144698735
Withdraw149982252022-06-20 19:17:27876 days ago1655752647IN
nahmii: Client Fund
0 ETH0.007640520
Withdraw149946842022-06-20 4:41:57877 days ago1655700117IN
nahmii: Client Fund
0 ETH0.0041342510
Withdraw149891192022-06-19 5:34:41878 days ago1655616881IN
nahmii: Client Fund
0 ETH0.0109477530
Withdraw149815702022-06-17 22:17:28879 days ago1655504248IN
nahmii: Client Fund
0 ETH0.0094886726
Withdraw149785682022-06-17 10:00:53879 days ago1655460053IN
nahmii: Client Fund
0 ETH0.0060849627
View all transactions

Latest 13 internal transactions

Advanced mode:
Parent Transaction Hash Block From To
207809572024-09-19 0:08:1155 days ago1726704491
nahmii: Client Fund
1.29788 ETH
149785682022-06-17 10:00:53879 days ago1655460053
nahmii: Client Fund
0.06890999 ETH
122179622021-04-11 10:00:301311 days ago1618135230
nahmii: Client Fund
0.6 ETH
112759762020-11-17 14:27:141456 days ago1605623234
nahmii: Client Fund
0.5 ETH
112759562020-11-17 14:22:101456 days ago1605622930
nahmii: Client Fund
0.0001 ETH
97023662020-03-19 13:27:131699 days ago1584624433
nahmii: Client Fund
0 ETH
97023552020-03-19 13:23:471699 days ago1584624227
nahmii: Client Fund
0 ETH
97023202020-03-19 13:14:381699 days ago1584623678
nahmii: Client Fund
0 ETH
96728982020-03-15 0:44:261704 days ago1584233066
nahmii: Client Fund
0 ETH
87758922019-10-20 6:01:271851 days ago1571551287
nahmii: Client Fund
0.00000001 ETH
87161852019-10-10 20:36:381860 days ago1570739798
nahmii: Client Fund
0.6 ETH
80342002019-06-26 14:42:251966 days ago1561560145
nahmii: Client Fund
0.05 ETH
76415382019-04-26 7:07:012028 days ago1556262421
nahmii: Client Fund
0.0001 ETH
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
ClientFund

Compiler Version
v0.4.25+commit.59dbf8f1

Optimization Enabled:
Yes with 0 runs

Other Settings:
default evmVersion

Contract Source Code (Solidity)

/**
 *Submitted for verification at Etherscan.io on 2019-07-12
*/

pragma solidity ^0.4.25;

pragma experimental ABIEncoderV2;

/*
 * Hubii Nahmii
 *
 * Compliant with the Hubii Nahmii specification v0.12.
 *
 * Copyright (C) 2017-2018 Hubii AS
 */


/**
 * @title Modifiable
 * @notice A contract with basic modifiers
 */
contract Modifiable {
    //
    // Modifiers
    // -----------------------------------------------------------------------------------------------------------------
    modifier notNullAddress(address _address) {
        require(_address != address(0));
        _;
    }

    modifier notThisAddress(address _address) {
        require(_address != address(this));
        _;
    }

    modifier notNullOrThisAddress(address _address) {
        require(_address != address(0));
        require(_address != address(this));
        _;
    }

    modifier notSameAddresses(address _address1, address _address2) {
        if (_address1 != _address2)
            _;
    }
}

/*
 * Hubii Nahmii
 *
 * Compliant with the Hubii Nahmii specification v0.12.
 *
 * Copyright (C) 2017-2018 Hubii AS
 */



/**
 * @title SelfDestructible
 * @notice Contract that allows for self-destruction
 */
contract SelfDestructible {
    //
    // Variables
    // -----------------------------------------------------------------------------------------------------------------
    bool public selfDestructionDisabled;

    //
    // Events
    // -----------------------------------------------------------------------------------------------------------------
    event SelfDestructionDisabledEvent(address wallet);
    event TriggerSelfDestructionEvent(address wallet);

    //
    // Functions
    // -----------------------------------------------------------------------------------------------------------------
    /// @notice Get the address of the destructor role
    function destructor()
    public
    view
    returns (address);

    /// @notice Disable self-destruction of this contract
    /// @dev This operation can not be undone
    function disableSelfDestruction()
    public
    {
        // Require that sender is the assigned destructor
        require(destructor() == msg.sender);

        // Disable self-destruction
        selfDestructionDisabled = true;

        // Emit event
        emit SelfDestructionDisabledEvent(msg.sender);
    }

    /// @notice Destroy this contract
    function triggerSelfDestruction()
    public
    {
        // Require that sender is the assigned destructor
        require(destructor() == msg.sender);

        // Require that self-destruction has not been disabled
        require(!selfDestructionDisabled);

        // Emit event
        emit TriggerSelfDestructionEvent(msg.sender);

        // Self-destruct and reward destructor
        selfdestruct(msg.sender);
    }
}

/*
 * Hubii Nahmii
 *
 * Compliant with the Hubii Nahmii specification v0.12.
 *
 * Copyright (C) 2017-2018 Hubii AS
 */






/**
 * @title Ownable
 * @notice A modifiable that has ownership roles
 */
contract Ownable is Modifiable, SelfDestructible {
    //
    // Variables
    // -----------------------------------------------------------------------------------------------------------------
    address public deployer;
    address public operator;

    //
    // Events
    // -----------------------------------------------------------------------------------------------------------------
    event SetDeployerEvent(address oldDeployer, address newDeployer);
    event SetOperatorEvent(address oldOperator, address newOperator);

    //
    // Constructor
    // -----------------------------------------------------------------------------------------------------------------
    constructor(address _deployer) internal notNullOrThisAddress(_deployer) {
        deployer = _deployer;
        operator = _deployer;
    }

    //
    // Functions
    // -----------------------------------------------------------------------------------------------------------------
    /// @notice Return the address that is able to initiate self-destruction
    function destructor()
    public
    view
    returns (address)
    {
        return deployer;
    }

    /// @notice Set the deployer of this contract
    /// @param newDeployer The address of the new deployer
    function setDeployer(address newDeployer)
    public
    onlyDeployer
    notNullOrThisAddress(newDeployer)
    {
        if (newDeployer != deployer) {
            // Set new deployer
            address oldDeployer = deployer;
            deployer = newDeployer;

            // Emit event
            emit SetDeployerEvent(oldDeployer, newDeployer);
        }
    }

    /// @notice Set the operator of this contract
    /// @param newOperator The address of the new operator
    function setOperator(address newOperator)
    public
    onlyOperator
    notNullOrThisAddress(newOperator)
    {
        if (newOperator != operator) {
            // Set new operator
            address oldOperator = operator;
            operator = newOperator;

            // Emit event
            emit SetOperatorEvent(oldOperator, newOperator);
        }
    }

    /// @notice Gauge whether message sender is deployer or not
    /// @return true if msg.sender is deployer, else false
    function isDeployer()
    internal
    view
    returns (bool)
    {
        return msg.sender == deployer;
    }

    /// @notice Gauge whether message sender is operator or not
    /// @return true if msg.sender is operator, else false
    function isOperator()
    internal
    view
    returns (bool)
    {
        return msg.sender == operator;
    }

    /// @notice Gauge whether message sender is operator or deployer on the one hand, or none of these on these on
    /// on the other hand
    /// @return true if msg.sender is operator, else false
    function isDeployerOrOperator()
    internal
    view
    returns (bool)
    {
        return isDeployer() || isOperator();
    }

    // Modifiers
    // -----------------------------------------------------------------------------------------------------------------
    modifier onlyDeployer() {
        require(isDeployer());
        _;
    }

    modifier notDeployer() {
        require(!isDeployer());
        _;
    }

    modifier onlyOperator() {
        require(isOperator());
        _;
    }

    modifier notOperator() {
        require(!isOperator());
        _;
    }

    modifier onlyDeployerOrOperator() {
        require(isDeployerOrOperator());
        _;
    }

    modifier notDeployerOrOperator() {
        require(!isDeployerOrOperator());
        _;
    }
}

/*
 * Hubii Nahmii
 *
 * Compliant with the Hubii Nahmii specification v0.12.
 *
 * Copyright (C) 2017-2018 Hubii AS
 */



/**
 * @title Beneficiary
 * @notice A recipient of ethers and tokens
 */
contract Beneficiary {
    /// @notice Receive ethers to the given wallet's given balance type
    /// @param wallet The address of the concerned wallet
    /// @param balanceType The target balance type of the wallet
    function receiveEthersTo(address wallet, string balanceType)
    public
    payable;

    /// @notice Receive token to the given wallet's given balance type
    /// @dev The wallet must approve of the token transfer prior to calling this function
    /// @param wallet The address of the concerned wallet
    /// @param balanceType The target balance type of the wallet
    /// @param amount The amount to deposit
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    /// @param standard The standard of the token ("" for default registered, "ERC20", "ERC721")
    function receiveTokensTo(address wallet, string balanceType, int256 amount, address currencyCt,
        uint256 currencyId, string standard)
    public;
}

/*
 * Hubii Nahmii
 *
 * Compliant with the Hubii Nahmii specification v0.12.
 *
 * Copyright (C) 2017-2018 Hubii AS
 */





/**
 * @title Benefactor
 * @notice An ownable that contains registered beneficiaries
 */
contract Benefactor is Ownable {
    //
    // Variables
    // -----------------------------------------------------------------------------------------------------------------
    address[] internal beneficiaries;
    mapping(address => uint256) internal beneficiaryIndexByAddress;

    //
    // Events
    // -----------------------------------------------------------------------------------------------------------------
    event RegisterBeneficiaryEvent(address beneficiary);
    event DeregisterBeneficiaryEvent(address beneficiary);

    //
    // Functions
    // -----------------------------------------------------------------------------------------------------------------
    /// @notice Register the given beneficiary
    /// @param beneficiary Address of beneficiary to be registered
    function registerBeneficiary(address beneficiary)
    public
    onlyDeployer
    notNullAddress(beneficiary)
    returns (bool)
    {
        if (beneficiaryIndexByAddress[beneficiary] > 0)
            return false;

        beneficiaries.push(beneficiary);
        beneficiaryIndexByAddress[beneficiary] = beneficiaries.length;

        // Emit event
        emit RegisterBeneficiaryEvent(beneficiary);

        return true;
    }

    /// @notice Deregister the given beneficiary
    /// @param beneficiary Address of beneficiary to be deregistered
    function deregisterBeneficiary(address beneficiary)
    public
    onlyDeployer
    notNullAddress(beneficiary)
    returns (bool)
    {
        if (beneficiaryIndexByAddress[beneficiary] == 0)
            return false;

        uint256 idx = beneficiaryIndexByAddress[beneficiary] - 1;
        if (idx < beneficiaries.length - 1) {
            // Remap the last item in the array to this index
            beneficiaries[idx] = beneficiaries[beneficiaries.length - 1];
            beneficiaryIndexByAddress[beneficiaries[idx]] = idx + 1;
        }
        beneficiaries.length--;
        beneficiaryIndexByAddress[beneficiary] = 0;

        // Emit event
        emit DeregisterBeneficiaryEvent(beneficiary);

        return true;
    }

    /// @notice Gauge whether the given address is the one of a registered beneficiary
    /// @param beneficiary Address of beneficiary
    /// @return true if beneficiary is registered, else false
    function isRegisteredBeneficiary(address beneficiary)
    public
    view
    returns (bool)
    {
        return beneficiaryIndexByAddress[beneficiary] > 0;
    }

    /// @notice Get the count of registered beneficiaries
    /// @return The count of registered beneficiaries
    function registeredBeneficiariesCount()
    public
    view
    returns (uint256)
    {
        return beneficiaries.length;
    }
}

/*
 * Hubii Nahmii
 *
 * Compliant with the Hubii Nahmii specification v0.12.
 *
 * Copyright (C) 2017-2018 Hubii AS
 */





/**
 * @title Servable
 * @notice An ownable that contains registered services and their actions
 */
contract Servable is Ownable {
    //
    // Types
    // -----------------------------------------------------------------------------------------------------------------
    struct ServiceInfo {
        bool registered;
        uint256 activationTimestamp;
        mapping(bytes32 => bool) actionsEnabledMap;
        bytes32[] actionsList;
    }

    //
    // Variables
    // -----------------------------------------------------------------------------------------------------------------
    mapping(address => ServiceInfo) internal registeredServicesMap;
    uint256 public serviceActivationTimeout;

    //
    // Events
    // -----------------------------------------------------------------------------------------------------------------
    event ServiceActivationTimeoutEvent(uint256 timeoutInSeconds);
    event RegisterServiceEvent(address service);
    event RegisterServiceDeferredEvent(address service, uint256 timeout);
    event DeregisterServiceEvent(address service);
    event EnableServiceActionEvent(address service, string action);
    event DisableServiceActionEvent(address service, string action);

    //
    // Functions
    // -----------------------------------------------------------------------------------------------------------------
    /// @notice Set the service activation timeout
    /// @param timeoutInSeconds The set timeout in unit of seconds
    function setServiceActivationTimeout(uint256 timeoutInSeconds)
    public
    onlyDeployer
    {
        serviceActivationTimeout = timeoutInSeconds;

        // Emit event
        emit ServiceActivationTimeoutEvent(timeoutInSeconds);
    }

    /// @notice Register a service contract whose activation is immediate
    /// @param service The address of the service contract to be registered
    function registerService(address service)
    public
    onlyDeployer
    notNullOrThisAddress(service)
    {
        _registerService(service, 0);

        // Emit event
        emit RegisterServiceEvent(service);
    }

    /// @notice Register a service contract whose activation is deferred by the service activation timeout
    /// @param service The address of the service contract to be registered
    function registerServiceDeferred(address service)
    public
    onlyDeployer
    notNullOrThisAddress(service)
    {
        _registerService(service, serviceActivationTimeout);

        // Emit event
        emit RegisterServiceDeferredEvent(service, serviceActivationTimeout);
    }

    /// @notice Deregister a service contract
    /// @param service The address of the service contract to be deregistered
    function deregisterService(address service)
    public
    onlyDeployer
    notNullOrThisAddress(service)
    {
        require(registeredServicesMap[service].registered);

        registeredServicesMap[service].registered = false;

        // Emit event
        emit DeregisterServiceEvent(service);
    }

    /// @notice Enable a named action in an already registered service contract
    /// @param service The address of the registered service contract
    /// @param action The name of the action to be enabled
    function enableServiceAction(address service, string action)
    public
    onlyDeployer
    notNullOrThisAddress(service)
    {
        require(registeredServicesMap[service].registered);

        bytes32 actionHash = hashString(action);

        require(!registeredServicesMap[service].actionsEnabledMap[actionHash]);

        registeredServicesMap[service].actionsEnabledMap[actionHash] = true;
        registeredServicesMap[service].actionsList.push(actionHash);

        // Emit event
        emit EnableServiceActionEvent(service, action);
    }

    /// @notice Enable a named action in a service contract
    /// @param service The address of the service contract
    /// @param action The name of the action to be disabled
    function disableServiceAction(address service, string action)
    public
    onlyDeployer
    notNullOrThisAddress(service)
    {
        bytes32 actionHash = hashString(action);

        require(registeredServicesMap[service].actionsEnabledMap[actionHash]);

        registeredServicesMap[service].actionsEnabledMap[actionHash] = false;

        // Emit event
        emit DisableServiceActionEvent(service, action);
    }

    /// @notice Gauge whether a service contract is registered
    /// @param service The address of the service contract
    /// @return true if service is registered, else false
    function isRegisteredService(address service)
    public
    view
    returns (bool)
    {
        return registeredServicesMap[service].registered;
    }

    /// @notice Gauge whether a service contract is registered and active
    /// @param service The address of the service contract
    /// @return true if service is registered and activate, else false
    function isRegisteredActiveService(address service)
    public
    view
    returns (bool)
    {
        return isRegisteredService(service) && block.timestamp >= registeredServicesMap[service].activationTimestamp;
    }

    /// @notice Gauge whether a service contract action is enabled which implies also registered and active
    /// @param service The address of the service contract
    /// @param action The name of action
    function isEnabledServiceAction(address service, string action)
    public
    view
    returns (bool)
    {
        bytes32 actionHash = hashString(action);
        return isRegisteredActiveService(service) && registeredServicesMap[service].actionsEnabledMap[actionHash];
    }

    //
    // Internal functions
    // -----------------------------------------------------------------------------------------------------------------
    function hashString(string _string)
    internal
    pure
    returns (bytes32)
    {
        return keccak256(abi.encodePacked(_string));
    }

    //
    // Private functions
    // -----------------------------------------------------------------------------------------------------------------
    function _registerService(address service, uint256 timeout)
    private
    {
        if (!registeredServicesMap[service].registered) {
            registeredServicesMap[service].registered = true;
            registeredServicesMap[service].activationTimestamp = block.timestamp + timeout;
        }
    }

    //
    // Modifiers
    // -----------------------------------------------------------------------------------------------------------------
    modifier onlyActiveService() {
        require(isRegisteredActiveService(msg.sender));
        _;
    }

    modifier onlyEnabledServiceAction(string action) {
        require(isEnabledServiceAction(msg.sender, action));
        _;
    }
}

/*
 * Hubii Nahmii
 *
 * Compliant with the Hubii Nahmii specification v0.12.
 *
 * Copyright (C) 2017-2018 Hubii AS
 */





/**
 * @title AuthorizableServable
 * @notice A servable that may be authorized and unauthorized
 */
contract AuthorizableServable is Servable {
    //
    // Variables
    // -----------------------------------------------------------------------------------------------------------------
    bool public initialServiceAuthorizationDisabled;

    mapping(address => bool) public initialServiceAuthorizedMap;
    mapping(address => mapping(address => bool)) public initialServiceWalletUnauthorizedMap;

    mapping(address => mapping(address => bool)) public serviceWalletAuthorizedMap;

    mapping(address => mapping(bytes32 => mapping(address => bool))) public serviceActionWalletAuthorizedMap;
    mapping(address => mapping(bytes32 => mapping(address => bool))) public serviceActionWalletTouchedMap;
    mapping(address => mapping(address => bytes32[])) public serviceWalletActionList;

    //
    // Events
    // -----------------------------------------------------------------------------------------------------------------
    event AuthorizeInitialServiceEvent(address wallet, address service);
    event AuthorizeRegisteredServiceEvent(address wallet, address service);
    event AuthorizeRegisteredServiceActionEvent(address wallet, address service, string action);
    event UnauthorizeRegisteredServiceEvent(address wallet, address service);
    event UnauthorizeRegisteredServiceActionEvent(address wallet, address service, string action);

    //
    // Functions
    // -----------------------------------------------------------------------------------------------------------------
    /// @notice Add service to initial whitelist of services
    /// @dev The service must be registered already
    /// @param service The address of the concerned registered service
    function authorizeInitialService(address service)
    public
    onlyDeployer
    notNullOrThisAddress(service)
    {
        require(!initialServiceAuthorizationDisabled);
        require(msg.sender != service);

        // Ensure service is registered
        require(registeredServicesMap[service].registered);

        // Enable all actions for given wallet
        initialServiceAuthorizedMap[service] = true;

        // Emit event
        emit AuthorizeInitialServiceEvent(msg.sender, service);
    }

    /// @notice Disable further initial authorization of services
    /// @dev This operation can not be undone
    function disableInitialServiceAuthorization()
    public
    onlyDeployer
    {
        initialServiceAuthorizationDisabled = true;
    }

    /// @notice Authorize the given registered service by enabling all of actions
    /// @dev The service must be registered already
    /// @param service The address of the concerned registered service
    function authorizeRegisteredService(address service)
    public
    notNullOrThisAddress(service)
    {
        require(msg.sender != service);

        // Ensure service is registered
        require(registeredServicesMap[service].registered);

        // Ensure service is not initial. Initial services are not authorized per action.
        require(!initialServiceAuthorizedMap[service]);

        // Enable all actions for given wallet
        serviceWalletAuthorizedMap[service][msg.sender] = true;

        // Emit event
        emit AuthorizeRegisteredServiceEvent(msg.sender, service);
    }

    /// @notice Unauthorize the given registered service by enabling all of actions
    /// @dev The service must be registered already
    /// @param service The address of the concerned registered service
    function unauthorizeRegisteredService(address service)
    public
    notNullOrThisAddress(service)
    {
        require(msg.sender != service);

        // Ensure service is registered
        require(registeredServicesMap[service].registered);

        // If initial service then disable it
        if (initialServiceAuthorizedMap[service])
            initialServiceWalletUnauthorizedMap[service][msg.sender] = true;

        // Else disable all actions for given wallet
        else {
            serviceWalletAuthorizedMap[service][msg.sender] = false;
            for (uint256 i = 0; i < serviceWalletActionList[service][msg.sender].length; i++)
                serviceActionWalletAuthorizedMap[service][serviceWalletActionList[service][msg.sender][i]][msg.sender] = true;
        }

        // Emit event
        emit UnauthorizeRegisteredServiceEvent(msg.sender, service);
    }

    /// @notice Gauge whether the given service is authorized for the given wallet
    /// @param service The address of the concerned registered service
    /// @param wallet The address of the concerned wallet
    /// @return true if service is authorized for the given wallet, else false
    function isAuthorizedRegisteredService(address service, address wallet)
    public
    view
    returns (bool)
    {
        return isRegisteredActiveService(service) &&
        (isInitialServiceAuthorizedForWallet(service, wallet) || serviceWalletAuthorizedMap[service][wallet]);
    }

    /// @notice Authorize the given registered service action
    /// @dev The service must be registered already
    /// @param service The address of the concerned registered service
    /// @param action The concerned service action
    function authorizeRegisteredServiceAction(address service, string action)
    public
    notNullOrThisAddress(service)
    {
        require(msg.sender != service);

        bytes32 actionHash = hashString(action);

        // Ensure service action is registered
        require(registeredServicesMap[service].registered && registeredServicesMap[service].actionsEnabledMap[actionHash]);

        // Ensure service is not initial
        require(!initialServiceAuthorizedMap[service]);

        // Enable service action for given wallet
        serviceWalletAuthorizedMap[service][msg.sender] = false;
        serviceActionWalletAuthorizedMap[service][actionHash][msg.sender] = true;
        if (!serviceActionWalletTouchedMap[service][actionHash][msg.sender]) {
            serviceActionWalletTouchedMap[service][actionHash][msg.sender] = true;
            serviceWalletActionList[service][msg.sender].push(actionHash);
        }

        // Emit event
        emit AuthorizeRegisteredServiceActionEvent(msg.sender, service, action);
    }

    /// @notice Unauthorize the given registered service action
    /// @dev The service must be registered already
    /// @param service The address of the concerned registered service
    /// @param action The concerned service action
    function unauthorizeRegisteredServiceAction(address service, string action)
    public
    notNullOrThisAddress(service)
    {
        require(msg.sender != service);

        bytes32 actionHash = hashString(action);

        // Ensure service is registered and action enabled
        require(registeredServicesMap[service].registered && registeredServicesMap[service].actionsEnabledMap[actionHash]);

        // Ensure service is not initial as it can not be unauthorized per action
        require(!initialServiceAuthorizedMap[service]);

        // Disable service action for given wallet
        serviceActionWalletAuthorizedMap[service][actionHash][msg.sender] = false;

        // Emit event
        emit UnauthorizeRegisteredServiceActionEvent(msg.sender, service, action);
    }

    /// @notice Gauge whether the given service action is authorized for the given wallet
    /// @param service The address of the concerned registered service
    /// @param action The concerned service action
    /// @param wallet The address of the concerned wallet
    /// @return true if service action is authorized for the given wallet, else false
    function isAuthorizedRegisteredServiceAction(address service, string action, address wallet)
    public
    view
    returns (bool)
    {
        bytes32 actionHash = hashString(action);

        return isEnabledServiceAction(service, action) &&
        (isInitialServiceAuthorizedForWallet(service, wallet) || serviceActionWalletAuthorizedMap[service][actionHash][wallet]);
    }

    function isInitialServiceAuthorizedForWallet(address service, address wallet)
    private
    view
    returns (bool)
    {
        return initialServiceAuthorizedMap[service] ? !initialServiceWalletUnauthorizedMap[service][wallet] : false;
    }

    //
    // Modifiers
    // -----------------------------------------------------------------------------------------------------------------
    modifier onlyAuthorizedService(address wallet) {
        require(isAuthorizedRegisteredService(msg.sender, wallet));
        _;
    }

    modifier onlyAuthorizedServiceAction(string action, address wallet) {
        require(isAuthorizedRegisteredServiceAction(msg.sender, action, wallet));
        _;
    }
}

/*
 * Hubii Nahmii
 *
 * Compliant with the Hubii Nahmii specification v0.12.
 *
 * Copyright (C) 2017-2018 Hubii AS
 */



/**
 * @title TransferController
 * @notice A base contract to handle transfers of different currency types
 */
contract TransferController {
    //
    // Events
    // -----------------------------------------------------------------------------------------------------------------
    event CurrencyTransferred(address from, address to, uint256 value,
        address currencyCt, uint256 currencyId);

    //
    // Functions
    // -----------------------------------------------------------------------------------------------------------------
    function isFungible()
    public
    view
    returns (bool);

    /// @notice MUST be called with DELEGATECALL
    function receive(address from, address to, uint256 value, address currencyCt, uint256 currencyId)
    public;

    /// @notice MUST be called with DELEGATECALL
    function approve(address to, uint256 value, address currencyCt, uint256 currencyId)
    public;

    /// @notice MUST be called with DELEGATECALL
    function dispatch(address from, address to, uint256 value, address currencyCt, uint256 currencyId)
    public;

    //----------------------------------------

    function getReceiveSignature()
    public
    pure
    returns (bytes4)
    {
        return bytes4(keccak256("receive(address,address,uint256,address,uint256)"));
    }

    function getApproveSignature()
    public
    pure
    returns (bytes4)
    {
        return bytes4(keccak256("approve(address,uint256,address,uint256)"));
    }

    function getDispatchSignature()
    public
    pure
    returns (bytes4)
    {
        return bytes4(keccak256("dispatch(address,address,uint256,address,uint256)"));
    }
}

/*
 * Hubii Nahmii
 *
 * Compliant with the Hubii Nahmii specification v0.12.
 *
 * Copyright (C) 2017-2018 Hubii AS
 */






/**
 * @title TransferControllerManager
 * @notice Handles the management of transfer controllers
 */
contract TransferControllerManager is Ownable {
    //
    // Constants
    // -----------------------------------------------------------------------------------------------------------------
    struct CurrencyInfo {
        bytes32 standard;
        bool blacklisted;
    }

    //
    // Variables
    // -----------------------------------------------------------------------------------------------------------------
    mapping(bytes32 => address) registeredTransferControllers;
    mapping(address => CurrencyInfo) registeredCurrencies;

    //
    // Events
    // -----------------------------------------------------------------------------------------------------------------
    event RegisterTransferControllerEvent(string standard, address controller);
    event ReassociateTransferControllerEvent(string oldStandard, string newStandard, address controller);

    event RegisterCurrencyEvent(address currencyCt, string standard);
    event DeregisterCurrencyEvent(address currencyCt);
    event BlacklistCurrencyEvent(address currencyCt);
    event WhitelistCurrencyEvent(address currencyCt);

    //
    // Constructor
    // -----------------------------------------------------------------------------------------------------------------
    constructor(address deployer) Ownable(deployer) public {
    }

    //
    // Functions
    // -----------------------------------------------------------------------------------------------------------------
    function registerTransferController(string standard, address controller)
    external
    onlyDeployer
    notNullAddress(controller)
    {
        require(bytes(standard).length > 0);
        bytes32 standardHash = keccak256(abi.encodePacked(standard));

        require(registeredTransferControllers[standardHash] == address(0));

        registeredTransferControllers[standardHash] = controller;

        // Emit event
        emit RegisterTransferControllerEvent(standard, controller);
    }

    function reassociateTransferController(string oldStandard, string newStandard, address controller)
    external
    onlyDeployer
    notNullAddress(controller)
    {
        require(bytes(newStandard).length > 0);
        bytes32 oldStandardHash = keccak256(abi.encodePacked(oldStandard));
        bytes32 newStandardHash = keccak256(abi.encodePacked(newStandard));

        require(registeredTransferControllers[oldStandardHash] != address(0));
        require(registeredTransferControllers[newStandardHash] == address(0));

        registeredTransferControllers[newStandardHash] = registeredTransferControllers[oldStandardHash];
        registeredTransferControllers[oldStandardHash] = address(0);

        // Emit event
        emit ReassociateTransferControllerEvent(oldStandard, newStandard, controller);
    }

    function registerCurrency(address currencyCt, string standard)
    external
    onlyOperator
    notNullAddress(currencyCt)
    {
        require(bytes(standard).length > 0);
        bytes32 standardHash = keccak256(abi.encodePacked(standard));

        require(registeredCurrencies[currencyCt].standard == bytes32(0));

        registeredCurrencies[currencyCt].standard = standardHash;

        // Emit event
        emit RegisterCurrencyEvent(currencyCt, standard);
    }

    function deregisterCurrency(address currencyCt)
    external
    onlyOperator
    {
        require(registeredCurrencies[currencyCt].standard != 0);

        registeredCurrencies[currencyCt].standard = bytes32(0);
        registeredCurrencies[currencyCt].blacklisted = false;

        // Emit event
        emit DeregisterCurrencyEvent(currencyCt);
    }

    function blacklistCurrency(address currencyCt)
    external
    onlyOperator
    {
        require(registeredCurrencies[currencyCt].standard != bytes32(0));

        registeredCurrencies[currencyCt].blacklisted = true;

        // Emit event
        emit BlacklistCurrencyEvent(currencyCt);
    }

    function whitelistCurrency(address currencyCt)
    external
    onlyOperator
    {
        require(registeredCurrencies[currencyCt].standard != bytes32(0));

        registeredCurrencies[currencyCt].blacklisted = false;

        // Emit event
        emit WhitelistCurrencyEvent(currencyCt);
    }

    /**
    @notice The provided standard takes priority over assigned interface to currency
    */
    function transferController(address currencyCt, string standard)
    public
    view
    returns (TransferController)
    {
        if (bytes(standard).length > 0) {
            bytes32 standardHash = keccak256(abi.encodePacked(standard));

            require(registeredTransferControllers[standardHash] != address(0));
            return TransferController(registeredTransferControllers[standardHash]);
        }

        require(registeredCurrencies[currencyCt].standard != bytes32(0));
        require(!registeredCurrencies[currencyCt].blacklisted);

        address controllerAddress = registeredTransferControllers[registeredCurrencies[currencyCt].standard];
        require(controllerAddress != address(0));

        return TransferController(controllerAddress);
    }
}

/*
 * Hubii Nahmii
 *
 * Compliant with the Hubii Nahmii specification v0.12.
 *
 * Copyright (C) 2017-2018 Hubii AS
 */







/**
 * @title TransferControllerManageable
 * @notice An ownable with a transfer controller manager
 */
contract TransferControllerManageable is Ownable {
    //
    // Variables
    // -----------------------------------------------------------------------------------------------------------------
    TransferControllerManager public transferControllerManager;

    //
    // Events
    // -----------------------------------------------------------------------------------------------------------------
    event SetTransferControllerManagerEvent(TransferControllerManager oldTransferControllerManager,
        TransferControllerManager newTransferControllerManager);

    //
    // Functions
    // -----------------------------------------------------------------------------------------------------------------
    /// @notice Set the currency manager contract
    /// @param newTransferControllerManager The (address of) TransferControllerManager contract instance
    function setTransferControllerManager(TransferControllerManager newTransferControllerManager)
    public
    onlyDeployer
    notNullAddress(newTransferControllerManager)
    notSameAddresses(newTransferControllerManager, transferControllerManager)
    {
        //set new currency manager
        TransferControllerManager oldTransferControllerManager = transferControllerManager;
        transferControllerManager = newTransferControllerManager;

        // Emit event
        emit SetTransferControllerManagerEvent(oldTransferControllerManager, newTransferControllerManager);
    }

    /// @notice Get the transfer controller of the given currency contract address and standard
    function transferController(address currencyCt, string standard)
    internal
    view
    returns (TransferController)
    {
        return transferControllerManager.transferController(currencyCt, standard);
    }

    //
    // Modifiers
    // -----------------------------------------------------------------------------------------------------------------
    modifier transferControllerManagerInitialized() {
        require(transferControllerManager != address(0));
        _;
    }
}

/*
 * Hubii Nahmii
 *
 * Compliant with the Hubii Nahmii specification v0.12.
 *
 * Copyright (C) 2017-2018 Hubii AS based on Open-Zeppelin's SafeMath library
 */



/**
 * @title     SafeMathIntLib
 * @dev       Math operations with safety checks that throw on error
 */
library SafeMathIntLib {
    int256 constant INT256_MIN = int256((uint256(1) << 255));
    int256 constant INT256_MAX = int256(~((uint256(1) << 255)));

    //
    //Functions below accept positive and negative integers and result must not overflow.
    //
    function div(int256 a, int256 b)
    internal
    pure
    returns (int256)
    {
        require(a != INT256_MIN || b != - 1);
        return a / b;
    }

    function mul(int256 a, int256 b)
    internal
    pure
    returns (int256)
    {
        require(a != - 1 || b != INT256_MIN);
        // overflow
        require(b != - 1 || a != INT256_MIN);
        // overflow
        int256 c = a * b;
        require((b == 0) || (c / b == a));
        return c;
    }

    function sub(int256 a, int256 b)
    internal
    pure
    returns (int256)
    {
        require((b >= 0 && a - b <= a) || (b < 0 && a - b > a));
        return a - b;
    }

    function add(int256 a, int256 b)
    internal
    pure
    returns (int256)
    {
        int256 c = a + b;
        require((b >= 0 && c >= a) || (b < 0 && c < a));
        return c;
    }

    //
    //Functions below only accept positive integers and result must be greater or equal to zero too.
    //
    function div_nn(int256 a, int256 b)
    internal
    pure
    returns (int256)
    {
        require(a >= 0 && b > 0);
        return a / b;
    }

    function mul_nn(int256 a, int256 b)
    internal
    pure
    returns (int256)
    {
        require(a >= 0 && b >= 0);
        int256 c = a * b;
        require(a == 0 || c / a == b);
        require(c >= 0);
        return c;
    }

    function sub_nn(int256 a, int256 b)
    internal
    pure
    returns (int256)
    {
        require(a >= 0 && b >= 0 && b <= a);
        return a - b;
    }

    function add_nn(int256 a, int256 b)
    internal
    pure
    returns (int256)
    {
        require(a >= 0 && b >= 0);
        int256 c = a + b;
        require(c >= a);
        return c;
    }

    //
    //Conversion and validation functions.
    //
    function abs(int256 a)
    public
    pure
    returns (int256)
    {
        return a < 0 ? neg(a) : a;
    }

    function neg(int256 a)
    public
    pure
    returns (int256)
    {
        return mul(a, - 1);
    }

    function toNonZeroInt256(uint256 a)
    public
    pure
    returns (int256)
    {
        require(a > 0 && a < (uint256(1) << 255));
        return int256(a);
    }

    function toInt256(uint256 a)
    public
    pure
    returns (int256)
    {
        require(a >= 0 && a < (uint256(1) << 255));
        return int256(a);
    }

    function toUInt256(int256 a)
    public
    pure
    returns (uint256)
    {
        require(a >= 0);
        return uint256(a);
    }

    function isNonZeroPositiveInt256(int256 a)
    public
    pure
    returns (bool)
    {
        return (a > 0);
    }

    function isPositiveInt256(int256 a)
    public
    pure
    returns (bool)
    {
        return (a >= 0);
    }

    function isNonZeroNegativeInt256(int256 a)
    public
    pure
    returns (bool)
    {
        return (a < 0);
    }

    function isNegativeInt256(int256 a)
    public
    pure
    returns (bool)
    {
        return (a <= 0);
    }

    //
    //Clamping functions.
    //
    function clamp(int256 a, int256 min, int256 max)
    public
    pure
    returns (int256)
    {
        if (a < min)
            return min;
        return (a > max) ? max : a;
    }

    function clampMin(int256 a, int256 min)
    public
    pure
    returns (int256)
    {
        return (a < min) ? min : a;
    }

    function clampMax(int256 a, int256 max)
    public
    pure
    returns (int256)
    {
        return (a > max) ? max : a;
    }
}

/*
 * Hubii Nahmii
 *
 * Compliant with the Hubii Nahmii specification v0.12.
 *
 * Copyright (C) 2017-2018 Hubii AS based on Open-Zeppelin's SafeMath library
 */



/**
 * @title     SafeMathUintLib
 * @dev       Math operations with safety checks that throw on error
 */
library SafeMathUintLib {
    function mul(uint256 a, uint256 b)
    internal
    pure
    returns (uint256)
    {
        uint256 c = a * b;
        assert(a == 0 || c / a == b);
        return c;
    }

    function div(uint256 a, uint256 b)
    internal
    pure
    returns (uint256)
    {
        // assert(b > 0); // Solidity automatically throws when dividing by 0
        uint256 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold
        return c;
    }

    function sub(uint256 a, uint256 b)
    internal
    pure
    returns (uint256)
    {
        assert(b <= a);
        return a - b;
    }

    function add(uint256 a, uint256 b)
    internal
    pure
    returns (uint256)
    {
        uint256 c = a + b;
        assert(c >= a);
        return c;
    }

    //
    //Clamping functions.
    //
    function clamp(uint256 a, uint256 min, uint256 max)
    public
    pure
    returns (uint256)
    {
        return (a > max) ? max : ((a < min) ? min : a);
    }

    function clampMin(uint256 a, uint256 min)
    public
    pure
    returns (uint256)
    {
        return (a < min) ? min : a;
    }

    function clampMax(uint256 a, uint256 max)
    public
    pure
    returns (uint256)
    {
        return (a > max) ? max : a;
    }
}

/*
 * Hubii Nahmii
 *
 * Compliant with the Hubii Nahmii specification v0.12.
 *
 * Copyright (C) 2017-2018 Hubii AS
 */




/**
 * @title     MonetaryTypesLib
 * @dev       Monetary data types
 */
library MonetaryTypesLib {
    //
    // Structures
    // -----------------------------------------------------------------------------------------------------------------
    struct Currency {
        address ct;
        uint256 id;
    }

    struct Figure {
        int256 amount;
        Currency currency;
    }
}

/*
 * Hubii Nahmii
 *
 * Compliant with the Hubii Nahmii specification v0.12.
 *
 * Copyright (C) 2017-2018 Hubii AS
 */







library CurrenciesLib {
    using SafeMathUintLib for uint256;

    //
    // Structures
    // -----------------------------------------------------------------------------------------------------------------
    struct Currencies {
        MonetaryTypesLib.Currency[] currencies;
        mapping(address => mapping(uint256 => uint256)) indexByCurrency;
    }

    //
    // Functions
    // -----------------------------------------------------------------------------------------------------------------
    function add(Currencies storage self, address currencyCt, uint256 currencyId)
    internal
    {
        // Index is 1-based
        if (0 == self.indexByCurrency[currencyCt][currencyId]) {
            self.currencies.push(MonetaryTypesLib.Currency(currencyCt, currencyId));
            self.indexByCurrency[currencyCt][currencyId] = self.currencies.length;
        }
    }

    function removeByCurrency(Currencies storage self, address currencyCt, uint256 currencyId)
    internal
    {
        // Index is 1-based
        uint256 index = self.indexByCurrency[currencyCt][currencyId];
        if (0 < index)
            removeByIndex(self, index - 1);
    }

    function removeByIndex(Currencies storage self, uint256 index)
    internal
    {
        require(index < self.currencies.length);

        address currencyCt = self.currencies[index].ct;
        uint256 currencyId = self.currencies[index].id;

        if (index < self.currencies.length - 1) {
            self.currencies[index] = self.currencies[self.currencies.length - 1];
            self.indexByCurrency[self.currencies[index].ct][self.currencies[index].id] = index + 1;
        }
        self.currencies.length--;
        self.indexByCurrency[currencyCt][currencyId] = 0;
    }

    function count(Currencies storage self)
    internal
    view
    returns (uint256)
    {
        return self.currencies.length;
    }

    function has(Currencies storage self, address currencyCt, uint256 currencyId)
    internal
    view
    returns (bool)
    {
        return 0 != self.indexByCurrency[currencyCt][currencyId];
    }

    function getByIndex(Currencies storage self, uint256 index)
    internal
    view
    returns (MonetaryTypesLib.Currency)
    {
        require(index < self.currencies.length);
        return self.currencies[index];
    }

    function getByIndices(Currencies storage self, uint256 low, uint256 up)
    internal
    view
    returns (MonetaryTypesLib.Currency[])
    {
        require(0 < self.currencies.length);
        require(low <= up);

        up = up.clampMax(self.currencies.length - 1);
        MonetaryTypesLib.Currency[] memory _currencies = new MonetaryTypesLib.Currency[](up - low + 1);
        for (uint256 i = low; i <= up; i++)
            _currencies[i - low] = self.currencies[i];

        return _currencies;
    }
}

/*
 * Hubii Nahmii
 *
 * Compliant with the Hubii Nahmii specification v0.12.
 *
 * Copyright (C) 2017-2018 Hubii AS
 */







library FungibleBalanceLib {
    using SafeMathIntLib for int256;
    using SafeMathUintLib for uint256;
    using CurrenciesLib for CurrenciesLib.Currencies;

    //
    // Structures
    // -----------------------------------------------------------------------------------------------------------------
    struct Record {
        int256 amount;
        uint256 blockNumber;
    }

    struct Balance {
        mapping(address => mapping(uint256 => int256)) amountByCurrency;
        mapping(address => mapping(uint256 => Record[])) recordsByCurrency;

        CurrenciesLib.Currencies inUseCurrencies;
        CurrenciesLib.Currencies everUsedCurrencies;
    }

    //
    // Functions
    // -----------------------------------------------------------------------------------------------------------------
    function get(Balance storage self, address currencyCt, uint256 currencyId)
    internal
    view
    returns (int256)
    {
        return self.amountByCurrency[currencyCt][currencyId];
    }

    function getByBlockNumber(Balance storage self, address currencyCt, uint256 currencyId, uint256 blockNumber)
    internal
    view
    returns (int256)
    {
        (int256 amount,) = recordByBlockNumber(self, currencyCt, currencyId, blockNumber);
        return amount;
    }

    function set(Balance storage self, int256 amount, address currencyCt, uint256 currencyId)
    internal
    {
        self.amountByCurrency[currencyCt][currencyId] = amount;

        self.recordsByCurrency[currencyCt][currencyId].push(
            Record(self.amountByCurrency[currencyCt][currencyId], block.number)
        );

        updateCurrencies(self, currencyCt, currencyId);
    }

    function add(Balance storage self, int256 amount, address currencyCt, uint256 currencyId)
    internal
    {
        self.amountByCurrency[currencyCt][currencyId] = self.amountByCurrency[currencyCt][currencyId].add(amount);

        self.recordsByCurrency[currencyCt][currencyId].push(
            Record(self.amountByCurrency[currencyCt][currencyId], block.number)
        );

        updateCurrencies(self, currencyCt, currencyId);
    }

    function sub(Balance storage self, int256 amount, address currencyCt, uint256 currencyId)
    internal
    {
        self.amountByCurrency[currencyCt][currencyId] = self.amountByCurrency[currencyCt][currencyId].sub(amount);

        self.recordsByCurrency[currencyCt][currencyId].push(
            Record(self.amountByCurrency[currencyCt][currencyId], block.number)
        );

        updateCurrencies(self, currencyCt, currencyId);
    }

    function transfer(Balance storage _from, Balance storage _to, int256 amount,
        address currencyCt, uint256 currencyId)
    internal
    {
        sub(_from, amount, currencyCt, currencyId);
        add(_to, amount, currencyCt, currencyId);
    }

    function add_nn(Balance storage self, int256 amount, address currencyCt, uint256 currencyId)
    internal
    {
        self.amountByCurrency[currencyCt][currencyId] = self.amountByCurrency[currencyCt][currencyId].add_nn(amount);

        self.recordsByCurrency[currencyCt][currencyId].push(
            Record(self.amountByCurrency[currencyCt][currencyId], block.number)
        );

        updateCurrencies(self, currencyCt, currencyId);
    }

    function sub_nn(Balance storage self, int256 amount, address currencyCt, uint256 currencyId)
    internal
    {
        self.amountByCurrency[currencyCt][currencyId] = self.amountByCurrency[currencyCt][currencyId].sub_nn(amount);

        self.recordsByCurrency[currencyCt][currencyId].push(
            Record(self.amountByCurrency[currencyCt][currencyId], block.number)
        );

        updateCurrencies(self, currencyCt, currencyId);
    }

    function transfer_nn(Balance storage _from, Balance storage _to, int256 amount,
        address currencyCt, uint256 currencyId)
    internal
    {
        sub_nn(_from, amount, currencyCt, currencyId);
        add_nn(_to, amount, currencyCt, currencyId);
    }

    function recordsCount(Balance storage self, address currencyCt, uint256 currencyId)
    internal
    view
    returns (uint256)
    {
        return self.recordsByCurrency[currencyCt][currencyId].length;
    }

    function recordByBlockNumber(Balance storage self, address currencyCt, uint256 currencyId, uint256 blockNumber)
    internal
    view
    returns (int256, uint256)
    {
        uint256 index = indexByBlockNumber(self, currencyCt, currencyId, blockNumber);
        return 0 < index ? recordByIndex(self, currencyCt, currencyId, index - 1) : (0, 0);
    }

    function recordByIndex(Balance storage self, address currencyCt, uint256 currencyId, uint256 index)
    internal
    view
    returns (int256, uint256)
    {
        if (0 == self.recordsByCurrency[currencyCt][currencyId].length)
            return (0, 0);

        index = index.clampMax(self.recordsByCurrency[currencyCt][currencyId].length - 1);
        Record storage record = self.recordsByCurrency[currencyCt][currencyId][index];
        return (record.amount, record.blockNumber);
    }

    function lastRecord(Balance storage self, address currencyCt, uint256 currencyId)
    internal
    view
    returns (int256, uint256)
    {
        if (0 == self.recordsByCurrency[currencyCt][currencyId].length)
            return (0, 0);

        Record storage record = self.recordsByCurrency[currencyCt][currencyId][self.recordsByCurrency[currencyCt][currencyId].length - 1];
        return (record.amount, record.blockNumber);
    }

    function hasInUseCurrency(Balance storage self, address currencyCt, uint256 currencyId)
    internal
    view
    returns (bool)
    {
        return self.inUseCurrencies.has(currencyCt, currencyId);
    }

    function hasEverUsedCurrency(Balance storage self, address currencyCt, uint256 currencyId)
    internal
    view
    returns (bool)
    {
        return self.everUsedCurrencies.has(currencyCt, currencyId);
    }

    function updateCurrencies(Balance storage self, address currencyCt, uint256 currencyId)
    internal
    {
        if (0 == self.amountByCurrency[currencyCt][currencyId] && self.inUseCurrencies.has(currencyCt, currencyId))
            self.inUseCurrencies.removeByCurrency(currencyCt, currencyId);
        else if (!self.inUseCurrencies.has(currencyCt, currencyId)) {
            self.inUseCurrencies.add(currencyCt, currencyId);
            self.everUsedCurrencies.add(currencyCt, currencyId);
        }
    }

    function indexByBlockNumber(Balance storage self, address currencyCt, uint256 currencyId, uint256 blockNumber)
    internal
    view
    returns (uint256)
    {
        if (0 == self.recordsByCurrency[currencyCt][currencyId].length)
            return 0;
        for (uint256 i = self.recordsByCurrency[currencyCt][currencyId].length; i > 0; i--)
            if (self.recordsByCurrency[currencyCt][currencyId][i - 1].blockNumber <= blockNumber)
                return i;
        return 0;
    }
}

/*
 * Hubii Nahmii
 *
 * Compliant with the Hubii Nahmii specification v0.12.
 *
 * Copyright (C) 2017-2018 Hubii AS
 */







library NonFungibleBalanceLib {
    using SafeMathIntLib for int256;
    using SafeMathUintLib for uint256;
    using CurrenciesLib for CurrenciesLib.Currencies;

    //
    // Structures
    // -----------------------------------------------------------------------------------------------------------------
    struct Record {
        int256[] ids;
        uint256 blockNumber;
    }

    struct Balance {
        mapping(address => mapping(uint256 => int256[])) idsByCurrency;
        mapping(address => mapping(uint256 => mapping(int256 => uint256))) idIndexById;
        mapping(address => mapping(uint256 => Record[])) recordsByCurrency;

        CurrenciesLib.Currencies inUseCurrencies;
        CurrenciesLib.Currencies everUsedCurrencies;
    }

    //
    // Functions
    // -----------------------------------------------------------------------------------------------------------------
    function get(Balance storage self, address currencyCt, uint256 currencyId)
    internal
    view
    returns (int256[])
    {
        return self.idsByCurrency[currencyCt][currencyId];
    }

    function getByIndices(Balance storage self, address currencyCt, uint256 currencyId, uint256 indexLow, uint256 indexUp)
    internal
    view
    returns (int256[])
    {
        if (0 == self.idsByCurrency[currencyCt][currencyId].length)
            return new int256[](0);

        indexUp = indexUp.clampMax(self.idsByCurrency[currencyCt][currencyId].length - 1);

        int256[] memory idsByCurrency = new int256[](indexUp - indexLow + 1);
        for (uint256 i = indexLow; i < indexUp; i++)
            idsByCurrency[i - indexLow] = self.idsByCurrency[currencyCt][currencyId][i];

        return idsByCurrency;
    }

    function idsCount(Balance storage self, address currencyCt, uint256 currencyId)
    internal
    view
    returns (uint256)
    {
        return self.idsByCurrency[currencyCt][currencyId].length;
    }

    function hasId(Balance storage self, int256 id, address currencyCt, uint256 currencyId)
    internal
    view
    returns (bool)
    {
        return 0 < self.idIndexById[currencyCt][currencyId][id];
    }

    function recordByBlockNumber(Balance storage self, address currencyCt, uint256 currencyId, uint256 blockNumber)
    internal
    view
    returns (int256[], uint256)
    {
        uint256 index = indexByBlockNumber(self, currencyCt, currencyId, blockNumber);
        return 0 < index ? recordByIndex(self, currencyCt, currencyId, index - 1) : (new int256[](0), 0);
    }

    function recordByIndex(Balance storage self, address currencyCt, uint256 currencyId, uint256 index)
    internal
    view
    returns (int256[], uint256)
    {
        if (0 == self.recordsByCurrency[currencyCt][currencyId].length)
            return (new int256[](0), 0);

        index = index.clampMax(self.recordsByCurrency[currencyCt][currencyId].length - 1);
        Record storage record = self.recordsByCurrency[currencyCt][currencyId][index];
        return (record.ids, record.blockNumber);
    }

    function lastRecord(Balance storage self, address currencyCt, uint256 currencyId)
    internal
    view
    returns (int256[], uint256)
    {
        if (0 == self.recordsByCurrency[currencyCt][currencyId].length)
            return (new int256[](0), 0);

        Record storage record = self.recordsByCurrency[currencyCt][currencyId][self.recordsByCurrency[currencyCt][currencyId].length - 1];
        return (record.ids, record.blockNumber);
    }

    function recordsCount(Balance storage self, address currencyCt, uint256 currencyId)
    internal
    view
    returns (uint256)
    {
        return self.recordsByCurrency[currencyCt][currencyId].length;
    }

    function set(Balance storage self, int256 id, address currencyCt, uint256 currencyId)
    internal
    {
        int256[] memory ids = new int256[](1);
        ids[0] = id;
        set(self, ids, currencyCt, currencyId);
    }

    function set(Balance storage self, int256[] ids, address currencyCt, uint256 currencyId)
    internal
    {
        uint256 i;
        for (i = 0; i < self.idsByCurrency[currencyCt][currencyId].length; i++)
            self.idIndexById[currencyCt][currencyId][self.idsByCurrency[currencyCt][currencyId][i]] = 0;

        self.idsByCurrency[currencyCt][currencyId] = ids;

        for (i = 0; i < self.idsByCurrency[currencyCt][currencyId].length; i++)
            self.idIndexById[currencyCt][currencyId][self.idsByCurrency[currencyCt][currencyId][i]] = i + 1;

        self.recordsByCurrency[currencyCt][currencyId].push(
            Record(self.idsByCurrency[currencyCt][currencyId], block.number)
        );

        updateInUseCurrencies(self, currencyCt, currencyId);
    }

    function reset(Balance storage self, address currencyCt, uint256 currencyId)
    internal
    {
        for (uint256 i = 0; i < self.idsByCurrency[currencyCt][currencyId].length; i++)
            self.idIndexById[currencyCt][currencyId][self.idsByCurrency[currencyCt][currencyId][i]] = 0;

        self.idsByCurrency[currencyCt][currencyId].length = 0;

        self.recordsByCurrency[currencyCt][currencyId].push(
            Record(self.idsByCurrency[currencyCt][currencyId], block.number)
        );

        updateInUseCurrencies(self, currencyCt, currencyId);
    }

    function add(Balance storage self, int256 id, address currencyCt, uint256 currencyId)
    internal
    returns (bool)
    {
        if (0 < self.idIndexById[currencyCt][currencyId][id])
            return false;

        self.idsByCurrency[currencyCt][currencyId].push(id);

        self.idIndexById[currencyCt][currencyId][id] = self.idsByCurrency[currencyCt][currencyId].length;

        self.recordsByCurrency[currencyCt][currencyId].push(
            Record(self.idsByCurrency[currencyCt][currencyId], block.number)
        );

        updateInUseCurrencies(self, currencyCt, currencyId);

        return true;
    }

    function sub(Balance storage self, int256 id, address currencyCt, uint256 currencyId)
    internal
    returns (bool)
    {
        uint256 index = self.idIndexById[currencyCt][currencyId][id];

        if (0 == index)
            return false;

        if (index < self.idsByCurrency[currencyCt][currencyId].length) {
            self.idsByCurrency[currencyCt][currencyId][index - 1] = self.idsByCurrency[currencyCt][currencyId][self.idsByCurrency[currencyCt][currencyId].length - 1];
            self.idIndexById[currencyCt][currencyId][self.idsByCurrency[currencyCt][currencyId][index - 1]] = index;
        }
        self.idsByCurrency[currencyCt][currencyId].length--;
        self.idIndexById[currencyCt][currencyId][id] = 0;

        self.recordsByCurrency[currencyCt][currencyId].push(
            Record(self.idsByCurrency[currencyCt][currencyId], block.number)
        );

        updateInUseCurrencies(self, currencyCt, currencyId);

        return true;
    }

    function transfer(Balance storage _from, Balance storage _to, int256 id,
        address currencyCt, uint256 currencyId)
    internal
    returns (bool)
    {
        return sub(_from, id, currencyCt, currencyId) && add(_to, id, currencyCt, currencyId);
    }

    function hasInUseCurrency(Balance storage self, address currencyCt, uint256 currencyId)
    internal
    view
    returns (bool)
    {
        return self.inUseCurrencies.has(currencyCt, currencyId);
    }

    function hasEverUsedCurrency(Balance storage self, address currencyCt, uint256 currencyId)
    internal
    view
    returns (bool)
    {
        return self.everUsedCurrencies.has(currencyCt, currencyId);
    }

    function updateInUseCurrencies(Balance storage self, address currencyCt, uint256 currencyId)
    internal
    {
        if (0 == self.idsByCurrency[currencyCt][currencyId].length && self.inUseCurrencies.has(currencyCt, currencyId))
            self.inUseCurrencies.removeByCurrency(currencyCt, currencyId);
        else if (!self.inUseCurrencies.has(currencyCt, currencyId)) {
            self.inUseCurrencies.add(currencyCt, currencyId);
            self.everUsedCurrencies.add(currencyCt, currencyId);
        }
    }

    function indexByBlockNumber(Balance storage self, address currencyCt, uint256 currencyId, uint256 blockNumber)
    internal
    view
    returns (uint256)
    {
        if (0 == self.recordsByCurrency[currencyCt][currencyId].length)
            return 0;
        for (uint256 i = self.recordsByCurrency[currencyCt][currencyId].length; i > 0; i--)
            if (self.recordsByCurrency[currencyCt][currencyId][i - 1].blockNumber <= blockNumber)
                return i;
        return 0;
    }
}

/*
 * Hubii Nahmii
 *
 * Compliant with the Hubii Nahmii specification v0.12.
 *
 * Copyright (C) 2017-2018 Hubii AS
 */










/**
 * @title Balance tracker
 * @notice An ownable to track balances of generic types
 */
contract BalanceTracker is Ownable, Servable {
    using SafeMathIntLib for int256;
    using SafeMathUintLib for uint256;
    using FungibleBalanceLib for FungibleBalanceLib.Balance;
    using NonFungibleBalanceLib for NonFungibleBalanceLib.Balance;

    //
    // Constants
    // -----------------------------------------------------------------------------------------------------------------
    string constant public DEPOSITED_BALANCE_TYPE = "deposited";
    string constant public SETTLED_BALANCE_TYPE = "settled";
    string constant public STAGED_BALANCE_TYPE = "staged";

    //
    // Structures
    // -----------------------------------------------------------------------------------------------------------------
    struct Wallet {
        mapping(bytes32 => FungibleBalanceLib.Balance) fungibleBalanceByType;
        mapping(bytes32 => NonFungibleBalanceLib.Balance) nonFungibleBalanceByType;
    }

    //
    // Variables
    // -----------------------------------------------------------------------------------------------------------------
    bytes32 public depositedBalanceType;
    bytes32 public settledBalanceType;
    bytes32 public stagedBalanceType;

    bytes32[] public _allBalanceTypes;
    bytes32[] public _activeBalanceTypes;

    bytes32[] public trackedBalanceTypes;
    mapping(bytes32 => bool) public trackedBalanceTypeMap;

    mapping(address => Wallet) private walletMap;

    address[] public trackedWallets;
    mapping(address => uint256) public trackedWalletIndexByWallet;

    //
    // Constructor
    // -----------------------------------------------------------------------------------------------------------------
    constructor(address deployer) Ownable(deployer)
    public
    {
        depositedBalanceType = keccak256(abi.encodePacked(DEPOSITED_BALANCE_TYPE));
        settledBalanceType = keccak256(abi.encodePacked(SETTLED_BALANCE_TYPE));
        stagedBalanceType = keccak256(abi.encodePacked(STAGED_BALANCE_TYPE));

        _allBalanceTypes.push(settledBalanceType);
        _allBalanceTypes.push(depositedBalanceType);
        _allBalanceTypes.push(stagedBalanceType);

        _activeBalanceTypes.push(settledBalanceType);
        _activeBalanceTypes.push(depositedBalanceType);
    }

    //
    // Functions
    // -----------------------------------------------------------------------------------------------------------------
    /// @notice Get the fungible balance (amount) of the given wallet, type and currency
    /// @param wallet The address of the concerned wallet
    /// @param _type The balance type
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    /// @return The stored balance
    function get(address wallet, bytes32 _type, address currencyCt, uint256 currencyId)
    public
    view
    returns (int256)
    {
        return walletMap[wallet].fungibleBalanceByType[_type].get(currencyCt, currencyId);
    }

    /// @notice Get the non-fungible balance (IDs) of the given wallet, type, currency and index range
    /// @param wallet The address of the concerned wallet
    /// @param _type The balance type
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    /// @param indexLow The lower index of IDs
    /// @param indexUp The upper index of IDs
    /// @return The stored balance
    function getByIndices(address wallet, bytes32 _type, address currencyCt, uint256 currencyId,
        uint256 indexLow, uint256 indexUp)
    public
    view
    returns (int256[])
    {
        return walletMap[wallet].nonFungibleBalanceByType[_type].getByIndices(
            currencyCt, currencyId, indexLow, indexUp
        );
    }

    /// @notice Get all the non-fungible balance (IDs) of the given wallet, type and currency
    /// @param wallet The address of the concerned wallet
    /// @param _type The balance type
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    /// @return The stored balance
    function getAll(address wallet, bytes32 _type, address currencyCt, uint256 currencyId)
    public
    view
    returns (int256[])
    {
        return walletMap[wallet].nonFungibleBalanceByType[_type].get(
            currencyCt, currencyId
        );
    }

    /// @notice Get the count of non-fungible IDs of the given wallet, type and currency
    /// @param wallet The address of the concerned wallet
    /// @param _type The balance type
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    /// @return The count of IDs
    function idsCount(address wallet, bytes32 _type, address currencyCt, uint256 currencyId)
    public
    view
    returns (uint256)
    {
        return walletMap[wallet].nonFungibleBalanceByType[_type].idsCount(
            currencyCt, currencyId
        );
    }

    /// @notice Gauge whether the ID is included in the given wallet, type and currency
    /// @param wallet The address of the concerned wallet
    /// @param _type The balance type
    /// @param id The ID of the concerned unit
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    /// @return true if ID is included, else false
    function hasId(address wallet, bytes32 _type, int256 id, address currencyCt, uint256 currencyId)
    public
    view
    returns (bool)
    {
        return walletMap[wallet].nonFungibleBalanceByType[_type].hasId(
            id, currencyCt, currencyId
        );
    }

    /// @notice Set the balance of the given wallet, type and currency to the given value
    /// @param wallet The address of the concerned wallet
    /// @param _type The balance type
    /// @param value The value (amount of fungible, id of non-fungible) to set
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    /// @param fungible True if setting fungible balance, else false
    function set(address wallet, bytes32 _type, int256 value, address currencyCt, uint256 currencyId, bool fungible)
    public
    onlyActiveService
    {
        // Update the balance
        if (fungible)
            walletMap[wallet].fungibleBalanceByType[_type].set(
                value, currencyCt, currencyId
            );

        else
            walletMap[wallet].nonFungibleBalanceByType[_type].set(
                value, currencyCt, currencyId
            );

        // Update balance type hashes
        _updateTrackedBalanceTypes(_type);

        // Update tracked wallets
        _updateTrackedWallets(wallet);
    }

    /// @notice Set the non-fungible balance IDs of the given wallet, type and currency to the given value
    /// @param wallet The address of the concerned wallet
    /// @param _type The balance type
    /// @param ids The ids of non-fungible) to set
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    function setIds(address wallet, bytes32 _type, int256[] ids, address currencyCt, uint256 currencyId)
    public
    onlyActiveService
    {
        // Update the balance
        walletMap[wallet].nonFungibleBalanceByType[_type].set(
            ids, currencyCt, currencyId
        );

        // Update balance type hashes
        _updateTrackedBalanceTypes(_type);

        // Update tracked wallets
        _updateTrackedWallets(wallet);
    }

    /// @notice Add the given value to the balance of the given wallet, type and currency
    /// @param wallet The address of the concerned wallet
    /// @param _type The balance type
    /// @param value The value (amount of fungible, id of non-fungible) to add
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    /// @param fungible True if adding fungible balance, else false
    function add(address wallet, bytes32 _type, int256 value, address currencyCt, uint256 currencyId,
        bool fungible)
    public
    onlyActiveService
    {
        // Update the balance
        if (fungible)
            walletMap[wallet].fungibleBalanceByType[_type].add(
                value, currencyCt, currencyId
            );
        else
            walletMap[wallet].nonFungibleBalanceByType[_type].add(
                value, currencyCt, currencyId
            );

        // Update balance type hashes
        _updateTrackedBalanceTypes(_type);

        // Update tracked wallets
        _updateTrackedWallets(wallet);
    }

    /// @notice Subtract the given value from the balance of the given wallet, type and currency
    /// @param wallet The address of the concerned wallet
    /// @param _type The balance type
    /// @param value The value (amount of fungible, id of non-fungible) to subtract
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    /// @param fungible True if subtracting fungible balance, else false
    function sub(address wallet, bytes32 _type, int256 value, address currencyCt, uint256 currencyId,
        bool fungible)
    public
    onlyActiveService
    {
        // Update the balance
        if (fungible)
            walletMap[wallet].fungibleBalanceByType[_type].sub(
                value, currencyCt, currencyId
            );
        else
            walletMap[wallet].nonFungibleBalanceByType[_type].sub(
                value, currencyCt, currencyId
            );

        // Update tracked wallets
        _updateTrackedWallets(wallet);
    }

    /// @notice Gauge whether this tracker has in-use data for the given wallet, type and currency
    /// @param wallet The address of the concerned wallet
    /// @param _type The balance type
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    /// @return true if data is stored, else false
    function hasInUseCurrency(address wallet, bytes32 _type, address currencyCt, uint256 currencyId)
    public
    view
    returns (bool)
    {
        return walletMap[wallet].fungibleBalanceByType[_type].hasInUseCurrency(currencyCt, currencyId)
        || walletMap[wallet].nonFungibleBalanceByType[_type].hasInUseCurrency(currencyCt, currencyId);
    }

    /// @notice Gauge whether this tracker has ever-used data for the given wallet, type and currency
    /// @param wallet The address of the concerned wallet
    /// @param _type The balance type
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    /// @return true if data is stored, else false
    function hasEverUsedCurrency(address wallet, bytes32 _type, address currencyCt, uint256 currencyId)
    public
    view
    returns (bool)
    {
        return walletMap[wallet].fungibleBalanceByType[_type].hasEverUsedCurrency(currencyCt, currencyId)
        || walletMap[wallet].nonFungibleBalanceByType[_type].hasEverUsedCurrency(currencyCt, currencyId);
    }

    /// @notice Get the count of fungible balance records for the given wallet, type and currency
    /// @param wallet The address of the concerned wallet
    /// @param _type The balance type
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    /// @return The count of balance log entries
    function fungibleRecordsCount(address wallet, bytes32 _type, address currencyCt, uint256 currencyId)
    public
    view
    returns (uint256)
    {
        return walletMap[wallet].fungibleBalanceByType[_type].recordsCount(currencyCt, currencyId);
    }

    /// @notice Get the fungible balance record for the given wallet, type, currency
    /// log entry index
    /// @param wallet The address of the concerned wallet
    /// @param _type The balance type
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    /// @param index The concerned record index
    /// @return The balance record
    function fungibleRecordByIndex(address wallet, bytes32 _type, address currencyCt, uint256 currencyId,
        uint256 index)
    public
    view
    returns (int256 amount, uint256 blockNumber)
    {
        return walletMap[wallet].fungibleBalanceByType[_type].recordByIndex(currencyCt, currencyId, index);
    }

    /// @notice Get the non-fungible balance record for the given wallet, type, currency
    /// block number
    /// @param wallet The address of the concerned wallet
    /// @param _type The balance type
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    /// @param _blockNumber The concerned block number
    /// @return The balance record
    function fungibleRecordByBlockNumber(address wallet, bytes32 _type, address currencyCt, uint256 currencyId,
        uint256 _blockNumber)
    public
    view
    returns (int256 amount, uint256 blockNumber)
    {
        return walletMap[wallet].fungibleBalanceByType[_type].recordByBlockNumber(currencyCt, currencyId, _blockNumber);
    }

    /// @notice Get the last (most recent) non-fungible balance record for the given wallet, type and currency
    /// @param wallet The address of the concerned wallet
    /// @param _type The balance type
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    /// @return The last log entry
    function lastFungibleRecord(address wallet, bytes32 _type, address currencyCt, uint256 currencyId)
    public
    view
    returns (int256 amount, uint256 blockNumber)
    {
        return walletMap[wallet].fungibleBalanceByType[_type].lastRecord(currencyCt, currencyId);
    }

    /// @notice Get the count of non-fungible balance records for the given wallet, type and currency
    /// @param wallet The address of the concerned wallet
    /// @param _type The balance type
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    /// @return The count of balance log entries
    function nonFungibleRecordsCount(address wallet, bytes32 _type, address currencyCt, uint256 currencyId)
    public
    view
    returns (uint256)
    {
        return walletMap[wallet].nonFungibleBalanceByType[_type].recordsCount(currencyCt, currencyId);
    }

    /// @notice Get the non-fungible balance record for the given wallet, type, currency
    /// and record index
    /// @param wallet The address of the concerned wallet
    /// @param _type The balance type
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    /// @param index The concerned record index
    /// @return The balance record
    function nonFungibleRecordByIndex(address wallet, bytes32 _type, address currencyCt, uint256 currencyId,
        uint256 index)
    public
    view
    returns (int256[] ids, uint256 blockNumber)
    {
        return walletMap[wallet].nonFungibleBalanceByType[_type].recordByIndex(currencyCt, currencyId, index);
    }

    /// @notice Get the non-fungible balance record for the given wallet, type, currency
    /// and block number
    /// @param wallet The address of the concerned wallet
    /// @param _type The balance type
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    /// @param _blockNumber The concerned block number
    /// @return The balance record
    function nonFungibleRecordByBlockNumber(address wallet, bytes32 _type, address currencyCt, uint256 currencyId,
        uint256 _blockNumber)
    public
    view
    returns (int256[] ids, uint256 blockNumber)
    {
        return walletMap[wallet].nonFungibleBalanceByType[_type].recordByBlockNumber(currencyCt, currencyId, _blockNumber);
    }

    /// @notice Get the last (most recent) non-fungible balance record for the given wallet, type and currency
    /// @param wallet The address of the concerned wallet
    /// @param _type The balance type
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    /// @return The last log entry
    function lastNonFungibleRecord(address wallet, bytes32 _type, address currencyCt, uint256 currencyId)
    public
    view
    returns (int256[] ids, uint256 blockNumber)
    {
        return walletMap[wallet].nonFungibleBalanceByType[_type].lastRecord(currencyCt, currencyId);
    }

    /// @notice Get the count of tracked balance types
    /// @return The count of tracked balance types
    function trackedBalanceTypesCount()
    public
    view
    returns (uint256)
    {
        return trackedBalanceTypes.length;
    }

    /// @notice Get the count of tracked wallets
    /// @return The count of tracked wallets
    function trackedWalletsCount()
    public
    view
    returns (uint256)
    {
        return trackedWallets.length;
    }

    /// @notice Get the default full set of balance types
    /// @return The set of all balance types
    function allBalanceTypes()
    public
    view
    returns (bytes32[])
    {
        return _allBalanceTypes;
    }

    /// @notice Get the default set of active balance types
    /// @return The set of active balance types
    function activeBalanceTypes()
    public
    view
    returns (bytes32[])
    {
        return _activeBalanceTypes;
    }

    /// @notice Get the subset of tracked wallets in the given index range
    /// @param low The lower index
    /// @param up The upper index
    /// @return The subset of tracked wallets
    function trackedWalletsByIndices(uint256 low, uint256 up)
    public
    view
    returns (address[])
    {
        require(0 < trackedWallets.length);
        require(low <= up);

        up = up.clampMax(trackedWallets.length - 1);
        address[] memory _trackedWallets = new address[](up - low + 1);
        for (uint256 i = low; i <= up; i++)
            _trackedWallets[i - low] = trackedWallets[i];

        return _trackedWallets;
    }

    //
    // Private functions
    // -----------------------------------------------------------------------------------------------------------------
    function _updateTrackedBalanceTypes(bytes32 _type)
    private
    {
        if (!trackedBalanceTypeMap[_type]) {
            trackedBalanceTypeMap[_type] = true;
            trackedBalanceTypes.push(_type);
        }
    }

    function _updateTrackedWallets(address wallet)
    private
    {
        if (0 == trackedWalletIndexByWallet[wallet]) {
            trackedWallets.push(wallet);
            trackedWalletIndexByWallet[wallet] = trackedWallets.length;
        }
    }
}

/*
 * Hubii Nahmii
 *
 * Compliant with the Hubii Nahmii specification v0.12.
 *
 * Copyright (C) 2017-2018 Hubii AS
 */






/**
 * @title BalanceTrackable
 * @notice An ownable that has a balance tracker property
 */
contract BalanceTrackable is Ownable {
    //
    // Variables
    // -----------------------------------------------------------------------------------------------------------------
    BalanceTracker public balanceTracker;
    bool public balanceTrackerFrozen;

    //
    // Events
    // -----------------------------------------------------------------------------------------------------------------
    event SetBalanceTrackerEvent(BalanceTracker oldBalanceTracker, BalanceTracker newBalanceTracker);
    event FreezeBalanceTrackerEvent();

    //
    // Functions
    // -----------------------------------------------------------------------------------------------------------------
    /// @notice Set the balance tracker contract
    /// @param newBalanceTracker The (address of) BalanceTracker contract instance
    function setBalanceTracker(BalanceTracker newBalanceTracker)
    public
    onlyDeployer
    notNullAddress(newBalanceTracker)
    notSameAddresses(newBalanceTracker, balanceTracker)
    {
        // Require that this contract has not been frozen
        require(!balanceTrackerFrozen);

        // Update fields
        BalanceTracker oldBalanceTracker = balanceTracker;
        balanceTracker = newBalanceTracker;

        // Emit event
        emit SetBalanceTrackerEvent(oldBalanceTracker, newBalanceTracker);
    }

    /// @notice Freeze the balance tracker from further updates
    /// @dev This operation can not be undone
    function freezeBalanceTracker()
    public
    onlyDeployer
    {
        balanceTrackerFrozen = true;

        // Emit event
        emit FreezeBalanceTrackerEvent();
    }

    //
    // Modifiers
    // -----------------------------------------------------------------------------------------------------------------
    modifier balanceTrackerInitialized() {
        require(balanceTracker != address(0));
        _;
    }
}

/*
 * Hubii Nahmii
 *
 * Compliant with the Hubii Nahmii specification v0.12.
 *
 * Copyright (C) 2017-2018 Hubii AS
 */






/**
 * @title Transaction tracker
 * @notice An ownable to track transactions of generic types
 */
contract TransactionTracker is Ownable, Servable {

    //
    // Structures
    // -----------------------------------------------------------------------------------------------------------------
    struct TransactionRecord {
        int256 value;
        uint256 blockNumber;
        address currencyCt;
        uint256 currencyId;
    }

    struct TransactionLog {
        TransactionRecord[] records;
        mapping(address => mapping(uint256 => uint256[])) recordIndicesByCurrency;
    }

    //
    // Constants
    // -----------------------------------------------------------------------------------------------------------------
    string constant public DEPOSIT_TRANSACTION_TYPE = "deposit";
    string constant public WITHDRAWAL_TRANSACTION_TYPE = "withdrawal";

    //
    // Variables
    // -----------------------------------------------------------------------------------------------------------------
    bytes32 public depositTransactionType;
    bytes32 public withdrawalTransactionType;

    mapping(address => mapping(bytes32 => TransactionLog)) private transactionLogByWalletType;

    //
    // Constructor
    // -----------------------------------------------------------------------------------------------------------------
    constructor(address deployer) Ownable(deployer)
    public
    {
        depositTransactionType = keccak256(abi.encodePacked(DEPOSIT_TRANSACTION_TYPE));
        withdrawalTransactionType = keccak256(abi.encodePacked(WITHDRAWAL_TRANSACTION_TYPE));
    }

    //
    // Functions
    // -----------------------------------------------------------------------------------------------------------------
    /// @notice Add a transaction record of the given wallet, type, value and currency
    /// @param wallet The address of the concerned wallet
    /// @param _type The transaction type
    /// @param value The concerned value (amount of fungible, id of non-fungible)
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    function add(address wallet, bytes32 _type, int256 value, address currencyCt,
        uint256 currencyId)
    public
    onlyActiveService
    {
        transactionLogByWalletType[wallet][_type].records.length++;

        uint256 index = transactionLogByWalletType[wallet][_type].records.length - 1;

        transactionLogByWalletType[wallet][_type].records[index].value = value;
        transactionLogByWalletType[wallet][_type].records[index].blockNumber = block.number;
        transactionLogByWalletType[wallet][_type].records[index].currencyCt = currencyCt;
        transactionLogByWalletType[wallet][_type].records[index].currencyId = currencyId;

        transactionLogByWalletType[wallet][_type].recordIndicesByCurrency[currencyCt][currencyId].push(index);
    }

    /// @notice Get the number of transaction records for the given wallet and type
    /// @param wallet The address of the concerned wallet
    /// @param _type The transaction type
    /// @return The count of transaction records
    function count(address wallet, bytes32 _type)
    public
    view
    returns (uint256)
    {
        return transactionLogByWalletType[wallet][_type].records.length;
    }

    /// @notice Get the transaction record for the given wallet and type by the given index
    /// @param wallet The address of the concerned wallet
    /// @param _type The transaction type
    /// @param index The concerned log index
    /// @return The transaction record
    function getByIndex(address wallet, bytes32 _type, uint256 index)
    public
    view
    returns (int256 value, uint256 blockNumber, address currencyCt, uint256 currencyId)
    {
        TransactionRecord storage entry = transactionLogByWalletType[wallet][_type].records[index];
        value = entry.value;
        blockNumber = entry.blockNumber;
        currencyCt = entry.currencyCt;
        currencyId = entry.currencyId;
    }

    /// @notice Get the transaction record for the given wallet and type by the given block number
    /// @param wallet The address of the concerned wallet
    /// @param _type The transaction type
    /// @param _blockNumber The concerned block number
    /// @return The transaction record
    function getByBlockNumber(address wallet, bytes32 _type, uint256 _blockNumber)
    public
    view
    returns (int256 value, uint256 blockNumber, address currencyCt, uint256 currencyId)
    {
        return getByIndex(wallet, _type, _indexByBlockNumber(wallet, _type, _blockNumber));
    }

    /// @notice Get the number of transaction records for the given wallet, type and currency
    /// @param wallet The address of the concerned wallet
    /// @param _type The transaction type
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    /// @return The count of transaction records
    function countByCurrency(address wallet, bytes32 _type, address currencyCt,
        uint256 currencyId)
    public
    view
    returns (uint256)
    {
        return transactionLogByWalletType[wallet][_type].recordIndicesByCurrency[currencyCt][currencyId].length;
    }

    /// @notice Get the transaction record for the given wallet, type and currency by the given index
    /// @param wallet The address of the concerned wallet
    /// @param _type The transaction type
    /// @param index The concerned log index
    /// @return The transaction record
    function getByCurrencyIndex(address wallet, bytes32 _type, address currencyCt,
        uint256 currencyId, uint256 index)
    public
    view
    returns (int256 value, uint256 blockNumber)
    {
        uint256 entryIndex = transactionLogByWalletType[wallet][_type].recordIndicesByCurrency[currencyCt][currencyId][index];

        TransactionRecord storage entry = transactionLogByWalletType[wallet][_type].records[entryIndex];
        value = entry.value;
        blockNumber = entry.blockNumber;
    }

    /// @notice Get the transaction record for the given wallet, type and currency by the given block number
    /// @param wallet The address of the concerned wallet
    /// @param _type The transaction type
    /// @param _blockNumber The concerned block number
    /// @return The transaction record
    function getByCurrencyBlockNumber(address wallet, bytes32 _type, address currencyCt,
        uint256 currencyId, uint256 _blockNumber)
    public
    view
    returns (int256 value, uint256 blockNumber)
    {
        return getByCurrencyIndex(
            wallet, _type, currencyCt, currencyId,
            _indexByCurrencyBlockNumber(
                wallet, _type, currencyCt, currencyId, _blockNumber
            )
        );
    }

    //
    // Private functions
    // -----------------------------------------------------------------------------------------------------------------
    function _indexByBlockNumber(address wallet, bytes32 _type, uint256 blockNumber)
    private
    view
    returns (uint256)
    {
        require(0 < transactionLogByWalletType[wallet][_type].records.length);
        for (uint256 i = transactionLogByWalletType[wallet][_type].records.length - 1; i >= 0; i--)
            if (blockNumber >= transactionLogByWalletType[wallet][_type].records[i].blockNumber)
                return i;
        revert();
    }

    function _indexByCurrencyBlockNumber(address wallet, bytes32 _type, address currencyCt,
        uint256 currencyId, uint256 blockNumber)
    private
    view
    returns (uint256)
    {
        require(0 < transactionLogByWalletType[wallet][_type].recordIndicesByCurrency[currencyCt][currencyId].length);
        for (uint256 i = transactionLogByWalletType[wallet][_type].recordIndicesByCurrency[currencyCt][currencyId].length - 1; i >= 0; i--) {
            uint256 j = transactionLogByWalletType[wallet][_type].recordIndicesByCurrency[currencyCt][currencyId][i];
            if (blockNumber >= transactionLogByWalletType[wallet][_type].records[j].blockNumber)
                return j;
        }
        revert();
    }
}

/*
 * Hubii Nahmii
 *
 * Compliant with the Hubii Nahmii specification v0.12.
 *
 * Copyright (C) 2017-2018 Hubii AS
 */






/**
 * @title TransactionTrackable
 * @notice An ownable that has a transaction tracker property
 */
contract TransactionTrackable is Ownable {
    //
    // Variables
    // -----------------------------------------------------------------------------------------------------------------
    TransactionTracker public transactionTracker;
    bool public transactionTrackerFrozen;

    //
    // Events
    // -----------------------------------------------------------------------------------------------------------------
    event SetTransactionTrackerEvent(TransactionTracker oldTransactionTracker, TransactionTracker newTransactionTracker);
    event FreezeTransactionTrackerEvent();

    //
    // Functions
    // -----------------------------------------------------------------------------------------------------------------
    /// @notice Set the transaction tracker contract
    /// @param newTransactionTracker The (address of) TransactionTracker contract instance
    function setTransactionTracker(TransactionTracker newTransactionTracker)
    public
    onlyDeployer
    notNullAddress(newTransactionTracker)
    notSameAddresses(newTransactionTracker, transactionTracker)
    {
        // Require that this contract has not been frozen
        require(!transactionTrackerFrozen);

        // Update fields
        TransactionTracker oldTransactionTracker = transactionTracker;
        transactionTracker = newTransactionTracker;

        // Emit event
        emit SetTransactionTrackerEvent(oldTransactionTracker, newTransactionTracker);
    }

    /// @notice Freeze the transaction tracker from further updates
    /// @dev This operation can not be undone
    function freezeTransactionTracker()
    public
    onlyDeployer
    {
        transactionTrackerFrozen = true;

        // Emit event
        emit FreezeTransactionTrackerEvent();
    }

    //
    // Modifiers
    // -----------------------------------------------------------------------------------------------------------------
    modifier transactionTrackerInitialized() {
        require(transactionTracker != address(0));
        _;
    }
}

/*
 * Hubii Nahmii
 *
 * Compliant with the Hubii Nahmii specification v0.12.
 *
 * Copyright (C) 2017-2018 Hubii AS
 */



library BlockNumbUintsLib {
    //
    // Structures
    // -----------------------------------------------------------------------------------------------------------------
    struct Entry {
        uint256 blockNumber;
        uint256 value;
    }

    struct BlockNumbUints {
        Entry[] entries;
    }

    //
    // Functions
    // -----------------------------------------------------------------------------------------------------------------
    function currentValue(BlockNumbUints storage self)
    internal
    view
    returns (uint256)
    {
        return valueAt(self, block.number);
    }

    function currentEntry(BlockNumbUints storage self)
    internal
    view
    returns (Entry)
    {
        return entryAt(self, block.number);
    }

    function valueAt(BlockNumbUints storage self, uint256 _blockNumber)
    internal
    view
    returns (uint256)
    {
        return entryAt(self, _blockNumber).value;
    }

    function entryAt(BlockNumbUints storage self, uint256 _blockNumber)
    internal
    view
    returns (Entry)
    {
        return self.entries[indexByBlockNumber(self, _blockNumber)];
    }

    function addEntry(BlockNumbUints storage self, uint256 blockNumber, uint256 value)
    internal
    {
        require(
            0 == self.entries.length ||
            blockNumber > self.entries[self.entries.length - 1].blockNumber
        );

        self.entries.push(Entry(blockNumber, value));
    }

    function count(BlockNumbUints storage self)
    internal
    view
    returns (uint256)
    {
        return self.entries.length;
    }

    function entries(BlockNumbUints storage self)
    internal
    view
    returns (Entry[])
    {
        return self.entries;
    }

    function indexByBlockNumber(BlockNumbUints storage self, uint256 blockNumber)
    internal
    view
    returns (uint256)
    {
        require(0 < self.entries.length);
        for (uint256 i = self.entries.length - 1; i >= 0; i--)
            if (blockNumber >= self.entries[i].blockNumber)
                return i;
        revert();
    }
}

/*
 * Hubii Nahmii
 *
 * Compliant with the Hubii Nahmii specification v0.12.
 *
 * Copyright (C) 2017-2018 Hubii AS
 */



library BlockNumbIntsLib {
    //
    // Structures
    // -----------------------------------------------------------------------------------------------------------------
    struct Entry {
        uint256 blockNumber;
        int256 value;
    }

    struct BlockNumbInts {
        Entry[] entries;
    }

    //
    // Functions
    // -----------------------------------------------------------------------------------------------------------------
    function currentValue(BlockNumbInts storage self)
    internal
    view
    returns (int256)
    {
        return valueAt(self, block.number);
    }

    function currentEntry(BlockNumbInts storage self)
    internal
    view
    returns (Entry)
    {
        return entryAt(self, block.number);
    }

    function valueAt(BlockNumbInts storage self, uint256 _blockNumber)
    internal
    view
    returns (int256)
    {
        return entryAt(self, _blockNumber).value;
    }

    function entryAt(BlockNumbInts storage self, uint256 _blockNumber)
    internal
    view
    returns (Entry)
    {
        return self.entries[indexByBlockNumber(self, _blockNumber)];
    }

    function addEntry(BlockNumbInts storage self, uint256 blockNumber, int256 value)
    internal
    {
        require(
            0 == self.entries.length ||
            blockNumber > self.entries[self.entries.length - 1].blockNumber
        );

        self.entries.push(Entry(blockNumber, value));
    }

    function count(BlockNumbInts storage self)
    internal
    view
    returns (uint256)
    {
        return self.entries.length;
    }

    function entries(BlockNumbInts storage self)
    internal
    view
    returns (Entry[])
    {
        return self.entries;
    }

    function indexByBlockNumber(BlockNumbInts storage self, uint256 blockNumber)
    internal
    view
    returns (uint256)
    {
        require(0 < self.entries.length);
        for (uint256 i = self.entries.length - 1; i >= 0; i--)
            if (blockNumber >= self.entries[i].blockNumber)
                return i;
        revert();
    }
}

/*
 * Hubii Nahmii
 *
 * Compliant with the Hubii Nahmii specification v0.12.
 *
 * Copyright (C) 2017-2018 Hubii AS
 */



library ConstantsLib {
    // Get the fraction that represents the entirety, equivalent of 100%
    function PARTS_PER()
    public
    pure
    returns (int256)
    {
        return 1e18;
    }
}

/*
 * Hubii Nahmii
 *
 * Compliant with the Hubii Nahmii specification v0.12.
 *
 * Copyright (C) 2017-2018 Hubii AS
 */






library BlockNumbDisdIntsLib {
    using SafeMathIntLib for int256;

    //
    // Structures
    // -----------------------------------------------------------------------------------------------------------------
    struct Discount {
        int256 tier;
        int256 value;
    }

    struct Entry {
        uint256 blockNumber;
        int256 nominal;
        Discount[] discounts;
    }

    struct BlockNumbDisdInts {
        Entry[] entries;
    }

    //
    // Functions
    // -----------------------------------------------------------------------------------------------------------------
    function currentNominalValue(BlockNumbDisdInts storage self)
    internal
    view
    returns (int256)
    {
        return nominalValueAt(self, block.number);
    }

    function currentDiscountedValue(BlockNumbDisdInts storage self, int256 tier)
    internal
    view
    returns (int256)
    {
        return discountedValueAt(self, block.number, tier);
    }

    function currentEntry(BlockNumbDisdInts storage self)
    internal
    view
    returns (Entry)
    {
        return entryAt(self, block.number);
    }

    function nominalValueAt(BlockNumbDisdInts storage self, uint256 _blockNumber)
    internal
    view
    returns (int256)
    {
        return entryAt(self, _blockNumber).nominal;
    }

    function discountedValueAt(BlockNumbDisdInts storage self, uint256 _blockNumber, int256 tier)
    internal
    view
    returns (int256)
    {
        Entry memory entry = entryAt(self, _blockNumber);
        if (0 < entry.discounts.length) {
            uint256 index = indexByTier(entry.discounts, tier);
            if (0 < index)
                return entry.nominal.mul(
                    ConstantsLib.PARTS_PER().sub(entry.discounts[index - 1].value)
                ).div(
                    ConstantsLib.PARTS_PER()
                );
            else
                return entry.nominal;
        } else
            return entry.nominal;
    }

    function entryAt(BlockNumbDisdInts storage self, uint256 _blockNumber)
    internal
    view
    returns (Entry)
    {
        return self.entries[indexByBlockNumber(self, _blockNumber)];
    }

    function addNominalEntry(BlockNumbDisdInts storage self, uint256 blockNumber, int256 nominal)
    internal
    {
        require(
            0 == self.entries.length ||
            blockNumber > self.entries[self.entries.length - 1].blockNumber
        );

        self.entries.length++;
        Entry storage entry = self.entries[self.entries.length - 1];

        entry.blockNumber = blockNumber;
        entry.nominal = nominal;
    }

    function addDiscountedEntry(BlockNumbDisdInts storage self, uint256 blockNumber, int256 nominal,
        int256[] discountTiers, int256[] discountValues)
    internal
    {
        require(discountTiers.length == discountValues.length);

        addNominalEntry(self, blockNumber, nominal);

        Entry storage entry = self.entries[self.entries.length - 1];
        for (uint256 i = 0; i < discountTiers.length; i++)
            entry.discounts.push(Discount(discountTiers[i], discountValues[i]));
    }

    function count(BlockNumbDisdInts storage self)
    internal
    view
    returns (uint256)
    {
        return self.entries.length;
    }

    function entries(BlockNumbDisdInts storage self)
    internal
    view
    returns (Entry[])
    {
        return self.entries;
    }

    function indexByBlockNumber(BlockNumbDisdInts storage self, uint256 blockNumber)
    internal
    view
    returns (uint256)
    {
        require(0 < self.entries.length);
        for (uint256 i = self.entries.length - 1; i >= 0; i--)
            if (blockNumber >= self.entries[i].blockNumber)
                return i;
        revert();
    }

    /// @dev The index returned here is 1-based
    function indexByTier(Discount[] discounts, int256 tier)
    internal
    pure
    returns (uint256)
    {
        require(0 < discounts.length);
        for (uint256 i = discounts.length; i > 0; i--)
            if (tier >= discounts[i - 1].tier)
                return i;
        return 0;
    }
}

/*
 * Hubii Nahmii
 *
 * Compliant with the Hubii Nahmii specification v0.12.
 *
 * Copyright (C) 2017-2018 Hubii AS
 */





library BlockNumbCurrenciesLib {
    //
    // Structures
    // -----------------------------------------------------------------------------------------------------------------
    struct Entry {
        uint256 blockNumber;
        MonetaryTypesLib.Currency currency;
    }

    struct BlockNumbCurrencies {
        mapping(address => mapping(uint256 => Entry[])) entriesByCurrency;
    }

    //
    // Functions
    // -----------------------------------------------------------------------------------------------------------------
    function currentCurrency(BlockNumbCurrencies storage self, MonetaryTypesLib.Currency referenceCurrency)
    internal
    view
    returns (MonetaryTypesLib.Currency storage)
    {
        return currencyAt(self, referenceCurrency, block.number);
    }

    function currentEntry(BlockNumbCurrencies storage self, MonetaryTypesLib.Currency referenceCurrency)
    internal
    view
    returns (Entry storage)
    {
        return entryAt(self, referenceCurrency, block.number);
    }

    function currencyAt(BlockNumbCurrencies storage self, MonetaryTypesLib.Currency referenceCurrency,
        uint256 _blockNumber)
    internal
    view
    returns (MonetaryTypesLib.Currency storage)
    {
        return entryAt(self, referenceCurrency, _blockNumber).currency;
    }

    function entryAt(BlockNumbCurrencies storage self, MonetaryTypesLib.Currency referenceCurrency,
        uint256 _blockNumber)
    internal
    view
    returns (Entry storage)
    {
        return self.entriesByCurrency[referenceCurrency.ct][referenceCurrency.id][indexByBlockNumber(self, referenceCurrency, _blockNumber)];
    }

    function addEntry(BlockNumbCurrencies storage self, uint256 blockNumber,
        MonetaryTypesLib.Currency referenceCurrency, MonetaryTypesLib.Currency currency)
    internal
    {
        require(
            0 == self.entriesByCurrency[referenceCurrency.ct][referenceCurrency.id].length ||
            blockNumber > self.entriesByCurrency[referenceCurrency.ct][referenceCurrency.id][self.entriesByCurrency[referenceCurrency.ct][referenceCurrency.id].length - 1].blockNumber
        );

        self.entriesByCurrency[referenceCurrency.ct][referenceCurrency.id].push(Entry(blockNumber, currency));
    }

    function count(BlockNumbCurrencies storage self, MonetaryTypesLib.Currency referenceCurrency)
    internal
    view
    returns (uint256)
    {
        return self.entriesByCurrency[referenceCurrency.ct][referenceCurrency.id].length;
    }

    function entriesByCurrency(BlockNumbCurrencies storage self, MonetaryTypesLib.Currency referenceCurrency)
    internal
    view
    returns (Entry[] storage)
    {
        return self.entriesByCurrency[referenceCurrency.ct][referenceCurrency.id];
    }

    function indexByBlockNumber(BlockNumbCurrencies storage self, MonetaryTypesLib.Currency referenceCurrency, uint256 blockNumber)
    internal
    view
    returns (uint256)
    {
        require(0 < self.entriesByCurrency[referenceCurrency.ct][referenceCurrency.id].length);
        for (uint256 i = self.entriesByCurrency[referenceCurrency.ct][referenceCurrency.id].length - 1; i >= 0; i--)
            if (blockNumber >= self.entriesByCurrency[referenceCurrency.ct][referenceCurrency.id][i].blockNumber)
                return i;
        revert();
    }
}

/*
 * Hubii Nahmii
 *
 * Compliant with the Hubii Nahmii specification v0.12.
 *
 * Copyright (C) 2017-2018 Hubii AS
 */















/**
 * @title Configuration
 * @notice An oracle for configurations values
 */
contract Configuration is Modifiable, Ownable, Servable {
    using SafeMathIntLib for int256;
    using BlockNumbUintsLib for BlockNumbUintsLib.BlockNumbUints;
    using BlockNumbIntsLib for BlockNumbIntsLib.BlockNumbInts;
    using BlockNumbDisdIntsLib for BlockNumbDisdIntsLib.BlockNumbDisdInts;
    using BlockNumbCurrenciesLib for BlockNumbCurrenciesLib.BlockNumbCurrencies;

    //
    // Constants
    // -----------------------------------------------------------------------------------------------------------------
    string constant public OPERATIONAL_MODE_ACTION = "operational_mode";

    //
    // Enums
    // -----------------------------------------------------------------------------------------------------------------
    enum OperationalMode {Normal, Exit}

    //
    // Variables
    // -----------------------------------------------------------------------------------------------------------------
    OperationalMode public operationalMode = OperationalMode.Normal;

    BlockNumbUintsLib.BlockNumbUints private updateDelayBlocksByBlockNumber;
    BlockNumbUintsLib.BlockNumbUints private confirmationBlocksByBlockNumber;

    BlockNumbDisdIntsLib.BlockNumbDisdInts private tradeMakerFeeByBlockNumber;
    BlockNumbDisdIntsLib.BlockNumbDisdInts private tradeTakerFeeByBlockNumber;
    BlockNumbDisdIntsLib.BlockNumbDisdInts private paymentFeeByBlockNumber;
    mapping(address => mapping(uint256 => BlockNumbDisdIntsLib.BlockNumbDisdInts)) private currencyPaymentFeeByBlockNumber;

    BlockNumbIntsLib.BlockNumbInts private tradeMakerMinimumFeeByBlockNumber;
    BlockNumbIntsLib.BlockNumbInts private tradeTakerMinimumFeeByBlockNumber;
    BlockNumbIntsLib.BlockNumbInts private paymentMinimumFeeByBlockNumber;
    mapping(address => mapping(uint256 => BlockNumbIntsLib.BlockNumbInts)) private currencyPaymentMinimumFeeByBlockNumber;

    BlockNumbCurrenciesLib.BlockNumbCurrencies private feeCurrencies;

    BlockNumbUintsLib.BlockNumbUints private walletLockTimeoutByBlockNumber;
    BlockNumbUintsLib.BlockNumbUints private cancelOrderChallengeTimeoutByBlockNumber;
    BlockNumbUintsLib.BlockNumbUints private settlementChallengeTimeoutByBlockNumber;

    BlockNumbUintsLib.BlockNumbUints private walletSettlementStakeFractionByBlockNumber;
    BlockNumbUintsLib.BlockNumbUints private operatorSettlementStakeFractionByBlockNumber;
    BlockNumbUintsLib.BlockNumbUints private fraudStakeFractionByBlockNumber;

    uint256 public earliestSettlementBlockNumber;
    bool public earliestSettlementBlockNumberUpdateDisabled;

    //
    // Events
    // -----------------------------------------------------------------------------------------------------------------
    event SetOperationalModeExitEvent();
    event SetUpdateDelayBlocksEvent(uint256 fromBlockNumber, uint256 newBlocks);
    event SetConfirmationBlocksEvent(uint256 fromBlockNumber, uint256 newBlocks);
    event SetTradeMakerFeeEvent(uint256 fromBlockNumber, int256 nominal, int256[] discountTiers, int256[] discountValues);
    event SetTradeTakerFeeEvent(uint256 fromBlockNumber, int256 nominal, int256[] discountTiers, int256[] discountValues);
    event SetPaymentFeeEvent(uint256 fromBlockNumber, int256 nominal, int256[] discountTiers, int256[] discountValues);
    event SetCurrencyPaymentFeeEvent(uint256 fromBlockNumber, address currencyCt, uint256 currencyId, int256 nominal,
        int256[] discountTiers, int256[] discountValues);
    event SetTradeMakerMinimumFeeEvent(uint256 fromBlockNumber, int256 nominal);
    event SetTradeTakerMinimumFeeEvent(uint256 fromBlockNumber, int256 nominal);
    event SetPaymentMinimumFeeEvent(uint256 fromBlockNumber, int256 nominal);
    event SetCurrencyPaymentMinimumFeeEvent(uint256 fromBlockNumber, address currencyCt, uint256 currencyId, int256 nominal);
    event SetFeeCurrencyEvent(uint256 fromBlockNumber, address referenceCurrencyCt, uint256 referenceCurrencyId,
        address feeCurrencyCt, uint256 feeCurrencyId);
    event SetWalletLockTimeoutEvent(uint256 fromBlockNumber, uint256 timeoutInSeconds);
    event SetCancelOrderChallengeTimeoutEvent(uint256 fromBlockNumber, uint256 timeoutInSeconds);
    event SetSettlementChallengeTimeoutEvent(uint256 fromBlockNumber, uint256 timeoutInSeconds);
    event SetWalletSettlementStakeFractionEvent(uint256 fromBlockNumber, uint256 stakeFraction);
    event SetOperatorSettlementStakeFractionEvent(uint256 fromBlockNumber, uint256 stakeFraction);
    event SetFraudStakeFractionEvent(uint256 fromBlockNumber, uint256 stakeFraction);
    event SetEarliestSettlementBlockNumberEvent(uint256 earliestSettlementBlockNumber);
    event DisableEarliestSettlementBlockNumberUpdateEvent();

    //
    // Constructor
    // -----------------------------------------------------------------------------------------------------------------
    constructor(address deployer) Ownable(deployer) public {
        updateDelayBlocksByBlockNumber.addEntry(block.number, 0);
    }

    //

    // Public functions
    // -----------------------------------------------------------------------------------------------------------------
    /// @notice Set operational mode to Exit
    /// @dev Once operational mode is set to Exit it may not be set back to Normal
    function setOperationalModeExit()
    public
    onlyEnabledServiceAction(OPERATIONAL_MODE_ACTION)
    {
        operationalMode = OperationalMode.Exit;
        emit SetOperationalModeExitEvent();
    }

    /// @notice Return true if operational mode is Normal
    function isOperationalModeNormal()
    public
    view
    returns (bool)
    {
        return OperationalMode.Normal == operationalMode;
    }

    /// @notice Return true if operational mode is Exit
    function isOperationalModeExit()
    public
    view
    returns (bool)
    {
        return OperationalMode.Exit == operationalMode;
    }

    /// @notice Get the current value of update delay blocks
    /// @return The value of update delay blocks
    function updateDelayBlocks()
    public
    view
    returns (uint256)
    {
        return updateDelayBlocksByBlockNumber.currentValue();
    }

    /// @notice Get the count of update delay blocks values
    /// @return The count of update delay blocks values
    function updateDelayBlocksCount()
    public
    view
    returns (uint256)
    {
        return updateDelayBlocksByBlockNumber.count();
    }

    /// @notice Set the number of update delay blocks
    /// @param fromBlockNumber Block number from which the update applies
    /// @param newUpdateDelayBlocks The new update delay blocks value
    function setUpdateDelayBlocks(uint256 fromBlockNumber, uint256 newUpdateDelayBlocks)
    public
    onlyOperator
    onlyDelayedBlockNumber(fromBlockNumber)
    {
        updateDelayBlocksByBlockNumber.addEntry(fromBlockNumber, newUpdateDelayBlocks);
        emit SetUpdateDelayBlocksEvent(fromBlockNumber, newUpdateDelayBlocks);
    }

    /// @notice Get the current value of confirmation blocks
    /// @return The value of confirmation blocks
    function confirmationBlocks()
    public
    view
    returns (uint256)
    {
        return confirmationBlocksByBlockNumber.currentValue();
    }

    /// @notice Get the count of confirmation blocks values
    /// @return The count of confirmation blocks values
    function confirmationBlocksCount()
    public
    view
    returns (uint256)
    {
        return confirmationBlocksByBlockNumber.count();
    }

    /// @notice Set the number of confirmation blocks
    /// @param fromBlockNumber Block number from which the update applies
    /// @param newConfirmationBlocks The new confirmation blocks value
    function setConfirmationBlocks(uint256 fromBlockNumber, uint256 newConfirmationBlocks)
    public
    onlyOperator
    onlyDelayedBlockNumber(fromBlockNumber)
    {
        confirmationBlocksByBlockNumber.addEntry(fromBlockNumber, newConfirmationBlocks);
        emit SetConfirmationBlocksEvent(fromBlockNumber, newConfirmationBlocks);
    }

    /// @notice Get number of trade maker fee block number tiers
    function tradeMakerFeesCount()
    public
    view
    returns (uint256)
    {
        return tradeMakerFeeByBlockNumber.count();
    }

    /// @notice Get trade maker relative fee at given block number, possibly discounted by discount tier value
    /// @param blockNumber The concerned block number
    /// @param discountTier The concerned discount tier
    function tradeMakerFee(uint256 blockNumber, int256 discountTier)
    public
    view
    returns (int256)
    {
        return tradeMakerFeeByBlockNumber.discountedValueAt(blockNumber, discountTier);
    }

    /// @notice Set trade maker nominal relative fee and discount tiers and values at given block number tier
    /// @param fromBlockNumber Block number from which the update applies
    /// @param nominal Nominal relative fee
    /// @param nominal Discount tier levels
    /// @param nominal Discount values
    function setTradeMakerFee(uint256 fromBlockNumber, int256 nominal, int256[] discountTiers, int256[] discountValues)
    public
    onlyOperator
    onlyDelayedBlockNumber(fromBlockNumber)
    {
        tradeMakerFeeByBlockNumber.addDiscountedEntry(fromBlockNumber, nominal, discountTiers, discountValues);
        emit SetTradeMakerFeeEvent(fromBlockNumber, nominal, discountTiers, discountValues);
    }

    /// @notice Get number of trade taker fee block number tiers
    function tradeTakerFeesCount()
    public
    view
    returns (uint256)
    {
        return tradeTakerFeeByBlockNumber.count();
    }

    /// @notice Get trade taker relative fee at given block number, possibly discounted by discount tier value
    /// @param blockNumber The concerned block number
    /// @param discountTier The concerned discount tier
    function tradeTakerFee(uint256 blockNumber, int256 discountTier)
    public
    view
    returns (int256)
    {
        return tradeTakerFeeByBlockNumber.discountedValueAt(blockNumber, discountTier);
    }

    /// @notice Set trade taker nominal relative fee and discount tiers and values at given block number tier
    /// @param fromBlockNumber Block number from which the update applies
    /// @param nominal Nominal relative fee
    /// @param nominal Discount tier levels
    /// @param nominal Discount values
    function setTradeTakerFee(uint256 fromBlockNumber, int256 nominal, int256[] discountTiers, int256[] discountValues)
    public
    onlyOperator
    onlyDelayedBlockNumber(fromBlockNumber)
    {
        tradeTakerFeeByBlockNumber.addDiscountedEntry(fromBlockNumber, nominal, discountTiers, discountValues);
        emit SetTradeTakerFeeEvent(fromBlockNumber, nominal, discountTiers, discountValues);
    }

    /// @notice Get number of payment fee block number tiers
    function paymentFeesCount()
    public
    view
    returns (uint256)
    {
        return paymentFeeByBlockNumber.count();
    }

    /// @notice Get payment relative fee at given block number, possibly discounted by discount tier value
    /// @param blockNumber The concerned block number
    /// @param discountTier The concerned discount tier
    function paymentFee(uint256 blockNumber, int256 discountTier)
    public
    view
    returns (int256)
    {
        return paymentFeeByBlockNumber.discountedValueAt(blockNumber, discountTier);
    }

    /// @notice Set payment nominal relative fee and discount tiers and values at given block number tier
    /// @param fromBlockNumber Block number from which the update applies
    /// @param nominal Nominal relative fee
    /// @param nominal Discount tier levels
    /// @param nominal Discount values
    function setPaymentFee(uint256 fromBlockNumber, int256 nominal, int256[] discountTiers, int256[] discountValues)
    public
    onlyOperator
    onlyDelayedBlockNumber(fromBlockNumber)
    {
        paymentFeeByBlockNumber.addDiscountedEntry(fromBlockNumber, nominal, discountTiers, discountValues);
        emit SetPaymentFeeEvent(fromBlockNumber, nominal, discountTiers, discountValues);
    }

    /// @notice Get number of payment fee block number tiers of given currency
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    function currencyPaymentFeesCount(address currencyCt, uint256 currencyId)
    public
    view
    returns (uint256)
    {
        return currencyPaymentFeeByBlockNumber[currencyCt][currencyId].count();
    }

    /// @notice Get payment relative fee for given currency at given block number, possibly discounted by
    /// discount tier value
    /// @param blockNumber The concerned block number
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    /// @param discountTier The concerned discount tier
    function currencyPaymentFee(uint256 blockNumber, address currencyCt, uint256 currencyId, int256 discountTier)
    public
    view
    returns (int256)
    {
        if (0 < currencyPaymentFeeByBlockNumber[currencyCt][currencyId].count())
            return currencyPaymentFeeByBlockNumber[currencyCt][currencyId].discountedValueAt(
                blockNumber, discountTier
            );
        else
            return paymentFee(blockNumber, discountTier);
    }

    /// @notice Set payment nominal relative fee and discount tiers and values for given currency at given
    /// block number tier
    /// @param fromBlockNumber Block number from which the update applies
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    /// @param nominal Nominal relative fee
    /// @param nominal Discount tier levels
    /// @param nominal Discount values
    function setCurrencyPaymentFee(uint256 fromBlockNumber, address currencyCt, uint256 currencyId, int256 nominal,
        int256[] discountTiers, int256[] discountValues)
    public
    onlyOperator
    onlyDelayedBlockNumber(fromBlockNumber)
    {
        currencyPaymentFeeByBlockNumber[currencyCt][currencyId].addDiscountedEntry(
            fromBlockNumber, nominal, discountTiers, discountValues
        );
        emit SetCurrencyPaymentFeeEvent(
            fromBlockNumber, currencyCt, currencyId, nominal, discountTiers, discountValues
        );
    }

    /// @notice Get number of minimum trade maker fee block number tiers
    function tradeMakerMinimumFeesCount()
    public
    view
    returns (uint256)
    {
        return tradeMakerMinimumFeeByBlockNumber.count();
    }

    /// @notice Get trade maker minimum relative fee at given block number
    /// @param blockNumber The concerned block number
    function tradeMakerMinimumFee(uint256 blockNumber)
    public
    view
    returns (int256)
    {
        return tradeMakerMinimumFeeByBlockNumber.valueAt(blockNumber);
    }

    /// @notice Set trade maker minimum relative fee at given block number tier
    /// @param fromBlockNumber Block number from which the update applies
    /// @param nominal Minimum relative fee
    function setTradeMakerMinimumFee(uint256 fromBlockNumber, int256 nominal)
    public
    onlyOperator
    onlyDelayedBlockNumber(fromBlockNumber)
    {
        tradeMakerMinimumFeeByBlockNumber.addEntry(fromBlockNumber, nominal);
        emit SetTradeMakerMinimumFeeEvent(fromBlockNumber, nominal);
    }

    /// @notice Get number of minimum trade taker fee block number tiers
    function tradeTakerMinimumFeesCount()
    public
    view
    returns (uint256)
    {
        return tradeTakerMinimumFeeByBlockNumber.count();
    }

    /// @notice Get trade taker minimum relative fee at given block number
    /// @param blockNumber The concerned block number
    function tradeTakerMinimumFee(uint256 blockNumber)
    public
    view
    returns (int256)
    {
        return tradeTakerMinimumFeeByBlockNumber.valueAt(blockNumber);
    }

    /// @notice Set trade taker minimum relative fee at given block number tier
    /// @param fromBlockNumber Block number from which the update applies
    /// @param nominal Minimum relative fee
    function setTradeTakerMinimumFee(uint256 fromBlockNumber, int256 nominal)
    public
    onlyOperator
    onlyDelayedBlockNumber(fromBlockNumber)
    {
        tradeTakerMinimumFeeByBlockNumber.addEntry(fromBlockNumber, nominal);
        emit SetTradeTakerMinimumFeeEvent(fromBlockNumber, nominal);
    }

    /// @notice Get number of minimum payment fee block number tiers
    function paymentMinimumFeesCount()
    public
    view
    returns (uint256)
    {
        return paymentMinimumFeeByBlockNumber.count();
    }

    /// @notice Get payment minimum relative fee at given block number
    /// @param blockNumber The concerned block number
    function paymentMinimumFee(uint256 blockNumber)
    public
    view
    returns (int256)
    {
        return paymentMinimumFeeByBlockNumber.valueAt(blockNumber);
    }

    /// @notice Set payment minimum relative fee at given block number tier
    /// @param fromBlockNumber Block number from which the update applies
    /// @param nominal Minimum relative fee
    function setPaymentMinimumFee(uint256 fromBlockNumber, int256 nominal)
    public
    onlyOperator
    onlyDelayedBlockNumber(fromBlockNumber)
    {
        paymentMinimumFeeByBlockNumber.addEntry(fromBlockNumber, nominal);
        emit SetPaymentMinimumFeeEvent(fromBlockNumber, nominal);
    }

    /// @notice Get number of minimum payment fee block number tiers for given currency
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    function currencyPaymentMinimumFeesCount(address currencyCt, uint256 currencyId)
    public
    view
    returns (uint256)
    {
        return currencyPaymentMinimumFeeByBlockNumber[currencyCt][currencyId].count();
    }

    /// @notice Get payment minimum relative fee for given currency at given block number
    /// @param blockNumber The concerned block number
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    function currencyPaymentMinimumFee(uint256 blockNumber, address currencyCt, uint256 currencyId)
    public
    view
    returns (int256)
    {
        if (0 < currencyPaymentMinimumFeeByBlockNumber[currencyCt][currencyId].count())
            return currencyPaymentMinimumFeeByBlockNumber[currencyCt][currencyId].valueAt(blockNumber);
        else
            return paymentMinimumFee(blockNumber);
    }

    /// @notice Set payment minimum relative fee for given currency at given block number tier
    /// @param fromBlockNumber Block number from which the update applies
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    /// @param nominal Minimum relative fee
    function setCurrencyPaymentMinimumFee(uint256 fromBlockNumber, address currencyCt, uint256 currencyId, int256 nominal)
    public
    onlyOperator
    onlyDelayedBlockNumber(fromBlockNumber)
    {
        currencyPaymentMinimumFeeByBlockNumber[currencyCt][currencyId].addEntry(fromBlockNumber, nominal);
        emit SetCurrencyPaymentMinimumFeeEvent(fromBlockNumber, currencyCt, currencyId, nominal);
    }

    /// @notice Get number of fee currencies for the given reference currency
    /// @param currencyCt The address of the concerned reference currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned reference currency (0 for ETH and ERC20)
    function feeCurrenciesCount(address currencyCt, uint256 currencyId)
    public
    view
    returns (uint256)
    {
        return feeCurrencies.count(MonetaryTypesLib.Currency(currencyCt, currencyId));
    }

    /// @notice Get the fee currency for the given reference currency at given block number
    /// @param blockNumber The concerned block number
    /// @param currencyCt The address of the concerned reference currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned reference currency (0 for ETH and ERC20)
    function feeCurrency(uint256 blockNumber, address currencyCt, uint256 currencyId)
    public
    view
    returns (address ct, uint256 id)
    {
        MonetaryTypesLib.Currency storage _feeCurrency = feeCurrencies.currencyAt(
            MonetaryTypesLib.Currency(currencyCt, currencyId), blockNumber
        );
        ct = _feeCurrency.ct;
        id = _feeCurrency.id;
    }

    /// @notice Set the fee currency for the given reference currency at given block number
    /// @param fromBlockNumber Block number from which the update applies
    /// @param referenceCurrencyCt The address of the concerned reference currency contract (address(0) == ETH)
    /// @param referenceCurrencyId The ID of the concerned reference currency (0 for ETH and ERC20)
    /// @param feeCurrencyCt The address of the concerned fee currency contract (address(0) == ETH)
    /// @param feeCurrencyId The ID of the concerned fee currency (0 for ETH and ERC20)
    function setFeeCurrency(uint256 fromBlockNumber, address referenceCurrencyCt, uint256 referenceCurrencyId,
        address feeCurrencyCt, uint256 feeCurrencyId)
    public
    onlyOperator
    onlyDelayedBlockNumber(fromBlockNumber)
    {
        feeCurrencies.addEntry(
            fromBlockNumber,
            MonetaryTypesLib.Currency(referenceCurrencyCt, referenceCurrencyId),
            MonetaryTypesLib.Currency(feeCurrencyCt, feeCurrencyId)
        );
        emit SetFeeCurrencyEvent(fromBlockNumber, referenceCurrencyCt, referenceCurrencyId,
            feeCurrencyCt, feeCurrencyId);
    }

    /// @notice Get the current value of wallet lock timeout
    /// @return The value of wallet lock timeout
    function walletLockTimeout()
    public
    view
    returns (uint256)
    {
        return walletLockTimeoutByBlockNumber.currentValue();
    }

    /// @notice Set timeout of wallet lock
    /// @param fromBlockNumber Block number from which the update applies
    /// @param timeoutInSeconds Timeout duration in seconds
    function setWalletLockTimeout(uint256 fromBlockNumber, uint256 timeoutInSeconds)
    public
    onlyOperator
    onlyDelayedBlockNumber(fromBlockNumber)
    {
        walletLockTimeoutByBlockNumber.addEntry(fromBlockNumber, timeoutInSeconds);
        emit SetWalletLockTimeoutEvent(fromBlockNumber, timeoutInSeconds);
    }

    /// @notice Get the current value of cancel order challenge timeout
    /// @return The value of cancel order challenge timeout
    function cancelOrderChallengeTimeout()
    public
    view
    returns (uint256)
    {
        return cancelOrderChallengeTimeoutByBlockNumber.currentValue();
    }

    /// @notice Set timeout of cancel order challenge
    /// @param fromBlockNumber Block number from which the update applies
    /// @param timeoutInSeconds Timeout duration in seconds
    function setCancelOrderChallengeTimeout(uint256 fromBlockNumber, uint256 timeoutInSeconds)
    public
    onlyOperator
    onlyDelayedBlockNumber(fromBlockNumber)
    {
        cancelOrderChallengeTimeoutByBlockNumber.addEntry(fromBlockNumber, timeoutInSeconds);
        emit SetCancelOrderChallengeTimeoutEvent(fromBlockNumber, timeoutInSeconds);
    }

    /// @notice Get the current value of settlement challenge timeout
    /// @return The value of settlement challenge timeout
    function settlementChallengeTimeout()
    public
    view
    returns (uint256)
    {
        return settlementChallengeTimeoutByBlockNumber.currentValue();
    }

    /// @notice Set timeout of settlement challenges
    /// @param fromBlockNumber Block number from which the update applies
    /// @param timeoutInSeconds Timeout duration in seconds
    function setSettlementChallengeTimeout(uint256 fromBlockNumber, uint256 timeoutInSeconds)
    public
    onlyOperator
    onlyDelayedBlockNumber(fromBlockNumber)
    {
        settlementChallengeTimeoutByBlockNumber.addEntry(fromBlockNumber, timeoutInSeconds);
        emit SetSettlementChallengeTimeoutEvent(fromBlockNumber, timeoutInSeconds);
    }

    /// @notice Get the current value of wallet settlement stake fraction
    /// @return The value of wallet settlement stake fraction
    function walletSettlementStakeFraction()
    public
    view
    returns (uint256)
    {
        return walletSettlementStakeFractionByBlockNumber.currentValue();
    }

    /// @notice Set fraction of security bond that will be gained from successfully challenging
    /// in settlement challenge triggered by wallet
    /// @param fromBlockNumber Block number from which the update applies
    /// @param stakeFraction The fraction gained
    function setWalletSettlementStakeFraction(uint256 fromBlockNumber, uint256 stakeFraction)
    public
    onlyOperator
    onlyDelayedBlockNumber(fromBlockNumber)
    {
        walletSettlementStakeFractionByBlockNumber.addEntry(fromBlockNumber, stakeFraction);
        emit SetWalletSettlementStakeFractionEvent(fromBlockNumber, stakeFraction);
    }

    /// @notice Get the current value of operator settlement stake fraction
    /// @return The value of operator settlement stake fraction
    function operatorSettlementStakeFraction()
    public
    view
    returns (uint256)
    {
        return operatorSettlementStakeFractionByBlockNumber.currentValue();
    }

    /// @notice Set fraction of security bond that will be gained from successfully challenging
    /// in settlement challenge triggered by operator
    /// @param fromBlockNumber Block number from which the update applies
    /// @param stakeFraction The fraction gained
    function setOperatorSettlementStakeFraction(uint256 fromBlockNumber, uint256 stakeFraction)
    public
    onlyOperator
    onlyDelayedBlockNumber(fromBlockNumber)
    {
        operatorSettlementStakeFractionByBlockNumber.addEntry(fromBlockNumber, stakeFraction);
        emit SetOperatorSettlementStakeFractionEvent(fromBlockNumber, stakeFraction);
    }

    /// @notice Get the current value of fraud stake fraction
    /// @return The value of fraud stake fraction
    function fraudStakeFraction()
    public
    view
    returns (uint256)
    {
        return fraudStakeFractionByBlockNumber.currentValue();
    }

    /// @notice Set fraction of security bond that will be gained from successfully challenging
    /// in fraud challenge
    /// @param fromBlockNumber Block number from which the update applies
    /// @param stakeFraction The fraction gained
    function setFraudStakeFraction(uint256 fromBlockNumber, uint256 stakeFraction)
    public
    onlyOperator
    onlyDelayedBlockNumber(fromBlockNumber)
    {
        fraudStakeFractionByBlockNumber.addEntry(fromBlockNumber, stakeFraction);
        emit SetFraudStakeFractionEvent(fromBlockNumber, stakeFraction);
    }

    /// @notice Set the block number of the earliest settlement initiation
    /// @param _earliestSettlementBlockNumber The block number of the earliest settlement
    function setEarliestSettlementBlockNumber(uint256 _earliestSettlementBlockNumber)
    public
    onlyOperator
    {
        earliestSettlementBlockNumber = _earliestSettlementBlockNumber;
        emit SetEarliestSettlementBlockNumberEvent(earliestSettlementBlockNumber);
    }

    /// @notice Disable further updates to the earliest settlement block number
    /// @dev This operation can not be undone
    function disableEarliestSettlementBlockNumberUpdate()
    public
    onlyOperator
    {
        earliestSettlementBlockNumberUpdateDisabled = true;
        emit DisableEarliestSettlementBlockNumberUpdateEvent();
    }

    //
    // Modifiers
    // -----------------------------------------------------------------------------------------------------------------
    modifier onlyDelayedBlockNumber(uint256 blockNumber) {
        require(
            0 == updateDelayBlocksByBlockNumber.count() ||
        blockNumber >= block.number + updateDelayBlocksByBlockNumber.currentValue()
        );
        _;
    }
}

/*
 * Hubii Nahmii
 *
 * Compliant with the Hubii Nahmii specification v0.12.
 *
 * Copyright (C) 2017-2018 Hubii AS
 */






/**
 * @title Benefactor
 * @notice An ownable that has a client fund property
 */
contract Configurable is Ownable {
    //
    // Variables
    // -----------------------------------------------------------------------------------------------------------------
    Configuration public configuration;

    //
    // Events
    // -----------------------------------------------------------------------------------------------------------------
    event SetConfigurationEvent(Configuration oldConfiguration, Configuration newConfiguration);

    //
    // Functions
    // -----------------------------------------------------------------------------------------------------------------
    /// @notice Set the configuration contract
    /// @param newConfiguration The (address of) Configuration contract instance
    function setConfiguration(Configuration newConfiguration)
    public
    onlyDeployer
    notNullAddress(newConfiguration)
    notSameAddresses(newConfiguration, configuration)
    {
        // Set new configuration
        Configuration oldConfiguration = configuration;
        configuration = newConfiguration;

        // Emit event
        emit SetConfigurationEvent(oldConfiguration, newConfiguration);
    }

    //
    // Modifiers
    // -----------------------------------------------------------------------------------------------------------------
    modifier configurationInitialized() {
        require(configuration != address(0));
        _;
    }
}

/*
 * Hubii Nahmii
 *
 * Compliant with the Hubii Nahmii specification v0.12.
 *
 * Copyright (C) 2017-2018 Hubii AS
 */








/**
 * @title Wallet locker
 * @notice An ownable to lock and unlock wallets' balance holdings of specific currency(ies)
 */
contract WalletLocker is Ownable, Configurable, AuthorizableServable {
    using SafeMathUintLib for uint256;

    //
    // Structures
    // -----------------------------------------------------------------------------------------------------------------
    struct FungibleLock {
        address locker;
        address currencyCt;
        uint256 currencyId;
        int256 amount;
        uint256 unlockTime;
    }

    struct NonFungibleLock {
        address locker;
        address currencyCt;
        uint256 currencyId;
        int256[] ids;
        uint256 unlockTime;
    }

    //
    // Variables
    // -----------------------------------------------------------------------------------------------------------------
    mapping(address => FungibleLock[]) public walletFungibleLocks;
    mapping(address => mapping(address => mapping(uint256 => mapping(address => uint256)))) public lockedCurrencyLockerFungibleLockIndex;
    mapping(address => mapping(address => mapping(uint256 => uint256))) public walletCurrencyFungibleLockCount;

    mapping(address => NonFungibleLock[]) public walletNonFungibleLocks;
    mapping(address => mapping(address => mapping(uint256 => mapping(address => uint256)))) public lockedCurrencyLockerNonFungibleLockIndex;
    mapping(address => mapping(address => mapping(uint256 => uint256))) public walletCurrencyNonFungibleLockCount;

    //
    // Events
    // -----------------------------------------------------------------------------------------------------------------
    event LockFungibleByProxyEvent(address lockedWallet, address lockerWallet, int256 amount,
        address currencyCt, uint256 currencyId);
    event LockNonFungibleByProxyEvent(address lockedWallet, address lockerWallet, int256[] ids,
        address currencyCt, uint256 currencyId);
    event UnlockFungibleEvent(address lockedWallet, address lockerWallet, int256 amount, address currencyCt,
        uint256 currencyId);
    event UnlockFungibleByProxyEvent(address lockedWallet, address lockerWallet, int256 amount, address currencyCt,
        uint256 currencyId);
    event UnlockNonFungibleEvent(address lockedWallet, address lockerWallet, int256[] ids, address currencyCt,
        uint256 currencyId);
    event UnlockNonFungibleByProxyEvent(address lockedWallet, address lockerWallet, int256[] ids, address currencyCt,
        uint256 currencyId);

    //
    // Constructor
    // -----------------------------------------------------------------------------------------------------------------
    constructor(address deployer) Ownable(deployer)
    public
    {
    }

    //
    // Functions
    // -----------------------------------------------------------------------------------------------------------------

    /// @notice Lock the given locked wallet's fungible amount of currency on behalf of the given locker wallet
    /// @param lockedWallet The address of wallet that will be locked
    /// @param lockerWallet The address of wallet that locks
    /// @param amount The amount to be locked
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    function lockFungibleByProxy(address lockedWallet, address lockerWallet, int256 amount,
        address currencyCt, uint256 currencyId)
    public
    onlyAuthorizedService(lockedWallet)
    {
        // Require that locked and locker wallets are not identical
        require(lockedWallet != lockerWallet);

        // Get index of lock
        uint256 lockIndex = lockedCurrencyLockerFungibleLockIndex[lockedWallet][currencyCt][currencyId][lockedWallet];

        // Require that there is no existing conflicting lock
        require(
            (0 == lockIndex) ||
            (block.timestamp >= walletFungibleLocks[lockedWallet][lockIndex - 1].unlockTime)
        );

        // Add lock object for this triplet of locked wallet, currency and locker wallet if it does not exist
        if (0 == lockIndex) {
            lockIndex = ++(walletFungibleLocks[lockedWallet].length);
            lockedCurrencyLockerFungibleLockIndex[lockedWallet][currencyCt][currencyId][lockerWallet] = lockIndex;
            walletCurrencyFungibleLockCount[lockedWallet][currencyCt][currencyId]++;
        }

        // Update lock parameters
        walletFungibleLocks[lockedWallet][lockIndex - 1].locker = lockerWallet;
        walletFungibleLocks[lockedWallet][lockIndex - 1].amount = amount;
        walletFungibleLocks[lockedWallet][lockIndex - 1].currencyCt = currencyCt;
        walletFungibleLocks[lockedWallet][lockIndex - 1].currencyId = currencyId;
        walletFungibleLocks[lockedWallet][lockIndex - 1].unlockTime =
        block.timestamp.add(configuration.walletLockTimeout());

        // Emit event
        emit LockFungibleByProxyEvent(lockedWallet, lockerWallet, amount, currencyCt, currencyId);
    }

    /// @notice Lock the given locked wallet's non-fungible IDs of currency on behalf of the given locker wallet
    /// @param lockedWallet The address of wallet that will be locked
    /// @param lockerWallet The address of wallet that locks
    /// @param ids The IDs to be locked
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    function lockNonFungibleByProxy(address lockedWallet, address lockerWallet, int256[] ids,
        address currencyCt, uint256 currencyId)
    public
    onlyAuthorizedService(lockedWallet)
    {
        // Require that locked and locker wallets are not identical
        require(lockedWallet != lockerWallet);

        // Get index of lock
        uint256 lockIndex = lockedCurrencyLockerNonFungibleLockIndex[lockedWallet][currencyCt][currencyId][lockedWallet];

        // Require that there is no existing conflicting lock
        require(
            (0 == lockIndex) ||
            (block.timestamp >= walletNonFungibleLocks[lockedWallet][lockIndex - 1].unlockTime)
        );

        // Add lock object for this triplet of locked wallet, currency and locker wallet if it does not exist
        if (0 == lockIndex) {
            lockIndex = ++(walletNonFungibleLocks[lockedWallet].length);
            lockedCurrencyLockerNonFungibleLockIndex[lockedWallet][currencyCt][currencyId][lockerWallet] = lockIndex;
            walletCurrencyNonFungibleLockCount[lockedWallet][currencyCt][currencyId]++;
        }

        // Update lock parameters
        walletNonFungibleLocks[lockedWallet][lockIndex - 1].locker = lockerWallet;
        walletNonFungibleLocks[lockedWallet][lockIndex - 1].ids = ids;
        walletNonFungibleLocks[lockedWallet][lockIndex - 1].currencyCt = currencyCt;
        walletNonFungibleLocks[lockedWallet][lockIndex - 1].currencyId = currencyId;
        walletNonFungibleLocks[lockedWallet][lockIndex - 1].unlockTime =
        block.timestamp.add(configuration.walletLockTimeout());

        // Emit event
        emit LockNonFungibleByProxyEvent(lockedWallet, lockerWallet, ids, currencyCt, currencyId);
    }

    /// @notice Unlock the given locked wallet's fungible amount of currency previously
    /// locked by the given locker wallet
    /// @param lockedWallet The address of the locked wallet
    /// @param lockerWallet The address of the locker wallet
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    function unlockFungible(address lockedWallet, address lockerWallet, address currencyCt, uint256 currencyId)
    public
    {
        // Get index of lock
        uint256 lockIndex = lockedCurrencyLockerFungibleLockIndex[lockedWallet][currencyCt][currencyId][lockerWallet];

        // Return if no lock exists
        if (0 == lockIndex)
            return;

        // Require that unlock timeout has expired
        require(
            block.timestamp >= walletFungibleLocks[lockedWallet][lockIndex - 1].unlockTime
        );

        // Unlock
        int256 amount = _unlockFungible(lockedWallet, lockerWallet, currencyCt, currencyId, lockIndex);

        // Emit event
        emit UnlockFungibleEvent(lockedWallet, lockerWallet, amount, currencyCt, currencyId);
    }

    /// @notice Unlock by proxy the given locked wallet's fungible amount of currency previously
    /// locked by the given locker wallet
    /// @param lockedWallet The address of the locked wallet
    /// @param lockerWallet The address of the locker wallet
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    function unlockFungibleByProxy(address lockedWallet, address lockerWallet, address currencyCt, uint256 currencyId)
    public
    onlyAuthorizedService(lockedWallet)
    {
        // Get index of lock
        uint256 lockIndex = lockedCurrencyLockerFungibleLockIndex[lockedWallet][currencyCt][currencyId][lockerWallet];

        // Return if no lock exists
        if (0 == lockIndex)
            return;

        // Unlock
        int256 amount = _unlockFungible(lockedWallet, lockerWallet, currencyCt, currencyId, lockIndex);

        // Emit event
        emit UnlockFungibleByProxyEvent(lockedWallet, lockerWallet, amount, currencyCt, currencyId);
    }

    /// @notice Unlock the given locked wallet's non-fungible IDs of currency previously
    /// locked by the given locker wallet
    /// @param lockedWallet The address of the locked wallet
    /// @param lockerWallet The address of the locker wallet
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    function unlockNonFungible(address lockedWallet, address lockerWallet, address currencyCt, uint256 currencyId)
    public
    {
        // Get index of lock
        uint256 lockIndex = lockedCurrencyLockerNonFungibleLockIndex[lockedWallet][currencyCt][currencyId][lockerWallet];

        // Return if no lock exists
        if (0 == lockIndex)
            return;

        // Require that unlock timeout has expired
        require(
            block.timestamp >= walletNonFungibleLocks[lockedWallet][lockIndex - 1].unlockTime
        );

        // Unlock
        int256[] memory ids = _unlockNonFungible(lockedWallet, lockerWallet, currencyCt, currencyId, lockIndex);

        // Emit event
        emit UnlockNonFungibleEvent(lockedWallet, lockerWallet, ids, currencyCt, currencyId);
    }

    /// @notice Unlock by proxy the given locked wallet's non-fungible IDs of currency previously
    /// locked by the given locker wallet
    /// @param lockedWallet The address of the locked wallet
    /// @param lockerWallet The address of the locker wallet
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    function unlockNonFungibleByProxy(address lockedWallet, address lockerWallet, address currencyCt, uint256 currencyId)
    public
    onlyAuthorizedService(lockedWallet)
    {
        // Get index of lock
        uint256 lockIndex = lockedCurrencyLockerNonFungibleLockIndex[lockedWallet][currencyCt][currencyId][lockerWallet];

        // Return if no lock exists
        if (0 == lockIndex)
            return;

        // Unlock
        int256[] memory ids = _unlockNonFungible(lockedWallet, lockerWallet, currencyCt, currencyId, lockIndex);

        // Emit event
        emit UnlockNonFungibleByProxyEvent(lockedWallet, lockerWallet, ids, currencyCt, currencyId);
    }

    /// @notice Get the fungible amount of the given currency held by locked wallet that is
    /// locked by locker wallet
    /// @param lockedWallet The address of the locked wallet
    /// @param lockerWallet The address of the locker wallet
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    function lockedAmount(address lockedWallet, address lockerWallet, address currencyCt, uint256 currencyId)
    public
    view
    returns (int256)
    {
        uint256 lockIndex = lockedCurrencyLockerFungibleLockIndex[lockedWallet][currencyCt][currencyId][lockerWallet];

        if (0 == lockIndex)
            return 0;

        return walletFungibleLocks[lockedWallet][lockIndex - 1].amount;
    }

    /// @notice Get the count of non-fungible IDs of the given currency held by locked wallet that is
    /// locked by locker wallet
    /// @param lockedWallet The address of the locked wallet
    /// @param lockerWallet The address of the locker wallet
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    function lockedIdsCount(address lockedWallet, address lockerWallet, address currencyCt, uint256 currencyId)
    public
    view
    returns (uint256)
    {
        uint256 lockIndex = lockedCurrencyLockerNonFungibleLockIndex[lockedWallet][currencyCt][currencyId][lockerWallet];

        if (0 == lockIndex)
            return 0;

        return walletNonFungibleLocks[lockedWallet][lockIndex - 1].ids.length;
    }

    /// @notice Get the set of non-fungible IDs of the given currency held by locked wallet that is
    /// locked by locker wallet and whose indices are in the given range of indices
    /// @param lockedWallet The address of the locked wallet
    /// @param lockerWallet The address of the locker wallet
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    /// @param low The lower ID index
    /// @param up The upper ID index
    function lockedIdsByIndices(address lockedWallet, address lockerWallet, address currencyCt, uint256 currencyId,
        uint256 low, uint256 up)
    public
    view
    returns (int256[])
    {
        uint256 lockIndex = lockedCurrencyLockerNonFungibleLockIndex[lockedWallet][currencyCt][currencyId][lockerWallet];

        if (0 == lockIndex)
            return new int256[](0);

        NonFungibleLock storage lock = walletNonFungibleLocks[lockedWallet][lockIndex - 1];

        if (0 == lock.ids.length)
            return new int256[](0);

        up = up.clampMax(lock.ids.length - 1);
        int256[] memory _ids = new int256[](up - low + 1);
        for (uint256 i = low; i <= up; i++)
            _ids[i - low] = lock.ids[i];

        return _ids;
    }

    /// @notice Gauge whether the given wallet is locked
    /// @param wallet The address of the concerned wallet
    /// @return true if wallet is locked, else false
    function isLocked(address wallet)
    public
    view
    returns (bool)
    {
        return 0 < walletFungibleLocks[wallet].length ||
        0 < walletNonFungibleLocks[wallet].length;
    }

    /// @notice Gauge whether the given wallet and currency is locked
    /// @param wallet The address of the concerned wallet
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    /// @return true if wallet/currency pair is locked, else false
    function isLocked(address wallet, address currencyCt, uint256 currencyId)
    public
    view
    returns (bool)
    {
        return 0 < walletCurrencyFungibleLockCount[wallet][currencyCt][currencyId] ||
        0 < walletCurrencyNonFungibleLockCount[wallet][currencyCt][currencyId];
    }

    /// @notice Gauge whether the given locked wallet and currency is locked by the given locker wallet
    /// @param lockedWallet The address of the concerned locked wallet
    /// @param lockerWallet The address of the concerned locker wallet
    /// @return true if lockedWallet is locked by lockerWallet, else false
    function isLocked(address lockedWallet, address lockerWallet, address currencyCt, uint256 currencyId)
    public
    view
    returns (bool)
    {
        return 0 < lockedCurrencyLockerFungibleLockIndex[lockedWallet][currencyCt][currencyId][lockerWallet] ||
        0 < lockedCurrencyLockerNonFungibleLockIndex[lockedWallet][currencyCt][currencyId][lockerWallet];
    }

    //
    //
    // Private functions
    // -----------------------------------------------------------------------------------------------------------------
    function _unlockFungible(address lockedWallet, address lockerWallet, address currencyCt, uint256 currencyId, uint256 lockIndex)
    private
    returns (int256)
    {
        int256 amount = walletFungibleLocks[lockedWallet][lockIndex - 1].amount;

        if (lockIndex < walletFungibleLocks[lockedWallet].length) {
            walletFungibleLocks[lockedWallet][lockIndex - 1] =
            walletFungibleLocks[lockedWallet][walletFungibleLocks[lockedWallet].length - 1];

            lockedCurrencyLockerFungibleLockIndex[lockedWallet][currencyCt][currencyId][walletFungibleLocks[lockedWallet][lockIndex - 1].locker] = lockIndex;
        }
        walletFungibleLocks[lockedWallet].length--;

        lockedCurrencyLockerFungibleLockIndex[lockedWallet][currencyCt][currencyId][lockerWallet] = 0;

        walletCurrencyFungibleLockCount[lockedWallet][currencyCt][currencyId]--;

        return amount;
    }

    function _unlockNonFungible(address lockedWallet, address lockerWallet, address currencyCt, uint256 currencyId, uint256 lockIndex)
    private
    returns (int256[])
    {
        int256[] memory ids = walletNonFungibleLocks[lockedWallet][lockIndex - 1].ids;

        if (lockIndex < walletNonFungibleLocks[lockedWallet].length) {
            walletNonFungibleLocks[lockedWallet][lockIndex - 1] =
            walletNonFungibleLocks[lockedWallet][walletNonFungibleLocks[lockedWallet].length - 1];

            lockedCurrencyLockerNonFungibleLockIndex[lockedWallet][currencyCt][currencyId][walletNonFungibleLocks[lockedWallet][lockIndex - 1].locker] = lockIndex;
        }
        walletNonFungibleLocks[lockedWallet].length--;

        lockedCurrencyLockerNonFungibleLockIndex[lockedWallet][currencyCt][currencyId][lockerWallet] = 0;

        walletCurrencyNonFungibleLockCount[lockedWallet][currencyCt][currencyId]--;

        return ids;
    }
}

/*
 * Hubii Nahmii
 *
 * Compliant with the Hubii Nahmii specification v0.12.
 *
 * Copyright (C) 2017-2018 Hubii AS
 */






/**
 * @title WalletLockable
 * @notice An ownable that has a wallet locker property
 */
contract WalletLockable is Ownable {
    //
    // Variables
    // -----------------------------------------------------------------------------------------------------------------
    WalletLocker public walletLocker;
    bool public walletLockerFrozen;

    //
    // Events
    // -----------------------------------------------------------------------------------------------------------------
    event SetWalletLockerEvent(WalletLocker oldWalletLocker, WalletLocker newWalletLocker);
    event FreezeWalletLockerEvent();

    //
    // Functions
    // -----------------------------------------------------------------------------------------------------------------
    /// @notice Set the wallet locker contract
    /// @param newWalletLocker The (address of) WalletLocker contract instance
    function setWalletLocker(WalletLocker newWalletLocker)
    public
    onlyDeployer
    notNullAddress(newWalletLocker)
    notSameAddresses(newWalletLocker, walletLocker)
    {
        // Require that this contract has not been frozen
        require(!walletLockerFrozen);

        // Update fields
        WalletLocker oldWalletLocker = walletLocker;
        walletLocker = newWalletLocker;

        // Emit event
        emit SetWalletLockerEvent(oldWalletLocker, newWalletLocker);
    }

    /// @notice Freeze the balance tracker from further updates
    /// @dev This operation can not be undone
    function freezeWalletLocker()
    public
    onlyDeployer
    {
        walletLockerFrozen = true;

        // Emit event
        emit FreezeWalletLockerEvent();
    }

    //
    // Modifiers
    // -----------------------------------------------------------------------------------------------------------------
    modifier walletLockerInitialized() {
        require(walletLocker != address(0));
        _;
    }
}

/*
 * Hubii Nahmii
 *
 * Compliant with the Hubii Nahmii specification v0.12.
 *
 * Copyright (C) 2017-2018 Hubii AS
 */







/**
 * @title AccrualBeneficiary
 * @notice A beneficiary of accruals
 */
contract AccrualBeneficiary is Beneficiary {
    //
    // Functions
    // -----------------------------------------------------------------------------------------------------------------
    event CloseAccrualPeriodEvent();

    //
    // Functions
    // -----------------------------------------------------------------------------------------------------------------
    function closeAccrualPeriod(MonetaryTypesLib.Currency[])
    public
    {
        emit CloseAccrualPeriodEvent();
    }
}

/*
 * Hubii Nahmii
 *
 * Compliant with the Hubii Nahmii specification v0.12.
 *
 * Copyright (C) 2017-2018 Hubii AS
 */



library TxHistoryLib {
    //
    // Structures
    // -----------------------------------------------------------------------------------------------------------------
    struct AssetEntry {
        int256 amount;
        uint256 blockNumber;
        address currencyCt;      //0 for ethers
        uint256 currencyId;
    }

    struct TxHistory {
        AssetEntry[] deposits;
        mapping(address => mapping(uint256 => AssetEntry[])) currencyDeposits;

        AssetEntry[] withdrawals;
        mapping(address => mapping(uint256 => AssetEntry[])) currencyWithdrawals;
    }

    //
    // Functions
    // -----------------------------------------------------------------------------------------------------------------
    function addDeposit(TxHistory storage self, int256 amount, address currencyCt, uint256 currencyId)
    internal
    {
        AssetEntry memory deposit = AssetEntry(amount, block.number, currencyCt, currencyId);
        self.deposits.push(deposit);
        self.currencyDeposits[currencyCt][currencyId].push(deposit);
    }

    function addWithdrawal(TxHistory storage self, int256 amount, address currencyCt, uint256 currencyId)
    internal
    {
        AssetEntry memory withdrawal = AssetEntry(amount, block.number, currencyCt, currencyId);
        self.withdrawals.push(withdrawal);
        self.currencyWithdrawals[currencyCt][currencyId].push(withdrawal);
    }

    //----

    function deposit(TxHistory storage self, uint index)
    internal
    view
    returns (int256 amount, uint256 blockNumber, address currencyCt, uint256 currencyId)
    {
        require(index < self.deposits.length);

        amount = self.deposits[index].amount;
        blockNumber = self.deposits[index].blockNumber;
        currencyCt = self.deposits[index].currencyCt;
        currencyId = self.deposits[index].currencyId;
    }

    function depositsCount(TxHistory storage self)
    internal
    view
    returns (uint256)
    {
        return self.deposits.length;
    }

    function currencyDeposit(TxHistory storage self, address currencyCt, uint256 currencyId, uint index)
    internal
    view
    returns (int256 amount, uint256 blockNumber)
    {
        require(index < self.currencyDeposits[currencyCt][currencyId].length);

        amount = self.currencyDeposits[currencyCt][currencyId][index].amount;
        blockNumber = self.currencyDeposits[currencyCt][currencyId][index].blockNumber;
    }

    function currencyDepositsCount(TxHistory storage self, address currencyCt, uint256 currencyId)
    internal
    view
    returns (uint256)
    {
        return self.currencyDeposits[currencyCt][currencyId].length;
    }

    //----

    function withdrawal(TxHistory storage self, uint index)
    internal
    view
    returns (int256 amount, uint256 blockNumber, address currencyCt, uint256 currencyId)
    {
        require(index < self.withdrawals.length);

        amount = self.withdrawals[index].amount;
        blockNumber = self.withdrawals[index].blockNumber;
        currencyCt = self.withdrawals[index].currencyCt;
        currencyId = self.withdrawals[index].currencyId;
    }

    function withdrawalsCount(TxHistory storage self)
    internal
    view
    returns (uint256)
    {
        return self.withdrawals.length;
    }

    function currencyWithdrawal(TxHistory storage self, address currencyCt, uint256 currencyId, uint index)
    internal
    view
    returns (int256 amount, uint256 blockNumber)
    {
        require(index < self.currencyWithdrawals[currencyCt][currencyId].length);

        amount = self.currencyWithdrawals[currencyCt][currencyId][index].amount;
        blockNumber = self.currencyWithdrawals[currencyCt][currencyId][index].blockNumber;
    }

    function currencyWithdrawalsCount(TxHistory storage self, address currencyCt, uint256 currencyId)
    internal
    view
    returns (uint256)
    {
        return self.currencyWithdrawals[currencyCt][currencyId].length;
    }
}

/**
 * @title ERC20 interface
 * @dev see https://github.com/ethereum/EIPs/issues/20
 */
interface IERC20 {
  function totalSupply() external view returns (uint256);

  function balanceOf(address who) external view returns (uint256);

  function allowance(address owner, address spender)
    external view returns (uint256);

  function transfer(address to, uint256 value) external returns (bool);

  function approve(address spender, uint256 value)
    external returns (bool);

  function transferFrom(address from, address to, uint256 value)
    external returns (bool);

  event Transfer(
    address indexed from,
    address indexed to,
    uint256 value
  );

  event Approval(
    address indexed owner,
    address indexed spender,
    uint256 value
  );
}

/**
 * @title SafeMath
 * @dev Math operations with safety checks that revert on error
 */
library SafeMath {

  /**
  * @dev Multiplies two numbers, reverts on overflow.
  */
  function mul(uint256 a, uint256 b) internal pure returns (uint256) {
    // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
    // benefit is lost if 'b' is also tested.
    // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
    if (a == 0) {
      return 0;
    }

    uint256 c = a * b;
    require(c / a == b);

    return c;
  }

  /**
  * @dev Integer division of two numbers truncating the quotient, reverts on division by zero.
  */
  function div(uint256 a, uint256 b) internal pure returns (uint256) {
    require(b > 0); // Solidity only automatically asserts when dividing by 0
    uint256 c = a / b;
    // assert(a == b * c + a % b); // There is no case in which this doesn't hold

    return c;
  }

  /**
  * @dev Subtracts two numbers, reverts on overflow (i.e. if subtrahend is greater than minuend).
  */
  function sub(uint256 a, uint256 b) internal pure returns (uint256) {
    require(b <= a);
    uint256 c = a - b;

    return c;
  }

  /**
  * @dev Adds two numbers, reverts on overflow.
  */
  function add(uint256 a, uint256 b) internal pure returns (uint256) {
    uint256 c = a + b;
    require(c >= a);

    return c;
  }

  /**
  * @dev Divides two numbers and returns the remainder (unsigned integer modulo),
  * reverts when dividing by zero.
  */
  function mod(uint256 a, uint256 b) internal pure returns (uint256) {
    require(b != 0);
    return a % b;
  }
}

/**
 * @title Standard ERC20 token
 *
 * @dev Implementation of the basic standard token.
 * https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md
 * Originally based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
 */
contract ERC20 is IERC20 {
  using SafeMath for uint256;

  mapping (address => uint256) private _balances;

  mapping (address => mapping (address => uint256)) private _allowed;

  uint256 private _totalSupply;

  /**
  * @dev Total number of tokens in existence
  */
  function totalSupply() public view returns (uint256) {
    return _totalSupply;
  }

  /**
  * @dev Gets the balance of the specified address.
  * @param owner The address to query the balance of.
  * @return An uint256 representing the amount owned by the passed address.
  */
  function balanceOf(address owner) public view returns (uint256) {
    return _balances[owner];
  }

  /**
   * @dev Function to check the amount of tokens that an owner allowed to a spender.
   * @param owner address The address which owns the funds.
   * @param spender address The address which will spend the funds.
   * @return A uint256 specifying the amount of tokens still available for the spender.
   */
  function allowance(
    address owner,
    address spender
   )
    public
    view
    returns (uint256)
  {
    return _allowed[owner][spender];
  }

  /**
  * @dev Transfer token for a specified address
  * @param to The address to transfer to.
  * @param value The amount to be transferred.
  */
  function transfer(address to, uint256 value) public returns (bool) {
    _transfer(msg.sender, to, value);
    return true;
  }

  /**
   * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
   * Beware that changing an allowance with this method brings the risk that someone may use both the old
   * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
   * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
   * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
   * @param spender The address which will spend the funds.
   * @param value The amount of tokens to be spent.
   */
  function approve(address spender, uint256 value) public returns (bool) {
    require(spender != address(0));

    _allowed[msg.sender][spender] = value;
    emit Approval(msg.sender, spender, value);
    return true;
  }

  /**
   * @dev Transfer tokens from one address to another
   * @param from address The address which you want to send tokens from
   * @param to address The address which you want to transfer to
   * @param value uint256 the amount of tokens to be transferred
   */
  function transferFrom(
    address from,
    address to,
    uint256 value
  )
    public
    returns (bool)
  {
    require(value <= _allowed[from][msg.sender]);

    _allowed[from][msg.sender] = _allowed[from][msg.sender].sub(value);
    _transfer(from, to, value);
    return true;
  }

  /**
   * @dev Increase the amount of tokens that an owner allowed to a spender.
   * approve should be called when allowed_[_spender] == 0. To increment
   * allowed value is better to use this function to avoid 2 calls (and wait until
   * the first transaction is mined)
   * From MonolithDAO Token.sol
   * @param spender The address which will spend the funds.
   * @param addedValue The amount of tokens to increase the allowance by.
   */
  function increaseAllowance(
    address spender,
    uint256 addedValue
  )
    public
    returns (bool)
  {
    require(spender != address(0));

    _allowed[msg.sender][spender] = (
      _allowed[msg.sender][spender].add(addedValue));
    emit Approval(msg.sender, spender, _allowed[msg.sender][spender]);
    return true;
  }

  /**
   * @dev Decrease the amount of tokens that an owner allowed to a spender.
   * approve should be called when allowed_[_spender] == 0. To decrement
   * allowed value is better to use this function to avoid 2 calls (and wait until
   * the first transaction is mined)
   * From MonolithDAO Token.sol
   * @param spender The address which will spend the funds.
   * @param subtractedValue The amount of tokens to decrease the allowance by.
   */
  function decreaseAllowance(
    address spender,
    uint256 subtractedValue
  )
    public
    returns (bool)
  {
    require(spender != address(0));

    _allowed[msg.sender][spender] = (
      _allowed[msg.sender][spender].sub(subtractedValue));
    emit Approval(msg.sender, spender, _allowed[msg.sender][spender]);
    return true;
  }

  /**
  * @dev Transfer token for a specified addresses
  * @param from The address to transfer from.
  * @param to The address to transfer to.
  * @param value The amount to be transferred.
  */
  function _transfer(address from, address to, uint256 value) internal {
    require(value <= _balances[from]);
    require(to != address(0));

    _balances[from] = _balances[from].sub(value);
    _balances[to] = _balances[to].add(value);
    emit Transfer(from, to, value);
  }

  /**
   * @dev Internal function that mints an amount of the token and assigns it to
   * an account. This encapsulates the modification of balances such that the
   * proper events are emitted.
   * @param account The account that will receive the created tokens.
   * @param value The amount that will be created.
   */
  function _mint(address account, uint256 value) internal {
    require(account != 0);
    _totalSupply = _totalSupply.add(value);
    _balances[account] = _balances[account].add(value);
    emit Transfer(address(0), account, value);
  }

  /**
   * @dev Internal function that burns an amount of the token of a given
   * account.
   * @param account The account whose tokens will be burnt.
   * @param value The amount that will be burnt.
   */
  function _burn(address account, uint256 value) internal {
    require(account != 0);
    require(value <= _balances[account]);

    _totalSupply = _totalSupply.sub(value);
    _balances[account] = _balances[account].sub(value);
    emit Transfer(account, address(0), value);
  }

  /**
   * @dev Internal function that burns an amount of the token of a given
   * account, deducting from the sender's allowance for said account. Uses the
   * internal burn function.
   * @param account The account whose tokens will be burnt.
   * @param value The amount that will be burnt.
   */
  function _burnFrom(address account, uint256 value) internal {
    require(value <= _allowed[account][msg.sender]);

    // Should https://github.com/OpenZeppelin/zeppelin-solidity/issues/707 be accepted,
    // this function needs to emit an event with the updated approval.
    _allowed[account][msg.sender] = _allowed[account][msg.sender].sub(
      value);
    _burn(account, value);
  }
}

/**
 * @title Roles
 * @dev Library for managing addresses assigned to a Role.
 */
library Roles {
  struct Role {
    mapping (address => bool) bearer;
  }

  /**
   * @dev give an account access to this role
   */
  function add(Role storage role, address account) internal {
    require(account != address(0));
    require(!has(role, account));

    role.bearer[account] = true;
  }

  /**
   * @dev remove an account's access to this role
   */
  function remove(Role storage role, address account) internal {
    require(account != address(0));
    require(has(role, account));

    role.bearer[account] = false;
  }

  /**
   * @dev check if an account has this role
   * @return bool
   */
  function has(Role storage role, address account)
    internal
    view
    returns (bool)
  {
    require(account != address(0));
    return role.bearer[account];
  }
}

contract MinterRole {
  using Roles for Roles.Role;

  event MinterAdded(address indexed account);
  event MinterRemoved(address indexed account);

  Roles.Role private minters;

  constructor() internal {
    _addMinter(msg.sender);
  }

  modifier onlyMinter() {
    require(isMinter(msg.sender));
    _;
  }

  function isMinter(address account) public view returns (bool) {
    return minters.has(account);
  }

  function addMinter(address account) public onlyMinter {
    _addMinter(account);
  }

  function renounceMinter() public {
    _removeMinter(msg.sender);
  }

  function _addMinter(address account) internal {
    minters.add(account);
    emit MinterAdded(account);
  }

  function _removeMinter(address account) internal {
    minters.remove(account);
    emit MinterRemoved(account);
  }
}

/**
 * @title ERC20Mintable
 * @dev ERC20 minting logic
 */
contract ERC20Mintable is ERC20, MinterRole {
  /**
   * @dev Function to mint tokens
   * @param to The address that will receive the minted tokens.
   * @param value The amount of tokens to mint.
   * @return A boolean that indicates if the operation was successful.
   */
  function mint(
    address to,
    uint256 value
  )
    public
    onlyMinter
    returns (bool)
  {
    _mint(to, value);
    return true;
  }
}

/*
 * Hubii Nahmii
 *
 * Compliant with the Hubii Nahmii specification v0.12.
 *
 * Copyright (C) 2017-2018 Hubii AS
 */






/**
 * @title RevenueToken
 * @dev Implementation of the EIP20 standard token (also known as ERC20 token) with added
 * calculation of balance blocks at every transfer.
 */
contract RevenueToken is ERC20Mintable {
    using SafeMath for uint256;

    bool public mintingDisabled;

    address[] public holders;

    mapping(address => bool) public holdersMap;

    mapping(address => uint256[]) public balances;

    mapping(address => uint256[]) public balanceBlocks;

    mapping(address => uint256[]) public balanceBlockNumbers;

    event DisableMinting();

    /**
     * @notice Disable further minting
     * @dev This operation can not be undone
     */
    function disableMinting()
    public
    onlyMinter
    {
        mintingDisabled = true;

        emit DisableMinting();
    }

    /**
     * @notice Mint tokens
     * @param to The address that will receive the minted tokens.
     * @param value The amount of tokens to mint.
     * @return A boolean that indicates if the operation was successful.
     */
    function mint(address to, uint256 value)
    public
    onlyMinter
    returns (bool)
    {
        require(!mintingDisabled);

        // Call super's mint, including event emission
        bool minted = super.mint(to, value);

        if (minted) {
            // Adjust balance blocks
            addBalanceBlocks(to);

            // Add to the token holders list
            if (!holdersMap[to]) {
                holdersMap[to] = true;
                holders.push(to);
            }
        }

        return minted;
    }

    /**
     * @notice Transfer token for a specified address
     * @param to The address to transfer to.
     * @param value The amount to be transferred.
     * @return A boolean that indicates if the operation was successful.
     */
    function transfer(address to, uint256 value)
    public
    returns (bool)
    {
        // Call super's transfer, including event emission
        bool transferred = super.transfer(to, value);

        if (transferred) {
            // Adjust balance blocks
            addBalanceBlocks(msg.sender);
            addBalanceBlocks(to);

            // Add to the token holders list
            if (!holdersMap[to]) {
                holdersMap[to] = true;
                holders.push(to);
            }
        }

        return transferred;
    }

    /**
     * @notice Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
     * @dev Beware that to change the approve amount you first have to reduce the addresses'
     * allowance to zero by calling `approve(spender, 0)` if it is not already 0 to mitigate the race
     * condition described here:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     * @param spender The address which will spend the funds.
     * @param value The amount of tokens to be spent.
     */
    function approve(address spender, uint256 value)
    public
    returns (bool)
    {
        // Prevent the update of non-zero allowance
        require(0 == value || 0 == allowance(msg.sender, spender));

        // Call super's approve, including event emission
        return super.approve(spender, value);
    }

    /**
     * @dev Transfer tokens from one address to another
     * @param from address The address which you want to send tokens from
     * @param to address The address which you want to transfer to
     * @param value uint256 the amount of tokens to be transferred
     * @return A boolean that indicates if the operation was successful.
     */
    function transferFrom(address from, address to, uint256 value)
    public
    returns (bool)
    {
        // Call super's transferFrom, including event emission
        bool transferred = super.transferFrom(from, to, value);

        if (transferred) {
            // Adjust balance blocks
            addBalanceBlocks(from);
            addBalanceBlocks(to);

            // Add to the token holders list
            if (!holdersMap[to]) {
                holdersMap[to] = true;
                holders.push(to);
            }
        }

        return transferred;
    }

    /**
     * @notice Calculate the amount of balance blocks, i.e. the area under the curve (AUC) of
     * balance as function of block number
     * @dev The AUC is used as weight for the share of revenue that a token holder may claim
     * @param account The account address for which calculation is done
     * @param startBlock The start block number considered
     * @param endBlock The end block number considered
     * @return The calculated AUC
     */
    function balanceBlocksIn(address account, uint256 startBlock, uint256 endBlock)
    public
    view
    returns (uint256)
    {
        require(startBlock < endBlock);
        require(account != address(0));

        if (balanceBlockNumbers[account].length == 0 || endBlock < balanceBlockNumbers[account][0])
            return 0;

        uint256 i = 0;
        while (i < balanceBlockNumbers[account].length && balanceBlockNumbers[account][i] < startBlock)
            i++;

        uint256 r;
        if (i >= balanceBlockNumbers[account].length)
            r = balances[account][balanceBlockNumbers[account].length - 1].mul(endBlock.sub(startBlock));

        else {
            uint256 l = (i == 0) ? startBlock : balanceBlockNumbers[account][i - 1];

            uint256 h = balanceBlockNumbers[account][i];
            if (h > endBlock)
                h = endBlock;

            h = h.sub(startBlock);
            r = (h == 0) ? 0 : balanceBlocks[account][i].mul(h).div(balanceBlockNumbers[account][i].sub(l));
            i++;

            while (i < balanceBlockNumbers[account].length && balanceBlockNumbers[account][i] < endBlock) {
                r = r.add(balanceBlocks[account][i]);
                i++;
            }

            if (i >= balanceBlockNumbers[account].length)
                r = r.add(
                    balances[account][balanceBlockNumbers[account].length - 1].mul(
                        endBlock.sub(balanceBlockNumbers[account][balanceBlockNumbers[account].length - 1])
                    )
                );

            else if (balanceBlockNumbers[account][i - 1] < endBlock)
                r = r.add(
                    balanceBlocks[account][i].mul(
                        endBlock.sub(balanceBlockNumbers[account][i - 1])
                    ).div(
                        balanceBlockNumbers[account][i].sub(balanceBlockNumbers[account][i - 1])
                    )
                );
        }

        return r;
    }

    /**
     * @notice Get the count of balance updates for the given account
     * @return The count of balance updates
     */
    function balanceUpdatesCount(address account)
    public
    view
    returns (uint256)
    {
        return balanceBlocks[account].length;
    }

    /**
     * @notice Get the count of holders
     * @return The count of holders
     */
    function holdersCount()
    public
    view
    returns (uint256)
    {
        return holders.length;
    }

    /**
     * @notice Get the subset of holders (optionally with positive balance only) in the given 0 based index range
     * @param low The lower inclusive index
     * @param up The upper inclusive index
     * @param posOnly List only positive balance holders
     * @return The subset of positive balance registered holders in the given range
     */
    function holdersByIndices(uint256 low, uint256 up, bool posOnly)
    public
    view
    returns (address[])
    {
        require(low <= up);

        up = up > holders.length - 1 ? holders.length - 1 : up;

        uint256 length = 0;
        if (posOnly) {
            for (uint256 i = low; i <= up; i++)
                if (0 < balanceOf(holders[i]))
                    length++;
        } else
            length = up - low + 1;

        address[] memory _holders = new address[](length);

        uint256 j = 0;
        for (i = low; i <= up; i++)
            if (!posOnly || 0 < balanceOf(holders[i]))
                _holders[j++] = holders[i];

        return _holders;
    }

    function addBalanceBlocks(address account)
    private
    {
        uint256 length = balanceBlockNumbers[account].length;
        balances[account].push(balanceOf(account));
        if (0 < length)
            balanceBlocks[account].push(
                balances[account][length - 1].mul(
                    block.number.sub(balanceBlockNumbers[account][length - 1])
                )
            );
        else
            balanceBlocks[account].push(0);
        balanceBlockNumbers[account].push(block.number);
    }
}

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure.
 * To use this library you can add a `using SafeERC20 for ERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {

  using SafeMath for uint256;

  function safeTransfer(
    IERC20 token,
    address to,
    uint256 value
  )
    internal
  {
    require(token.transfer(to, value));
  }

  function safeTransferFrom(
    IERC20 token,
    address from,
    address to,
    uint256 value
  )
    internal
  {
    require(token.transferFrom(from, to, value));
  }

  function safeApprove(
    IERC20 token,
    address spender,
    uint256 value
  )
    internal
  {
    // safeApprove should only be called when setting an initial allowance, 
    // or when resetting it to zero. To increase and decrease it, use 
    // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
    require((value == 0) || (token.allowance(msg.sender, spender) == 0));
    require(token.approve(spender, value));
  }

  function safeIncreaseAllowance(
    IERC20 token,
    address spender,
    uint256 value
  )
    internal
  {
    uint256 newAllowance = token.allowance(address(this), spender).add(value);
    require(token.approve(spender, newAllowance));
  }

  function safeDecreaseAllowance(
    IERC20 token,
    address spender,
    uint256 value
  )
    internal
  {
    uint256 newAllowance = token.allowance(address(this), spender).sub(value);
    require(token.approve(spender, newAllowance));
  }
}

/*
 * Hubii Nahmii
 *
 * Compliant with the Hubii Nahmii specification v0.12.
 *
 * Copyright (C) 2017-2018 Hubii AS
 */







/**
 * @title Balance tracker
 * @notice An ownable that allows a beneficiary to extract tokens in
 *   a number of batches each a given release time
 */
contract TokenMultiTimelock is Ownable {
    using SafeERC20 for IERC20;

    //
    // Structures
    // -----------------------------------------------------------------------------------------------------------------
    struct Release {
        uint256 earliestReleaseTime;
        uint256 amount;
        uint256 blockNumber;
        bool done;
    }

    //
    // Variables
    // -----------------------------------------------------------------------------------------------------------------
    IERC20 public token;
    address public beneficiary;

    Release[] public releases;
    uint256 public totalLockedAmount;
    uint256 public executedReleasesCount;

    //
    // Events
    // -----------------------------------------------------------------------------------------------------------------
    event SetTokenEvent(IERC20 token);
    event SetBeneficiaryEvent(address beneficiary);
    event DefineReleaseEvent(uint256 earliestReleaseTime, uint256 amount, uint256 blockNumber);
    event SetReleaseBlockNumberEvent(uint256 index, uint256 blockNumber);
    event ReleaseEvent(uint256 index, uint256 blockNumber, uint256 earliestReleaseTime,
        uint256 actualReleaseTime, uint256 amount);

    //
    // Constructor
    // -----------------------------------------------------------------------------------------------------------------
    constructor(address deployer)
    Ownable(deployer)
    public
    {
    }

    //
    // Functions
    // -----------------------------------------------------------------------------------------------------------------
    /// @notice Set the address of token
    /// @param _token The address of token
    function setToken(IERC20 _token)
    public
    onlyOperator
    notNullOrThisAddress(_token)
    {
        // Require that the token has not previously been set
        require(address(token) == address(0));

        // Update beneficiary
        token = _token;

        // Emit event
        emit SetTokenEvent(token);
    }

    /// @notice Set the address of beneficiary
    /// @param _beneficiary The new address of beneficiary
    function setBeneficiary(address _beneficiary)
    public
    onlyOperator
    notNullAddress(_beneficiary)
    {
        // Update beneficiary
        beneficiary = _beneficiary;

        // Emit event
        emit SetBeneficiaryEvent(beneficiary);
    }

    /// @notice Define a set of new releases
    /// @param earliestReleaseTimes The timestamp after which the corresponding amount may be released
    /// @param amounts The amounts to be released
    /// @param releaseBlockNumbers The set release block numbers for releases whose earliest release time
    /// is in the past
    function defineReleases(uint256[] earliestReleaseTimes, uint256[] amounts, uint256[] releaseBlockNumbers)
    onlyOperator
    public
    {
        require(earliestReleaseTimes.length == amounts.length);
        require(earliestReleaseTimes.length >= releaseBlockNumbers.length);

        // Require that token address has been set
        require(address(token) != address(0));

        for (uint256 i = 0; i < earliestReleaseTimes.length; i++) {
            // Update the total amount locked by this contract
            totalLockedAmount += amounts[i];

            // Require that total amount locked is smaller than or equal to the token balance of
            // this contract
            require(token.balanceOf(address(this)) >= totalLockedAmount);

            // Retrieve early block number where available
            uint256 blockNumber = i < releaseBlockNumbers.length ? releaseBlockNumbers[i] : 0;

            // Add release
            releases.push(Release(earliestReleaseTimes[i], amounts[i], blockNumber, false));

            // Emit event
            emit DefineReleaseEvent(earliestReleaseTimes[i], amounts[i], blockNumber);
        }
    }

    /// @notice Get the count of releases
    /// @return The number of defined releases
    function releasesCount()
    public
    view
    returns (uint256)
    {
        return releases.length;
    }

    /// @notice Set the block number of a release that is not done
    /// @param index The index of the release
    /// @param blockNumber The updated block number
    function setReleaseBlockNumber(uint256 index, uint256 blockNumber)
    public
    onlyBeneficiary
    {
        // Require that the release is not done
        require(!releases[index].done);

        // Update the release block number
        releases[index].blockNumber = blockNumber;

        // Emit event
        emit SetReleaseBlockNumberEvent(index, blockNumber);
    }

    /// @notice Transfers tokens held in the indicated release to beneficiary.
    /// @param index The index of the release
    function release(uint256 index)
    public
    onlyBeneficiary
    {
        // Get the release object
        Release storage _release = releases[index];

        // Require that this release has been properly defined by having non-zero amount
        require(0 < _release.amount);

        // Require that this release has not already been executed
        require(!_release.done);

        // Require that the current timestamp is beyond the nominal release time
        require(block.timestamp >= _release.earliestReleaseTime);

        // Set release done
        _release.done = true;

        // Set release block number if not previously set
        if (0 == _release.blockNumber)
            _release.blockNumber = block.number;

        // Bump number of executed releases
        executedReleasesCount++;

        // Decrement the total locked amount
        totalLockedAmount -= _release.amount;

        // Execute transfer
        token.safeTransfer(beneficiary, _release.amount);

        // Emit event
        emit ReleaseEvent(index, _release.blockNumber, _release.earliestReleaseTime, block.timestamp, _release.amount);
    }

    // Modifiers
    // -----------------------------------------------------------------------------------------------------------------
    modifier onlyBeneficiary() {
        require(msg.sender == beneficiary);
        _;
    }
}

/*
 * Hubii Nahmii
 *
 * Compliant with the Hubii Nahmii specification v0.12.
 *
 * Copyright (C) 2017-2018 Hubii AS
 */







contract RevenueTokenManager is TokenMultiTimelock {
    using SafeMathUintLib for uint256;

    //
    // Variables
    // -----------------------------------------------------------------------------------------------------------------
    uint256[] public totalReleasedAmounts;
    uint256[] public totalReleasedAmountBlocks;

    //
    // Constructor
    // -----------------------------------------------------------------------------------------------------------------
    constructor(address deployer)
    public
    TokenMultiTimelock(deployer)
    {
    }

    //
    // Functions
    // -----------------------------------------------------------------------------------------------------------------
    /// @notice Transfers tokens held in the indicated release to beneficiary
    /// and update amount blocks
    /// @param index The index of the release
    function release(uint256 index)
    public
    onlyBeneficiary
    {
        // Call release of multi timelock
        super.release(index);

        // Add amount blocks
        _addAmountBlocks(index);
    }

    /// @notice Calculate the released amount blocks, i.e. the area under the curve (AUC) of
    /// release amount as function of block number
    /// @param startBlock The start block number considered
    /// @param endBlock The end block number considered
    /// @return The calculated AUC
    function releasedAmountBlocksIn(uint256 startBlock, uint256 endBlock)
    public
    view
    returns (uint256)
    {
        require(startBlock < endBlock);

        if (executedReleasesCount == 0 || endBlock < releases[0].blockNumber)
            return 0;

        uint256 i = 0;
        while (i < executedReleasesCount && releases[i].blockNumber < startBlock)
            i++;

        uint256 r;
        if (i >= executedReleasesCount)
            r = totalReleasedAmounts[executedReleasesCount - 1].mul(endBlock.sub(startBlock));

        else {
            uint256 l = (i == 0) ? startBlock : releases[i - 1].blockNumber;

            uint256 h = releases[i].blockNumber;
            if (h > endBlock)
                h = endBlock;

            h = h.sub(startBlock);
            r = (h == 0) ? 0 : totalReleasedAmountBlocks[i].mul(h).div(releases[i].blockNumber.sub(l));
            i++;

            while (i < executedReleasesCount && releases[i].blockNumber < endBlock) {
                r = r.add(totalReleasedAmountBlocks[i]);
                i++;
            }

            if (i >= executedReleasesCount)
                r = r.add(
                    totalReleasedAmounts[executedReleasesCount - 1].mul(
                        endBlock.sub(releases[executedReleasesCount - 1].blockNumber)
                    )
                );

            else if (releases[i - 1].blockNumber < endBlock)
                r = r.add(
                    totalReleasedAmountBlocks[i].mul(
                        endBlock.sub(releases[i - 1].blockNumber)
                    ).div(
                        releases[i].blockNumber.sub(releases[i - 1].blockNumber)
                    )
                );
        }

        return r;
    }

    /// @notice Get the block number of the release
    /// @param index The index of the release
    /// @return The block number of the release;
    function releaseBlockNumbers(uint256 index)
    public
    view
    returns (uint256)
    {
        return releases[index].blockNumber;
    }

    //
    // Private functions
    // -----------------------------------------------------------------------------------------------------------------
    function _addAmountBlocks(uint256 index)
    private
    {
        // Push total amount released and total released amount blocks
        if (0 < index) {
            totalReleasedAmounts.push(
                totalReleasedAmounts[index - 1] + releases[index].amount
            );
            totalReleasedAmountBlocks.push(
                totalReleasedAmounts[index - 1].mul(
                    releases[index].blockNumber.sub(releases[index - 1].blockNumber)
                )
            );

        } else {
            totalReleasedAmounts.push(releases[index].amount);
            totalReleasedAmountBlocks.push(0);
        }
    }
}

/*
 * Hubii Nahmii
 *
 * Compliant with the Hubii Nahmii specification v0.12.
 *
 * Copyright (C) 2017-2018 Hubii AS
 */



















/**
 * @title TokenHolderRevenueFund
 * @notice Fund that manages the revenue earned by revenue token holders.
 */
contract TokenHolderRevenueFund is Ownable, AccrualBeneficiary, Servable, TransferControllerManageable {
    using SafeMathIntLib for int256;
    using SafeMathUintLib for uint256;
    using FungibleBalanceLib for FungibleBalanceLib.Balance;
    using TxHistoryLib for TxHistoryLib.TxHistory;
    using CurrenciesLib for CurrenciesLib.Currencies;

    //
    // Constants
    // -----------------------------------------------------------------------------------------------------------------
    string constant public CLOSE_ACCRUAL_PERIOD_ACTION = "close_accrual_period";

    //
    // Variables
    // -----------------------------------------------------------------------------------------------------------------
    RevenueTokenManager public revenueTokenManager;

    FungibleBalanceLib.Balance private periodAccrual;
    CurrenciesLib.Currencies private periodCurrencies;

    FungibleBalanceLib.Balance private aggregateAccrual;
    CurrenciesLib.Currencies private aggregateCurrencies;

    TxHistoryLib.TxHistory private txHistory;

    mapping(address => mapping(address => mapping(uint256 => uint256[]))) public claimedAccrualBlockNumbersByWalletCurrency;

    mapping(address => mapping(uint256 => uint256[])) public accrualBlockNumbersByCurrency;
    mapping(address => mapping(uint256 => mapping(uint256 => int256))) aggregateAccrualAmountByCurrencyBlockNumber;

    mapping(address => FungibleBalanceLib.Balance) private stagedByWallet;

    //
    // Events
    // -----------------------------------------------------------------------------------------------------------------
    event SetRevenueTokenManagerEvent(RevenueTokenManager oldRevenueTokenManager,
        RevenueTokenManager newRevenueTokenManager);
    event ReceiveEvent(address wallet, int256 amount, address currencyCt,
        uint256 currencyId);
    event WithdrawEvent(address to, int256 amount, address currencyCt, uint256 currencyId);
    event CloseAccrualPeriodEvent(int256 periodAmount, int256 aggregateAmount, address currencyCt,
        uint256 currencyId);
    event ClaimAndTransferToBeneficiaryEvent(address wallet, string balanceType, int256 amount,
        address currencyCt, uint256 currencyId, string standard);
    event ClaimAndTransferToBeneficiaryByProxyEvent(address wallet, string balanceType, int256 amount,
        address currencyCt, uint256 currencyId, string standard);
    event ClaimAndStageEvent(address from, int256 amount, address currencyCt, uint256 currencyId);
    event WithdrawEvent(address from, int256 amount, address currencyCt, uint256 currencyId,
        string standard);

    //
    // Constructor
    // -----------------------------------------------------------------------------------------------------------------
    constructor(address deployer) Ownable(deployer) public {
    }

    //
    // Functions
    // -----------------------------------------------------------------------------------------------------------------
    /// @notice Set the revenue token manager contract
    /// @param newRevenueTokenManager The (address of) RevenueTokenManager contract instance
    function setRevenueTokenManager(RevenueTokenManager newRevenueTokenManager)
    public
    onlyDeployer
    notNullAddress(newRevenueTokenManager)
    {
        if (newRevenueTokenManager != revenueTokenManager) {
            // Set new revenue token
            RevenueTokenManager oldRevenueTokenManager = revenueTokenManager;
            revenueTokenManager = newRevenueTokenManager;

            // Emit event
            emit SetRevenueTokenManagerEvent(oldRevenueTokenManager, newRevenueTokenManager);
        }
    }

    /// @notice Fallback function that deposits ethers
    function() public payable {
        receiveEthersTo(msg.sender, "");
    }

    /// @notice Receive ethers to
    /// @param wallet The concerned wallet address
    function receiveEthersTo(address wallet, string)
    public
    payable
    {
        int256 amount = SafeMathIntLib.toNonZeroInt256(msg.value);

        // Add to balances
        periodAccrual.add(amount, address(0), 0);
        aggregateAccrual.add(amount, address(0), 0);

        // Add currency to in-use lists
        periodCurrencies.add(address(0), 0);
        aggregateCurrencies.add(address(0), 0);

        // Add to transaction history
        txHistory.addDeposit(amount, address(0), 0);

        // Emit event
        emit ReceiveEvent(wallet, amount, address(0), 0);
    }

    /// @notice Receive tokens
    /// @param amount The concerned amount
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    /// @param standard The standard of token ("ERC20", "ERC721")
    function receiveTokens(string, int256 amount, address currencyCt, uint256 currencyId,
        string standard)
    public
    {
        receiveTokensTo(msg.sender, "", amount, currencyCt, currencyId, standard);
    }

    /// @notice Receive tokens to
    /// @param wallet The address of the concerned wallet
    /// @param amount The concerned amount
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    /// @param standard The standard of token ("ERC20", "ERC721")
    function receiveTokensTo(address wallet, string, int256 amount, address currencyCt,
        uint256 currencyId, string standard)
    public
    {
        require(amount.isNonZeroPositiveInt256());

        // Execute transfer
        TransferController controller = transferController(currencyCt, standard);
        require(
            address(controller).delegatecall(
                controller.getReceiveSignature(), msg.sender, this, uint256(amount), currencyCt, currencyId
            )
        );

        // Add to balances
        periodAccrual.add(amount, currencyCt, currencyId);
        aggregateAccrual.add(amount, currencyCt, currencyId);

        // Add currency to in-use lists
        periodCurrencies.add(currencyCt, currencyId);
        aggregateCurrencies.add(currencyCt, currencyId);

        // Add to transaction history
        txHistory.addDeposit(amount, currencyCt, currencyId);

        // Emit event
        emit ReceiveEvent(wallet, amount, currencyCt, currencyId);
    }

    /// @notice Get the period accrual balance of the given currency
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    /// @return The current period's accrual balance
    function periodAccrualBalance(address currencyCt, uint256 currencyId)
    public
    view
    returns (int256)
    {
        return periodAccrual.get(currencyCt, currencyId);
    }

    /// @notice Get the aggregate accrual balance of the given currency, including contribution from the
    /// current accrual period
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    /// @return The aggregate accrual balance
    function aggregateAccrualBalance(address currencyCt, uint256 currencyId)
    public
    view
    returns (int256)
    {
        return aggregateAccrual.get(currencyCt, currencyId);
    }

    /// @notice Get the count of currencies recorded in the accrual period
    /// @return The number of currencies in the current accrual period
    function periodCurrenciesCount()
    public
    view
    returns (uint256)
    {
        return periodCurrencies.count();
    }

    /// @notice Get the currencies with indices in the given range that have been recorded in the current accrual period
    /// @param low The lower currency index
    /// @param up The upper currency index
    /// @return The currencies of the given index range in the current accrual period
    function periodCurrenciesByIndices(uint256 low, uint256 up)
    public
    view
    returns (MonetaryTypesLib.Currency[])
    {
        return periodCurrencies.getByIndices(low, up);
    }

    /// @notice Get the count of currencies ever recorded
    /// @return The number of currencies ever recorded
    function aggregateCurrenciesCount()
    public
    view
    returns (uint256)
    {
        return aggregateCurrencies.count();
    }

    /// @notice Get the currencies with indices in the given range that have ever been recorded
    /// @param low The lower currency index
    /// @param up The upper currency index
    /// @return The currencies of the given index range ever recorded
    function aggregateCurrenciesByIndices(uint256 low, uint256 up)
    public
    view
    returns (MonetaryTypesLib.Currency[])
    {
        return aggregateCurrencies.getByIndices(low, up);
    }

    /// @notice Get the count of deposits
    /// @return The count of deposits
    function depositsCount()
    public
    view
    returns (uint256)
    {
        return txHistory.depositsCount();
    }

    /// @notice Get the deposit at the given index
    /// @return The deposit at the given index
    function deposit(uint index)
    public
    view
    returns (int256 amount, uint256 blockNumber, address currencyCt, uint256 currencyId)
    {
        return txHistory.deposit(index);
    }

    /// @notice Get the staged balance of the given wallet and currency
    /// @param wallet The address of the concerned wallet
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    /// @return The staged balance
    function stagedBalance(address wallet, address currencyCt, uint256 currencyId)
    public
    view
    returns (int256)
    {
        return stagedByWallet[wallet].get(currencyCt, currencyId);
    }

    /// @notice Close the current accrual period of the given currencies
    /// @param currencies The concerned currencies
    function closeAccrualPeriod(MonetaryTypesLib.Currency[] currencies)
    public
    onlyEnabledServiceAction(CLOSE_ACCRUAL_PERIOD_ACTION)
    {
        // Clear period accrual stats
        for (uint256 i = 0; i < currencies.length; i++) {
            MonetaryTypesLib.Currency memory currency = currencies[i];

            // Get the amount of the accrual period
            int256 periodAmount = periodAccrual.get(currency.ct, currency.id);

            // Register this block number as accrual block number of currency
            accrualBlockNumbersByCurrency[currency.ct][currency.id].push(block.number);

            // Store the aggregate accrual balance of currency at this block number
            aggregateAccrualAmountByCurrencyBlockNumber[currency.ct][currency.id][block.number] = aggregateAccrualBalance(
                currency.ct, currency.id
            );

            if (periodAmount > 0) {
                // Reset period accrual of currency
                periodAccrual.set(0, currency.ct, currency.id);

                // Remove currency from period in-use list
                periodCurrencies.removeByCurrency(currency.ct, currency.id);
            }

            // Emit event
            emit CloseAccrualPeriodEvent(
                periodAmount,
                aggregateAccrualAmountByCurrencyBlockNumber[currency.ct][currency.id][block.number],
                currency.ct, currency.id
            );
        }
    }

    /// @notice Claim accrual and transfer to beneficiary
    /// @param beneficiary The concerned beneficiary
    /// @param destWallet The concerned destination wallet of the transfer
    /// @param balanceType The target balance type
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    /// @param standard The standard of the token ("" for default registered, "ERC20", "ERC721")
    function claimAndTransferToBeneficiary(Beneficiary beneficiary, address destWallet, string balanceType,
        address currencyCt, uint256 currencyId, string standard)
    public
    {
        // Claim accrual and obtain the claimed amount
        int256 claimedAmount = _claim(msg.sender, currencyCt, currencyId);

        // Transfer ETH to the beneficiary
        if (address(0) == currencyCt && 0 == currencyId)
            beneficiary.receiveEthersTo.value(uint256(claimedAmount))(destWallet, balanceType);

        else {
            // Approve of beneficiary
            TransferController controller = transferController(currencyCt, standard);
            require(
                address(controller).delegatecall(
                    controller.getApproveSignature(), beneficiary, uint256(claimedAmount), currencyCt, currencyId
                )
            );

            // Transfer tokens to the beneficiary
            beneficiary.receiveTokensTo(destWallet, balanceType, claimedAmount, currencyCt, currencyId, standard);
        }

        // Emit event
        emit ClaimAndTransferToBeneficiaryEvent(msg.sender, balanceType, claimedAmount, currencyCt, currencyId, standard);
    }

    /// @notice Claim accrual and stage for later withdrawal
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    function claimAndStage(address currencyCt, uint256 currencyId)
    public
    {
        // Claim accrual and obtain the claimed amount
        int256 claimedAmount = _claim(msg.sender, currencyCt, currencyId);

        // Update staged balance
        stagedByWallet[msg.sender].add(claimedAmount, currencyCt, currencyId);

        // Emit event
        emit ClaimAndStageEvent(msg.sender, claimedAmount, currencyCt, currencyId);
    }

    /// @notice Withdraw from staged balance of msg.sender
    /// @param amount The concerned amount
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    /// @param standard The standard of the token ("" for default registered, "ERC20", "ERC721")
    function withdraw(int256 amount, address currencyCt, uint256 currencyId, string standard)
    public
    {
        // Require that amount is strictly positive
        require(amount.isNonZeroPositiveInt256());

        // Clamp amount to the max given by staged balance
        amount = amount.clampMax(stagedByWallet[msg.sender].get(currencyCt, currencyId));

        // Subtract to per-wallet staged balance
        stagedByWallet[msg.sender].sub(amount, currencyCt, currencyId);

        // Execute transfer
        if (address(0) == currencyCt && 0 == currencyId)
            msg.sender.transfer(uint256(amount));

        else {
            TransferController controller = transferController(currencyCt, standard);
            require(
                address(controller).delegatecall(
                    controller.getDispatchSignature(), this, msg.sender, uint256(amount), currencyCt, currencyId
                )
            );
        }

        // Emit event
        emit WithdrawEvent(msg.sender, amount, currencyCt, currencyId, standard);
    }

    //
    // Private functions
    // -----------------------------------------------------------------------------------------------------------------
    function _claim(address wallet, address currencyCt, uint256 currencyId)
    private
    returns (int256)
    {
        // Require that at least one accrual period has terminated
        require(0 < accrualBlockNumbersByCurrency[currencyCt][currencyId].length);

        // Calculate lower block number as last accrual block number claimed for currency c by wallet OR 0
        uint256[] storage claimedAccrualBlockNumbers = claimedAccrualBlockNumbersByWalletCurrency[wallet][currencyCt][currencyId];
        uint256 bnLow = (0 == claimedAccrualBlockNumbers.length ? 0 : claimedAccrualBlockNumbers[claimedAccrualBlockNumbers.length - 1]);

        // Set upper block number as last accrual block number
        uint256 bnUp = accrualBlockNumbersByCurrency[currencyCt][currencyId][accrualBlockNumbersByCurrency[currencyCt][currencyId].length - 1];

        // Require that lower block number is below upper block number
        require(bnLow < bnUp);

        // Calculate the amount that is claimable in the span between lower and upper block numbers
        int256 claimableAmount = aggregateAccrualAmountByCurrencyBlockNumber[currencyCt][currencyId][bnUp]
        - (0 == bnLow ? 0 : aggregateAccrualAmountByCurrencyBlockNumber[currencyCt][currencyId][bnLow]);

        // Require that claimable amount is strictly positive
        require(0 < claimableAmount);

        // Retrieve the balance blocks of wallet
        int256 walletBalanceBlocks = int256(
            RevenueToken(revenueTokenManager.token()).balanceBlocksIn(wallet, bnLow, bnUp)
        );

        // Retrieve the released amount blocks
        int256 releasedAmountBlocks = int256(
            revenueTokenManager.releasedAmountBlocksIn(bnLow, bnUp)
        );

        // Calculate the claimed amount
        int256 claimedAmount = walletBalanceBlocks.mul_nn(claimableAmount).mul_nn(1e18).div_nn(releasedAmountBlocks.mul_nn(1e18));

        // Store upper bound as the last claimed accrual block number for currency
        claimedAccrualBlockNumbers.push(bnUp);

        // Return the claimed amount
        return claimedAmount;
    }

    function _transferToBeneficiary(Beneficiary beneficiary, address destWallet, string balanceType,
        int256 amount, address currencyCt, uint256 currencyId, string standard)
    private
    {
        // Transfer ETH to the beneficiary
        if (address(0) == currencyCt && 0 == currencyId)
            beneficiary.receiveEthersTo.value(uint256(amount))(destWallet, balanceType);

        else {
            // Approve of beneficiary
            TransferController controller = transferController(currencyCt, standard);
            require(
                address(controller).delegatecall(
                    controller.getApproveSignature(), beneficiary, uint256(amount), currencyCt, currencyId
                )
            );

            // Transfer tokens to the beneficiary
            beneficiary.receiveTokensTo(destWallet, balanceType, amount, currencyCt, currencyId, standard);
        }
    }
}

/*
 * Hubii Nahmii
 *
 * Compliant with the Hubii Nahmii specification v0.12.
 *
 * Copyright (C) 2017-2018 Hubii AS
 */
















/**
 * @title Client fund
 * @notice Where clients’ crypto is deposited into, staged and withdrawn from.
 */
contract ClientFund is Ownable, Beneficiary, Benefactor, AuthorizableServable, TransferControllerManageable,
BalanceTrackable, TransactionTrackable, WalletLockable {
    using SafeMathIntLib for int256;

    address[] public seizedWallets;
    mapping(address => bool) public seizedByWallet;

    TokenHolderRevenueFund public tokenHolderRevenueFund;

    //
    // Events
    // -----------------------------------------------------------------------------------------------------------------
    event SetTokenHolderRevenueFundEvent(TokenHolderRevenueFund oldTokenHolderRevenueFund,
        TokenHolderRevenueFund newTokenHolderRevenueFund);
    event ReceiveEvent(address wallet, string balanceType, int256 value, address currencyCt,
        uint256 currencyId, string standard);
    event WithdrawEvent(address wallet, int256 value, address currencyCt, uint256 currencyId,
        string standard);
    event StageEvent(address wallet, int256 value, address currencyCt, uint256 currencyId);
    event UnstageEvent(address wallet, int256 value, address currencyCt, uint256 currencyId);
    event UpdateSettledBalanceEvent(address wallet, int256 value, address currencyCt,
        uint256 currencyId);
    event StageToBeneficiaryEvent(address sourceWallet, address beneficiary, int256 value,
        address currencyCt, uint256 currencyId, string standard);
    event TransferToBeneficiaryEvent(address wallet, address beneficiary, int256 value,
        address currencyCt, uint256 currencyId);
    event SeizeBalancesEvent(address seizedWallet, address seizerWallet, int256 value,
        address currencyCt, uint256 currencyId);
    event ClaimRevenueEvent(address claimer, string balanceType, address currencyCt,
        uint256 currencyId, string standard);

    //
    // Constructor
    // -----------------------------------------------------------------------------------------------------------------
    constructor(address deployer) Ownable(deployer) Beneficiary() Benefactor()
    public
    {
        serviceActivationTimeout = 1 weeks;
    }

    //
    // Functions
    // -----------------------------------------------------------------------------------------------------------------
    /// @notice Set the token holder revenue fund contract
    /// @param newTokenHolderRevenueFund The (address of) TokenHolderRevenueFund contract instance
    function setTokenHolderRevenueFund(TokenHolderRevenueFund newTokenHolderRevenueFund)
    public
    onlyDeployer
    notNullAddress(newTokenHolderRevenueFund)
    notSameAddresses(newTokenHolderRevenueFund, tokenHolderRevenueFund)
    {
        // Set new token holder revenue fund
        TokenHolderRevenueFund oldTokenHolderRevenueFund = tokenHolderRevenueFund;
        tokenHolderRevenueFund = newTokenHolderRevenueFund;

        // Emit event
        emit SetTokenHolderRevenueFundEvent(oldTokenHolderRevenueFund, newTokenHolderRevenueFund);
    }

    /// @notice Fallback function that deposits ethers to msg.sender's deposited balance
    function()
    public
    payable
    {
        receiveEthersTo(msg.sender, balanceTracker.DEPOSITED_BALANCE_TYPE());
    }

    /// @notice Receive ethers to the given wallet's balance of the given type
    /// @param wallet The address of the concerned wallet
    /// @param balanceType The target balance type
    function receiveEthersTo(address wallet, string balanceType)
    public
    payable
    {
        int256 value = SafeMathIntLib.toNonZeroInt256(msg.value);

        // Register reception
        _receiveTo(wallet, balanceType, value, address(0), 0, true);

        // Emit event
        emit ReceiveEvent(wallet, balanceType, value, address(0), 0, "");
    }

    /// @notice Receive token to msg.sender's balance of the given type
    /// @dev The wallet must approve of this ClientFund's transfer prior to calling this function
    /// @param balanceType The target balance type
    /// @param value The value (amount of fungible, id of non-fungible) to receive
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    /// @param standard The standard of the token ("" for default registered, "ERC20", "ERC721")
    function receiveTokens(string balanceType, int256 value, address currencyCt,
        uint256 currencyId, string standard)
    public
    {
        receiveTokensTo(msg.sender, balanceType, value, currencyCt, currencyId, standard);
    }

    /// @notice Receive token to the given wallet's balance of the given type
    /// @dev The wallet must approve of this ClientFund's transfer prior to calling this function
    /// @param wallet The address of the concerned wallet
    /// @param balanceType The target balance type
    /// @param value The value (amount of fungible, id of non-fungible) to receive
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    function receiveTokensTo(address wallet, string balanceType, int256 value, address currencyCt,
        uint256 currencyId, string standard)
    public
    {
        require(value.isNonZeroPositiveInt256());

        // Get transfer controller
        TransferController controller = transferController(currencyCt, standard);

        // Execute transfer
        require(
            address(controller).delegatecall(
                controller.getReceiveSignature(), msg.sender, this, uint256(value), currencyCt, currencyId
            )
        );

        // Register reception
        _receiveTo(wallet, balanceType, value, currencyCt, currencyId, controller.isFungible());

        // Emit event
        emit ReceiveEvent(wallet, balanceType, value, currencyCt, currencyId, standard);
    }

    /// @notice Update the settled balance by the difference between provided off-chain balance amount
    /// and deposited on-chain balance, where deposited balance is resolved at the given block number
    /// @param wallet The address of the concerned wallet
    /// @param value The target balance value (amount of fungible, id of non-fungible), i.e. off-chain balance
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    /// @param standard The standard of the token ("" for default registered, "ERC20", "ERC721")
    /// @param blockNumber The block number to which the settled balance is updated
    function updateSettledBalance(address wallet, int256 value, address currencyCt, uint256 currencyId,
        string standard, uint256 blockNumber)
    public
    onlyAuthorizedService(wallet)
    notNullAddress(wallet)
    {
        require(value.isPositiveInt256());

        if (_isFungible(currencyCt, currencyId, standard)) {
            (int256 depositedValue,) = balanceTracker.fungibleRecordByBlockNumber(
                wallet, balanceTracker.depositedBalanceType(), currencyCt, currencyId, blockNumber
            );
            balanceTracker.set(
                wallet, balanceTracker.settledBalanceType(), value.sub(depositedValue),
                currencyCt, currencyId, true
            );

        } else {
            balanceTracker.sub(
                wallet, balanceTracker.depositedBalanceType(), value, currencyCt, currencyId, false
            );
            balanceTracker.add(
                wallet, balanceTracker.settledBalanceType(), value, currencyCt, currencyId, false
            );
        }

        // Emit event
        emit UpdateSettledBalanceEvent(wallet, value, currencyCt, currencyId);
    }

    /// @notice Stage a value for subsequent withdrawal
    /// @param wallet The address of the concerned wallet
    /// @param value The value (amount of fungible, id of non-fungible) to deposit
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    /// @param standard The standard of the token ("" for default registered, "ERC20", "ERC721")
    function stage(address wallet, int256 value, address currencyCt, uint256 currencyId,
        string standard)
    public
    onlyAuthorizedService(wallet)
    {
        require(value.isNonZeroPositiveInt256());

        // Deduce fungibility
        bool fungible = _isFungible(currencyCt, currencyId, standard);

        // Subtract stage value from settled, possibly also from deposited
        value = _subtractSequentially(wallet, balanceTracker.activeBalanceTypes(), value, currencyCt, currencyId, fungible);

        // Add to staged
        balanceTracker.add(
            wallet, balanceTracker.stagedBalanceType(), value, currencyCt, currencyId, fungible
        );

        // Emit event
        emit StageEvent(wallet, value, currencyCt, currencyId);
    }

    /// @notice Unstage a staged value
    /// @param value The value (amount of fungible, id of non-fungible) to deposit
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    /// @param standard The standard of the token ("" for default registered, "ERC20", "ERC721")
    function unstage(int256 value, address currencyCt, uint256 currencyId, string standard)
    public
    {
        require(value.isNonZeroPositiveInt256());

        // Deduce fungibility
        bool fungible = _isFungible(currencyCt, currencyId, standard);

        // Subtract unstage value from staged
        value = _subtractFromStaged(msg.sender, value, currencyCt, currencyId, fungible);

        balanceTracker.add(
            msg.sender, balanceTracker.depositedBalanceType(), value, currencyCt, currencyId, fungible
        );

        // Emit event
        emit UnstageEvent(msg.sender, value, currencyCt, currencyId);
    }

    /// @notice Stage the value from wallet to the given beneficiary and targeted to wallet
    /// @param wallet The address of the concerned wallet
    /// @param beneficiary The (address of) concerned beneficiary contract
    /// @param value The value (amount of fungible, id of non-fungible) to stage
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    /// @param standard The standard of the token ("" for default registered, "ERC20", "ERC721")
    function stageToBeneficiary(address wallet, Beneficiary beneficiary, int256 value,
        address currencyCt, uint256 currencyId, string standard)
    public
    onlyAuthorizedService(wallet)
    {
        // Deduce fungibility
        bool fungible = _isFungible(currencyCt, currencyId, standard);

        // Subtract stage value from settled, possibly also from deposited
        value = _subtractSequentially(wallet, balanceTracker.activeBalanceTypes(), value, currencyCt, currencyId, fungible);

        // Execute transfer
        _transferToBeneficiary(wallet, beneficiary, value, currencyCt, currencyId, standard);

        // Emit event
        emit StageToBeneficiaryEvent(wallet, beneficiary, value, currencyCt, currencyId, standard);
    }

    /// @notice Transfer the given value of currency to the given beneficiary without target wallet
    /// @param wallet The address of the concerned wallet
    /// @param beneficiary The (address of) concerned beneficiary contract
    /// @param value The value (amount of fungible, id of non-fungible) to transfer
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    /// @param standard The standard of the token ("" for default registered, "ERC20", "ERC721")
    function transferToBeneficiary(address wallet, Beneficiary beneficiary, int256 value,
        address currencyCt, uint256 currencyId, string standard)
    public
    onlyAuthorizedService(wallet)
    {
        // Execute transfer
        _transferToBeneficiary(wallet, beneficiary, value, currencyCt, currencyId, standard);

        // Emit event
        emit TransferToBeneficiaryEvent(wallet, beneficiary, value, currencyCt, currencyId);
    }

    /// @notice Seize balances in the given currency of the given wallet, provided that the wallet
    /// is locked by the caller
    /// @param wallet The address of the concerned wallet whose balances are seized
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    /// @param standard The standard of the token ("" for default registered, "ERC20", "ERC721")
    function seizeBalances(address wallet, address currencyCt, uint256 currencyId, string standard)
    public
    {
        if (_isFungible(currencyCt, currencyId, standard))
            _seizeFungibleBalances(wallet, msg.sender, currencyCt, currencyId);

        else
            _seizeNonFungibleBalances(wallet, msg.sender, currencyCt, currencyId);

        // Add to the store of seized wallets
        if (!seizedByWallet[wallet]) {
            seizedByWallet[wallet] = true;
            seizedWallets.push(wallet);
        }
    }

    /// @notice Withdraw the given amount from staged balance
    /// @param value The value (amount of fungible, id of non-fungible) to withdraw
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    /// @param standard The standard of the token ("" for default registered, "ERC20", "ERC721")
    function withdraw(int256 value, address currencyCt, uint256 currencyId, string standard)
    public
    {
        require(value.isNonZeroPositiveInt256());

        // Require that msg.sender and currency is not locked
        require(!walletLocker.isLocked(msg.sender, currencyCt, currencyId));

        // Deduce fungibility
        bool fungible = _isFungible(currencyCt, currencyId, standard);

        // Subtract unstage value from staged
        value = _subtractFromStaged(msg.sender, value, currencyCt, currencyId, fungible);

        // Log record of this transaction
        transactionTracker.add(
            msg.sender, transactionTracker.withdrawalTransactionType(), value, currencyCt, currencyId
        );

        // Execute transfer
        _transferToWallet(msg.sender, value, currencyCt, currencyId, standard);

        // Emit event
        emit WithdrawEvent(msg.sender, value, currencyCt, currencyId, standard);
    }

    /// @notice Get the seized status of given wallet
    /// @param wallet The address of the concerned wallet
    /// @return true if wallet is seized, false otherwise
    function isSeizedWallet(address wallet)
    public
    view
    returns (bool)
    {
        return seizedByWallet[wallet];
    }

    /// @notice Get the number of wallets whose funds have been seized
    /// @return Number of wallets
    function seizedWalletsCount()
    public
    view
    returns (uint256)
    {
        return seizedWallets.length;
    }

    /// @notice Claim revenue from token holder revenue fund based on this contract's holdings of the
    /// revenue token, this so that revenue may be shared amongst revenue token holders in nahmii
    /// @param claimer The concerned address of claimer that will subsequently distribute revenue in nahmii
    /// @param balanceType The target balance type for the reception in this contract
    /// @param currencyCt The address of the concerned currency contract (address(0) == ETH)
    /// @param currencyId The ID of the concerned currency (0 for ETH and ERC20)
    /// @param standard The standard of the token ("" for default registered, "ERC20", "ERC721")
    function claimRevenue(address claimer, string balanceType, address currencyCt,
        uint256 currencyId, string standard)
    public
    onlyOperator
    {
        tokenHolderRevenueFund.claimAndTransferToBeneficiary(
            this, claimer, balanceType,
            currencyCt, currencyId, standard
        );

        emit ClaimRevenueEvent(claimer, balanceType, currencyCt, currencyId, standard);
    }

    //
    // Private functions
    // -----------------------------------------------------------------------------------------------------------------
    function _receiveTo(address wallet, string balanceType, int256 value, address currencyCt,
        uint256 currencyId, bool fungible)
    private
    {
        bytes32 balanceHash = 0 < bytes(balanceType).length ?
        keccak256(abi.encodePacked(balanceType)) :
        balanceTracker.depositedBalanceType();

        // Add to per-wallet staged balance
        if (balanceTracker.stagedBalanceType() == balanceHash)
            balanceTracker.add(
                wallet, balanceTracker.stagedBalanceType(), value, currencyCt, currencyId, fungible
            );

        // Add to per-wallet deposited balance
        else if (balanceTracker.depositedBalanceType() == balanceHash) {
            balanceTracker.add(
                wallet, balanceTracker.depositedBalanceType(), value, currencyCt, currencyId, fungible
            );

            // Log record of this transaction
            transactionTracker.add(
                wallet, transactionTracker.depositTransactionType(), value, currencyCt, currencyId
            );
        }

        else
            revert();
    }

    function _subtractSequentially(address wallet, bytes32[] balanceTypes, int256 value, address currencyCt,
        uint256 currencyId, bool fungible)
    private
    returns (int256)
    {
        if (fungible)
            return _subtractFungibleSequentially(wallet, balanceTypes, value, currencyCt, currencyId);
        else
            return _subtractNonFungibleSequentially(wallet, balanceTypes, value, currencyCt, currencyId);
    }

    function _subtractFungibleSequentially(address wallet, bytes32[] balanceTypes, int256 amount, address currencyCt, uint256 currencyId)
    private
    returns (int256)
    {
        // Require positive amount
        require(0 <= amount);

        uint256 i;
        int256 totalBalanceAmount = 0;
        for (i = 0; i < balanceTypes.length; i++)
            totalBalanceAmount = totalBalanceAmount.add(
                balanceTracker.get(
                    wallet, balanceTypes[i], currencyCt, currencyId
                )
            );

        // Clamp amount to stage
        amount = amount.clampMax(totalBalanceAmount);

        int256 _amount = amount;
        for (i = 0; i < balanceTypes.length; i++) {
            int256 typeAmount = balanceTracker.get(
                wallet, balanceTypes[i], currencyCt, currencyId
            );

            if (typeAmount >= _amount) {
                balanceTracker.sub(
                    wallet, balanceTypes[i], _amount, currencyCt, currencyId, true
                );
                break;

            } else {
                balanceTracker.set(
                    wallet, balanceTypes[i], 0, currencyCt, currencyId, true
                );
                _amount = _amount.sub(typeAmount);
            }
        }

        return amount;
    }

    function _subtractNonFungibleSequentially(address wallet, bytes32[] balanceTypes, int256 id, address currencyCt, uint256 currencyId)
    private
    returns (int256)
    {
        for (uint256 i = 0; i < balanceTypes.length; i++)
            if (balanceTracker.hasId(wallet, balanceTypes[i], id, currencyCt, currencyId)) {
                balanceTracker.sub(wallet, balanceTypes[i], id, currencyCt, currencyId, false);
                break;
            }

        return id;
    }

    function _subtractFromStaged(address wallet, int256 value, address currencyCt, uint256 currencyId, bool fungible)
    private
    returns (int256)
    {
        if (fungible) {
            // Clamp value to unstage
            value = value.clampMax(
                balanceTracker.get(wallet, balanceTracker.stagedBalanceType(), currencyCt, currencyId)
            );

            // Require positive value
            require(0 <= value);

        } else {
            // Require that value is included in staged balance
            require(balanceTracker.hasId(wallet, balanceTracker.stagedBalanceType(), value, currencyCt, currencyId));
        }

        // Subtract from deposited balance
        balanceTracker.sub(wallet, balanceTracker.stagedBalanceType(), value, currencyCt, currencyId, fungible);

        return value;
    }

    function _transferToBeneficiary(address destWallet, Beneficiary beneficiary,
        int256 value, address currencyCt, uint256 currencyId, string standard)
    private
    {
        require(value.isNonZeroPositiveInt256());
        require(isRegisteredBeneficiary(beneficiary));

        // Transfer funds to the beneficiary
        if (address(0) == currencyCt && 0 == currencyId)
            beneficiary.receiveEthersTo.value(uint256(value))(destWallet, "");

        else {
            // Approve of beneficiary
            TransferController controller = transferController(currencyCt, standard);
            require(
                address(controller).delegatecall(
                    controller.getApproveSignature(), beneficiary, uint256(value), currencyCt, currencyId
                )
            );

            // Transfer funds to the beneficiary
            beneficiary.receiveTokensTo(destWallet, "", value, currencyCt, currencyId, standard);
        }
    }

    function _transferToWallet(address wallet,
        int256 value, address currencyCt, uint256 currencyId, string standard)
    private
    {
        // Transfer ETH
        if (address(0) == currencyCt && 0 == currencyId)
            wallet.transfer(uint256(value));

        // Transfer token
        else {
            TransferController controller = transferController(currencyCt, standard);
            require(
                address(controller).delegatecall(
                    controller.getDispatchSignature(), this, wallet, uint256(value), currencyCt, currencyId
                )
            );
        }
    }

    function _seizeFungibleBalances(address lockedWallet, address lockerWallet, address currencyCt,
        uint256 currencyId)
    private
    {
        // Get the locked amount
        int256 amount = walletLocker.lockedAmount(lockedWallet, lockerWallet, currencyCt, currencyId);

        // Require that locked amount is strictly positive
        require(amount > 0);

        // Subtract stage value from settled, possibly also from deposited
        _subtractFungibleSequentially(lockedWallet, balanceTracker.allBalanceTypes(), amount, currencyCt, currencyId);

        // Add to staged balance of sender
        balanceTracker.add(
            lockerWallet, balanceTracker.stagedBalanceType(), amount, currencyCt, currencyId, true
        );

        // Emit event
        emit SeizeBalancesEvent(lockedWallet, lockerWallet, amount, currencyCt, currencyId);
    }

    function _seizeNonFungibleBalances(address lockedWallet, address lockerWallet, address currencyCt,
        uint256 currencyId)
    private
    {
        // Require that locked ids has entries
        uint256 lockedIdsCount = walletLocker.lockedIdsCount(lockedWallet, lockerWallet, currencyCt, currencyId);
        require(0 < lockedIdsCount);

        // Get the locked amount
        int256[] memory ids = walletLocker.lockedIdsByIndices(
            lockedWallet, lockerWallet, currencyCt, currencyId, 0, lockedIdsCount - 1
        );

        for (uint256 i = 0; i < ids.length; i++) {
            // Subtract from settled, possibly also from deposited
            _subtractNonFungibleSequentially(lockedWallet, balanceTracker.allBalanceTypes(), ids[i], currencyCt, currencyId);

            // Add to staged balance of sender
            balanceTracker.add(
                lockerWallet, balanceTracker.stagedBalanceType(), ids[i], currencyCt, currencyId, false
            );

            // Emit event
            emit SeizeBalancesEvent(lockedWallet, lockerWallet, ids[i], currencyCt, currencyId);
        }
    }

    function _isFungible(address currencyCt, uint256 currencyId, string standard)
    private
    view
    returns (bool)
    {
        return (address(0) == currencyCt && 0 == currencyId) || transferController(currencyCt, standard).isFungible();
    }
}

Contract Security Audit

Contract ABI

[{"constant":false,"inputs":[{"name":"service","type":"address"}],"name":"authorizeRegisteredService","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"beneficiary","type":"address"}],"name":"isRegisteredBeneficiary","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"service","type":"address"},{"name":"action","type":"string"},{"name":"wallet","type":"address"}],"name":"isAuthorizedRegisteredServiceAction","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"value","type":"int256"},{"name":"currencyCt","type":"address"},{"name":"currencyId","type":"uint256"},{"name":"standard","type":"string"}],"name":"withdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"wallet","type":"address"},{"name":"beneficiary","type":"address"},{"name":"value","type":"int256"},{"name":"currencyCt","type":"address"},{"name":"currencyId","type":"uint256"},{"name":"standard","type":"string"}],"name":"transferToBeneficiary","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"service","type":"address"}],"name":"isRegisteredActiveService","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"balanceTracker","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"initialServiceAuthorizedMap","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"value","type":"int256"},{"name":"currencyCt","type":"address"},{"name":"currencyId","type":"uint256"},{"name":"standard","type":"string"}],"name":"unstage","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"beneficiary","type":"address"}],"name":"deregisterBeneficiary","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"newTokenHolderRevenueFund","type":"address"}],"name":"setTokenHolderRevenueFund","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"seizedWalletsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"walletLockerFrozen","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"triggerSelfDestruction","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"service","type":"address"},{"name":"action","type":"string"}],"name":"enableServiceAction","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"service","type":"address"}],"name":"isRegisteredService","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"selfDestructionDisabled","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"claimer","type":"address"},{"name":"balanceType","type":"string"},{"name":"currencyCt","type":"address"},{"name":"currencyId","type":"uint256"},{"name":"standard","type":"string"}],"name":"claimRevenue","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"service","type":"address"},{"name":"action","type":"string"}],"name":"disableServiceAction","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"serviceActivationTimeout","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"balanceTrackerFrozen","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"destructor","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"wallet","type":"address"},{"name":"currencyCt","type":"address"},{"name":"currencyId","type":"uint256"},{"name":"standard","type":"string"}],"name":"seizeBalances","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"service","type":"address"}],"name":"authorizeInitialService","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"service","type":"address"}],"name":"deregisterService","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"operator","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"service","type":"address"},{"name":"wallet","type":"address"}],"name":"isAuthorizedRegisteredService","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newBalanceTracker","type":"address"}],"name":"setBalanceTracker","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"freezeWalletLocker","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"service","type":"address"},{"name":"action","type":"string"}],"name":"isEnabledServiceAction","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"wallet","type":"address"},{"name":"balanceType","type":"string"},{"name":"value","type":"int256"},{"name":"currencyCt","type":"address"},{"name":"currencyId","type":"uint256"},{"name":"standard","type":"string"}],"name":"receiveTokensTo","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"service","type":"address"},{"name":"action","type":"string"}],"name":"authorizeRegisteredServiceAction","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"address"}],"name":"initialServiceWalletUnauthorizedMap","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"wallet","type":"address"},{"name":"value","type":"int256"},{"name":"currencyCt","type":"address"},{"name":"currencyId","type":"uint256"},{"name":"standard","type":"string"},{"name":"blockNumber","type":"uint256"}],"name":"updateSettledBalance","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"registeredBeneficiariesCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"bytes32"},{"name":"","type":"address"}],"name":"serviceActionWalletTouchedMap","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"disableSelfDestruction","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"address"}],"name":"serviceWalletAuthorizedMap","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tokenHolderRevenueFund","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"timeoutInSeconds","type":"uint256"}],"name":"setServiceActivationTimeout","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"initialServiceAuthorizationDisabled","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"bytes32"},{"name":"","type":"address"}],"name":"serviceActionWalletAuthorizedMap","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newWalletLocker","type":"address"}],"name":"setWalletLocker","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"freezeTransactionTracker","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"transactionTrackerFrozen","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"service","type":"address"},{"name":"action","type":"string"}],"name":"unauthorizeRegisteredServiceAction","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"service","type":"address"}],"name":"registerServiceDeferred","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"newDeployer","type":"address"}],"name":"setDeployer","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"service","type":"address"}],"name":"unauthorizeRegisteredService","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"transactionTracker","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"wallet","type":"address"},{"name":"balanceType","type":"string"}],"name":"receiveEthersTo","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"wallet","type":"address"},{"name":"value","type":"int256"},{"name":"currencyCt","type":"address"},{"name":"currencyId","type":"uint256"},{"name":"standard","type":"string"}],"name":"stage","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"transferControllerManager","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newOperator","type":"address"}],"name":"setOperator","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"freezeBalanceTracker","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"seizedWallets","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"balanceType","type":"string"},{"name":"value","type":"int256"},{"name":"currencyCt","type":"address"},{"name":"currencyId","type":"uint256"},{"name":"standard","type":"string"}],"name":"receiveTokens","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"wallet","type":"address"}],"name":"isSeizedWallet","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"beneficiary","type":"address"}],"name":"registerBeneficiary","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"newTransactionTracker","type":"address"}],"name":"setTransactionTracker","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"newTransferControllerManager","type":"address"}],"name":"setTransferControllerManager","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"deployer","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"seizedByWallet","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"disableInitialServiceAuthorization","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"address"},{"name":"","type":"uint256"}],"name":"serviceWalletActionList","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"walletLocker","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"wallet","type":"address"},{"name":"beneficiary","type":"address"},{"name":"value","type":"int256"},{"name":"currencyCt","type":"address"},{"name":"currencyId","type":"uint256"},{"name":"standard","type":"string"}],"name":"stageToBeneficiary","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"service","type":"address"}],"name":"registerService","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"deployer","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldTokenHolderRevenueFund","type":"address"},{"indexed":false,"name":"newTokenHolderRevenueFund","type":"address"}],"name":"SetTokenHolderRevenueFundEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"wallet","type":"address"},{"indexed":false,"name":"balanceType","type":"string"},{"indexed":false,"name":"value","type":"int256"},{"indexed":false,"name":"currencyCt","type":"address"},{"indexed":false,"name":"currencyId","type":"uint256"},{"indexed":false,"name":"standard","type":"string"}],"name":"ReceiveEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"wallet","type":"address"},{"indexed":false,"name":"value","type":"int256"},{"indexed":false,"name":"currencyCt","type":"address"},{"indexed":false,"name":"currencyId","type":"uint256"},{"indexed":false,"name":"standard","type":"string"}],"name":"WithdrawEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"wallet","type":"address"},{"indexed":false,"name":"value","type":"int256"},{"indexed":false,"name":"currencyCt","type":"address"},{"indexed":false,"name":"currencyId","type":"uint256"}],"name":"StageEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"wallet","type":"address"},{"indexed":false,"name":"value","type":"int256"},{"indexed":false,"name":"currencyCt","type":"address"},{"indexed":false,"name":"currencyId","type":"uint256"}],"name":"UnstageEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"wallet","type":"address"},{"indexed":false,"name":"value","type":"int256"},{"indexed":false,"name":"currencyCt","type":"address"},{"indexed":false,"name":"currencyId","type":"uint256"}],"name":"UpdateSettledBalanceEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sourceWallet","type":"address"},{"indexed":false,"name":"beneficiary","type":"address"},{"indexed":false,"name":"value","type":"int256"},{"indexed":false,"name":"currencyCt","type":"address"},{"indexed":false,"name":"currencyId","type":"uint256"},{"indexed":false,"name":"standard","type":"string"}],"name":"StageToBeneficiaryEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"wallet","type":"address"},{"indexed":false,"name":"beneficiary","type":"address"},{"indexed":false,"name":"value","type":"int256"},{"indexed":false,"name":"currencyCt","type":"address"},{"indexed":false,"name":"currencyId","type":"uint256"}],"name":"TransferToBeneficiaryEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"seizedWallet","type":"address"},{"indexed":false,"name":"seizerWallet","type":"address"},{"indexed":false,"name":"value","type":"int256"},{"indexed":false,"name":"currencyCt","type":"address"},{"indexed":false,"name":"currencyId","type":"uint256"}],"name":"SeizeBalancesEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"claimer","type":"address"},{"indexed":false,"name":"balanceType","type":"string"},{"indexed":false,"name":"currencyCt","type":"address"},{"indexed":false,"name":"currencyId","type":"uint256"},{"indexed":false,"name":"standard","type":"string"}],"name":"ClaimRevenueEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldWalletLocker","type":"address"},{"indexed":false,"name":"newWalletLocker","type":"address"}],"name":"SetWalletLockerEvent","type":"event"},{"anonymous":false,"inputs":[],"name":"FreezeWalletLockerEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldTransactionTracker","type":"address"},{"indexed":false,"name":"newTransactionTracker","type":"address"}],"name":"SetTransactionTrackerEvent","type":"event"},{"anonymous":false,"inputs":[],"name":"FreezeTransactionTrackerEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldBalanceTracker","type":"address"},{"indexed":false,"name":"newBalanceTracker","type":"address"}],"name":"SetBalanceTrackerEvent","type":"event"},{"anonymous":false,"inputs":[],"name":"FreezeBalanceTrackerEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldTransferControllerManager","type":"address"},{"indexed":false,"name":"newTransferControllerManager","type":"address"}],"name":"SetTransferControllerManagerEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"wallet","type":"address"},{"indexed":false,"name":"service","type":"address"}],"name":"AuthorizeInitialServiceEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"wallet","type":"address"},{"indexed":false,"name":"service","type":"address"}],"name":"AuthorizeRegisteredServiceEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"wallet","type":"address"},{"indexed":false,"name":"service","type":"address"},{"indexed":false,"name":"action","type":"string"}],"name":"AuthorizeRegisteredServiceActionEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"wallet","type":"address"},{"indexed":false,"name":"service","type":"address"}],"name":"UnauthorizeRegisteredServiceEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"wallet","type":"address"},{"indexed":false,"name":"service","type":"address"},{"indexed":false,"name":"action","type":"string"}],"name":"UnauthorizeRegisteredServiceActionEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"timeoutInSeconds","type":"uint256"}],"name":"ServiceActivationTimeoutEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"service","type":"address"}],"name":"RegisterServiceEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"service","type":"address"},{"indexed":false,"name":"timeout","type":"uint256"}],"name":"RegisterServiceDeferredEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"service","type":"address"}],"name":"DeregisterServiceEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"service","type":"address"},{"indexed":false,"name":"action","type":"string"}],"name":"EnableServiceActionEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"service","type":"address"},{"indexed":false,"name":"action","type":"string"}],"name":"DisableServiceActionEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"beneficiary","type":"address"}],"name":"RegisterBeneficiaryEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"beneficiary","type":"address"}],"name":"DeregisterBeneficiaryEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldDeployer","type":"address"},{"indexed":false,"name":"newDeployer","type":"address"}],"name":"SetDeployerEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldOperator","type":"address"},{"indexed":false,"name":"newOperator","type":"address"}],"name":"SetOperatorEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"wallet","type":"address"}],"name":"SelfDestructionDisabledEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"wallet","type":"address"}],"name":"TriggerSelfDestructionEvent","type":"event"}]

60806040523480156200001157600080fd5b5060405160208062005e758339810180604052620000339190810190620000bd565b8080600160a060020a03811615156200004b57600080fd5b600160a060020a0381163014156200006257600080fd5b506000805461010060a860020a031916610100600160a060020a039390931692830217905560018054600160a060020a03191690911790555062093a80600555620000f2565b6000620000b68251620000e6565b9392505050565b600060208284031215620000d057600080fd5b6000620000de8484620000a8565b949350505050565b600160a060020a031690565b615d7380620001026000396000f3006080604052600436106103085763ffffffff60e060020a60003504166301f3241f81146103a257806304fd3130146103c25780630c5d24fa146103f85780630f200f9b14610418578063158a03421461043857806317ba3f9014610458578063191390921461047857806319507d8a1461049a57806319e56365146104ba5780631caf1d6d146104da5780631da826be146104fa5780632145cd201461051a578063240625d81461053c5780632738a112146105515780632b5672e3146105665780632b91e0a9146105865780632f013a00146105a65780633b159b37146105bb5780633b58c501146105db5780633b7bea6c146105fb5780633e59b706146106105780634476d23b14610625578063449fb346146106475780634ddf63f61461066757806350b08c0214610687578063570ca735146106a757806357925b53146106bc578063598b75ad146106dc5780635dc60def146106fc5780635df1b0a3146107115780635f2896511461073157806362ac76211461075157806362f1e6b31461077157806364ba47521461079157806367299f36146107b1578063677ec9d6146107c657806370327ea1146107e65780637953d64d146107fb5780637dac26c71461081b5780637e15dd5c146108305780637f584f511461085057806384cfb40914610865578063892860b2146108855780638995d42f146108a557806389f90a80146108ba5780638a2f30cc146108cf5780638e8cef12146108ef578063962147351461090f578063a3e5175d1461092f578063a4cf2f1c1461094f578063a7654a1f14610964578063ad468df914610972578063af30b31214610992578063b3ab15fb146109a7578063b52d470a146109c7578063b9bb9cd9146109dc578063c45a9a7f146109fc578063cc0dad4a14610a1c578063cdd8b2b214610a3c578063d25a18e514610a5c578063d3b41bd214610a7c578063d5f3948814610a9c578063df9774d814610ab1578063e167d4f614610ad1578063e3d3e9fc14610ae6578063e8ae41e314610b06578063f8297dbe14610b1b578063fdd9ec7d14610b3b575b6103a033600e60009054906101000a9004600160a060020a0316600160a060020a0316635106c2036040518163ffffffff1660e060020a028152600401600060405180830381600087803b15801561035f57600080fd5b505af1158015610373573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261039b9190810190615626565b610b5b565b005b3480156103ae57600080fd5b506103a06103bd366004615092565b610c2d565b3480156103ce57600080fd5b506103e26103dd366004615092565b610d27565b6040516103ef9190615b83565b60405180910390f35b34801561040457600080fd5b506103e26104133660046153f4565b610d43565b34801561042457600080fd5b506103a06104333660046155d4565b610dad565b34801561044457600080fd5b506103a06104533660046151f2565b61102b565b34801561046457600080fd5b506103e2610473366004615092565b611097565b34801561048457600080fd5b5061048d6110cf565b6040516103ef9190615b9f565b3480156104a657600080fd5b506103e26104b5366004615092565b6110de565b3480156104c657600080fd5b506103a06104d53660046155d4565b6110f3565b3480156104e657600080fd5b506103e26104f5366004615092565b6112c2565b34801561050657600080fd5b506103a0610515366004615598565b611458565b34801561052657600080fd5b5061052f6114f7565b6040516103ef9190615b91565b34801561054857600080fd5b506103e26114fe565b34801561055d57600080fd5b506103a061150e565b34801561057257600080fd5b506103a06105813660046153ad565b611574565b34801561059257600080fd5b506103e26105a1366004615092565b6116a4565b3480156105b257600080fd5b506103e26116c2565b3480156105c757600080fd5b506103a06105d636600461543d565b6116cb565b3480156105e757600080fd5b506103a06105f63660046153ad565b611784565b34801561060757600080fd5b5061052f611866565b34801561061c57600080fd5b506103e261186c565b34801561063157600080fd5b5061063a61187c565b6040516103ef9190615724565b34801561065357600080fd5b506103a0610662366004615137565b611890565b34801561067357600080fd5b506103a0610682366004615092565b61194b565b34801561069357600080fd5b506103a06106a2366004615092565b611a2c565b3480156106b357600080fd5b5061063a611ae2565b3480156106c857600080fd5b506103e26106d73660046150b0565b611af1565b3480156106e857600080fd5b506103a06106f7366004615598565b611b44565b34801561070857600080fd5b506103a0611bf3565b34801561071d57600080fd5b506103e261072c3660046153ad565b611c47565b34801561073d57600080fd5b506103a061074c366004615489565b611c98565b34801561075d57600080fd5b506103a061076c3660046153ad565b611ecc565b34801561077d57600080fd5b506103e261078c3660046150b0565b6120a1565b34801561079d57600080fd5b506103a06107ac36600461531c565b6120c1565b3480156107bd57600080fd5b5061052f6125a9565b3480156107d257600080fd5b506103e26107e13660046151af565b6125af565b3480156107f257600080fd5b506103a06125d5565b34801561080757600080fd5b506103e26108163660046150b0565b612638565b34801561082757600080fd5b5061048d612658565b34801561083c57600080fd5b506103a061084b366004615694565b612667565b34801561085c57600080fd5b506103e26126ba565b34801561087157600080fd5b506103e26108803660046151af565b6126c3565b34801561089157600080fd5b506103a06108a0366004615598565b6126e9565b3480156108b157600080fd5b506103a0612798565b3480156108c657600080fd5b506103e26127ec565b3480156108db57600080fd5b506103a06108ea3660046153ad565b6127fc565b3480156108fb57600080fd5b506103a061090a366004615092565b612931565b34801561091b57600080fd5b506103a061092a366004615092565b6129af565b34801561093b57600080fd5b506103a061094a366004615092565b612a6d565b34801561095b57600080fd5b5061048d612c33565b6103a061039b3660046153ad565b34801561097e57600080fd5b506103a061098d366004615290565b612c42565b34801561099e57600080fd5b5061048d612eb5565b3480156109b357600080fd5b506103a06109c2366004615092565b612ec4565b3480156109d357600080fd5b506103a0612f6e565b3480156109e857600080fd5b5061063a6109f7366004615694565b612fc2565b348015610a0857600080fd5b506103a0610a1736600461565a565b612fea565b348015610a2857600080fd5b506103e2610a37366004615092565b612ff8565b348015610a4857600080fd5b506103e2610a57366004615092565b613016565b348015610a6857600080fd5b506103a0610a77366004615598565b613105565b348015610a8857600080fd5b506103a0610a97366004615598565b6131b4565b348015610aa857600080fd5b5061063a61324c565b348015610abd57600080fd5b506103e2610acc366004615092565b613260565b348015610add57600080fd5b506103a0613275565b348015610af257600080fd5b5061052f610b013660046150ea565b613297565b348015610b1257600080fd5b5061048d6132d4565b348015610b2757600080fd5b506103a0610b363660046151f2565b6132e3565b348015610b4757600080fd5b506103a0610b56366004615092565b6133ba565b60405160e060020a63802d10af028152600090732fcb98529d58669e229c453de4b4705bb6b2d4149063802d10af90610b98903490600401615b91565b60206040518083038186803b158015610bb057600080fd5b505af4158015610bc4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610be8919081019061555c565b9050610bfa8383836000806001613433565b600080516020615d1a833981519152838383600080604051610c20959493929190615a66565b60405180910390a1505050565b80600160a060020a0381161515610c4357600080fd5b600160a060020a038116301415610c5957600080fd5b33600160a060020a0383161415610c6f57600080fd5b600160a060020a03821660009081526004602052604090205460ff161515610c9657600080fd5b600160a060020a03821660009081526007602052604090205460ff1615610cbc57600080fd5b600160a060020a038216600090815260096020908152604080832033808552925291829020805460ff1916600117905590517f7406a64e4e8c04b2a11d4cb484eb07cd5762012eafc449900b8d92de008ff27f91610d1b918590615732565b60405180910390a15050565b600160a060020a03166000908152600360205260408120541190565b600080610d4f84613934565b9050610d5b8585611c47565b8015610da45750610d6c85846139fe565b80610da45750600160a060020a038086166000908152600a6020908152604080832085845282528083209387168352929052205460ff165b95945050505050565b60405160e160020a6351750e53028152600090732fcb98529d58669e229c453de4b4705bb6b2d4149063a2ea1ca690610dea908890600401615b91565b60206040518083038186803b158015610e0257600080fd5b505af4158015610e16573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610e3a919081019061553e565b1515610e4557600080fd5b60105460405160e160020a6315c3cbc1028152600160a060020a0390911690632b87978290610e7c9033908890889060040161584b565b602060405180830381600087803b158015610e9657600080fd5b505af1158015610eaa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610ece919081019061553e565b15610ed857600080fd5b610ee3848484613a55565b9050610ef23386868685613af1565b600f546040805160e060020a63bde440cb0281529051929750600160a060020a0390911691639cab96d6913391849163bde440cb9160048083019260209291908290030181600087803b158015610f4857600080fd5b505af1158015610f5c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610f80919081019061555c565b8888886040518663ffffffff1660e060020a028152600401610fa69594939291906158c3565b600060405180830381600087803b158015610fc057600080fd5b505af1158015610fd4573d6000803e3d6000fd5b50505050610fe53386868686613e9a565b7f8e0adde15ffb742f47e7888277a26cea416253fcb63186dc4949fe493e92b77c338686868660405161101c9594939291906159d0565b60405180910390a15050505050565b856110363382611af1565b151561104157600080fd5b61104f878787878787613fee565b7f70a0e35287634a0b4f96e261e0b71d0ae930837dabd35de9dfdad491a4eb6d378787878787604051611086959493929190615955565b60405180910390a150505050505050565b60006110a2826116a4565b80156110c95750600160a060020a0382166000908152600460205260409020600101544210155b92915050565b600e54600160a060020a031681565b60076020526000908152604090205460ff1681565b60405160e160020a6351750e53028152600090732fcb98529d58669e229c453de4b4705bb6b2d4149063a2ea1ca690611130908890600401615b91565b60206040518083038186803b15801561114857600080fd5b505af415801561115c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611180919081019061553e565b151561118b57600080fd5b611196848484613a55565b90506111a53386868685613af1565b600e546040805160e460020a630ded11310281529051929750600160a060020a039091169163b95ae1cd913391849163ded113109160048083019260209291908290030181600087803b1580156111fb57600080fd5b505af115801561120f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611233919081019061555c565b888888876040518763ffffffff1660e060020a02815260040161125b969594939291906158de565b600060405180830381600087803b15801561127557600080fd5b505af1158015611289573d6000803e3d6000fd5b505050507f013970fdfcc3b9ba3657604311c39270a2b090a94528cfdfd09abaddc6e8fb523386868660405161101c9493929190615873565b6000806112cd61427c565b15156112d857600080fd5b82600160a060020a03811615156112ee57600080fd5b600160a060020a03841660009081526003602052604090205415156113165760009250611451565b600160a060020a0384166000908152600360205260409020546002546000199182019350018210156113e55760028054600019810190811061135457fe5b60009182526020909120015460028054600160a060020a03909216918490811061137a57fe5b9060005260206000200160006101000a815481600160a060020a030219169083600160a060020a0316021790555081600101600360006002858154811015156113bf57fe5b6000918252602080832090910154600160a060020a031683528201929092526040019020555b60028054906113f8906000198301614e7d565b50600160a060020a03841660009081526003602052604080822091909155517f33f51cf787669db5b5aff6ba597298fd010748de93aaadef29aae876cef4a8c390611444908690615724565b60405180910390a1600192505b5050919050565b600061146261427c565b151561146d57600080fd5b81600160a060020a038116151561148357600080fd5b6013548390600160a060020a0390811690821681146114f05760138054600160a060020a03878116600160a060020a0319831617909255604051911694507f059245e3752d2ad85a00b28db68f4b944c8269ef3c6bd378055bea9cd1fd07409061101c9086908890615bad565b5050505050565b6011545b90565b60105460a060020a900460ff1681565b3361151761187c565b600160a060020a03161461152a57600080fd5b60005460ff161561153a57600080fd5b7f787a5d936e74f4b564b9153575886059829c78cd9927b1be5e0d976b317ef736336040516115699190615724565b60405180910390a133ff5b600061157e61427c565b151561158957600080fd5b82600160a060020a038116151561159f57600080fd5b600160a060020a0381163014156115b557600080fd5b600160a060020a03841660009081526004602052604090205460ff1615156115dc57600080fd5b6115e583613934565b600160a060020a038516600090815260046020908152604080832084845260020190915290205490925060ff161561161c57600080fd5b600160a060020a0384166000908152600460208181526040808420868552600281018352818520805460ff19166001908117909155938352600301805493840181558452922001839055517fec1b982d69bfc1a6fd8becf191d9a633486c822d3bdedd52303ca81fa6357501906116969086908690615a17565b60405180910390a150505050565b600160a060020a031660009081526004602052604090205460ff1690565b60005460ff1681565b6116d3614292565b15156116de57600080fd5b60135460405160e060020a63ce256213028152600160a060020a039091169063ce2562139061171b90309089908990899089908990600401615bc8565b600060405180830381600087803b15801561173557600080fd5b505af1158015611749573d6000803e3d6000fd5b505050507fe413728068242467b29b57e8f9c4da8b4a4eb0ea41abe0cadddba823c043e088858585858560405161101c959493929190615a37565b600061178e61427c565b151561179957600080fd5b82600160a060020a03811615156117af57600080fd5b600160a060020a0381163014156117c557600080fd5b6117ce83613934565b600160a060020a038516600090815260046020908152604080832084845260020190915290205490925060ff16151561180657600080fd5b600160a060020a038416600090815260046020908152604080832085845260020190915290819020805460ff19169055517f92f4af6130f47c92f3899e06611c8c6ef191fb81207ad2675d134af619d2fe7d906116969086908690615a17565b60055481565b600e5460a060020a900460ff1681565b6000546101009004600160a060020a031690565b61189b838383613a55565b156118b1576118ac843385856142a3565b6118bd565b6118bd843385856144e5565b600160a060020a03841660009081526012602052604090205460ff16151561194557600160a060020a0384166000818152601260205260408120805460ff191660019081179091556011805491820181559091527f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c68018054600160a060020a03191690911790555b50505050565b61195361427c565b151561195e57600080fd5b80600160a060020a038116151561197457600080fd5b600160a060020a03811630141561198a57600080fd5b60065460ff161561199a57600080fd5b33600160a060020a03831614156119b057600080fd5b600160a060020a03821660009081526004602052604090205460ff1615156119d757600080fd5b600160a060020a03821660009081526007602052604090819020805460ff19166001179055517f81c5f2034d05958dd641755044582c0aba29a94070f0a2641bb554325328bf1e90610d1b9033908590615732565b611a3461427c565b1515611a3f57600080fd5b80600160a060020a0381161515611a5557600080fd5b600160a060020a038116301415611a6b57600080fd5b600160a060020a03821660009081526004602052604090205460ff161515611a9257600080fd5b600160a060020a03821660009081526004602052604090819020805460ff19169055517f777645c5437dfbf962f57a281ead25f3d513f2f8e938685bbfc1738e81c9880e90610d1b908490615724565b600154600160a060020a031681565b6000611afc83611097565b8015611b3d5750611b0d83836139fe565b80611b3d5750600160a060020a0380841660009081526009602090815260408083209386168352929052205460ff165b9392505050565b6000611b4e61427c565b1515611b5957600080fd5b81600160a060020a0381161515611b6f57600080fd5b600e548390600160a060020a0390811690821681146114f057600e5460a060020a900460ff1615611b9f57600080fd5b600e8054600160a060020a03878116600160a060020a0319831617909255604051911694507fb2a91d3a71b0c5bc7c083153b3474378e489506ba98bd4ddb1b9056fdc594bb59061101c9086908890615bad565b611bfb61427c565b1515611c0657600080fd5b6010805460a060020a60ff02191660a060020a1790556040517f56ec8900b9c4bf84f4b715a53068ca06961dd49084c07b481931e2c2045346e690600090a1565b600080611c5383613934565b9050611c5e84611097565b8015611c905750600160a060020a038416600090815260046020908152604080832084845260020190915290205460ff165b949350505050565b60405160e160020a6351750e53028152600090732fcb98529d58669e229c453de4b4705bb6b2d4149063a2ea1ca690611cd5908890600401615b91565b60206040518083038186803b158015611ced57600080fd5b505af4158015611d01573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611d25919081019061553e565b1515611d3057600080fd5b611d3a8483614829565b905080600160a060020a031681600160a060020a031663ad502f576040518163ffffffff1660e060020a028152600401602060405180830381600087803b158015611d8457600080fd5b505af1158015611d98573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611dbc919081019061557a565b6040805160e060020a9283900463ffffffff8116909302815233600482015230602482015260448101899052600160a060020a038816606482015260848101879052905160a48083019260009291908290030181865af4925050501515611e2257600080fd5b611ea5878787878786600160a060020a031663a89c2e5a6040518163ffffffff1660e060020a028152600401602060405180830381600087803b158015611e6857600080fd5b505af1158015611e7c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611ea0919081019061553e565b613433565b600080516020615d1a83398151915287878787878760405161108696959493929190615ac0565b600082600160a060020a0381161515611ee457600080fd5b600160a060020a038116301415611efa57600080fd5b33600160a060020a0385161415611f1057600080fd5b611f1983613934565b600160a060020a03851660009081526004602052604090205490925060ff168015611f6a5750600160a060020a038416600090815260046020908152604080832085845260020190915290205460ff165b1515611f7557600080fd5b600160a060020a03841660009081526007602052604090205460ff1615611f9b57600080fd5b600160a060020a038416600081815260096020908152604080832033808552908352818420805460ff19908116909155858552600a84528285208886528452828520828652845282852080549091166001179055938352600b82528083208684528252808320938352929052205460ff16151561206e57600160a060020a0384166000818152600b60209081526040808320868452825280832033808552908352818420805460ff19166001908117909155948452600c8352818420908452825282208054938401815582529020018290555b7fc007803323f3ca89cb3f8af1a12f354dc108e9dd4988c312b3f7a878914987ce3385856040516116969392919061581e565b600860209081526000928352604080842090915290825290205460ff1681565b6000866120ce3382611af1565b15156120d957600080fd5b87600160a060020a03811615156120ef57600080fd5b60405160e060020a636b84d931028152732fcb98529d58669e229c453de4b4705bb6b2d41490636b84d93190612129908b90600401615b91565b60206040518083038186803b15801561214157600080fd5b505af4158015612155573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612179919081019061553e565b151561218457600080fd5b61218f878787613a55565b1561239457600e546040805160e460020a630ded11310281529051600160a060020a039092169163a246138c918c91849163ded113109160048083019260209291908290030181600087803b1580156121e757600080fd5b505af11580156121fb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061221f919081019061555c565b8a8a896040518663ffffffff1660e060020a02815260040161224595949392919061588e565b6040805180830381600087803b15801561225e57600080fd5b505af1158015612272573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061229691908101906155f6565b50600e546040805160e260020a631194bb1d0281529051929550600160a060020a039091169163d062ec1d918c918491634652ec749160048083019260209291908290030181600087803b1580156122ed57600080fd5b505af1158015612301573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612325919081019061555c565b6123358c8863ffffffff6148b116565b8b8b60016040518763ffffffff1660e060020a02815260040161235d969594939291906158de565b600060405180830381600087803b15801561237757600080fd5b505af115801561238b573d6000803e3d6000fd5b50505050612561565b600e546040805160e460020a630ded11310281529051600160a060020a0390921691637d7a23aa918c91849163ded113109160048083019260209291908290030181600087803b1580156123e757600080fd5b505af11580156123fb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061241f919081019061555c565b8b8b8b60006040518763ffffffff1660e060020a028152600401612448969594939291906158de565b600060405180830381600087803b15801561246257600080fd5b505af1158015612476573d6000803e3d6000fd5b5050600e546040805160e260020a631194bb1d0281529051600160a060020a03909216935063b95ae1cd92508c918491634652ec749160048083019260209291908290030181600087803b1580156124cd57600080fd5b505af11580156124e1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612505919081019061555c565b8b8b8b60006040518763ffffffff1660e060020a02815260040161252e969594939291906158de565b600060405180830381600087803b15801561254857600080fd5b505af115801561255c573d6000803e3d6000fd5b505050505b7f6ded3dfe9bc210e5b60b695bac6bd623844003b866c14264d44e91b8352a4fad898989896040516125969493929190615873565b60405180910390a1505050505050505050565b60025490565b600b60209081526000938452604080852082529284528284209052825290205460ff1681565b336125de61187c565b600160a060020a0316146125f157600080fd5b6000805460ff191660011790556040517fd5a2a04a775c741c2ca0dc46ea7ce4835190e1aaf1ca018def0e82568ec336169061262e903390615724565b60405180910390a1565b600960209081526000928352604080842090915290825290205460ff1681565b601354600160a060020a031681565b61266f61427c565b151561267a57600080fd5b60058190556040517f4f1d324a1cbffc352fb64f1d73d95e4b200d92f99ef39bf2d7f5b41f946909d6906126af908390615b91565b60405180910390a150565b60065460ff1681565b600a60209081526000938452604080852082529284528284209052825290205460ff1681565b60006126f361427c565b15156126fe57600080fd5b81600160a060020a038116151561271457600080fd5b6010548390600160a060020a0390811690821681146114f05760105460a060020a900460ff161561274457600080fd5b60108054600160a060020a03878116600160a060020a0319831617909255604051911694507fa44d361e26327b72a7ccbeae801b3c5cd7677ea4fa74168b289e273c46bfecfc9061101c9086908890615bad565b6127a061427c565b15156127ab57600080fd5b600f805460a060020a60ff02191660a060020a1790556040517fd7d1b6dcaeec9afa87c8c53d9da393505dee41c9ace60ab22f3b23db19ac282c90600090a1565b600f5460a060020a900460ff1681565b600082600160a060020a038116151561281457600080fd5b600160a060020a03811630141561282a57600080fd5b33600160a060020a038516141561284057600080fd5b61284983613934565b600160a060020a03851660009081526004602052604090205490925060ff16801561289a5750600160a060020a038416600090815260046020908152604080832085845260020190915290205460ff165b15156128a557600080fd5b600160a060020a03841660009081526007602052604090205460ff16156128cb57600080fd5b600160a060020a0384166000908152600a60209081526040808320858452825280832033808552925291829020805460ff1916905590517f6fa0cdb0b5c22004ccfcaee11eed450e0184dda8edd417ae2410f0a883fdb74091611696918790879061581e565b61293961427c565b151561294457600080fd5b80600160a060020a038116151561295a57600080fd5b600160a060020a03811630141561297057600080fd5b61297c826005546148ed565b7f7f3a8349917003ed377f6e9ae1608b92edd893903f8983bd274b1f8373dd3b1182600554604051610d1b929190615b68565b60006129b961427c565b15156129c457600080fd5b81600160a060020a03811615156129da57600080fd5b600160a060020a0381163014156129f057600080fd5b600054600160a060020a038481166101009092041614612a685760008054600160a060020a0385811661010090810261010060a860020a0319841617909355604051929091041692507f977e5fa58e458501775e0008d275006294c5249e3c08d1d0e3a9f3acad14f6e490610c209084908690615732565b505050565b600081600160a060020a0381161515612a8557600080fd5b600160a060020a038116301415612a9b57600080fd5b33600160a060020a0384161415612ab157600080fd5b600160a060020a03831660009081526004602052604090205460ff161515612ad857600080fd5b600160a060020a03831660009081526007602052604090205460ff1615612b2c57600160a060020a03831660009081526008602090815260408083203384529091529020805460ff19166001179055612c02565b600160a060020a03831660009081526009602090815260408083203384529091528120805460ff1916905591505b600160a060020a0383166000908152600c60209081526040808320338452909152902054821015612c0257600160a060020a0383166000908152600a60209081526040808320600c835281842033855290925282208054600193919086908110612bc057fe5b600091825260208083209190910154835282810193909352604091820181203382529092529020805460ff191691151591909117905560019190910190612b5a565b7fe25d4094f2181ddef4410dcc1539a7488abd881a90c56078314b9f70affc5f7e3384604051610c20929190615732565b600f54600160a060020a031681565b600085612c4f3382611af1565b1515612c5a57600080fd5b60405160e160020a6351750e53028152732fcb98529d58669e229c453de4b4705bb6b2d4149063a2ea1ca690612c94908990600401615b91565b60206040518083038186803b158015612cac57600080fd5b505af4158015612cc0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612ce4919081019061553e565b1515612cef57600080fd5b612cfa858585613a55565b9150612d9887600e60009054906101000a9004600160a060020a0316600160a060020a031663e47da3526040518163ffffffff1660e060020a028152600401600060405180830381600087803b158015612d5357600080fd5b505af1158015612d67573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612d8f91908101906154d6565b8888888761493f565b600e546040805160e260020a631e758dcf0281529051929850600160a060020a039091169163b95ae1cd918a9184916379d6373c9160048083019260209291908290030181600087803b158015612dee57600080fd5b505af1158015612e02573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612e26919081019061555c565b898989886040518763ffffffff1660e060020a028152600401612e4e969594939291906158de565b600060405180830381600087803b158015612e6857600080fd5b505af1158015612e7c573d6000803e3d6000fd5b505050507f573abd56f40c687bfaaabb8d68c68c84cc343b51fe661ff2080e2a6977b3c2b4878787876040516110869493929190615873565b600d54600160a060020a031681565b6000612ece614292565b1515612ed957600080fd5b81600160a060020a0381161515612eef57600080fd5b600160a060020a038116301415612f0557600080fd5b600154600160a060020a03848116911614612a685760018054600160a060020a03858116600160a060020a0319831617909255604051911692507f9f611b789425d0d5b90b920f1b2852907dd865c80074a30b1629aaa041d1812c90610c209084908690615732565b612f7661427c565b1515612f8157600080fd5b600e805460a060020a60ff02191660a060020a1790556040517f9c567b65fe8caab5ec7bc979a498a1322c1d4baf01a30727cbca137187560ea290600090a1565b6011805482908110612fd057fe5b600091825260209091200154600160a060020a0316905081565b6114f0338686868686611c98565b600160a060020a031660009081526012602052604090205460ff1690565b600061302061427c565b151561302b57600080fd5b81600160a060020a038116151561304157600080fd5b600160a060020a038316600090815260036020526040812054111561306957600091506130ff565b600280546001810182557f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace018054600160a060020a031916600160a060020a03861690811790915590546000918252600360205260409182902055517fdd62ff6d9c23793871e7d4796bde6c5dfb2bf8bcbd4186411cee3eb9f6189521906130f2908590615724565b60405180910390a1600191505b50919050565b600061310f61427c565b151561311a57600080fd5b81600160a060020a038116151561313057600080fd5b600f548390600160a060020a0390811690821681146114f057600f5460a060020a900460ff161561316057600080fd5b600f8054600160a060020a03878116600160a060020a0319831617909255604051911694507fa935cd13e8b6831b708081e1b4745e4a9574dc3aae730671c18d113a5497c90b9061101c9086908890615bad565b60006131be61427c565b15156131c957600080fd5b81600160a060020a03811615156131df57600080fd5b600d548390600160a060020a0390811690821681146114f057600d8054600160a060020a03878116600160a060020a0319831617909255604051911694507fee9ef6d53e152e48b1ebc1c23943fb2b96e847465ed779a0e2121a388ea83d2f9061101c9086908890615bad565b6000546101009004600160a060020a031681565b60126020526000908152604090205460ff1681565b61327d61427c565b151561328857600080fd5b6006805460ff19166001179055565b600c602052826000526040600020602052816000526040600020818154811015156132be57fe5b9060005260206000200160009250925050505481565b601054600160a060020a031681565b6000866132f03382611af1565b15156132fb57600080fd5b613306858585613a55565b915061335f88600e60009054906101000a9004600160a060020a0316600160a060020a031663e47da3526040518163ffffffff1660e060020a028152600401600060405180830381600087803b158015612d5357600080fd5b955061336f888888888888613fee565b7f4c50864967876f6fcf5151a5e907d94d9c7b3d7780ca1c7f576680a2126ba39b8888888888886040516133a896959493929190615970565b60405180910390a15050505050505050565b6133c261427c565b15156133cd57600080fd5b80600160a060020a03811615156133e357600080fd5b600160a060020a0381163014156133f957600080fd5b6134048260006148ed565b7fb619d545cb511bd5f02907a1eac4f4cb8dace7b1846852fb0bf5e553937481c182604051610d1b9190615724565b600085516000106134ce57600e60009054906101000a9004600160a060020a0316600160a060020a031663ded113106040518163ffffffff1660e060020a028152600401602060405180830381600087803b15801561349157600080fd5b505af11580156134a5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506134c9919081019061555c565b613592565b856040516020018082805190602001908083835b602083106135015780518252601f1990920191602091820191016134e2565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040516020818303038152906040526040518082805190602001908083835b602083106135645780518252601f199092019160209182019101613545565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390205b600e546040805160e260020a631e758dcf02815290519293508392600160a060020a03909216916379d6373c916004808201926020929091908290030181600087803b1580156135e157600080fd5b505af11580156135f5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613619919081019061555c565b141561370957600e546040805160e260020a631e758dcf0281529051600160a060020a039092169163b95ae1cd918a9184916379d6373c9160048083019260209291908290030181600087803b15801561367257600080fd5b505af1158015613686573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506136aa919081019061555c565b888888886040518763ffffffff1660e060020a0281526004016136d2969594939291906158de565b600060405180830381600087803b1580156136ec57600080fd5b505af1158015613700573d6000803e3d6000fd5b5050505061392b565b600e546040805160e460020a630ded113102815290518392600160a060020a03169163ded113109160048083019260209291908290030181600087803b15801561375257600080fd5b505af1158015613766573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061378a919081019061555c565b141561392657600e546040805160e460020a630ded11310281529051600160a060020a039092169163b95ae1cd918a91849163ded113109160048083019260209291908290030181600087803b1580156137e357600080fd5b505af11580156137f7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061381b919081019061555c565b888888886040518763ffffffff1660e060020a028152600401613843969594939291906158de565b600060405180830381600087803b15801561385d57600080fd5b505af1158015613871573d6000803e3d6000fd5b5050600f546040805160e060020a63313004a90281529051600160a060020a039092169350639cab96d692508a91849163313004a99160048083019260209291908290030181600087803b1580156138c857600080fd5b505af11580156138dc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613900919081019061555c565b8888886040518663ffffffff1660e060020a0281526004016136d29594939291906158c3565b600080fd5b50505050505050565b6000816040516020018082805190602001908083835b602083106139695780518252601f19909201916020918201910161394a565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040516020818303038152906040526040518082805190602001908083835b602083106139cc5780518252601f1990920191602091820191016139ad565b5181516020939093036101000a6000190180199091169216919091179052604051920182900390912095945050505050565b600160a060020a03821660009081526007602052604081205460ff16613a25576000611b3d565b50600160a060020a03918216600090815260086020908152604080832093909416825291909152205460ff161590565b6000600160a060020a038416158015613a6c575082155b80611c905750613a7c8483614829565b600160a060020a031663a89c2e5a6040518163ffffffff1660e060020a028152600401602060405180830381600087803b158015613ab957600080fd5b505af1158015613acd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611c90919081019061553e565b60008115613c9d57600e546040805160e260020a631e758dcf0281529051732fcb98529d58669e229c453de4b4705bb6b2d4149263755875b2928992600160a060020a03909216916371f4ec51918c9184916379d6373c9160048083019260209291908290030181600087803b158015613b6a57600080fd5b505af1158015613b7e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613ba2919081019061555c565b8a8a6040518563ffffffff1660e060020a028152600401613bc69493929190615873565b602060405180830381600087803b158015613be057600080fd5b505af1158015613bf4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613c18919081019061555c565b6040518363ffffffff1660e060020a028152600401613c38929190615c04565b60206040518083038186803b158015613c5057600080fd5b505af4158015613c64573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613c88919081019061555c565b94506000851215613c9857600080fd5b613dab565b600e546040805160e260020a631e758dcf0281529051600160a060020a0390921691635962480691899184916379d6373c9160048083019260209291908290030181600087803b158015613cf057600080fd5b505af1158015613d04573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613d28919081019061555c565b8888886040518663ffffffff1660e060020a028152600401613d4e9594939291906158c3565b602060405180830381600087803b158015613d6857600080fd5b505af1158015613d7c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613da0919081019061553e565b1515613dab57600080fd5b600e546040805160e260020a631e758dcf0281529051600160a060020a0390921691637d7a23aa91899184916379d6373c9160048083019260209291908290030181600087803b158015613dfe57600080fd5b505af1158015613e12573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613e36919081019061555c565b888888886040518763ffffffff1660e060020a028152600401613e5e969594939291906158de565b600060405180830381600087803b158015613e7857600080fd5b505af1158015613e8c573d6000803e3d6000fd5b509698975050505050505050565b6000600160a060020a038416158015613eb1575082155b15613ef257604051600160a060020a0387169086156108fc029087906000818181858888f19350505050158015613eec573d6000803e3d6000fd5b50613fe6565b613efc8483614829565b905080600160a060020a031681600160a060020a0316637228941f6040518163ffffffff1660e060020a028152600401602060405180830381600087803b158015613f4657600080fd5b505af1158015613f5a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613f7e919081019061557a565b6040805160e060020a9283900463ffffffff81169093028152306004820152600160a060020a038a81166024830152604482018a90528816606482015260848101879052905160a48083019260009291908290030181865af4925050501515613fe657600080fd5b505050505050565b60405160e160020a6351750e53028152600090732fcb98529d58669e229c453de4b4705bb6b2d4149063a2ea1ca69061402b908890600401615b91565b60206040518083038186803b15801561404357600080fd5b505af4158015614057573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061407b919081019061553e565b151561408657600080fd5b61408f86610d27565b151561409a57600080fd5b600160a060020a0384161580156140af575082155b1561411c5760405160e060020a63a7654a1f028152600160a060020a0387169063a7654a1f9087906140e5908b90600401615aef565b6000604051808303818588803b1580156140fe57600080fd5b505af1158015614112573d6000803e3d6000fd5b505050505061392b565b6141268483614829565b905080600160a060020a031681600160a060020a0316631711a8946040518163ffffffff1660e060020a028152600401602060405180830381600087803b15801561417057600080fd5b505af1158015614184573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506141a8919081019061557a565b6040805160e060020a9283900463ffffffff81169093028152600160a060020a038a81166004830152602482018a90528816604482015260648101879052905160848083019260009291908290030181865af492505050151561420a57600080fd5b60405160e060020a635f289651028152600160a060020a03871690635f28965190614241908a908990899089908990600401615b0e565b600060405180830381600087803b15801561425b57600080fd5b505af115801561426f573d6000803e3d6000fd5b5050505050505050505050565b6000546101009004600160a060020a0316331490565b600154600160a060020a0316331490565b60105460405160e060020a630d044085028152600091600160a060020a031690630d044085906142dd90889088908890889060040161574d565b602060405180830381600087803b1580156142f757600080fd5b505af115801561430b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061432f919081019061555c565b90506000811361433e57600080fd5b6143d985600e60009054906101000a9004600160a060020a0316600160a060020a031663ec3aa67e6040518163ffffffff1660e060020a028152600401600060405180830381600087803b15801561439557600080fd5b505af11580156143a9573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526143d191908101906154d6565b838686614972565b50600e546040805160e260020a631e758dcf0281529051600160a060020a039092169163b95ae1cd91879184916379d6373c9160048083019260209291908290030181600087803b15801561442d57600080fd5b505af1158015614441573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250614465919081019061555c565b84878760016040518763ffffffff1660e060020a02815260040161448e969594939291906158de565b600060405180830381600087803b1580156144a857600080fd5b505af11580156144bc573d6000803e3d6000fd5b50505050600080516020615cfa833981519152858583868660405161101c9594939291906157dc565b60105460405160e060020a639f45b2ab0281526000916060918391600160a060020a031690639f45b2ab90614524908a908a908a908a9060040161574d565b602060405180830381600087803b15801561453e57600080fd5b505af1158015614552573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250614576919081019061555c565b92506000831161458557600080fd5b60105460405160e160020a6374b72475028152600160a060020a039091169063e96e48ea906145c7908a908a908a908a906000906000198c0190600401615782565b600060405180830381600087803b1580156145e157600080fd5b505af11580156145f5573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261461d919081019061550a565b9150600090505b815181101561392b576146df87600e60009054906101000a9004600160a060020a0316600160a060020a031663ec3aa67e6040518163ffffffff1660e060020a028152600401600060405180830381600087803b15801561468457600080fd5b505af1158015614698573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526146c091908101906154d6565b84848151811015156146ce57fe5b906020019060200201518888614ced565b50600e546040805160e260020a631e758dcf0281529051600160a060020a039092169163b95ae1cd91899184916379d6373c9160048083019260209291908290030181600087803b15801561473357600080fd5b505af1158015614747573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061476b919081019061555c565b858581518110151561477957fe5b90602001906020020151898960006040518763ffffffff1660e060020a0281526004016147ab969594939291906158de565b600060405180830381600087803b1580156147c557600080fd5b505af11580156147d9573d6000803e3d6000fd5b50505050600080516020615cfa833981519152878784848151811015156147fc57fe5b9060200190602002015188886040516148199594939291906157dc565b60405180910390a1600101614624565b600d5460405160e460020a630c8a483b028152600091600160a060020a03169063c8a483b09061485f9086908690600401615a17565b602060405180830381600087803b15801561487957600080fd5b505af115801561488d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611b3d91908101906155b6565b60008082121580156148c557508282840313155b806148dc57506000821280156148dc575082828403135b15156148e757600080fd5b50900390565b600160a060020a03821660009081526004602052604090205460ff16151561493b57600160a060020a0382166000908152600460205260409020805460ff1916600190811782554283019101555b5050565b6000811561495b576149548787878787614972565b9050614968565b6149548787878787614ced565b9695505050505050565b6000808080808781131561498557600080fd5b60009250600093505b8851841015614a5857600e548951614a4b91600160a060020a0316906371f4ec51908d908d90899081106149be57fe5b906020019060200201518b8b6040518563ffffffff1660e060020a0281526004016149ec9493929190615873565b602060405180830381600087803b158015614a0657600080fd5b505af1158015614a1a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250614a3e919081019061555c565b849063ffffffff614e4816565b600190940193925061498e565b60405160e160020a633aac3ad9028152732fcb98529d58669e229c453de4b4705bb6b2d4149063755875b290614a94908b908790600401615c04565b60206040518083038186803b158015614aac57600080fd5b505af4158015614ac0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250614ae4919081019061555c565b9750879150600093505b8851841015614cdf57600e548951600160a060020a03909116906371f4ec51908c908c9088908110614b1c57fe5b906020019060200201518a8a6040518563ffffffff1660e060020a028152600401614b4a9493929190615873565b602060405180830381600087803b158015614b6457600080fd5b505af1158015614b78573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250614b9c919081019061555c565b9050818112614c3457600e548951600160a060020a0390911690637d7a23aa908c908c9088908110614bca57fe5b90602001906020020151858b8b60016040518763ffffffff1660e060020a028152600401614bfd969594939291906158de565b600060405180830381600087803b158015614c1757600080fd5b505af1158015614c2b573d6000803e3d6000fd5b50505050614cdf565b600e548951600160a060020a039091169063d062ec1d908c908c9088908110614c5957fe5b9060200190602002015160008b8b60016040518763ffffffff1660e060020a028152600401614c8d9695949392919061592d565b600060405180830381600087803b158015614ca757600080fd5b505af1158015614cbb573d6000803e3d6000fd5b50505050614cd281836148b190919063ffffffff16565b9150600190930192614aee565b509598975050505050505050565b6000805b8551811015614e3d57600e548651600160a060020a03909116906359624806908990899085908110614d1f57fe5b906020019060200201518888886040518663ffffffff1660e060020a028152600401614d4f9594939291906158c3565b602060405180830381600087803b158015614d6957600080fd5b505af1158015614d7d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250614da1919081019061553e565b15614e3557600e548651600160a060020a0390911690637d7a23aa908990899085908110614dcb57fe5b9060200190602002015188888860006040518763ffffffff1660e060020a028152600401614dfe969594939291906158de565b600060405180830381600087803b158015614e1857600080fd5b505af1158015614e2c573d6000803e3d6000fd5b50505050614e3d565b600101614cf1565b509295945050505050565b6000828201818312801590614e5d5750838112155b80614e725750600083128015614e7257508381125b1515611b3d57600080fd5b815481835581811115612a6857600083815260209020612a689181019083016114fb91905b80821115614eb65760008155600101614ea2565b5090565b6000611b3d8235615c83565b6000601f82018313614ed757600080fd5b8151614eea614ee582615c38565b615c12565b91508181835260208401935060208101905083856020840282011115614f0f57600080fd5b60005b83811015614f3b5781614f258882614fcd565b8452506020928301929190910190600101614f12565b5050505092915050565b6000601f82018313614f5657600080fd5b8151614f64614ee582615c38565b91508181835260208401935060208101905083856020840282011115614f8957600080fd5b60005b83811015614f3b5781614f9f8882614fcd565b8452506020928301929190910190600101614f8c565b6000611b3d8251615c8f565b6000611b3d82356114fb565b6000611b3d82516114fb565b6000611b3d8251615c94565b6000611b3d8235615ca1565b6000611b3d8251615ca1565b6000601f8201831361500e57600080fd5b815161501c614ee582615c58565b9150808252602083016020830185838301111561503857600080fd5b615043838284615cc3565b50505092915050565b6000601f8201831361505d57600080fd5b813561506b614ee582615c58565b9150808252602083016020830185838301111561508757600080fd5b615043838284615cb7565b6000602082840312156150a457600080fd5b6000611c908484614eba565b600080604083850312156150c357600080fd5b60006150cf8585614eba565b92505060206150e085828601614eba565b9150509250929050565b6000806000606084860312156150ff57600080fd5b600061510b8686614eba565b935050602061511c86828701614eba565b925050604061512d86828701614fc1565b9150509250925092565b6000806000806080858703121561514d57600080fd5b60006151598787614eba565b945050602061516a87828801614eba565b935050604061517b87828801614fc1565b92505060608501356001604060020a0381111561519757600080fd5b6151a38782880161504c565b91505092959194509250565b6000806000606084860312156151c457600080fd5b60006151d08686614eba565b93505060206151e186828701614fc1565b925050604061512d86828701614eba565b60008060008060008060c0878903121561520b57600080fd5b60006152178989614eba565b965050602061522889828a01614fe5565b955050604061523989828a01614fc1565b945050606061524a89828a01614eba565b935050608061525b89828a01614fc1565b92505060a08701356001604060020a0381111561527757600080fd5b61528389828a0161504c565b9150509295509295509295565b600080600080600060a086880312156152a857600080fd5b60006152b48888614eba565b95505060206152c588828901614fc1565b94505060406152d688828901614eba565b93505060606152e788828901614fc1565b92505060808601356001604060020a0381111561530357600080fd5b61530f8882890161504c565b9150509295509295909350565b60008060008060008060c0878903121561533557600080fd5b60006153418989614eba565b965050602061535289828a01614fc1565b955050604061536389828a01614eba565b945050606061537489828a01614fc1565b93505060808701356001604060020a0381111561539057600080fd5b61539c89828a0161504c565b92505060a061528389828a01614fc1565b600080604083850312156153c057600080fd5b60006153cc8585614eba565b92505060208301356001604060020a038111156153e857600080fd5b6150e08582860161504c565b60008060006060848603121561540957600080fd5b60006154158686614eba565b93505060208401356001604060020a0381111561543157600080fd5b6151e18682870161504c565b600080600080600060a0868803121561545557600080fd5b60006154618888614eba565b95505060208601356001604060020a0381111561547d57600080fd5b6152c58882890161504c565b60008060008060008060c087890312156154a257600080fd5b60006154ae8989614eba565b96505060208701356001604060020a038111156154ca57600080fd5b61522889828a0161504c565b6000602082840312156154e857600080fd5b81516001604060020a038111156154fe57600080fd5b611c9084828501614ec6565b60006020828403121561551c57600080fd5b81516001604060020a0381111561553257600080fd5b611c9084828501614f45565b60006020828403121561555057600080fd5b6000611c908484614fb5565b60006020828403121561556e57600080fd5b6000611c908484614fcd565b60006020828403121561558c57600080fd5b6000611c908484614fd9565b6000602082840312156155aa57600080fd5b6000611c908484614fe5565b6000602082840312156155c857600080fd5b6000611c908484614ff1565b600080600080608085870312156155ea57600080fd5b60006151598787614fc1565b6000806040838503121561560957600080fd5b60006156158585614fcd565b92505060206150e085828601614fcd565b60006020828403121561563857600080fd5b81516001604060020a0381111561564e57600080fd5b611c9084828501614ffd565b600080600080600060a0868803121561567257600080fd5b85356001604060020a0381111561568857600080fd5b6152b48882890161504c565b6000602082840312156156a657600080fd5b6000611c908484614fc1565b6156bb81615c83565b82525050565b6156bb81615c8f565b6156bb816114fb565b6156bb81615ca1565b6156bb81615cac565b60006156f082615c7f565b808452615704816020860160208601615cc3565b61570d81615cef565b9093016020019392505050565b6000815260200190565b602081016110c982846156b2565b6040810161574082856156b2565b611b3d60208301846156b2565b6080810161575b82876156b2565b61576860208301866156b2565b61577560408301856156b2565b610da460608301846156ca565b60c0810161579082896156b2565b61579d60208301886156b2565b6157aa60408301876156b2565b6157b760608301866156ca565b6157c460808301856156dc565b6157d160a08301846156ca565b979650505050505050565b60a081016157ea82886156b2565b6157f760208301876156b2565b61580460408301866156ca565b61581160608301856156b2565b61496860808301846156ca565b6060810161582c82866156b2565b61583960208301856156b2565b8181036040830152610da481846156e5565b6060810161585982866156b2565b61586660208301856156b2565b611c9060408301846156ca565b6080810161588182876156b2565b61576860208301866156ca565b60a0810161589c82886156b2565b6158a960208301876156ca565b6158b660408301866156b2565b61581160608301856156ca565b60a081016158d182886156b2565b6157f760208301876156ca565b60c081016158ec82896156b2565b6158f960208301886156ca565b61590660408301876156ca565b61591360608301866156b2565b61592060808301856156ca565b6157d160a08301846156c1565b60c0810161593b82896156b2565b61594860208301886156ca565b61590660408301876156dc565b60a0810161596382886156b2565b6157f760208301876156d3565b60c0810161597e82896156b2565b61598b60208301886156d3565b61599860408301876156ca565b6159a560608301866156b2565b6159b260808301856156ca565b81810360a08301526159c481846156e5565b98975050505050505050565b60a081016159de82886156b2565b6159eb60208301876156ca565b6159f860408301866156b2565b615a0560608301856156ca565b81810360808301526157d181846156e5565b60408101615a2582856156b2565b8181036020830152611c9081846156e5565b60a08101615a4582886156b2565b8181036020830152615a5781876156e5565b90506159f860408301866156b2565b60c08101615a7482886156b2565b8181036020830152615a8681876156e5565b9050615a9560408301866156ca565b615aa260608301856156b2565b615aaf60808301846156dc565b81810360a08301526157d18161571a565b60c08101615ace82896156b2565b8181036020830152615ae081886156e5565b905061599860408301876156ca565b60408101615afd82846156b2565b8181036020830152611b3d8161571a565b60c08101615b1c82886156b2565b8181036020830152615b2d8161571a565b9050615b3c60408301876156ca565b615b4960608301866156b2565b615b5660808301856156ca565b81810360a08301526157d181846156e5565b60408101615b7682856156b2565b611b3d60208301846156ca565b602081016110c982846156c1565b602081016110c982846156ca565b602081016110c982846156d3565b60408101615bbb82856156d3565b611b3d60208301846156d3565b60c08101615bd682896156d3565b615be360208301886156b2565b8181036040830152615bf581876156e5565b90506159a560608301866156b2565b60408101615b7682856156ca565b6040518181016001604060020a0381118282101715615c3057600080fd5b604052919050565b60006001604060020a03821115615c4e57600080fd5b5060209081020190565b60006001604060020a03821115615c6e57600080fd5b506020601f91909101601f19160190565b5190565b600160a060020a031690565b151590565b600160e060020a03191690565b60006110c982615c83565b60006110c9826114fb565b82818337506000910152565b60005b83811015615cde578181015183820152602001615cc6565b838111156119455750506000910152565b601f01601f19169056007c750f44230ed3db46106f40b3f7c4707c7814035e69e09d43699ccbe46b1061b1054d4e81cc81e966ba33c7ce386432dcf8d4d78798e7a1f7f0b4aaef360330a265627a7a72305820ce6f378288254722acd2a3e964384447d0f597ec432ccc1a5f7bf051aee000666c6578706572696d656e74616cf50037000000000000000000000000f05179bac3d1fbef58a2fcd7ad0f769840027cc6

Deployed Bytecode

0x6080604052600436106103085763ffffffff60e060020a60003504166301f3241f81146103a257806304fd3130146103c25780630c5d24fa146103f85780630f200f9b14610418578063158a03421461043857806317ba3f9014610458578063191390921461047857806319507d8a1461049a57806319e56365146104ba5780631caf1d6d146104da5780631da826be146104fa5780632145cd201461051a578063240625d81461053c5780632738a112146105515780632b5672e3146105665780632b91e0a9146105865780632f013a00146105a65780633b159b37146105bb5780633b58c501146105db5780633b7bea6c146105fb5780633e59b706146106105780634476d23b14610625578063449fb346146106475780634ddf63f61461066757806350b08c0214610687578063570ca735146106a757806357925b53146106bc578063598b75ad146106dc5780635dc60def146106fc5780635df1b0a3146107115780635f2896511461073157806362ac76211461075157806362f1e6b31461077157806364ba47521461079157806367299f36146107b1578063677ec9d6146107c657806370327ea1146107e65780637953d64d146107fb5780637dac26c71461081b5780637e15dd5c146108305780637f584f511461085057806384cfb40914610865578063892860b2146108855780638995d42f146108a557806389f90a80146108ba5780638a2f30cc146108cf5780638e8cef12146108ef578063962147351461090f578063a3e5175d1461092f578063a4cf2f1c1461094f578063a7654a1f14610964578063ad468df914610972578063af30b31214610992578063b3ab15fb146109a7578063b52d470a146109c7578063b9bb9cd9146109dc578063c45a9a7f146109fc578063cc0dad4a14610a1c578063cdd8b2b214610a3c578063d25a18e514610a5c578063d3b41bd214610a7c578063d5f3948814610a9c578063df9774d814610ab1578063e167d4f614610ad1578063e3d3e9fc14610ae6578063e8ae41e314610b06578063f8297dbe14610b1b578063fdd9ec7d14610b3b575b6103a033600e60009054906101000a9004600160a060020a0316600160a060020a0316635106c2036040518163ffffffff1660e060020a028152600401600060405180830381600087803b15801561035f57600080fd5b505af1158015610373573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261039b9190810190615626565b610b5b565b005b3480156103ae57600080fd5b506103a06103bd366004615092565b610c2d565b3480156103ce57600080fd5b506103e26103dd366004615092565b610d27565b6040516103ef9190615b83565b60405180910390f35b34801561040457600080fd5b506103e26104133660046153f4565b610d43565b34801561042457600080fd5b506103a06104333660046155d4565b610dad565b34801561044457600080fd5b506103a06104533660046151f2565b61102b565b34801561046457600080fd5b506103e2610473366004615092565b611097565b34801561048457600080fd5b5061048d6110cf565b6040516103ef9190615b9f565b3480156104a657600080fd5b506103e26104b5366004615092565b6110de565b3480156104c657600080fd5b506103a06104d53660046155d4565b6110f3565b3480156104e657600080fd5b506103e26104f5366004615092565b6112c2565b34801561050657600080fd5b506103a0610515366004615598565b611458565b34801561052657600080fd5b5061052f6114f7565b6040516103ef9190615b91565b34801561054857600080fd5b506103e26114fe565b34801561055d57600080fd5b506103a061150e565b34801561057257600080fd5b506103a06105813660046153ad565b611574565b34801561059257600080fd5b506103e26105a1366004615092565b6116a4565b3480156105b257600080fd5b506103e26116c2565b3480156105c757600080fd5b506103a06105d636600461543d565b6116cb565b3480156105e757600080fd5b506103a06105f63660046153ad565b611784565b34801561060757600080fd5b5061052f611866565b34801561061c57600080fd5b506103e261186c565b34801561063157600080fd5b5061063a61187c565b6040516103ef9190615724565b34801561065357600080fd5b506103a0610662366004615137565b611890565b34801561067357600080fd5b506103a0610682366004615092565b61194b565b34801561069357600080fd5b506103a06106a2366004615092565b611a2c565b3480156106b357600080fd5b5061063a611ae2565b3480156106c857600080fd5b506103e26106d73660046150b0565b611af1565b3480156106e857600080fd5b506103a06106f7366004615598565b611b44565b34801561070857600080fd5b506103a0611bf3565b34801561071d57600080fd5b506103e261072c3660046153ad565b611c47565b34801561073d57600080fd5b506103a061074c366004615489565b611c98565b34801561075d57600080fd5b506103a061076c3660046153ad565b611ecc565b34801561077d57600080fd5b506103e261078c3660046150b0565b6120a1565b34801561079d57600080fd5b506103a06107ac36600461531c565b6120c1565b3480156107bd57600080fd5b5061052f6125a9565b3480156107d257600080fd5b506103e26107e13660046151af565b6125af565b3480156107f257600080fd5b506103a06125d5565b34801561080757600080fd5b506103e26108163660046150b0565b612638565b34801561082757600080fd5b5061048d612658565b34801561083c57600080fd5b506103a061084b366004615694565b612667565b34801561085c57600080fd5b506103e26126ba565b34801561087157600080fd5b506103e26108803660046151af565b6126c3565b34801561089157600080fd5b506103a06108a0366004615598565b6126e9565b3480156108b157600080fd5b506103a0612798565b3480156108c657600080fd5b506103e26127ec565b3480156108db57600080fd5b506103a06108ea3660046153ad565b6127fc565b3480156108fb57600080fd5b506103a061090a366004615092565b612931565b34801561091b57600080fd5b506103a061092a366004615092565b6129af565b34801561093b57600080fd5b506103a061094a366004615092565b612a6d565b34801561095b57600080fd5b5061048d612c33565b6103a061039b3660046153ad565b34801561097e57600080fd5b506103a061098d366004615290565b612c42565b34801561099e57600080fd5b5061048d612eb5565b3480156109b357600080fd5b506103a06109c2366004615092565b612ec4565b3480156109d357600080fd5b506103a0612f6e565b3480156109e857600080fd5b5061063a6109f7366004615694565b612fc2565b348015610a0857600080fd5b506103a0610a1736600461565a565b612fea565b348015610a2857600080fd5b506103e2610a37366004615092565b612ff8565b348015610a4857600080fd5b506103e2610a57366004615092565b613016565b348015610a6857600080fd5b506103a0610a77366004615598565b613105565b348015610a8857600080fd5b506103a0610a97366004615598565b6131b4565b348015610aa857600080fd5b5061063a61324c565b348015610abd57600080fd5b506103e2610acc366004615092565b613260565b348015610add57600080fd5b506103a0613275565b348015610af257600080fd5b5061052f610b013660046150ea565b613297565b348015610b1257600080fd5b5061048d6132d4565b348015610b2757600080fd5b506103a0610b363660046151f2565b6132e3565b348015610b4757600080fd5b506103a0610b56366004615092565b6133ba565b60405160e060020a63802d10af028152600090732fcb98529d58669e229c453de4b4705bb6b2d4149063802d10af90610b98903490600401615b91565b60206040518083038186803b158015610bb057600080fd5b505af4158015610bc4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610be8919081019061555c565b9050610bfa8383836000806001613433565b600080516020615d1a833981519152838383600080604051610c20959493929190615a66565b60405180910390a1505050565b80600160a060020a0381161515610c4357600080fd5b600160a060020a038116301415610c5957600080fd5b33600160a060020a0383161415610c6f57600080fd5b600160a060020a03821660009081526004602052604090205460ff161515610c9657600080fd5b600160a060020a03821660009081526007602052604090205460ff1615610cbc57600080fd5b600160a060020a038216600090815260096020908152604080832033808552925291829020805460ff1916600117905590517f7406a64e4e8c04b2a11d4cb484eb07cd5762012eafc449900b8d92de008ff27f91610d1b918590615732565b60405180910390a15050565b600160a060020a03166000908152600360205260408120541190565b600080610d4f84613934565b9050610d5b8585611c47565b8015610da45750610d6c85846139fe565b80610da45750600160a060020a038086166000908152600a6020908152604080832085845282528083209387168352929052205460ff165b95945050505050565b60405160e160020a6351750e53028152600090732fcb98529d58669e229c453de4b4705bb6b2d4149063a2ea1ca690610dea908890600401615b91565b60206040518083038186803b158015610e0257600080fd5b505af4158015610e16573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610e3a919081019061553e565b1515610e4557600080fd5b60105460405160e160020a6315c3cbc1028152600160a060020a0390911690632b87978290610e7c9033908890889060040161584b565b602060405180830381600087803b158015610e9657600080fd5b505af1158015610eaa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610ece919081019061553e565b15610ed857600080fd5b610ee3848484613a55565b9050610ef23386868685613af1565b600f546040805160e060020a63bde440cb0281529051929750600160a060020a0390911691639cab96d6913391849163bde440cb9160048083019260209291908290030181600087803b158015610f4857600080fd5b505af1158015610f5c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610f80919081019061555c565b8888886040518663ffffffff1660e060020a028152600401610fa69594939291906158c3565b600060405180830381600087803b158015610fc057600080fd5b505af1158015610fd4573d6000803e3d6000fd5b50505050610fe53386868686613e9a565b7f8e0adde15ffb742f47e7888277a26cea416253fcb63186dc4949fe493e92b77c338686868660405161101c9594939291906159d0565b60405180910390a15050505050565b856110363382611af1565b151561104157600080fd5b61104f878787878787613fee565b7f70a0e35287634a0b4f96e261e0b71d0ae930837dabd35de9dfdad491a4eb6d378787878787604051611086959493929190615955565b60405180910390a150505050505050565b60006110a2826116a4565b80156110c95750600160a060020a0382166000908152600460205260409020600101544210155b92915050565b600e54600160a060020a031681565b60076020526000908152604090205460ff1681565b60405160e160020a6351750e53028152600090732fcb98529d58669e229c453de4b4705bb6b2d4149063a2ea1ca690611130908890600401615b91565b60206040518083038186803b15801561114857600080fd5b505af415801561115c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611180919081019061553e565b151561118b57600080fd5b611196848484613a55565b90506111a53386868685613af1565b600e546040805160e460020a630ded11310281529051929750600160a060020a039091169163b95ae1cd913391849163ded113109160048083019260209291908290030181600087803b1580156111fb57600080fd5b505af115801561120f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611233919081019061555c565b888888876040518763ffffffff1660e060020a02815260040161125b969594939291906158de565b600060405180830381600087803b15801561127557600080fd5b505af1158015611289573d6000803e3d6000fd5b505050507f013970fdfcc3b9ba3657604311c39270a2b090a94528cfdfd09abaddc6e8fb523386868660405161101c9493929190615873565b6000806112cd61427c565b15156112d857600080fd5b82600160a060020a03811615156112ee57600080fd5b600160a060020a03841660009081526003602052604090205415156113165760009250611451565b600160a060020a0384166000908152600360205260409020546002546000199182019350018210156113e55760028054600019810190811061135457fe5b60009182526020909120015460028054600160a060020a03909216918490811061137a57fe5b9060005260206000200160006101000a815481600160a060020a030219169083600160a060020a0316021790555081600101600360006002858154811015156113bf57fe5b6000918252602080832090910154600160a060020a031683528201929092526040019020555b60028054906113f8906000198301614e7d565b50600160a060020a03841660009081526003602052604080822091909155517f33f51cf787669db5b5aff6ba597298fd010748de93aaadef29aae876cef4a8c390611444908690615724565b60405180910390a1600192505b5050919050565b600061146261427c565b151561146d57600080fd5b81600160a060020a038116151561148357600080fd5b6013548390600160a060020a0390811690821681146114f05760138054600160a060020a03878116600160a060020a0319831617909255604051911694507f059245e3752d2ad85a00b28db68f4b944c8269ef3c6bd378055bea9cd1fd07409061101c9086908890615bad565b5050505050565b6011545b90565b60105460a060020a900460ff1681565b3361151761187c565b600160a060020a03161461152a57600080fd5b60005460ff161561153a57600080fd5b7f787a5d936e74f4b564b9153575886059829c78cd9927b1be5e0d976b317ef736336040516115699190615724565b60405180910390a133ff5b600061157e61427c565b151561158957600080fd5b82600160a060020a038116151561159f57600080fd5b600160a060020a0381163014156115b557600080fd5b600160a060020a03841660009081526004602052604090205460ff1615156115dc57600080fd5b6115e583613934565b600160a060020a038516600090815260046020908152604080832084845260020190915290205490925060ff161561161c57600080fd5b600160a060020a0384166000908152600460208181526040808420868552600281018352818520805460ff19166001908117909155938352600301805493840181558452922001839055517fec1b982d69bfc1a6fd8becf191d9a633486c822d3bdedd52303ca81fa6357501906116969086908690615a17565b60405180910390a150505050565b600160a060020a031660009081526004602052604090205460ff1690565b60005460ff1681565b6116d3614292565b15156116de57600080fd5b60135460405160e060020a63ce256213028152600160a060020a039091169063ce2562139061171b90309089908990899089908990600401615bc8565b600060405180830381600087803b15801561173557600080fd5b505af1158015611749573d6000803e3d6000fd5b505050507fe413728068242467b29b57e8f9c4da8b4a4eb0ea41abe0cadddba823c043e088858585858560405161101c959493929190615a37565b600061178e61427c565b151561179957600080fd5b82600160a060020a03811615156117af57600080fd5b600160a060020a0381163014156117c557600080fd5b6117ce83613934565b600160a060020a038516600090815260046020908152604080832084845260020190915290205490925060ff16151561180657600080fd5b600160a060020a038416600090815260046020908152604080832085845260020190915290819020805460ff19169055517f92f4af6130f47c92f3899e06611c8c6ef191fb81207ad2675d134af619d2fe7d906116969086908690615a17565b60055481565b600e5460a060020a900460ff1681565b6000546101009004600160a060020a031690565b61189b838383613a55565b156118b1576118ac843385856142a3565b6118bd565b6118bd843385856144e5565b600160a060020a03841660009081526012602052604090205460ff16151561194557600160a060020a0384166000818152601260205260408120805460ff191660019081179091556011805491820181559091527f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c68018054600160a060020a03191690911790555b50505050565b61195361427c565b151561195e57600080fd5b80600160a060020a038116151561197457600080fd5b600160a060020a03811630141561198a57600080fd5b60065460ff161561199a57600080fd5b33600160a060020a03831614156119b057600080fd5b600160a060020a03821660009081526004602052604090205460ff1615156119d757600080fd5b600160a060020a03821660009081526007602052604090819020805460ff19166001179055517f81c5f2034d05958dd641755044582c0aba29a94070f0a2641bb554325328bf1e90610d1b9033908590615732565b611a3461427c565b1515611a3f57600080fd5b80600160a060020a0381161515611a5557600080fd5b600160a060020a038116301415611a6b57600080fd5b600160a060020a03821660009081526004602052604090205460ff161515611a9257600080fd5b600160a060020a03821660009081526004602052604090819020805460ff19169055517f777645c5437dfbf962f57a281ead25f3d513f2f8e938685bbfc1738e81c9880e90610d1b908490615724565b600154600160a060020a031681565b6000611afc83611097565b8015611b3d5750611b0d83836139fe565b80611b3d5750600160a060020a0380841660009081526009602090815260408083209386168352929052205460ff165b9392505050565b6000611b4e61427c565b1515611b5957600080fd5b81600160a060020a0381161515611b6f57600080fd5b600e548390600160a060020a0390811690821681146114f057600e5460a060020a900460ff1615611b9f57600080fd5b600e8054600160a060020a03878116600160a060020a0319831617909255604051911694507fb2a91d3a71b0c5bc7c083153b3474378e489506ba98bd4ddb1b9056fdc594bb59061101c9086908890615bad565b611bfb61427c565b1515611c0657600080fd5b6010805460a060020a60ff02191660a060020a1790556040517f56ec8900b9c4bf84f4b715a53068ca06961dd49084c07b481931e2c2045346e690600090a1565b600080611c5383613934565b9050611c5e84611097565b8015611c905750600160a060020a038416600090815260046020908152604080832084845260020190915290205460ff165b949350505050565b60405160e160020a6351750e53028152600090732fcb98529d58669e229c453de4b4705bb6b2d4149063a2ea1ca690611cd5908890600401615b91565b60206040518083038186803b158015611ced57600080fd5b505af4158015611d01573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611d25919081019061553e565b1515611d3057600080fd5b611d3a8483614829565b905080600160a060020a031681600160a060020a031663ad502f576040518163ffffffff1660e060020a028152600401602060405180830381600087803b158015611d8457600080fd5b505af1158015611d98573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611dbc919081019061557a565b6040805160e060020a9283900463ffffffff8116909302815233600482015230602482015260448101899052600160a060020a038816606482015260848101879052905160a48083019260009291908290030181865af4925050501515611e2257600080fd5b611ea5878787878786600160a060020a031663a89c2e5a6040518163ffffffff1660e060020a028152600401602060405180830381600087803b158015611e6857600080fd5b505af1158015611e7c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611ea0919081019061553e565b613433565b600080516020615d1a83398151915287878787878760405161108696959493929190615ac0565b600082600160a060020a0381161515611ee457600080fd5b600160a060020a038116301415611efa57600080fd5b33600160a060020a0385161415611f1057600080fd5b611f1983613934565b600160a060020a03851660009081526004602052604090205490925060ff168015611f6a5750600160a060020a038416600090815260046020908152604080832085845260020190915290205460ff165b1515611f7557600080fd5b600160a060020a03841660009081526007602052604090205460ff1615611f9b57600080fd5b600160a060020a038416600081815260096020908152604080832033808552908352818420805460ff19908116909155858552600a84528285208886528452828520828652845282852080549091166001179055938352600b82528083208684528252808320938352929052205460ff16151561206e57600160a060020a0384166000818152600b60209081526040808320868452825280832033808552908352818420805460ff19166001908117909155948452600c8352818420908452825282208054938401815582529020018290555b7fc007803323f3ca89cb3f8af1a12f354dc108e9dd4988c312b3f7a878914987ce3385856040516116969392919061581e565b600860209081526000928352604080842090915290825290205460ff1681565b6000866120ce3382611af1565b15156120d957600080fd5b87600160a060020a03811615156120ef57600080fd5b60405160e060020a636b84d931028152732fcb98529d58669e229c453de4b4705bb6b2d41490636b84d93190612129908b90600401615b91565b60206040518083038186803b15801561214157600080fd5b505af4158015612155573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612179919081019061553e565b151561218457600080fd5b61218f878787613a55565b1561239457600e546040805160e460020a630ded11310281529051600160a060020a039092169163a246138c918c91849163ded113109160048083019260209291908290030181600087803b1580156121e757600080fd5b505af11580156121fb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061221f919081019061555c565b8a8a896040518663ffffffff1660e060020a02815260040161224595949392919061588e565b6040805180830381600087803b15801561225e57600080fd5b505af1158015612272573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061229691908101906155f6565b50600e546040805160e260020a631194bb1d0281529051929550600160a060020a039091169163d062ec1d918c918491634652ec749160048083019260209291908290030181600087803b1580156122ed57600080fd5b505af1158015612301573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612325919081019061555c565b6123358c8863ffffffff6148b116565b8b8b60016040518763ffffffff1660e060020a02815260040161235d969594939291906158de565b600060405180830381600087803b15801561237757600080fd5b505af115801561238b573d6000803e3d6000fd5b50505050612561565b600e546040805160e460020a630ded11310281529051600160a060020a0390921691637d7a23aa918c91849163ded113109160048083019260209291908290030181600087803b1580156123e757600080fd5b505af11580156123fb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061241f919081019061555c565b8b8b8b60006040518763ffffffff1660e060020a028152600401612448969594939291906158de565b600060405180830381600087803b15801561246257600080fd5b505af1158015612476573d6000803e3d6000fd5b5050600e546040805160e260020a631194bb1d0281529051600160a060020a03909216935063b95ae1cd92508c918491634652ec749160048083019260209291908290030181600087803b1580156124cd57600080fd5b505af11580156124e1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612505919081019061555c565b8b8b8b60006040518763ffffffff1660e060020a02815260040161252e969594939291906158de565b600060405180830381600087803b15801561254857600080fd5b505af115801561255c573d6000803e3d6000fd5b505050505b7f6ded3dfe9bc210e5b60b695bac6bd623844003b866c14264d44e91b8352a4fad898989896040516125969493929190615873565b60405180910390a1505050505050505050565b60025490565b600b60209081526000938452604080852082529284528284209052825290205460ff1681565b336125de61187c565b600160a060020a0316146125f157600080fd5b6000805460ff191660011790556040517fd5a2a04a775c741c2ca0dc46ea7ce4835190e1aaf1ca018def0e82568ec336169061262e903390615724565b60405180910390a1565b600960209081526000928352604080842090915290825290205460ff1681565b601354600160a060020a031681565b61266f61427c565b151561267a57600080fd5b60058190556040517f4f1d324a1cbffc352fb64f1d73d95e4b200d92f99ef39bf2d7f5b41f946909d6906126af908390615b91565b60405180910390a150565b60065460ff1681565b600a60209081526000938452604080852082529284528284209052825290205460ff1681565b60006126f361427c565b15156126fe57600080fd5b81600160a060020a038116151561271457600080fd5b6010548390600160a060020a0390811690821681146114f05760105460a060020a900460ff161561274457600080fd5b60108054600160a060020a03878116600160a060020a0319831617909255604051911694507fa44d361e26327b72a7ccbeae801b3c5cd7677ea4fa74168b289e273c46bfecfc9061101c9086908890615bad565b6127a061427c565b15156127ab57600080fd5b600f805460a060020a60ff02191660a060020a1790556040517fd7d1b6dcaeec9afa87c8c53d9da393505dee41c9ace60ab22f3b23db19ac282c90600090a1565b600f5460a060020a900460ff1681565b600082600160a060020a038116151561281457600080fd5b600160a060020a03811630141561282a57600080fd5b33600160a060020a038516141561284057600080fd5b61284983613934565b600160a060020a03851660009081526004602052604090205490925060ff16801561289a5750600160a060020a038416600090815260046020908152604080832085845260020190915290205460ff165b15156128a557600080fd5b600160a060020a03841660009081526007602052604090205460ff16156128cb57600080fd5b600160a060020a0384166000908152600a60209081526040808320858452825280832033808552925291829020805460ff1916905590517f6fa0cdb0b5c22004ccfcaee11eed450e0184dda8edd417ae2410f0a883fdb74091611696918790879061581e565b61293961427c565b151561294457600080fd5b80600160a060020a038116151561295a57600080fd5b600160a060020a03811630141561297057600080fd5b61297c826005546148ed565b7f7f3a8349917003ed377f6e9ae1608b92edd893903f8983bd274b1f8373dd3b1182600554604051610d1b929190615b68565b60006129b961427c565b15156129c457600080fd5b81600160a060020a03811615156129da57600080fd5b600160a060020a0381163014156129f057600080fd5b600054600160a060020a038481166101009092041614612a685760008054600160a060020a0385811661010090810261010060a860020a0319841617909355604051929091041692507f977e5fa58e458501775e0008d275006294c5249e3c08d1d0e3a9f3acad14f6e490610c209084908690615732565b505050565b600081600160a060020a0381161515612a8557600080fd5b600160a060020a038116301415612a9b57600080fd5b33600160a060020a0384161415612ab157600080fd5b600160a060020a03831660009081526004602052604090205460ff161515612ad857600080fd5b600160a060020a03831660009081526007602052604090205460ff1615612b2c57600160a060020a03831660009081526008602090815260408083203384529091529020805460ff19166001179055612c02565b600160a060020a03831660009081526009602090815260408083203384529091528120805460ff1916905591505b600160a060020a0383166000908152600c60209081526040808320338452909152902054821015612c0257600160a060020a0383166000908152600a60209081526040808320600c835281842033855290925282208054600193919086908110612bc057fe5b600091825260208083209190910154835282810193909352604091820181203382529092529020805460ff191691151591909117905560019190910190612b5a565b7fe25d4094f2181ddef4410dcc1539a7488abd881a90c56078314b9f70affc5f7e3384604051610c20929190615732565b600f54600160a060020a031681565b600085612c4f3382611af1565b1515612c5a57600080fd5b60405160e160020a6351750e53028152732fcb98529d58669e229c453de4b4705bb6b2d4149063a2ea1ca690612c94908990600401615b91565b60206040518083038186803b158015612cac57600080fd5b505af4158015612cc0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612ce4919081019061553e565b1515612cef57600080fd5b612cfa858585613a55565b9150612d9887600e60009054906101000a9004600160a060020a0316600160a060020a031663e47da3526040518163ffffffff1660e060020a028152600401600060405180830381600087803b158015612d5357600080fd5b505af1158015612d67573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612d8f91908101906154d6565b8888888761493f565b600e546040805160e260020a631e758dcf0281529051929850600160a060020a039091169163b95ae1cd918a9184916379d6373c9160048083019260209291908290030181600087803b158015612dee57600080fd5b505af1158015612e02573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612e26919081019061555c565b898989886040518763ffffffff1660e060020a028152600401612e4e969594939291906158de565b600060405180830381600087803b158015612e6857600080fd5b505af1158015612e7c573d6000803e3d6000fd5b505050507f573abd56f40c687bfaaabb8d68c68c84cc343b51fe661ff2080e2a6977b3c2b4878787876040516110869493929190615873565b600d54600160a060020a031681565b6000612ece614292565b1515612ed957600080fd5b81600160a060020a0381161515612eef57600080fd5b600160a060020a038116301415612f0557600080fd5b600154600160a060020a03848116911614612a685760018054600160a060020a03858116600160a060020a0319831617909255604051911692507f9f611b789425d0d5b90b920f1b2852907dd865c80074a30b1629aaa041d1812c90610c209084908690615732565b612f7661427c565b1515612f8157600080fd5b600e805460a060020a60ff02191660a060020a1790556040517f9c567b65fe8caab5ec7bc979a498a1322c1d4baf01a30727cbca137187560ea290600090a1565b6011805482908110612fd057fe5b600091825260209091200154600160a060020a0316905081565b6114f0338686868686611c98565b600160a060020a031660009081526012602052604090205460ff1690565b600061302061427c565b151561302b57600080fd5b81600160a060020a038116151561304157600080fd5b600160a060020a038316600090815260036020526040812054111561306957600091506130ff565b600280546001810182557f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace018054600160a060020a031916600160a060020a03861690811790915590546000918252600360205260409182902055517fdd62ff6d9c23793871e7d4796bde6c5dfb2bf8bcbd4186411cee3eb9f6189521906130f2908590615724565b60405180910390a1600191505b50919050565b600061310f61427c565b151561311a57600080fd5b81600160a060020a038116151561313057600080fd5b600f548390600160a060020a0390811690821681146114f057600f5460a060020a900460ff161561316057600080fd5b600f8054600160a060020a03878116600160a060020a0319831617909255604051911694507fa935cd13e8b6831b708081e1b4745e4a9574dc3aae730671c18d113a5497c90b9061101c9086908890615bad565b60006131be61427c565b15156131c957600080fd5b81600160a060020a03811615156131df57600080fd5b600d548390600160a060020a0390811690821681146114f057600d8054600160a060020a03878116600160a060020a0319831617909255604051911694507fee9ef6d53e152e48b1ebc1c23943fb2b96e847465ed779a0e2121a388ea83d2f9061101c9086908890615bad565b6000546101009004600160a060020a031681565b60126020526000908152604090205460ff1681565b61327d61427c565b151561328857600080fd5b6006805460ff19166001179055565b600c602052826000526040600020602052816000526040600020818154811015156132be57fe5b9060005260206000200160009250925050505481565b601054600160a060020a031681565b6000866132f03382611af1565b15156132fb57600080fd5b613306858585613a55565b915061335f88600e60009054906101000a9004600160a060020a0316600160a060020a031663e47da3526040518163ffffffff1660e060020a028152600401600060405180830381600087803b158015612d5357600080fd5b955061336f888888888888613fee565b7f4c50864967876f6fcf5151a5e907d94d9c7b3d7780ca1c7f576680a2126ba39b8888888888886040516133a896959493929190615970565b60405180910390a15050505050505050565b6133c261427c565b15156133cd57600080fd5b80600160a060020a03811615156133e357600080fd5b600160a060020a0381163014156133f957600080fd5b6134048260006148ed565b7fb619d545cb511bd5f02907a1eac4f4cb8dace7b1846852fb0bf5e553937481c182604051610d1b9190615724565b600085516000106134ce57600e60009054906101000a9004600160a060020a0316600160a060020a031663ded113106040518163ffffffff1660e060020a028152600401602060405180830381600087803b15801561349157600080fd5b505af11580156134a5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506134c9919081019061555c565b613592565b856040516020018082805190602001908083835b602083106135015780518252601f1990920191602091820191016134e2565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040516020818303038152906040526040518082805190602001908083835b602083106135645780518252601f199092019160209182019101613545565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390205b600e546040805160e260020a631e758dcf02815290519293508392600160a060020a03909216916379d6373c916004808201926020929091908290030181600087803b1580156135e157600080fd5b505af11580156135f5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613619919081019061555c565b141561370957600e546040805160e260020a631e758dcf0281529051600160a060020a039092169163b95ae1cd918a9184916379d6373c9160048083019260209291908290030181600087803b15801561367257600080fd5b505af1158015613686573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506136aa919081019061555c565b888888886040518763ffffffff1660e060020a0281526004016136d2969594939291906158de565b600060405180830381600087803b1580156136ec57600080fd5b505af1158015613700573d6000803e3d6000fd5b5050505061392b565b600e546040805160e460020a630ded113102815290518392600160a060020a03169163ded113109160048083019260209291908290030181600087803b15801561375257600080fd5b505af1158015613766573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061378a919081019061555c565b141561392657600e546040805160e460020a630ded11310281529051600160a060020a039092169163b95ae1cd918a91849163ded113109160048083019260209291908290030181600087803b1580156137e357600080fd5b505af11580156137f7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061381b919081019061555c565b888888886040518763ffffffff1660e060020a028152600401613843969594939291906158de565b600060405180830381600087803b15801561385d57600080fd5b505af1158015613871573d6000803e3d6000fd5b5050600f546040805160e060020a63313004a90281529051600160a060020a039092169350639cab96d692508a91849163313004a99160048083019260209291908290030181600087803b1580156138c857600080fd5b505af11580156138dc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613900919081019061555c565b8888886040518663ffffffff1660e060020a0281526004016136d29594939291906158c3565b600080fd5b50505050505050565b6000816040516020018082805190602001908083835b602083106139695780518252601f19909201916020918201910161394a565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040516020818303038152906040526040518082805190602001908083835b602083106139cc5780518252601f1990920191602091820191016139ad565b5181516020939093036101000a6000190180199091169216919091179052604051920182900390912095945050505050565b600160a060020a03821660009081526007602052604081205460ff16613a25576000611b3d565b50600160a060020a03918216600090815260086020908152604080832093909416825291909152205460ff161590565b6000600160a060020a038416158015613a6c575082155b80611c905750613a7c8483614829565b600160a060020a031663a89c2e5a6040518163ffffffff1660e060020a028152600401602060405180830381600087803b158015613ab957600080fd5b505af1158015613acd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611c90919081019061553e565b60008115613c9d57600e546040805160e260020a631e758dcf0281529051732fcb98529d58669e229c453de4b4705bb6b2d4149263755875b2928992600160a060020a03909216916371f4ec51918c9184916379d6373c9160048083019260209291908290030181600087803b158015613b6a57600080fd5b505af1158015613b7e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613ba2919081019061555c565b8a8a6040518563ffffffff1660e060020a028152600401613bc69493929190615873565b602060405180830381600087803b158015613be057600080fd5b505af1158015613bf4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613c18919081019061555c565b6040518363ffffffff1660e060020a028152600401613c38929190615c04565b60206040518083038186803b158015613c5057600080fd5b505af4158015613c64573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613c88919081019061555c565b94506000851215613c9857600080fd5b613dab565b600e546040805160e260020a631e758dcf0281529051600160a060020a0390921691635962480691899184916379d6373c9160048083019260209291908290030181600087803b158015613cf057600080fd5b505af1158015613d04573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613d28919081019061555c565b8888886040518663ffffffff1660e060020a028152600401613d4e9594939291906158c3565b602060405180830381600087803b158015613d6857600080fd5b505af1158015613d7c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613da0919081019061553e565b1515613dab57600080fd5b600e546040805160e260020a631e758dcf0281529051600160a060020a0390921691637d7a23aa91899184916379d6373c9160048083019260209291908290030181600087803b158015613dfe57600080fd5b505af1158015613e12573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613e36919081019061555c565b888888886040518763ffffffff1660e060020a028152600401613e5e969594939291906158de565b600060405180830381600087803b158015613e7857600080fd5b505af1158015613e8c573d6000803e3d6000fd5b509698975050505050505050565b6000600160a060020a038416158015613eb1575082155b15613ef257604051600160a060020a0387169086156108fc029087906000818181858888f19350505050158015613eec573d6000803e3d6000fd5b50613fe6565b613efc8483614829565b905080600160a060020a031681600160a060020a0316637228941f6040518163ffffffff1660e060020a028152600401602060405180830381600087803b158015613f4657600080fd5b505af1158015613f5a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613f7e919081019061557a565b6040805160e060020a9283900463ffffffff81169093028152306004820152600160a060020a038a81166024830152604482018a90528816606482015260848101879052905160a48083019260009291908290030181865af4925050501515613fe657600080fd5b505050505050565b60405160e160020a6351750e53028152600090732fcb98529d58669e229c453de4b4705bb6b2d4149063a2ea1ca69061402b908890600401615b91565b60206040518083038186803b15801561404357600080fd5b505af4158015614057573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061407b919081019061553e565b151561408657600080fd5b61408f86610d27565b151561409a57600080fd5b600160a060020a0384161580156140af575082155b1561411c5760405160e060020a63a7654a1f028152600160a060020a0387169063a7654a1f9087906140e5908b90600401615aef565b6000604051808303818588803b1580156140fe57600080fd5b505af1158015614112573d6000803e3d6000fd5b505050505061392b565b6141268483614829565b905080600160a060020a031681600160a060020a0316631711a8946040518163ffffffff1660e060020a028152600401602060405180830381600087803b15801561417057600080fd5b505af1158015614184573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506141a8919081019061557a565b6040805160e060020a9283900463ffffffff81169093028152600160a060020a038a81166004830152602482018a90528816604482015260648101879052905160848083019260009291908290030181865af492505050151561420a57600080fd5b60405160e060020a635f289651028152600160a060020a03871690635f28965190614241908a908990899089908990600401615b0e565b600060405180830381600087803b15801561425b57600080fd5b505af115801561426f573d6000803e3d6000fd5b5050505050505050505050565b6000546101009004600160a060020a0316331490565b600154600160a060020a0316331490565b60105460405160e060020a630d044085028152600091600160a060020a031690630d044085906142dd90889088908890889060040161574d565b602060405180830381600087803b1580156142f757600080fd5b505af115801561430b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061432f919081019061555c565b90506000811361433e57600080fd5b6143d985600e60009054906101000a9004600160a060020a0316600160a060020a031663ec3aa67e6040518163ffffffff1660e060020a028152600401600060405180830381600087803b15801561439557600080fd5b505af11580156143a9573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526143d191908101906154d6565b838686614972565b50600e546040805160e260020a631e758dcf0281529051600160a060020a039092169163b95ae1cd91879184916379d6373c9160048083019260209291908290030181600087803b15801561442d57600080fd5b505af1158015614441573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250614465919081019061555c565b84878760016040518763ffffffff1660e060020a02815260040161448e969594939291906158de565b600060405180830381600087803b1580156144a857600080fd5b505af11580156144bc573d6000803e3d6000fd5b50505050600080516020615cfa833981519152858583868660405161101c9594939291906157dc565b60105460405160e060020a639f45b2ab0281526000916060918391600160a060020a031690639f45b2ab90614524908a908a908a908a9060040161574d565b602060405180830381600087803b15801561453e57600080fd5b505af1158015614552573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250614576919081019061555c565b92506000831161458557600080fd5b60105460405160e160020a6374b72475028152600160a060020a039091169063e96e48ea906145c7908a908a908a908a906000906000198c0190600401615782565b600060405180830381600087803b1580156145e157600080fd5b505af11580156145f5573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261461d919081019061550a565b9150600090505b815181101561392b576146df87600e60009054906101000a9004600160a060020a0316600160a060020a031663ec3aa67e6040518163ffffffff1660e060020a028152600401600060405180830381600087803b15801561468457600080fd5b505af1158015614698573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526146c091908101906154d6565b84848151811015156146ce57fe5b906020019060200201518888614ced565b50600e546040805160e260020a631e758dcf0281529051600160a060020a039092169163b95ae1cd91899184916379d6373c9160048083019260209291908290030181600087803b15801561473357600080fd5b505af1158015614747573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061476b919081019061555c565b858581518110151561477957fe5b90602001906020020151898960006040518763ffffffff1660e060020a0281526004016147ab969594939291906158de565b600060405180830381600087803b1580156147c557600080fd5b505af11580156147d9573d6000803e3d6000fd5b50505050600080516020615cfa833981519152878784848151811015156147fc57fe5b9060200190602002015188886040516148199594939291906157dc565b60405180910390a1600101614624565b600d5460405160e460020a630c8a483b028152600091600160a060020a03169063c8a483b09061485f9086908690600401615a17565b602060405180830381600087803b15801561487957600080fd5b505af115801561488d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611b3d91908101906155b6565b60008082121580156148c557508282840313155b806148dc57506000821280156148dc575082828403135b15156148e757600080fd5b50900390565b600160a060020a03821660009081526004602052604090205460ff16151561493b57600160a060020a0382166000908152600460205260409020805460ff1916600190811782554283019101555b5050565b6000811561495b576149548787878787614972565b9050614968565b6149548787878787614ced565b9695505050505050565b6000808080808781131561498557600080fd5b60009250600093505b8851841015614a5857600e548951614a4b91600160a060020a0316906371f4ec51908d908d90899081106149be57fe5b906020019060200201518b8b6040518563ffffffff1660e060020a0281526004016149ec9493929190615873565b602060405180830381600087803b158015614a0657600080fd5b505af1158015614a1a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250614a3e919081019061555c565b849063ffffffff614e4816565b600190940193925061498e565b60405160e160020a633aac3ad9028152732fcb98529d58669e229c453de4b4705bb6b2d4149063755875b290614a94908b908790600401615c04565b60206040518083038186803b158015614aac57600080fd5b505af4158015614ac0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250614ae4919081019061555c565b9750879150600093505b8851841015614cdf57600e548951600160a060020a03909116906371f4ec51908c908c9088908110614b1c57fe5b906020019060200201518a8a6040518563ffffffff1660e060020a028152600401614b4a9493929190615873565b602060405180830381600087803b158015614b6457600080fd5b505af1158015614b78573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250614b9c919081019061555c565b9050818112614c3457600e548951600160a060020a0390911690637d7a23aa908c908c9088908110614bca57fe5b90602001906020020151858b8b60016040518763ffffffff1660e060020a028152600401614bfd969594939291906158de565b600060405180830381600087803b158015614c1757600080fd5b505af1158015614c2b573d6000803e3d6000fd5b50505050614cdf565b600e548951600160a060020a039091169063d062ec1d908c908c9088908110614c5957fe5b9060200190602002015160008b8b60016040518763ffffffff1660e060020a028152600401614c8d9695949392919061592d565b600060405180830381600087803b158015614ca757600080fd5b505af1158015614cbb573d6000803e3d6000fd5b50505050614cd281836148b190919063ffffffff16565b9150600190930192614aee565b509598975050505050505050565b6000805b8551811015614e3d57600e548651600160a060020a03909116906359624806908990899085908110614d1f57fe5b906020019060200201518888886040518663ffffffff1660e060020a028152600401614d4f9594939291906158c3565b602060405180830381600087803b158015614d6957600080fd5b505af1158015614d7d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250614da1919081019061553e565b15614e3557600e548651600160a060020a0390911690637d7a23aa908990899085908110614dcb57fe5b9060200190602002015188888860006040518763ffffffff1660e060020a028152600401614dfe969594939291906158de565b600060405180830381600087803b158015614e1857600080fd5b505af1158015614e2c573d6000803e3d6000fd5b50505050614e3d565b600101614cf1565b509295945050505050565b6000828201818312801590614e5d5750838112155b80614e725750600083128015614e7257508381125b1515611b3d57600080fd5b815481835581811115612a6857600083815260209020612a689181019083016114fb91905b80821115614eb65760008155600101614ea2565b5090565b6000611b3d8235615c83565b6000601f82018313614ed757600080fd5b8151614eea614ee582615c38565b615c12565b91508181835260208401935060208101905083856020840282011115614f0f57600080fd5b60005b83811015614f3b5781614f258882614fcd565b8452506020928301929190910190600101614f12565b5050505092915050565b6000601f82018313614f5657600080fd5b8151614f64614ee582615c38565b91508181835260208401935060208101905083856020840282011115614f8957600080fd5b60005b83811015614f3b5781614f9f8882614fcd565b8452506020928301929190910190600101614f8c565b6000611b3d8251615c8f565b6000611b3d82356114fb565b6000611b3d82516114fb565b6000611b3d8251615c94565b6000611b3d8235615ca1565b6000611b3d8251615ca1565b6000601f8201831361500e57600080fd5b815161501c614ee582615c58565b9150808252602083016020830185838301111561503857600080fd5b615043838284615cc3565b50505092915050565b6000601f8201831361505d57600080fd5b813561506b614ee582615c58565b9150808252602083016020830185838301111561508757600080fd5b615043838284615cb7565b6000602082840312156150a457600080fd5b6000611c908484614eba565b600080604083850312156150c357600080fd5b60006150cf8585614eba565b92505060206150e085828601614eba565b9150509250929050565b6000806000606084860312156150ff57600080fd5b600061510b8686614eba565b935050602061511c86828701614eba565b925050604061512d86828701614fc1565b9150509250925092565b6000806000806080858703121561514d57600080fd5b60006151598787614eba565b945050602061516a87828801614eba565b935050604061517b87828801614fc1565b92505060608501356001604060020a0381111561519757600080fd5b6151a38782880161504c565b91505092959194509250565b6000806000606084860312156151c457600080fd5b60006151d08686614eba565b93505060206151e186828701614fc1565b925050604061512d86828701614eba565b60008060008060008060c0878903121561520b57600080fd5b60006152178989614eba565b965050602061522889828a01614fe5565b955050604061523989828a01614fc1565b945050606061524a89828a01614eba565b935050608061525b89828a01614fc1565b92505060a08701356001604060020a0381111561527757600080fd5b61528389828a0161504c565b9150509295509295509295565b600080600080600060a086880312156152a857600080fd5b60006152b48888614eba565b95505060206152c588828901614fc1565b94505060406152d688828901614eba565b93505060606152e788828901614fc1565b92505060808601356001604060020a0381111561530357600080fd5b61530f8882890161504c565b9150509295509295909350565b60008060008060008060c0878903121561533557600080fd5b60006153418989614eba565b965050602061535289828a01614fc1565b955050604061536389828a01614eba565b945050606061537489828a01614fc1565b93505060808701356001604060020a0381111561539057600080fd5b61539c89828a0161504c565b92505060a061528389828a01614fc1565b600080604083850312156153c057600080fd5b60006153cc8585614eba565b92505060208301356001604060020a038111156153e857600080fd5b6150e08582860161504c565b60008060006060848603121561540957600080fd5b60006154158686614eba565b93505060208401356001604060020a0381111561543157600080fd5b6151e18682870161504c565b600080600080600060a0868803121561545557600080fd5b60006154618888614eba565b95505060208601356001604060020a0381111561547d57600080fd5b6152c58882890161504c565b60008060008060008060c087890312156154a257600080fd5b60006154ae8989614eba565b96505060208701356001604060020a038111156154ca57600080fd5b61522889828a0161504c565b6000602082840312156154e857600080fd5b81516001604060020a038111156154fe57600080fd5b611c9084828501614ec6565b60006020828403121561551c57600080fd5b81516001604060020a0381111561553257600080fd5b611c9084828501614f45565b60006020828403121561555057600080fd5b6000611c908484614fb5565b60006020828403121561556e57600080fd5b6000611c908484614fcd565b60006020828403121561558c57600080fd5b6000611c908484614fd9565b6000602082840312156155aa57600080fd5b6000611c908484614fe5565b6000602082840312156155c857600080fd5b6000611c908484614ff1565b600080600080608085870312156155ea57600080fd5b60006151598787614fc1565b6000806040838503121561560957600080fd5b60006156158585614fcd565b92505060206150e085828601614fcd565b60006020828403121561563857600080fd5b81516001604060020a0381111561564e57600080fd5b611c9084828501614ffd565b600080600080600060a0868803121561567257600080fd5b85356001604060020a0381111561568857600080fd5b6152b48882890161504c565b6000602082840312156156a657600080fd5b6000611c908484614fc1565b6156bb81615c83565b82525050565b6156bb81615c8f565b6156bb816114fb565b6156bb81615ca1565b6156bb81615cac565b60006156f082615c7f565b808452615704816020860160208601615cc3565b61570d81615cef565b9093016020019392505050565b6000815260200190565b602081016110c982846156b2565b6040810161574082856156b2565b611b3d60208301846156b2565b6080810161575b82876156b2565b61576860208301866156b2565b61577560408301856156b2565b610da460608301846156ca565b60c0810161579082896156b2565b61579d60208301886156b2565b6157aa60408301876156b2565b6157b760608301866156ca565b6157c460808301856156dc565b6157d160a08301846156ca565b979650505050505050565b60a081016157ea82886156b2565b6157f760208301876156b2565b61580460408301866156ca565b61581160608301856156b2565b61496860808301846156ca565b6060810161582c82866156b2565b61583960208301856156b2565b8181036040830152610da481846156e5565b6060810161585982866156b2565b61586660208301856156b2565b611c9060408301846156ca565b6080810161588182876156b2565b61576860208301866156ca565b60a0810161589c82886156b2565b6158a960208301876156ca565b6158b660408301866156b2565b61581160608301856156ca565b60a081016158d182886156b2565b6157f760208301876156ca565b60c081016158ec82896156b2565b6158f960208301886156ca565b61590660408301876156ca565b61591360608301866156b2565b61592060808301856156ca565b6157d160a08301846156c1565b60c0810161593b82896156b2565b61594860208301886156ca565b61590660408301876156dc565b60a0810161596382886156b2565b6157f760208301876156d3565b60c0810161597e82896156b2565b61598b60208301886156d3565b61599860408301876156ca565b6159a560608301866156b2565b6159b260808301856156ca565b81810360a08301526159c481846156e5565b98975050505050505050565b60a081016159de82886156b2565b6159eb60208301876156ca565b6159f860408301866156b2565b615a0560608301856156ca565b81810360808301526157d181846156e5565b60408101615a2582856156b2565b8181036020830152611c9081846156e5565b60a08101615a4582886156b2565b8181036020830152615a5781876156e5565b90506159f860408301866156b2565b60c08101615a7482886156b2565b8181036020830152615a8681876156e5565b9050615a9560408301866156ca565b615aa260608301856156b2565b615aaf60808301846156dc565b81810360a08301526157d18161571a565b60c08101615ace82896156b2565b8181036020830152615ae081886156e5565b905061599860408301876156ca565b60408101615afd82846156b2565b8181036020830152611b3d8161571a565b60c08101615b1c82886156b2565b8181036020830152615b2d8161571a565b9050615b3c60408301876156ca565b615b4960608301866156b2565b615b5660808301856156ca565b81810360a08301526157d181846156e5565b60408101615b7682856156b2565b611b3d60208301846156ca565b602081016110c982846156c1565b602081016110c982846156ca565b602081016110c982846156d3565b60408101615bbb82856156d3565b611b3d60208301846156d3565b60c08101615bd682896156d3565b615be360208301886156b2565b8181036040830152615bf581876156e5565b90506159a560608301866156b2565b60408101615b7682856156ca565b6040518181016001604060020a0381118282101715615c3057600080fd5b604052919050565b60006001604060020a03821115615c4e57600080fd5b5060209081020190565b60006001604060020a03821115615c6e57600080fd5b506020601f91909101601f19160190565b5190565b600160a060020a031690565b151590565b600160e060020a03191690565b60006110c982615c83565b60006110c9826114fb565b82818337506000910152565b60005b83811015615cde578181015183820152602001615cc6565b838111156119455750506000910152565b601f01601f19169056007c750f44230ed3db46106f40b3f7c4707c7814035e69e09d43699ccbe46b1061b1054d4e81cc81e966ba33c7ce386432dcf8d4d78798e7a1f7f0b4aaef360330a265627a7a72305820ce6f378288254722acd2a3e964384447d0f597ec432ccc1a5f7bf051aee000666c6578706572696d656e74616cf50037

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

000000000000000000000000f05179bac3d1fbef58a2fcd7ad0f769840027cc6

-----Decoded View---------------
Arg [0] : deployer (address): 0xf05179bAc3D1fbEF58A2fcD7AD0F769840027cc6

-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 000000000000000000000000f05179bac3d1fbef58a2fcd7ad0f769840027cc6


Libraries Used


Swarm Source

bzzr://ce6f378288254722acd2a3e964384447d0f597ec432ccc1a5f7bf051aee00066

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.