ETH Price: $2,676.87 (-2.87%)

Contract

0x906fd331F5E382F05b8ae26900140c37f0Db139a
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Transfer211818272024-11-13 22:42:3596 days ago1731537755IN
0x906fd331...7f0Db139a
0 ETH0.0007049333.45673233
Transfer211784542024-11-13 11:24:2397 days ago1731497063IN
0x906fd331...7f0Db139a
0.0001 ETH0.0005892628
Transfer211748592024-11-12 23:22:2397 days ago1731453743IN
0x906fd331...7f0Db139a
0 ETH0.0005802827.54064616
Transfer211748102024-11-12 23:12:2397 days ago1731453143IN
0x906fd331...7f0Db139a
0 ETH0.0004867623.10215064
Transfer211747542024-11-12 23:01:1197 days ago1731452471IN
0x906fd331...7f0Db139a
0 ETH0.0005944928.21536112
Start Challenge ...200460572024-06-08 8:56:23255 days ago1717836983IN
0x906fd331...7f0Db139a
0 ETH0.0025155.858924
Start Challenge ...200036732024-06-02 10:55:11261 days ago1717325711IN
0x906fd331...7f0Db139a
0 ETH0.00490457.81930103
Start Challenge ...200036182024-06-02 10:44:11261 days ago1717325051IN
0x906fd331...7f0Db139a
0 ETH0.004831597.70246649
Start Challenge ...199897282024-05-31 12:10:47263 days ago1717157447IN
0x906fd331...7f0Db139a
0 ETH0.0076060712.12481425
Start Challenge ...199885792024-05-31 8:19:35263 days ago1717143575IN
0x906fd331...7f0Db139a
0 ETH0.003895316.42893744
Start Challenge ...149908542022-06-19 12:45:51975 days ago1655642751IN
0x906fd331...7f0Db139a
0 ETH0.0107193525
Start Challenge ...149908342022-06-19 12:41:10975 days ago1655642470IN
0x906fd331...7f0Db139a
0 ETH0.012963520
Start Challenge ...149907272022-06-19 12:15:21975 days ago1655640921IN
0x906fd331...7f0Db139a
0 ETH0.0156816725
Start Challenge ...149900882022-06-19 9:37:23975 days ago1655631443IN
0x906fd331...7f0Db139a
0 ETH0.0125419820
Start Challenge ...149900702022-06-19 9:34:16975 days ago1655631256IN
0x906fd331...7f0Db139a
0 ETH0.0084735420
Start Challenge ...149900582022-06-19 9:31:26975 days ago1655631086IN
0x906fd331...7f0Db139a
0 ETH0.0084735420
Start Challenge ...149900502022-06-19 9:29:53975 days ago1655630993IN
0x906fd331...7f0Db139a
0 ETH0.0084735420
Start Challenge ...149844672022-06-18 10:24:20976 days ago1655547860IN
0x906fd331...7f0Db139a
0 ETH0.0127103130
Start Challenge ...149844562022-06-18 10:21:02976 days ago1655547662IN
0x906fd331...7f0Db139a
0 ETH0.0127103130
Start Challenge ...149840862022-06-18 8:49:46976 days ago1655542186IN
0x906fd331...7f0Db139a
0 ETH0.0131199930
Start Challenge ...149838752022-06-18 7:58:02976 days ago1655539082IN
0x906fd331...7f0Db139a
0 ETH0.0311123470
Start Challenge ...149696912022-06-15 21:28:46978 days ago1655328526IN
0x906fd331...7f0Db139a
0 ETH0.0239429537
Start Challenge ...149635022022-06-14 19:47:03979 days ago1655236023IN
0x906fd331...7f0Db139a
0 ETH0.0323523550
Start Challenge ...149634752022-06-14 19:41:28979 days ago1655235688IN
0x906fd331...7f0Db139a
0 ETH0.0149835550
Start Challenge ...149631142022-06-14 18:14:47979 days ago1655230487IN
0x906fd331...7f0Db139a
0 ETH0.0194121330
View all transactions

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
DriipSettlementChallengeByPayment

Compiler Version
v0.5.11+commit.c082d0b4

Optimization Enabled:
Yes with 0 runs

Other Settings:
default evmVersion

Contract Source Code (Solidity)

/**
 *Submitted for verification at Etherscan.io on 2019-08-16
*/

pragma solidity >=0.4.25 <0.6.0;
pragma experimental ABIEncoderV2;


contract Modifiable {
    
    
    
    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)
            _;
    }
}

contract SelfDestructible {
    
    
    
    bool public selfDestructionDisabled;

    
    
    
    event SelfDestructionDisabledEvent(address wallet);
    event TriggerSelfDestructionEvent(address wallet);

    
    
    
    
    function destructor()
    public
    view
    returns (address);

    
    
    function disableSelfDestruction()
    public
    {
        
        require(destructor() == msg.sender);

        
        selfDestructionDisabled = true;

        
        emit SelfDestructionDisabledEvent(msg.sender);
    }

    
    function triggerSelfDestruction()
    public
    {
        
        require(destructor() == msg.sender);

        
        require(!selfDestructionDisabled);

        
        emit TriggerSelfDestructionEvent(msg.sender);

        
        selfdestruct(msg.sender);
    }
}

contract Ownable is Modifiable, SelfDestructible {
    
    
    
    address public deployer;
    address public operator;

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

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

    
    
    
    
    function destructor()
    public
    view
    returns (address)
    {
        return deployer;
    }

    
    
    function setDeployer(address newDeployer)
    public
    onlyDeployer
    notNullOrThisAddress(newDeployer)
    {
        if (newDeployer != deployer) {
            
            address oldDeployer = deployer;
            deployer = newDeployer;

            
            emit SetDeployerEvent(oldDeployer, newDeployer);
        }
    }

    
    
    function setOperator(address newOperator)
    public
    onlyOperator
    notNullOrThisAddress(newOperator)
    {
        if (newOperator != operator) {
            
            address oldOperator = operator;
            operator = newOperator;

            
            emit SetOperatorEvent(oldOperator, newOperator);
        }
    }

    
    
    function isDeployer()
    internal
    view
    returns (bool)
    {
        return msg.sender == deployer;
    }

    
    
    function isOperator()
    internal
    view
    returns (bool)
    {
        return msg.sender == operator;
    }

    
    
    
    function isDeployerOrOperator()
    internal
    view
    returns (bool)
    {
        return isDeployer() || isOperator();
    }

    
    
    modifier onlyDeployer() {
        require(isDeployer());
        _;
    }

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

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

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

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

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

contract Servable is Ownable {
    
    
    
    struct ServiceInfo {
        bool registered;
        uint256 activationTimestamp;
        mapping(bytes32 => bool) actionsEnabledMap;
        bytes32[] actionsList;
    }

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

    
    
    
    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);

    
    
    
    
    
    function setServiceActivationTimeout(uint256 timeoutInSeconds)
    public
    onlyDeployer
    {
        serviceActivationTimeout = timeoutInSeconds;

        
        emit ServiceActivationTimeoutEvent(timeoutInSeconds);
    }

    
    
    function registerService(address service)
    public
    onlyDeployer
    notNullOrThisAddress(service)
    {
        _registerService(service, 0);

        
        emit RegisterServiceEvent(service);
    }

    
    
    function registerServiceDeferred(address service)
    public
    onlyDeployer
    notNullOrThisAddress(service)
    {
        _registerService(service, serviceActivationTimeout);

        
        emit RegisterServiceDeferredEvent(service, serviceActivationTimeout);
    }

    
    
    function deregisterService(address service)
    public
    onlyDeployer
    notNullOrThisAddress(service)
    {
        require(registeredServicesMap[service].registered);

        registeredServicesMap[service].registered = false;

        
        emit DeregisterServiceEvent(service);
    }

    
    
    
    function enableServiceAction(address service, string memory 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 EnableServiceActionEvent(service, action);
    }

    
    
    
    function disableServiceAction(address service, string memory action)
    public
    onlyDeployer
    notNullOrThisAddress(service)
    {
        bytes32 actionHash = hashString(action);

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

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

        
        emit DisableServiceActionEvent(service, action);
    }

    
    
    
    function isRegisteredService(address service)
    public
    view
    returns (bool)
    {
        return registeredServicesMap[service].registered;
    }

    
    
    
    function isRegisteredActiveService(address service)
    public
    view
    returns (bool)
    {
        return isRegisteredService(service) && block.timestamp >= registeredServicesMap[service].activationTimestamp;
    }

    
    
    
    function isEnabledServiceAction(address service, string memory action)
    public
    view
    returns (bool)
    {
        bytes32 actionHash = hashString(action);
        return isRegisteredActiveService(service) && registeredServicesMap[service].actionsEnabledMap[actionHash];
    }

    
    
    
    function hashString(string memory _string)
    internal
    pure
    returns (bytes32)
    {
        return keccak256(abi.encodePacked(_string));
    }

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

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

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

library SafeMathIntLib {
    int256 constant INT256_MIN = int256((uint256(1) << 255));
    int256 constant INT256_MAX = int256(~((uint256(1) << 255)));

    
    
    
    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);
        
        require(b != - 1 || a != INT256_MIN);
        
        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;
    }

    
    
    
    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;
    }

    
    
    
    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);
    }

    
    
    
    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;
    }
}

library BlockNumbUintsLib {
    
    
    
    struct Entry {
        uint256 blockNumber;
        uint256 value;
    }

    struct BlockNumbUints {
        Entry[] entries;
    }

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

    function currentEntry(BlockNumbUints storage self)
    internal
    view
    returns (Entry memory)
    {
        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 memory)
    {
        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,
            "Later entry found [BlockNumbUintsLib.sol:62]"
        );

        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[] memory)
    {
        return self.entries;
    }

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

library BlockNumbIntsLib {
    
    
    
    struct Entry {
        uint256 blockNumber;
        int256 value;
    }

    struct BlockNumbInts {
        Entry[] entries;
    }

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

    function currentEntry(BlockNumbInts storage self)
    internal
    view
    returns (Entry memory)
    {
        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 memory)
    {
        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,
            "Later entry found [BlockNumbIntsLib.sol:62]"
        );

        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[] memory)
    {
        return self.entries;
    }

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

library ConstantsLib {
    
    function PARTS_PER()
    public
    pure
    returns (int256)
    {
        return 1e18;
    }
}

library BlockNumbDisdIntsLib {
    using SafeMathIntLib for int256;

    
    
    
    struct Discount {
        int256 tier;
        int256 value;
    }

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

    struct BlockNumbDisdInts {
        Entry[] entries;
    }

    
    
    
    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 memory)
    {
        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 memory)
    {
        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,
            "Later entry found [BlockNumbDisdIntsLib.sol:101]"
        );

        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[] memory discountTiers, int256[] memory discountValues)
    internal
    {
        require(discountTiers.length == discountValues.length, "Parameter array lengths mismatch [BlockNumbDisdIntsLib.sol:118]");

        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[] memory)
    {
        return self.entries;
    }

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

    
    function indexByTier(Discount[] memory discounts, int256 tier)
    internal
    pure
    returns (uint256)
    {
        require(0 < discounts.length, "No discounts found [BlockNumbDisdIntsLib.sol:161]");
        for (uint256 i = discounts.length; i > 0; i--)
            if (tier >= discounts[i - 1].tier)
                return i;
        return 0;
    }
}

library MonetaryTypesLib {
    
    
    
    struct Currency {
        address ct;
        uint256 id;
    }

    struct Figure {
        int256 amount;
        Currency currency;
    }

    struct NoncedAmount {
        uint256 nonce;
        int256 amount;
    }
}

library BlockNumbReferenceCurrenciesLib {
    
    
    
    struct Entry {
        uint256 blockNumber;
        MonetaryTypesLib.Currency currency;
    }

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

    
    
    
    function currentCurrency(BlockNumbReferenceCurrencies storage self, MonetaryTypesLib.Currency memory referenceCurrency)
    internal
    view
    returns (MonetaryTypesLib.Currency storage)
    {
        return currencyAt(self, referenceCurrency, block.number);
    }

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

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

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

    function addEntry(BlockNumbReferenceCurrencies storage self, uint256 blockNumber,
        MonetaryTypesLib.Currency memory referenceCurrency, MonetaryTypesLib.Currency memory 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,
            "Later entry found for currency [BlockNumbReferenceCurrenciesLib.sol:67]"
        );

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

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

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

    function indexByBlockNumber(BlockNumbReferenceCurrencies storage self, MonetaryTypesLib.Currency memory referenceCurrency, uint256 blockNumber)
    internal
    view
    returns (uint256)
    {
        require(0 < self.entriesByCurrency[referenceCurrency.ct][referenceCurrency.id].length, "No entries found for currency [BlockNumbReferenceCurrenciesLib.sol:97]");
        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();
    }
}

library BlockNumbFiguresLib {
    
    
    
    struct Entry {
        uint256 blockNumber;
        MonetaryTypesLib.Figure value;
    }

    struct BlockNumbFigures {
        Entry[] entries;
    }

    
    
    
    function currentValue(BlockNumbFigures storage self)
    internal
    view
    returns (MonetaryTypesLib.Figure storage)
    {
        return valueAt(self, block.number);
    }

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

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

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

    function addEntry(BlockNumbFigures storage self, uint256 blockNumber, MonetaryTypesLib.Figure memory value)
    internal
    {
        require(
            0 == self.entries.length ||
        blockNumber > self.entries[self.entries.length - 1].blockNumber,
            "Later entry found [BlockNumbFiguresLib.sol:65]"
        );

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

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

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

    function indexByBlockNumber(BlockNumbFigures storage self, uint256 blockNumber)
    internal
    view
    returns (uint256)
    {
        require(0 < self.entries.length, "No entries found [BlockNumbFiguresLib.sol:95]");
        for (uint256 i = self.entries.length - 1; i >= 0; i--)
            if (blockNumber >= self.entries[i].blockNumber)
                return i;
        revert();
    }
}

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 BlockNumbReferenceCurrenciesLib for BlockNumbReferenceCurrenciesLib.BlockNumbReferenceCurrencies;
    using BlockNumbFiguresLib for BlockNumbFiguresLib.BlockNumbFigures;

    
    
    
    string constant public OPERATIONAL_MODE_ACTION = "operational_mode";

    
    
    
    enum OperationalMode {Normal, Exit}

    
    
    
    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;

    BlockNumbReferenceCurrenciesLib.BlockNumbReferenceCurrencies private feeCurrencyByCurrencyBlockNumber;

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

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

    BlockNumbFiguresLib.BlockNumbFigures private operatorSettlementStakeByBlockNumber;

    uint256 public earliestSettlementBlockNumber;
    bool public earliestSettlementBlockNumberUpdateDisabled;

    
    
    
    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 SetOperatorSettlementStakeEvent(uint256 fromBlockNumber, int256 stakeAmount, address stakeCurrencyCt,
        uint256 stakeCurrencyId);
    event SetFraudStakeFractionEvent(uint256 fromBlockNumber, uint256 stakeFraction);
    event SetEarliestSettlementBlockNumberEvent(uint256 earliestSettlementBlockNumber);
    event DisableEarliestSettlementBlockNumberUpdateEvent();

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

    
    
    
    
    
    function setOperationalModeExit()
    public
    onlyEnabledServiceAction(OPERATIONAL_MODE_ACTION)
    {
        operationalMode = OperationalMode.Exit;
        emit SetOperationalModeExitEvent();
    }

    
    function isOperationalModeNormal()
    public
    view
    returns (bool)
    {
        return OperationalMode.Normal == operationalMode;
    }

    
    function isOperationalModeExit()
    public
    view
    returns (bool)
    {
        return OperationalMode.Exit == operationalMode;
    }

    
    
    function updateDelayBlocks()
    public
    view
    returns (uint256)
    {
        return updateDelayBlocksByBlockNumber.currentValue();
    }

    
    
    function updateDelayBlocksCount()
    public
    view
    returns (uint256)
    {
        return updateDelayBlocksByBlockNumber.count();
    }

    
    
    
    function setUpdateDelayBlocks(uint256 fromBlockNumber, uint256 newUpdateDelayBlocks)
    public
    onlyOperator
    onlyDelayedBlockNumber(fromBlockNumber)
    {
        updateDelayBlocksByBlockNumber.addEntry(fromBlockNumber, newUpdateDelayBlocks);
        emit SetUpdateDelayBlocksEvent(fromBlockNumber, newUpdateDelayBlocks);
    }

    
    
    function confirmationBlocks()
    public
    view
    returns (uint256)
    {
        return confirmationBlocksByBlockNumber.currentValue();
    }

    
    
    function confirmationBlocksCount()
    public
    view
    returns (uint256)
    {
        return confirmationBlocksByBlockNumber.count();
    }

    
    
    
    function setConfirmationBlocks(uint256 fromBlockNumber, uint256 newConfirmationBlocks)
    public
    onlyOperator
    onlyDelayedBlockNumber(fromBlockNumber)
    {
        confirmationBlocksByBlockNumber.addEntry(fromBlockNumber, newConfirmationBlocks);
        emit SetConfirmationBlocksEvent(fromBlockNumber, newConfirmationBlocks);
    }

    
    function tradeMakerFeesCount()
    public
    view
    returns (uint256)
    {
        return tradeMakerFeeByBlockNumber.count();
    }

    
    
    
    function tradeMakerFee(uint256 blockNumber, int256 discountTier)
    public
    view
    returns (int256)
    {
        return tradeMakerFeeByBlockNumber.discountedValueAt(blockNumber, discountTier);
    }

    
    
    
    
    
    function setTradeMakerFee(uint256 fromBlockNumber, int256 nominal, int256[] memory discountTiers, int256[] memory discountValues)
    public
    onlyOperator
    onlyDelayedBlockNumber(fromBlockNumber)
    {
        tradeMakerFeeByBlockNumber.addDiscountedEntry(fromBlockNumber, nominal, discountTiers, discountValues);
        emit SetTradeMakerFeeEvent(fromBlockNumber, nominal, discountTiers, discountValues);
    }

    
    function tradeTakerFeesCount()
    public
    view
    returns (uint256)
    {
        return tradeTakerFeeByBlockNumber.count();
    }

    
    
    
    function tradeTakerFee(uint256 blockNumber, int256 discountTier)
    public
    view
    returns (int256)
    {
        return tradeTakerFeeByBlockNumber.discountedValueAt(blockNumber, discountTier);
    }

    
    
    
    
    
    function setTradeTakerFee(uint256 fromBlockNumber, int256 nominal, int256[] memory discountTiers, int256[] memory discountValues)
    public
    onlyOperator
    onlyDelayedBlockNumber(fromBlockNumber)
    {
        tradeTakerFeeByBlockNumber.addDiscountedEntry(fromBlockNumber, nominal, discountTiers, discountValues);
        emit SetTradeTakerFeeEvent(fromBlockNumber, nominal, discountTiers, discountValues);
    }

    
    function paymentFeesCount()
    public
    view
    returns (uint256)
    {
        return paymentFeeByBlockNumber.count();
    }

    
    
    
    function paymentFee(uint256 blockNumber, int256 discountTier)
    public
    view
    returns (int256)
    {
        return paymentFeeByBlockNumber.discountedValueAt(blockNumber, discountTier);
    }

    
    
    
    
    
    function setPaymentFee(uint256 fromBlockNumber, int256 nominal, int256[] memory discountTiers, int256[] memory discountValues)
    public
    onlyOperator
    onlyDelayedBlockNumber(fromBlockNumber)
    {
        paymentFeeByBlockNumber.addDiscountedEntry(fromBlockNumber, nominal, discountTiers, discountValues);
        emit SetPaymentFeeEvent(fromBlockNumber, nominal, discountTiers, discountValues);
    }

    
    
    
    function currencyPaymentFeesCount(address currencyCt, uint256 currencyId)
    public
    view
    returns (uint256)
    {
        return currencyPaymentFeeByBlockNumber[currencyCt][currencyId].count();
    }

    
    
    
    
    
    
    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);
    }

    
    
    
    
    
    
    
    
    function setCurrencyPaymentFee(uint256 fromBlockNumber, address currencyCt, uint256 currencyId, int256 nominal,
        int256[] memory discountTiers, int256[] memory discountValues)
    public
    onlyOperator
    onlyDelayedBlockNumber(fromBlockNumber)
    {
        currencyPaymentFeeByBlockNumber[currencyCt][currencyId].addDiscountedEntry(
            fromBlockNumber, nominal, discountTiers, discountValues
        );
        emit SetCurrencyPaymentFeeEvent(
            fromBlockNumber, currencyCt, currencyId, nominal, discountTiers, discountValues
        );
    }

    
    function tradeMakerMinimumFeesCount()
    public
    view
    returns (uint256)
    {
        return tradeMakerMinimumFeeByBlockNumber.count();
    }

    
    
    function tradeMakerMinimumFee(uint256 blockNumber)
    public
    view
    returns (int256)
    {
        return tradeMakerMinimumFeeByBlockNumber.valueAt(blockNumber);
    }

    
    
    
    function setTradeMakerMinimumFee(uint256 fromBlockNumber, int256 nominal)
    public
    onlyOperator
    onlyDelayedBlockNumber(fromBlockNumber)
    {
        tradeMakerMinimumFeeByBlockNumber.addEntry(fromBlockNumber, nominal);
        emit SetTradeMakerMinimumFeeEvent(fromBlockNumber, nominal);
    }

    
    function tradeTakerMinimumFeesCount()
    public
    view
    returns (uint256)
    {
        return tradeTakerMinimumFeeByBlockNumber.count();
    }

    
    
    function tradeTakerMinimumFee(uint256 blockNumber)
    public
    view
    returns (int256)
    {
        return tradeTakerMinimumFeeByBlockNumber.valueAt(blockNumber);
    }

    
    
    
    function setTradeTakerMinimumFee(uint256 fromBlockNumber, int256 nominal)
    public
    onlyOperator
    onlyDelayedBlockNumber(fromBlockNumber)
    {
        tradeTakerMinimumFeeByBlockNumber.addEntry(fromBlockNumber, nominal);
        emit SetTradeTakerMinimumFeeEvent(fromBlockNumber, nominal);
    }

    
    function paymentMinimumFeesCount()
    public
    view
    returns (uint256)
    {
        return paymentMinimumFeeByBlockNumber.count();
    }

    
    
    function paymentMinimumFee(uint256 blockNumber)
    public
    view
    returns (int256)
    {
        return paymentMinimumFeeByBlockNumber.valueAt(blockNumber);
    }

    
    
    
    function setPaymentMinimumFee(uint256 fromBlockNumber, int256 nominal)
    public
    onlyOperator
    onlyDelayedBlockNumber(fromBlockNumber)
    {
        paymentMinimumFeeByBlockNumber.addEntry(fromBlockNumber, nominal);
        emit SetPaymentMinimumFeeEvent(fromBlockNumber, nominal);
    }

    
    
    
    function currencyPaymentMinimumFeesCount(address currencyCt, uint256 currencyId)
    public
    view
    returns (uint256)
    {
        return currencyPaymentMinimumFeeByBlockNumber[currencyCt][currencyId].count();
    }

    
    
    
    
    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);
    }

    
    
    
    
    
    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);
    }

    
    
    
    function feeCurrenciesCount(address currencyCt, uint256 currencyId)
    public
    view
    returns (uint256)
    {
        return feeCurrencyByCurrencyBlockNumber.count(MonetaryTypesLib.Currency(currencyCt, currencyId));
    }

    
    
    
    
    function feeCurrency(uint256 blockNumber, address currencyCt, uint256 currencyId)
    public
    view
    returns (address ct, uint256 id)
    {
        MonetaryTypesLib.Currency storage _feeCurrency = feeCurrencyByCurrencyBlockNumber.currencyAt(
            MonetaryTypesLib.Currency(currencyCt, currencyId), blockNumber
        );
        ct = _feeCurrency.ct;
        id = _feeCurrency.id;
    }

    
    
    
    
    
    
    function setFeeCurrency(uint256 fromBlockNumber, address referenceCurrencyCt, uint256 referenceCurrencyId,
        address feeCurrencyCt, uint256 feeCurrencyId)
    public
    onlyOperator
    onlyDelayedBlockNumber(fromBlockNumber)
    {
        feeCurrencyByCurrencyBlockNumber.addEntry(
            fromBlockNumber,
            MonetaryTypesLib.Currency(referenceCurrencyCt, referenceCurrencyId),
            MonetaryTypesLib.Currency(feeCurrencyCt, feeCurrencyId)
        );
        emit SetFeeCurrencyEvent(fromBlockNumber, referenceCurrencyCt, referenceCurrencyId,
            feeCurrencyCt, feeCurrencyId);
    }

    
    
    function walletLockTimeout()
    public
    view
    returns (uint256)
    {
        return walletLockTimeoutByBlockNumber.currentValue();
    }

    
    
    
    function setWalletLockTimeout(uint256 fromBlockNumber, uint256 timeoutInSeconds)
    public
    onlyOperator
    onlyDelayedBlockNumber(fromBlockNumber)
    {
        walletLockTimeoutByBlockNumber.addEntry(fromBlockNumber, timeoutInSeconds);
        emit SetWalletLockTimeoutEvent(fromBlockNumber, timeoutInSeconds);
    }

    
    
    function cancelOrderChallengeTimeout()
    public
    view
    returns (uint256)
    {
        return cancelOrderChallengeTimeoutByBlockNumber.currentValue();
    }

    
    
    
    function setCancelOrderChallengeTimeout(uint256 fromBlockNumber, uint256 timeoutInSeconds)
    public
    onlyOperator
    onlyDelayedBlockNumber(fromBlockNumber)
    {
        cancelOrderChallengeTimeoutByBlockNumber.addEntry(fromBlockNumber, timeoutInSeconds);
        emit SetCancelOrderChallengeTimeoutEvent(fromBlockNumber, timeoutInSeconds);
    }

    
    
    function settlementChallengeTimeout()
    public
    view
    returns (uint256)
    {
        return settlementChallengeTimeoutByBlockNumber.currentValue();
    }

    
    
    
    function setSettlementChallengeTimeout(uint256 fromBlockNumber, uint256 timeoutInSeconds)
    public
    onlyOperator
    onlyDelayedBlockNumber(fromBlockNumber)
    {
        settlementChallengeTimeoutByBlockNumber.addEntry(fromBlockNumber, timeoutInSeconds);
        emit SetSettlementChallengeTimeoutEvent(fromBlockNumber, timeoutInSeconds);
    }

    
    
    function fraudStakeFraction()
    public
    view
    returns (uint256)
    {
        return fraudStakeFractionByBlockNumber.currentValue();
    }

    
    
    
    
    function setFraudStakeFraction(uint256 fromBlockNumber, uint256 stakeFraction)
    public
    onlyOperator
    onlyDelayedBlockNumber(fromBlockNumber)
    {
        fraudStakeFractionByBlockNumber.addEntry(fromBlockNumber, stakeFraction);
        emit SetFraudStakeFractionEvent(fromBlockNumber, stakeFraction);
    }

    
    
    function walletSettlementStakeFraction()
    public
    view
    returns (uint256)
    {
        return walletSettlementStakeFractionByBlockNumber.currentValue();
    }

    
    
    
    
    function setWalletSettlementStakeFraction(uint256 fromBlockNumber, uint256 stakeFraction)
    public
    onlyOperator
    onlyDelayedBlockNumber(fromBlockNumber)
    {
        walletSettlementStakeFractionByBlockNumber.addEntry(fromBlockNumber, stakeFraction);
        emit SetWalletSettlementStakeFractionEvent(fromBlockNumber, stakeFraction);
    }

    
    
    function operatorSettlementStakeFraction()
    public
    view
    returns (uint256)
    {
        return operatorSettlementStakeFractionByBlockNumber.currentValue();
    }

    
    
    
    
    function setOperatorSettlementStakeFraction(uint256 fromBlockNumber, uint256 stakeFraction)
    public
    onlyOperator
    onlyDelayedBlockNumber(fromBlockNumber)
    {
        operatorSettlementStakeFractionByBlockNumber.addEntry(fromBlockNumber, stakeFraction);
        emit SetOperatorSettlementStakeFractionEvent(fromBlockNumber, stakeFraction);
    }

    
    
    function operatorSettlementStake()
    public
    view
    returns (int256 amount, address currencyCt, uint256 currencyId)
    {
        MonetaryTypesLib.Figure storage stake = operatorSettlementStakeByBlockNumber.currentValue();
        amount = stake.amount;
        currencyCt = stake.currency.ct;
        currencyId = stake.currency.id;
    }

    
    
    
    
    
    
    function setOperatorSettlementStake(uint256 fromBlockNumber, int256 stakeAmount,
        address stakeCurrencyCt, uint256 stakeCurrencyId)
    public
    onlyOperator
    onlyDelayedBlockNumber(fromBlockNumber)
    {
        MonetaryTypesLib.Figure memory stake = MonetaryTypesLib.Figure(stakeAmount, MonetaryTypesLib.Currency(stakeCurrencyCt, stakeCurrencyId));
        operatorSettlementStakeByBlockNumber.addEntry(fromBlockNumber, stake);
        emit SetOperatorSettlementStakeEvent(fromBlockNumber, stakeAmount, stakeCurrencyCt, stakeCurrencyId);
    }

    
    
    function setEarliestSettlementBlockNumber(uint256 _earliestSettlementBlockNumber)
    public
    onlyOperator
    {
        require(!earliestSettlementBlockNumberUpdateDisabled, "Earliest settlement block number update disabled [Configuration.sol:715]");

        earliestSettlementBlockNumber = _earliestSettlementBlockNumber;
        emit SetEarliestSettlementBlockNumberEvent(earliestSettlementBlockNumber);
    }

    
    
    function disableEarliestSettlementBlockNumberUpdate()
    public
    onlyOperator
    {
        earliestSettlementBlockNumberUpdateDisabled = true;
        emit DisableEarliestSettlementBlockNumberUpdateEvent();
    }

    
    
    
    modifier onlyDelayedBlockNumber(uint256 blockNumber) {
        require(
            0 == updateDelayBlocksByBlockNumber.count() ||
        blockNumber >= block.number + updateDelayBlocksByBlockNumber.currentValue(),
            "Block number not sufficiently delayed [Configuration.sol:735]"
        );
        _;
    }
}

contract Configurable is Ownable {
    
    
    
    Configuration public configuration;

    
    
    
    event SetConfigurationEvent(Configuration oldConfiguration, Configuration newConfiguration);

    
    
    
    
    
    function setConfiguration(Configuration newConfiguration)
    public
    onlyDeployer
    notNullAddress(address(newConfiguration))
    notSameAddresses(address(newConfiguration), address(configuration))
    {
        
        Configuration oldConfiguration = configuration;
        configuration = newConfiguration;

        
        emit SetConfigurationEvent(oldConfiguration, newConfiguration);
    }

    
    
    
    modifier configurationInitialized() {
        require(address(configuration) != address(0), "Configuration not initialized [Configurable.sol:52]");
        _;
    }
}

contract ConfigurableOperational is Configurable {
    
    
    
    modifier onlyOperationalModeNormal() {
        require(configuration.isOperationalModeNormal(), "Operational mode is not normal [ConfigurableOperational.sol:22]");
        _;
    }
}

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)
    {
        
        uint256 c = a / b;
        
        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;
    }

    
    
    
    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;
    }
}

library NahmiiTypesLib {
    
    
    
    enum ChallengePhase {Dispute, Closed}

    
    
    
    struct OriginFigure {
        uint256 originId;
        MonetaryTypesLib.Figure figure;
    }

    struct IntendedConjugateCurrency {
        MonetaryTypesLib.Currency intended;
        MonetaryTypesLib.Currency conjugate;
    }

    struct SingleFigureTotalOriginFigures {
        MonetaryTypesLib.Figure single;
        OriginFigure[] total;
    }

    struct TotalOriginFigures {
        OriginFigure[] total;
    }

    struct CurrentPreviousInt256 {
        int256 current;
        int256 previous;
    }

    struct SingleTotalInt256 {
        int256 single;
        int256 total;
    }

    struct IntendedConjugateCurrentPreviousInt256 {
        CurrentPreviousInt256 intended;
        CurrentPreviousInt256 conjugate;
    }

    struct IntendedConjugateSingleTotalInt256 {
        SingleTotalInt256 intended;
        SingleTotalInt256 conjugate;
    }

    struct WalletOperatorHashes {
        bytes32 wallet;
        bytes32 operator;
    }

    struct Signature {
        bytes32 r;
        bytes32 s;
        uint8 v;
    }

    struct Seal {
        bytes32 hash;
        Signature signature;
    }

    struct WalletOperatorSeal {
        Seal wallet;
        Seal operator;
    }
}

library PaymentTypesLib {
    
    
    
    enum PaymentPartyRole {Sender, Recipient}

    
    
    
    struct PaymentSenderParty {
        uint256 nonce;
        address wallet;

        NahmiiTypesLib.CurrentPreviousInt256 balances;

        NahmiiTypesLib.SingleFigureTotalOriginFigures fees;

        string data;
    }

    struct PaymentRecipientParty {
        uint256 nonce;
        address wallet;

        NahmiiTypesLib.CurrentPreviousInt256 balances;

        NahmiiTypesLib.TotalOriginFigures fees;
    }

    struct Operator {
        uint256 id;
        string data;
    }

    struct Payment {
        int256 amount;
        MonetaryTypesLib.Currency currency;

        PaymentSenderParty sender;
        PaymentRecipientParty recipient;

        
        NahmiiTypesLib.SingleTotalInt256 transfers;

        NahmiiTypesLib.WalletOperatorSeal seals;
        uint256 blockNumber;

        Operator operator;
    }

    
    
    
    function PAYMENT_KIND()
    public
    pure
    returns (string memory)
    {
        return "payment";
    }
}

contract PaymentHasher is Ownable {
    
    
    
    constructor(address deployer) Ownable(deployer) public {
    }

    
    
    
    function hashPaymentAsWallet(PaymentTypesLib.Payment memory payment)
    public
    pure
    returns (bytes32)
    {
        bytes32 amountCurrencyHash = hashPaymentAmountCurrency(payment);
        bytes32 senderHash = hashPaymentSenderPartyAsWallet(payment.sender);
        bytes32 recipientHash = hashAddress(payment.recipient.wallet);

        return keccak256(abi.encodePacked(amountCurrencyHash, senderHash, recipientHash));
    }

    function hashPaymentAsOperator(PaymentTypesLib.Payment memory payment)
    public
    pure
    returns (bytes32)
    {
        bytes32 walletSignatureHash = hashSignature(payment.seals.wallet.signature);
        bytes32 senderHash = hashPaymentSenderPartyAsOperator(payment.sender);
        bytes32 recipientHash = hashPaymentRecipientPartyAsOperator(payment.recipient);
        bytes32 transfersHash = hashSingleTotalInt256(payment.transfers);
        bytes32 operatorHash = hashString(payment.operator.data);

        return keccak256(abi.encodePacked(
                walletSignatureHash, senderHash, recipientHash, transfersHash, operatorHash
            ));
    }

    function hashPaymentAmountCurrency(PaymentTypesLib.Payment memory payment)
    public
    pure
    returns (bytes32)
    {
        return keccak256(abi.encodePacked(
                payment.amount,
                payment.currency.ct,
                payment.currency.id
            ));
    }

    function hashPaymentSenderPartyAsWallet(
        PaymentTypesLib.PaymentSenderParty memory paymentSenderParty)
    public
    pure
    returns (bytes32)
    {
        return keccak256(abi.encodePacked(
                paymentSenderParty.wallet,
                paymentSenderParty.data
            ));
    }

    function hashPaymentSenderPartyAsOperator(
        PaymentTypesLib.PaymentSenderParty memory paymentSenderParty)
    public
    pure
    returns (bytes32)
    {
        bytes32 rootHash = hashUint256(paymentSenderParty.nonce);
        bytes32 balancesHash = hashCurrentPreviousInt256(paymentSenderParty.balances);
        bytes32 singleFeeHash = hashFigure(paymentSenderParty.fees.single);
        bytes32 totalFeesHash = hashOriginFigures(paymentSenderParty.fees.total);

        return keccak256(abi.encodePacked(
                rootHash, balancesHash, singleFeeHash, totalFeesHash
            ));
    }

    function hashPaymentRecipientPartyAsOperator(
        PaymentTypesLib.PaymentRecipientParty memory paymentRecipientParty)
    public
    pure
    returns (bytes32)
    {
        bytes32 rootHash = hashUint256(paymentRecipientParty.nonce);
        bytes32 balancesHash = hashCurrentPreviousInt256(paymentRecipientParty.balances);
        bytes32 totalFeesHash = hashOriginFigures(paymentRecipientParty.fees.total);

        return keccak256(abi.encodePacked(
                rootHash, balancesHash, totalFeesHash
            ));
    }

    function hashCurrentPreviousInt256(
        NahmiiTypesLib.CurrentPreviousInt256 memory currentPreviousInt256)
    public
    pure
    returns (bytes32)
    {
        return keccak256(abi.encodePacked(
                currentPreviousInt256.current,
                currentPreviousInt256.previous
            ));
    }

    function hashSingleTotalInt256(
        NahmiiTypesLib.SingleTotalInt256 memory singleTotalInt256)
    public
    pure
    returns (bytes32)
    {
        return keccak256(abi.encodePacked(
                singleTotalInt256.single,
                singleTotalInt256.total
            ));
    }

    function hashFigure(MonetaryTypesLib.Figure memory figure)
    public
    pure
    returns (bytes32)
    {
        return keccak256(abi.encodePacked(
                figure.amount,
                figure.currency.ct,
                figure.currency.id
            ));
    }

    function hashOriginFigures(NahmiiTypesLib.OriginFigure[] memory originFigures)
    public
    pure
    returns (bytes32)
    {
        bytes32 hash;
        for (uint256 i = 0; i < originFigures.length; i++) {
            hash = keccak256(abi.encodePacked(
                    hash,
                    originFigures[i].originId,
                    originFigures[i].figure.amount,
                    originFigures[i].figure.currency.ct,
                    originFigures[i].figure.currency.id
                )
            );
        }
        return hash;
    }

    function hashUint256(uint256 _uint256)
    public
    pure
    returns (bytes32)
    {
        return keccak256(abi.encodePacked(_uint256));
    }

    function hashString(string memory _string)
    public
    pure
    returns (bytes32)
    {
        return keccak256(abi.encodePacked(_string));
    }

    function hashAddress(address _address)
    public
    pure
    returns (bytes32)
    {
        return keccak256(abi.encodePacked(_address));
    }

    function hashSignature(NahmiiTypesLib.Signature memory signature)
    public
    pure
    returns (bytes32)
    {
        return keccak256(abi.encodePacked(
                signature.v,
                signature.r,
                signature.s
            ));
    }
}

contract PaymentHashable is Ownable {
    
    
    
    PaymentHasher public paymentHasher;

    
    
    
    event SetPaymentHasherEvent(PaymentHasher oldPaymentHasher, PaymentHasher newPaymentHasher);

    
    
    
    
    
    function setPaymentHasher(PaymentHasher newPaymentHasher)
    public
    onlyDeployer
    notNullAddress(address(newPaymentHasher))
    notSameAddresses(address(newPaymentHasher), address(paymentHasher))
    {
        
        PaymentHasher oldPaymentHasher = paymentHasher;
        paymentHasher = newPaymentHasher;

        
        emit SetPaymentHasherEvent(oldPaymentHasher, newPaymentHasher);
    }

    
    
    
    modifier paymentHasherInitialized() {
        require(address(paymentHasher) != address(0), "Payment hasher not initialized [PaymentHashable.sol:52]");
        _;
    }
}

contract SignerManager is Ownable {
    using SafeMathUintLib for uint256;
    
    
    
    
    mapping(address => uint256) public signerIndicesMap; 
    address[] public signers;

    
    
    
    event RegisterSignerEvent(address signer);

    
    
    
    constructor(address deployer) Ownable(deployer) public {
        registerSigner(deployer);
    }

    
    
    
    
    
    
    function isSigner(address _address)
    public
    view
    returns (bool)
    {
        return 0 < signerIndicesMap[_address];
    }

    
    
    function signersCount()
    public
    view
    returns (uint256)
    {
        return signers.length;
    }

    
    
    
    function signerIndex(address _address)
    public
    view
    returns (uint256)
    {
        require(isSigner(_address), "Address not signer [SignerManager.sol:71]");
        return signerIndicesMap[_address] - 1;
    }

    
    
    function registerSigner(address newSigner)
    public
    onlyOperator
    notNullOrThisAddress(newSigner)
    {
        if (0 == signerIndicesMap[newSigner]) {
            
            signers.push(newSigner);
            signerIndicesMap[newSigner] = signers.length;

            
            emit RegisterSignerEvent(newSigner);
        }
    }

    
    
    
    
    function signersByIndices(uint256 low, uint256 up)
    public
    view
    returns (address[] memory)
    {
        require(0 < signers.length, "No signers found [SignerManager.sol:101]");
        require(low <= up, "Bounds parameters mismatch [SignerManager.sol:102]");

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

        return _signers;
    }
}

contract SignerManageable is Ownable {
    
    
    
    SignerManager public signerManager;

    
    
    
    event SetSignerManagerEvent(address oldSignerManager, address newSignerManager);

    
    
    
    constructor(address manager) public notNullAddress(manager) {
        signerManager = SignerManager(manager);
    }

    
    
    
    
    
    function setSignerManager(address newSignerManager)
    public
    onlyDeployer
    notNullOrThisAddress(newSignerManager)
    {
        if (newSignerManager != address(signerManager)) {
            
            address oldSignerManager = address(signerManager);
            signerManager = SignerManager(newSignerManager);

            
            emit SetSignerManagerEvent(oldSignerManager, newSignerManager);
        }
    }

    
    
    
    
    
    
    function ethrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s)
    public
    pure
    returns (address)
    {
        bytes memory prefix = "\x19Ethereum Signed Message:\n32";
        bytes32 prefixedHash = keccak256(abi.encodePacked(prefix, hash));
        return ecrecover(prefixedHash, v, r, s);
    }

    
    
    
    
    
    
    function isSignedByRegisteredSigner(bytes32 hash, uint8 v, bytes32 r, bytes32 s)
    public
    view
    returns (bool)
    {
        return signerManager.isSigner(ethrecover(hash, v, r, s));
    }

    
    
    
    
    
    
    
    function isSignedBy(bytes32 hash, uint8 v, bytes32 r, bytes32 s, address signer)
    public
    pure
    returns (bool)
    {
        return signer == ethrecover(hash, v, r, s);
    }

    
    
    modifier signerManagerInitialized() {
        require(address(signerManager) != address(0), "Signer manager not initialized [SignerManageable.sol:105]");
        _;
    }
}

contract Validator is Ownable, SignerManageable, Configurable, PaymentHashable {
    using SafeMathIntLib for int256;
    using SafeMathUintLib for uint256;

    
    
    
    constructor(address deployer, address signerManager) Ownable(deployer) SignerManageable(signerManager) public {
    }

    
    
    
    function isGenuineOperatorSignature(bytes32 hash, NahmiiTypesLib.Signature memory signature)
    public
    view
    returns (bool)
    {
        return isSignedByRegisteredSigner(hash, signature.v, signature.r, signature.s);
    }

    function isGenuineWalletSignature(bytes32 hash, NahmiiTypesLib.Signature memory signature, address wallet)
    public
    pure
    returns (bool)
    {
        return isSignedBy(hash, signature.v, signature.r, signature.s, wallet);
    }

    function isGenuinePaymentWalletHash(PaymentTypesLib.Payment memory payment)
    public
    view
    returns (bool)
    {
        return paymentHasher.hashPaymentAsWallet(payment) == payment.seals.wallet.hash;
    }

    function isGenuinePaymentOperatorHash(PaymentTypesLib.Payment memory payment)
    public
    view
    returns (bool)
    {
        return paymentHasher.hashPaymentAsOperator(payment) == payment.seals.operator.hash;
    }

    function isGenuinePaymentWalletSeal(PaymentTypesLib.Payment memory payment)
    public
    view
    returns (bool)
    {
        return isGenuinePaymentWalletHash(payment)
        && isGenuineWalletSignature(payment.seals.wallet.hash, payment.seals.wallet.signature, payment.sender.wallet);
    }

    function isGenuinePaymentOperatorSeal(PaymentTypesLib.Payment memory payment)
    public
    view
    returns (bool)
    {
        return isGenuinePaymentOperatorHash(payment)
        && isGenuineOperatorSignature(payment.seals.operator.hash, payment.seals.operator.signature);
    }

    function isGenuinePaymentSeals(PaymentTypesLib.Payment memory payment)
    public
    view
    returns (bool)
    {
        return isGenuinePaymentWalletSeal(payment) && isGenuinePaymentOperatorSeal(payment);
    }

    
    function isGenuinePaymentFeeOfFungible(PaymentTypesLib.Payment memory payment)
    public
    view
    returns (bool)
    {
        int256 feePartsPer = int256(ConstantsLib.PARTS_PER());

        int256 feeAmount = payment.amount
        .mul(
            configuration.currencyPaymentFee(
                payment.blockNumber, payment.currency.ct, payment.currency.id, payment.amount
            )
        ).div(feePartsPer);

        if (1 > feeAmount)
            feeAmount = 1;

        return (payment.sender.fees.single.amount == feeAmount);
    }

    
    function isGenuinePaymentFeeOfNonFungible(PaymentTypesLib.Payment memory payment)
    public
    view
    returns (bool)
    {
        (address feeCurrencyCt, uint256 feeCurrencyId) = configuration.feeCurrency(
            payment.blockNumber, payment.currency.ct, payment.currency.id
        );

        return feeCurrencyCt == payment.sender.fees.single.currency.ct
        && feeCurrencyId == payment.sender.fees.single.currency.id;
    }

    
    function isGenuinePaymentSenderOfFungible(PaymentTypesLib.Payment memory payment)
    public
    view
    returns (bool)
    {
        return (payment.sender.wallet != payment.recipient.wallet)
        && (!signerManager.isSigner(payment.sender.wallet))
        && (payment.sender.balances.current == payment.sender.balances.previous.sub(payment.transfers.single).sub(payment.sender.fees.single.amount));
    }

    
    function isGenuinePaymentRecipientOfFungible(PaymentTypesLib.Payment memory payment)
    public
    pure
    returns (bool)
    {
        return (payment.sender.wallet != payment.recipient.wallet)
        && (payment.recipient.balances.current == payment.recipient.balances.previous.add(payment.transfers.single));
    }

    
    function isGenuinePaymentSenderOfNonFungible(PaymentTypesLib.Payment memory payment)
    public
    view
    returns (bool)
    {
        return (payment.sender.wallet != payment.recipient.wallet)
        && (!signerManager.isSigner(payment.sender.wallet));
    }

    
    function isGenuinePaymentRecipientOfNonFungible(PaymentTypesLib.Payment memory payment)
    public
    pure
    returns (bool)
    {
        return (payment.sender.wallet != payment.recipient.wallet);
    }

    function isSuccessivePaymentsPartyNonces(
        PaymentTypesLib.Payment memory firstPayment,
        PaymentTypesLib.PaymentPartyRole firstPaymentPartyRole,
        PaymentTypesLib.Payment memory lastPayment,
        PaymentTypesLib.PaymentPartyRole lastPaymentPartyRole
    )
    public
    pure
    returns (bool)
    {
        uint256 firstNonce = (PaymentTypesLib.PaymentPartyRole.Sender == firstPaymentPartyRole ? firstPayment.sender.nonce : firstPayment.recipient.nonce);
        uint256 lastNonce = (PaymentTypesLib.PaymentPartyRole.Sender == lastPaymentPartyRole ? lastPayment.sender.nonce : lastPayment.recipient.nonce);
        return lastNonce == firstNonce.add(1);
    }

    function isGenuineSuccessivePaymentsBalances(
        PaymentTypesLib.Payment memory firstPayment,
        PaymentTypesLib.PaymentPartyRole firstPaymentPartyRole,
        PaymentTypesLib.Payment memory lastPayment,
        PaymentTypesLib.PaymentPartyRole lastPaymentPartyRole,
        int256 delta
    )
    public
    pure
    returns (bool)
    {
        NahmiiTypesLib.CurrentPreviousInt256 memory firstCurrentPreviousBalances = (PaymentTypesLib.PaymentPartyRole.Sender == firstPaymentPartyRole ? firstPayment.sender.balances : firstPayment.recipient.balances);
        NahmiiTypesLib.CurrentPreviousInt256 memory lastCurrentPreviousBalances = (PaymentTypesLib.PaymentPartyRole.Sender == lastPaymentPartyRole ? lastPayment.sender.balances : lastPayment.recipient.balances);

        return lastCurrentPreviousBalances.previous == firstCurrentPreviousBalances.current.add(delta);
    }

    function isGenuineSuccessivePaymentsTotalFees(
        PaymentTypesLib.Payment memory firstPayment,
        PaymentTypesLib.Payment memory lastPayment
    )
    public
    pure
    returns (bool)
    {
        MonetaryTypesLib.Figure memory firstTotalFee = getProtocolFigureByCurrency(firstPayment.sender.fees.total, lastPayment.sender.fees.single.currency);
        MonetaryTypesLib.Figure memory lastTotalFee = getProtocolFigureByCurrency(lastPayment.sender.fees.total, lastPayment.sender.fees.single.currency);
        return lastTotalFee.amount == firstTotalFee.amount.add(lastPayment.sender.fees.single.amount);
    }

    function isPaymentParty(PaymentTypesLib.Payment memory payment, address wallet)
    public
    pure
    returns (bool)
    {
        return wallet == payment.sender.wallet || wallet == payment.recipient.wallet;
    }

    function isPaymentSender(PaymentTypesLib.Payment memory payment, address wallet)
    public
    pure
    returns (bool)
    {
        return wallet == payment.sender.wallet;
    }

    function isPaymentRecipient(PaymentTypesLib.Payment memory payment, address wallet)
    public
    pure
    returns (bool)
    {
        return wallet == payment.recipient.wallet;
    }

    function isPaymentCurrency(PaymentTypesLib.Payment memory payment, MonetaryTypesLib.Currency memory currency)
    public
    pure
    returns (bool)
    {
        return currency.ct == payment.currency.ct && currency.id == payment.currency.id;
    }

    function isPaymentCurrencyNonFungible(PaymentTypesLib.Payment memory payment)
    public
    pure
    returns (bool)
    {
        return payment.currency.ct != payment.sender.fees.single.currency.ct
        || payment.currency.id != payment.sender.fees.single.currency.id;
    }

    
    
    
    function getProtocolFigureByCurrency(NahmiiTypesLib.OriginFigure[] memory originFigures, MonetaryTypesLib.Currency memory currency)
    private
    pure
    returns (MonetaryTypesLib.Figure memory) {
        for (uint256 i = 0; i < originFigures.length; i++)
            if (originFigures[i].figure.currency.ct == currency.ct && originFigures[i].figure.currency.id == currency.id
            && originFigures[i].originId == 0)
                return originFigures[i].figure;
        return MonetaryTypesLib.Figure(0, currency);
    }
}

library TradeTypesLib {
    
    
    
    enum CurrencyRole {Intended, Conjugate}
    enum LiquidityRole {Maker, Taker}
    enum Intention {Buy, Sell}
    enum TradePartyRole {Buyer, Seller}

    
    
    
    struct OrderPlacement {
        Intention intention;

        int256 amount;
        NahmiiTypesLib.IntendedConjugateCurrency currencies;
        int256 rate;

        NahmiiTypesLib.CurrentPreviousInt256 residuals;
    }

    struct Order {
        uint256 nonce;
        address wallet;

        OrderPlacement placement;

        NahmiiTypesLib.WalletOperatorSeal seals;
        uint256 blockNumber;
        uint256 operatorId;
    }

    struct TradeOrder {
        int256 amount;
        NahmiiTypesLib.WalletOperatorHashes hashes;
        NahmiiTypesLib.CurrentPreviousInt256 residuals;
    }

    struct TradeParty {
        uint256 nonce;
        address wallet;

        uint256 rollingVolume;

        LiquidityRole liquidityRole;

        TradeOrder order;

        NahmiiTypesLib.IntendedConjugateCurrentPreviousInt256 balances;

        NahmiiTypesLib.SingleFigureTotalOriginFigures fees;
    }

    struct Trade {
        uint256 nonce;

        int256 amount;
        NahmiiTypesLib.IntendedConjugateCurrency currencies;
        int256 rate;

        TradeParty buyer;
        TradeParty seller;

        
        
        NahmiiTypesLib.IntendedConjugateSingleTotalInt256 transfers;

        NahmiiTypesLib.Seal seal;
        uint256 blockNumber;
        uint256 operatorId;
    }

    
    
    
    function TRADE_KIND()
    public
    pure
    returns (string memory)
    {
        return "trade";
    }

    function ORDER_KIND()
    public
    pure
    returns (string memory)
    {
        return "order";
    }
}

contract Validatable is Ownable {
    
    
    
    Validator public validator;

    
    
    
    event SetValidatorEvent(Validator oldValidator, Validator newValidator);

    
    
    
    
    
    function setValidator(Validator newValidator)
    public
    onlyDeployer
    notNullAddress(address(newValidator))
    notSameAddresses(address(newValidator), address(validator))
    {
        
        Validator oldValidator = validator;
        validator = newValidator;

        
        emit SetValidatorEvent(oldValidator, newValidator);
    }

    
    
    
    modifier validatorInitialized() {
        require(address(validator) != address(0), "Validator not initialized [Validatable.sol:55]");
        _;
    }

    modifier onlyOperatorSealedPayment(PaymentTypesLib.Payment memory payment) {
        require(validator.isGenuinePaymentOperatorSeal(payment), "Payment operator seal not genuine [Validatable.sol:60]");
        _;
    }

    modifier onlySealedPayment(PaymentTypesLib.Payment memory payment) {
        require(validator.isGenuinePaymentSeals(payment), "Payment seals not genuine [Validatable.sol:65]");
        _;
    }

    modifier onlyPaymentParty(PaymentTypesLib.Payment memory payment, address wallet) {
        require(validator.isPaymentParty(payment, wallet), "Wallet not payment party [Validatable.sol:70]");
        _;
    }

    modifier onlyPaymentSender(PaymentTypesLib.Payment memory payment, address wallet) {
        require(validator.isPaymentSender(payment, wallet), "Wallet not payment sender [Validatable.sol:75]");
        _;
    }
}

contract AuthorizableServable is Servable {
    
    
    
    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;

    
    
    
    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);

    
    
    
    
    
    
    function authorizeInitialService(address service)
    public
    onlyDeployer
    notNullOrThisAddress(service)
    {
        require(!initialServiceAuthorizationDisabled);
        require(msg.sender != service);

        
        require(registeredServicesMap[service].registered);

        
        initialServiceAuthorizedMap[service] = true;

        
        emit AuthorizeInitialServiceEvent(msg.sender, service);
    }

    
    
    function disableInitialServiceAuthorization()
    public
    onlyDeployer
    {
        initialServiceAuthorizationDisabled = true;
    }

    
    
    
    function authorizeRegisteredService(address service)
    public
    notNullOrThisAddress(service)
    {
        require(msg.sender != service);

        
        require(registeredServicesMap[service].registered);

        
        require(!initialServiceAuthorizedMap[service]);

        
        serviceWalletAuthorizedMap[service][msg.sender] = true;

        
        emit AuthorizeRegisteredServiceEvent(msg.sender, service);
    }

    
    
    
    function unauthorizeRegisteredService(address service)
    public
    notNullOrThisAddress(service)
    {
        require(msg.sender != service);

        
        require(registeredServicesMap[service].registered);

        
        if (initialServiceAuthorizedMap[service])
            initialServiceWalletUnauthorizedMap[service][msg.sender] = true;

        
        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 UnauthorizeRegisteredServiceEvent(msg.sender, service);
    }

    
    
    
    
    function isAuthorizedRegisteredService(address service, address wallet)
    public
    view
    returns (bool)
    {
        return isRegisteredActiveService(service) &&
        (isInitialServiceAuthorizedForWallet(service, wallet) || serviceWalletAuthorizedMap[service][wallet]);
    }

    
    
    
    
    function authorizeRegisteredServiceAction(address service, string memory action)
    public
    notNullOrThisAddress(service)
    {
        require(msg.sender != service);

        bytes32 actionHash = hashString(action);

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

        
        require(!initialServiceAuthorizedMap[service]);

        
        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 AuthorizeRegisteredServiceActionEvent(msg.sender, service, action);
    }

    
    
    
    
    function unauthorizeRegisteredServiceAction(address service, string memory action)
    public
    notNullOrThisAddress(service)
    {
        require(msg.sender != service);

        bytes32 actionHash = hashString(action);

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

        
        require(!initialServiceAuthorizedMap[service]);

        
        serviceActionWalletAuthorizedMap[service][actionHash][msg.sender] = false;

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

    
    
    
    
    
    function isAuthorizedRegisteredServiceAction(address service, string memory action, address wallet)
    public
    view
    returns (bool)
    {
        bytes32 actionHash = hashString(action);

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

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

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

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

contract WalletLocker is Ownable, Configurable, AuthorizableServable {
    using SafeMathUintLib for uint256;

    
    
    
    struct FungibleLock {
        address locker;
        address currencyCt;
        uint256 currencyId;
        int256 amount;
        uint256 visibleTime;
        uint256 unlockTime;
    }

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

    
    
    
    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;

    
    
    
    event LockFungibleByProxyEvent(address lockedWallet, address lockerWallet, int256 amount,
        address currencyCt, uint256 currencyId, uint256 visibleTimeoutInSeconds);
    event LockNonFungibleByProxyEvent(address lockedWallet, address lockerWallet, int256[] ids,
        address currencyCt, uint256 currencyId, uint256 visibleTimeoutInSeconds);
    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(address deployer) Ownable(deployer)
    public
    {
    }

    
    
    

    
    
    
    
    
    
    
    function lockFungibleByProxy(address lockedWallet, address lockerWallet, int256 amount,
        address currencyCt, uint256 currencyId, uint256 visibleTimeoutInSeconds)
    public
    onlyAuthorizedService(lockedWallet)
    {
        
        require(lockedWallet != lockerWallet);

        
        uint256 lockIndex = lockedCurrencyLockerFungibleLockIndex[lockedWallet][currencyCt][currencyId][lockedWallet];

        
        require(
            (0 == lockIndex) ||
            (block.timestamp >= walletFungibleLocks[lockedWallet][lockIndex - 1].unlockTime)
        );

        
        if (0 == lockIndex) {
            lockIndex = ++(walletFungibleLocks[lockedWallet].length);
            lockedCurrencyLockerFungibleLockIndex[lockedWallet][currencyCt][currencyId][lockerWallet] = lockIndex;
            walletCurrencyFungibleLockCount[lockedWallet][currencyCt][currencyId]++;
        }

        
        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].visibleTime =
        block.timestamp.add(visibleTimeoutInSeconds);
        walletFungibleLocks[lockedWallet][lockIndex - 1].unlockTime =
        block.timestamp.add(configuration.walletLockTimeout());

        
        emit LockFungibleByProxyEvent(lockedWallet, lockerWallet, amount, currencyCt, currencyId, visibleTimeoutInSeconds);
    }

    
    
    
    
    
    
    
    function lockNonFungibleByProxy(address lockedWallet, address lockerWallet, int256[] memory ids,
        address currencyCt, uint256 currencyId, uint256 visibleTimeoutInSeconds)
    public
    onlyAuthorizedService(lockedWallet)
    {
        
        require(lockedWallet != lockerWallet);

        
        uint256 lockIndex = lockedCurrencyLockerNonFungibleLockIndex[lockedWallet][currencyCt][currencyId][lockedWallet];

        
        require(
            (0 == lockIndex) ||
            (block.timestamp >= walletNonFungibleLocks[lockedWallet][lockIndex - 1].unlockTime)
        );

        
        if (0 == lockIndex) {
            lockIndex = ++(walletNonFungibleLocks[lockedWallet].length);
            lockedCurrencyLockerNonFungibleLockIndex[lockedWallet][currencyCt][currencyId][lockerWallet] = lockIndex;
            walletCurrencyNonFungibleLockCount[lockedWallet][currencyCt][currencyId]++;
        }

        
        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].visibleTime =
        block.timestamp.add(visibleTimeoutInSeconds);
        walletNonFungibleLocks[lockedWallet][lockIndex - 1].unlockTime =
        block.timestamp.add(configuration.walletLockTimeout());

        
        emit LockNonFungibleByProxyEvent(lockedWallet, lockerWallet, ids, currencyCt, currencyId, visibleTimeoutInSeconds);
    }

    
    
    
    
    
    
    function unlockFungible(address lockedWallet, address lockerWallet, address currencyCt, uint256 currencyId)
    public
    {
        
        uint256 lockIndex = lockedCurrencyLockerFungibleLockIndex[lockedWallet][currencyCt][currencyId][lockerWallet];

        
        if (0 == lockIndex)
            return;

        
        require(
            block.timestamp >= walletFungibleLocks[lockedWallet][lockIndex - 1].unlockTime
        );

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

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

    
    
    
    
    
    
    function unlockFungibleByProxy(address lockedWallet, address lockerWallet, address currencyCt, uint256 currencyId)
    public
    onlyAuthorizedService(lockedWallet)
    {
        
        uint256 lockIndex = lockedCurrencyLockerFungibleLockIndex[lockedWallet][currencyCt][currencyId][lockerWallet];

        
        if (0 == lockIndex)
            return;

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

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

    
    
    
    
    
    
    function unlockNonFungible(address lockedWallet, address lockerWallet, address currencyCt, uint256 currencyId)
    public
    {
        
        uint256 lockIndex = lockedCurrencyLockerNonFungibleLockIndex[lockedWallet][currencyCt][currencyId][lockerWallet];

        
        if (0 == lockIndex)
            return;

        
        require(
            block.timestamp >= walletNonFungibleLocks[lockedWallet][lockIndex - 1].unlockTime
        );

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

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

    
    
    
    
    
    
    function unlockNonFungibleByProxy(address lockedWallet, address lockerWallet, address currencyCt, uint256 currencyId)
    public
    onlyAuthorizedService(lockedWallet)
    {
        
        uint256 lockIndex = lockedCurrencyLockerNonFungibleLockIndex[lockedWallet][currencyCt][currencyId][lockerWallet];

        
        if (0 == lockIndex)
            return;

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

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

    
    
    
    function fungibleLocksCount(address wallet)
    public
    view
    returns (uint256)
    {
        return walletFungibleLocks[wallet].length;
    }

    
    
    
    function nonFungibleLocksCount(address wallet)
    public
    view
    returns (uint256)
    {
        return walletNonFungibleLocks[wallet].length;
    }

    
    
    
    
    
    
    function lockedAmount(address lockedWallet, address lockerWallet, address currencyCt, uint256 currencyId)
    public
    view
    returns (int256)
    {
        uint256 lockIndex = lockedCurrencyLockerFungibleLockIndex[lockedWallet][currencyCt][currencyId][lockerWallet];

        if (0 == lockIndex || block.timestamp < walletFungibleLocks[lockedWallet][lockIndex - 1].visibleTime)
            return 0;

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

    
    
    
    
    
    
    function lockedIdsCount(address lockedWallet, address lockerWallet, address currencyCt, uint256 currencyId)
    public
    view
    returns (uint256)
    {
        uint256 lockIndex = lockedCurrencyLockerNonFungibleLockIndex[lockedWallet][currencyCt][currencyId][lockerWallet];

        if (0 == lockIndex || block.timestamp < walletNonFungibleLocks[lockedWallet][lockIndex - 1].visibleTime)
            return 0;

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

    
    
    
    
    
    
    
    
    function lockedIdsByIndices(address lockedWallet, address lockerWallet, address currencyCt, uint256 currencyId,
        uint256 low, uint256 up)
    public
    view
    returns (int256[] memory)
    {
        uint256 lockIndex = lockedCurrencyLockerNonFungibleLockIndex[lockedWallet][currencyCt][currencyId][lockerWallet];

        if (0 == lockIndex || block.timestamp < walletNonFungibleLocks[lockedWallet][lockIndex - 1].visibleTime)
            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;
    }

    
    
    
    function isLocked(address wallet)
    public
    view
    returns (bool)
    {
        return 0 < walletFungibleLocks[wallet].length ||
        0 < walletNonFungibleLocks[wallet].length;
    }

    
    
    
    
    
    function isLocked(address wallet, address currencyCt, uint256 currencyId)
    public
    view
    returns (bool)
    {
        return 0 < walletCurrencyFungibleLockCount[wallet][currencyCt][currencyId] ||
        0 < walletCurrencyNonFungibleLockCount[wallet][currencyCt][currencyId];
    }

    
    
    
    
    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];
    }

    
    
    
    
    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[] memory)
    {
        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;
    }
}

contract WalletLockable is Ownable {
    
    
    
    WalletLocker public walletLocker;
    bool public walletLockerFrozen;

    
    
    
    event SetWalletLockerEvent(WalletLocker oldWalletLocker, WalletLocker newWalletLocker);
    event FreezeWalletLockerEvent();

    
    
    
    
    
    function setWalletLocker(WalletLocker newWalletLocker)
    public
    onlyDeployer
    notNullAddress(address(newWalletLocker))
    notSameAddresses(address(newWalletLocker), address(walletLocker))
    {
        
        require(!walletLockerFrozen, "Wallet locker frozen [WalletLockable.sol:43]");

        
        WalletLocker oldWalletLocker = walletLocker;
        walletLocker = newWalletLocker;

        
        emit SetWalletLockerEvent(oldWalletLocker, newWalletLocker);
    }

    
    
    function freezeWalletLocker()
    public
    onlyDeployer
    {
        walletLockerFrozen = true;

        
        emit FreezeWalletLockerEvent();
    }

    
    
    
    modifier walletLockerInitialized() {
        require(address(walletLocker) != address(0), "Wallet locker not initialized [WalletLockable.sol:69]");
        _;
    }
}

library CurrenciesLib {
    using SafeMathUintLib for uint256;

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

    
    
    
    function add(Currencies storage self, address currencyCt, uint256 currencyId)
    internal
    {
        
        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
    {
        
        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, "Index out of bounds [CurrenciesLib.sol:51]");

        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 memory)
    {
        require(index < self.currencies.length, "Index out of bounds [CurrenciesLib.sol:85]");
        return self.currencies[index];
    }

    function getByIndices(Currencies storage self, uint256 low, uint256 up)
    internal
    view
    returns (MonetaryTypesLib.Currency[] memory)
    {
        require(0 < self.currencies.length, "No currencies found [CurrenciesLib.sol:94]");
        require(low <= up, "Bounds parameters mismatch [CurrenciesLib.sol:95]");

        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;
    }
}

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

    
    
    
    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;
    }

    
    
    
    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 setByBlockNumber(Balance storage self, int256 amount, address currencyCt, uint256 currencyId,
        uint256 blockNumber)
    internal
    {
        self.amountByCurrency[currencyCt][currencyId] = amount;

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

        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 addByBlockNumber(Balance storage self, int256 amount, address currencyCt, uint256 currencyId,
        uint256 blockNumber)
    internal
    {
        self.amountByCurrency[currencyCt][currencyId] = self.amountByCurrency[currencyCt][currencyId].add(amount);

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

        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 subByBlockNumber(Balance storage self, int256 amount, address currencyCt, uint256 currencyId,
        uint256 blockNumber)
    internal
    {
        self.amountByCurrency[currencyCt][currencyId] = self.amountByCurrency[currencyCt][currencyId].sub(amount);

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

        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;
    }
}

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

    
    
    
    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;
    }

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

    function getByIndices(Balance storage self, address currencyCt, uint256 currencyId, uint256 indexLow, uint256 indexUp)
    internal
    view
    returns (int256[] memory)
    {
        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[] memory, 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[] memory, 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[] memory, 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[] memory 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;
    }
}

contract BalanceTracker is Ownable, Servable {
    using SafeMathIntLib for int256;
    using SafeMathUintLib for uint256;
    using FungibleBalanceLib for FungibleBalanceLib.Balance;
    using NonFungibleBalanceLib for NonFungibleBalanceLib.Balance;

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

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

    
    
    
    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(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);
    }

    
    
    
    
    
    
    
    
    
    function get(address wallet, bytes32 _type, address currencyCt, uint256 currencyId)
    public
    view
    returns (int256)
    {
        return walletMap[wallet].fungibleBalanceByType[_type].get(currencyCt, currencyId);
    }

    
    
    
    
    
    
    
    
    function getByIndices(address wallet, bytes32 _type, address currencyCt, uint256 currencyId,
        uint256 indexLow, uint256 indexUp)
    public
    view
    returns (int256[] memory)
    {
        return walletMap[wallet].nonFungibleBalanceByType[_type].getByIndices(
            currencyCt, currencyId, indexLow, indexUp
        );
    }

    
    
    
    
    
    
    function getAll(address wallet, bytes32 _type, address currencyCt, uint256 currencyId)
    public
    view
    returns (int256[] memory)
    {
        return walletMap[wallet].nonFungibleBalanceByType[_type].get(
            currencyCt, currencyId
        );
    }

    
    
    
    
    
    
    function idsCount(address wallet, bytes32 _type, address currencyCt, uint256 currencyId)
    public
    view
    returns (uint256)
    {
        return walletMap[wallet].nonFungibleBalanceByType[_type].idsCount(
            currencyCt, currencyId
        );
    }

    
    
    
    
    
    
    
    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
        );
    }

    
    
    
    
    
    
    
    function set(address wallet, bytes32 _type, int256 value, address currencyCt, uint256 currencyId, bool fungible)
    public
    onlyActiveService
    {
        
        if (fungible)
            walletMap[wallet].fungibleBalanceByType[_type].set(
                value, currencyCt, currencyId
            );

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

        
        _updateTrackedBalanceTypes(_type);

        
        _updateTrackedWallets(wallet);
    }

    
    
    
    
    
    
    function setIds(address wallet, bytes32 _type, int256[] memory ids, address currencyCt, uint256 currencyId)
    public
    onlyActiveService
    {
        
        walletMap[wallet].nonFungibleBalanceByType[_type].set(
            ids, currencyCt, currencyId
        );

        
        _updateTrackedBalanceTypes(_type);

        
        _updateTrackedWallets(wallet);
    }

    
    
    
    
    
    
    
    function add(address wallet, bytes32 _type, int256 value, address currencyCt, uint256 currencyId,
        bool fungible)
    public
    onlyActiveService
    {
        
        if (fungible)
            walletMap[wallet].fungibleBalanceByType[_type].add(
                value, currencyCt, currencyId
            );
        else
            walletMap[wallet].nonFungibleBalanceByType[_type].add(
                value, currencyCt, currencyId
            );

        
        _updateTrackedBalanceTypes(_type);

        
        _updateTrackedWallets(wallet);
    }

    
    
    
    
    
    
    
    function sub(address wallet, bytes32 _type, int256 value, address currencyCt, uint256 currencyId,
        bool fungible)
    public
    onlyActiveService
    {
        
        if (fungible)
            walletMap[wallet].fungibleBalanceByType[_type].sub(
                value, currencyCt, currencyId
            );
        else
            walletMap[wallet].nonFungibleBalanceByType[_type].sub(
                value, currencyCt, currencyId
            );

        
        _updateTrackedWallets(wallet);
    }

    
    
    
    
    
    
    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);
    }

    
    
    
    
    
    
    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);
    }

    
    
    
    
    
    
    function fungibleRecordsCount(address wallet, bytes32 _type, address currencyCt, uint256 currencyId)
    public
    view
    returns (uint256)
    {
        return walletMap[wallet].fungibleBalanceByType[_type].recordsCount(currencyCt, currencyId);
    }

    
    
    
    
    
    
    
    
    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);
    }

    
    
    
    
    
    
    
    
    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);
    }

    
    
    
    
    
    
    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);
    }

    
    
    
    
    
    
    function nonFungibleRecordsCount(address wallet, bytes32 _type, address currencyCt, uint256 currencyId)
    public
    view
    returns (uint256)
    {
        return walletMap[wallet].nonFungibleBalanceByType[_type].recordsCount(currencyCt, currencyId);
    }

    
    
    
    
    
    
    
    
    function nonFungibleRecordByIndex(address wallet, bytes32 _type, address currencyCt, uint256 currencyId,
        uint256 index)
    public
    view
    returns (int256[] memory ids, uint256 blockNumber)
    {
        return walletMap[wallet].nonFungibleBalanceByType[_type].recordByIndex(currencyCt, currencyId, index);
    }

    
    
    
    
    
    
    
    
    function nonFungibleRecordByBlockNumber(address wallet, bytes32 _type, address currencyCt, uint256 currencyId,
        uint256 _blockNumber)
    public
    view
    returns (int256[] memory ids, uint256 blockNumber)
    {
        return walletMap[wallet].nonFungibleBalanceByType[_type].recordByBlockNumber(currencyCt, currencyId, _blockNumber);
    }

    
    
    
    
    
    
    function lastNonFungibleRecord(address wallet, bytes32 _type, address currencyCt, uint256 currencyId)
    public
    view
    returns (int256[] memory ids, uint256 blockNumber)
    {
        return walletMap[wallet].nonFungibleBalanceByType[_type].lastRecord(currencyCt, currencyId);
    }

    
    
    function trackedBalanceTypesCount()
    public
    view
    returns (uint256)
    {
        return trackedBalanceTypes.length;
    }

    
    
    function trackedWalletsCount()
    public
    view
    returns (uint256)
    {
        return trackedWallets.length;
    }

    
    
    function allBalanceTypes()
    public
    view
    returns (bytes32[] memory)
    {
        return _allBalanceTypes;
    }

    
    
    function activeBalanceTypes()
    public
    view
    returns (bytes32[] memory)
    {
        return _activeBalanceTypes;
    }

    
    
    
    
    function trackedWalletsByIndices(uint256 low, uint256 up)
    public
    view
    returns (address[] memory)
    {
        require(0 < trackedWallets.length, "No tracked wallets found [BalanceTracker.sol:473]");
        require(low <= up, "Bounds parameters mismatch [BalanceTracker.sol:474]");

        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;
    }

    
    
    
    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;
        }
    }
}

contract BalanceTrackable is Ownable {
    
    
    
    BalanceTracker public balanceTracker;
    bool public balanceTrackerFrozen;

    
    
    
    event SetBalanceTrackerEvent(BalanceTracker oldBalanceTracker, BalanceTracker newBalanceTracker);
    event FreezeBalanceTrackerEvent();

    
    
    
    
    
    function setBalanceTracker(BalanceTracker newBalanceTracker)
    public
    onlyDeployer
    notNullAddress(address(newBalanceTracker))
    notSameAddresses(address(newBalanceTracker), address(balanceTracker))
    {
        
        require(!balanceTrackerFrozen, "Balance tracker frozen [BalanceTrackable.sol:43]");

        
        BalanceTracker oldBalanceTracker = balanceTracker;
        balanceTracker = newBalanceTracker;

        
        emit SetBalanceTrackerEvent(oldBalanceTracker, newBalanceTracker);
    }

    
    
    function freezeBalanceTracker()
    public
    onlyDeployer
    {
        balanceTrackerFrozen = true;

        
        emit FreezeBalanceTrackerEvent();
    }

    
    
    
    modifier balanceTrackerInitialized() {
        require(address(balanceTracker) != address(0), "Balance tracker not initialized [BalanceTrackable.sol:69]");
        _;
    }
}

contract Beneficiary {
    
    
    
    function receiveEthersTo(address wallet, string memory balanceType)
    public
    payable;

    
    
    
    
    
    
    
    
    function receiveTokensTo(address wallet, string memory balanceType, int256 amount, address currencyCt,
        uint256 currencyId, string memory standard)
    public;
}

contract AccrualBeneficiary is Beneficiary {
    
    
    
    event CloseAccrualPeriodEvent();

    
    
    
    function closeAccrualPeriod(MonetaryTypesLib.Currency[] memory)
    public
    {
        emit CloseAccrualPeriodEvent();
    }
}

contract TransferController {
    
    
    
    event CurrencyTransferred(address from, address to, uint256 value,
        address currencyCt, uint256 currencyId);

    
    
    
    function isFungible()
    public
    view
    returns (bool);

    function standard()
    public
    view
    returns (string memory);

    
    function receive(address from, address to, uint256 value, address currencyCt, uint256 currencyId)
    public;

    
    function approve(address to, uint256 value, address currencyCt, uint256 currencyId)
    public;

    
    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)"));
    }
}

contract TransferControllerManager is Ownable {
    
    
    
    struct CurrencyInfo {
        bytes32 standard;
        bool blacklisted;
    }

    
    
    
    mapping(bytes32 => address) public registeredTransferControllers;
    mapping(address => CurrencyInfo) public registeredCurrencies;

    
    
    
    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(address deployer) Ownable(deployer) public {
    }

    
    
    
    function registerTransferController(string calldata standard, address controller)
    external
    onlyDeployer
    notNullAddress(controller)
    {
        require(bytes(standard).length > 0, "Empty standard not supported [TransferControllerManager.sol:58]");
        bytes32 standardHash = keccak256(abi.encodePacked(standard));

        registeredTransferControllers[standardHash] = controller;

        
        emit RegisterTransferControllerEvent(standard, controller);
    }

    function reassociateTransferController(string calldata oldStandard, string calldata newStandard, address controller)
    external
    onlyDeployer
    notNullAddress(controller)
    {
        require(bytes(newStandard).length > 0, "Empty new standard not supported [TransferControllerManager.sol:72]");
        bytes32 oldStandardHash = keccak256(abi.encodePacked(oldStandard));
        bytes32 newStandardHash = keccak256(abi.encodePacked(newStandard));

        require(registeredTransferControllers[oldStandardHash] != address(0), "Old standard not registered [TransferControllerManager.sol:76]");
        require(registeredTransferControllers[newStandardHash] == address(0), "New standard previously registered [TransferControllerManager.sol:77]");

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

        
        emit ReassociateTransferControllerEvent(oldStandard, newStandard, controller);
    }

    function registerCurrency(address currencyCt, string calldata standard)
    external
    onlyOperator
    notNullAddress(currencyCt)
    {
        require(bytes(standard).length > 0, "Empty standard not supported [TransferControllerManager.sol:91]");
        bytes32 standardHash = keccak256(abi.encodePacked(standard));

        require(registeredCurrencies[currencyCt].standard == bytes32(0), "Currency previously registered [TransferControllerManager.sol:94]");

        registeredCurrencies[currencyCt].standard = standardHash;

        
        emit RegisterCurrencyEvent(currencyCt, standard);
    }

    function deregisterCurrency(address currencyCt)
    external
    onlyOperator
    {
        require(registeredCurrencies[currencyCt].standard != 0, "Currency not registered [TransferControllerManager.sol:106]");

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

        
        emit DeregisterCurrencyEvent(currencyCt);
    }

    function blacklistCurrency(address currencyCt)
    external
    onlyOperator
    {
        require(registeredCurrencies[currencyCt].standard != bytes32(0), "Currency not registered [TransferControllerManager.sol:119]");

        registeredCurrencies[currencyCt].blacklisted = true;

        
        emit BlacklistCurrencyEvent(currencyCt);
    }

    function whitelistCurrency(address currencyCt)
    external
    onlyOperator
    {
        require(registeredCurrencies[currencyCt].standard != bytes32(0), "Currency not registered [TransferControllerManager.sol:131]");

        registeredCurrencies[currencyCt].blacklisted = false;

        
        emit WhitelistCurrencyEvent(currencyCt);
    }

    
    function transferController(address currencyCt, string memory standard)
    public
    view
    returns (TransferController)
    {
        if (bytes(standard).length > 0) {
            bytes32 standardHash = keccak256(abi.encodePacked(standard));

            require(registeredTransferControllers[standardHash] != address(0), "Standard not registered [TransferControllerManager.sol:150]");
            return TransferController(registeredTransferControllers[standardHash]);
        }

        require(registeredCurrencies[currencyCt].standard != bytes32(0), "Currency not registered [TransferControllerManager.sol:154]");
        require(!registeredCurrencies[currencyCt].blacklisted, "Currency blacklisted [TransferControllerManager.sol:155]");

        address controllerAddress = registeredTransferControllers[registeredCurrencies[currencyCt].standard];
        require(controllerAddress != address(0), "No matching transfer controller [TransferControllerManager.sol:158]");

        return TransferController(controllerAddress);
    }
}

contract TransferControllerManageable is Ownable {
    
    
    
    TransferControllerManager public transferControllerManager;

    
    
    
    event SetTransferControllerManagerEvent(TransferControllerManager oldTransferControllerManager,
        TransferControllerManager newTransferControllerManager);

    
    
    
    
    
    function setTransferControllerManager(TransferControllerManager newTransferControllerManager)
    public
    onlyDeployer
    notNullAddress(address(newTransferControllerManager))
    notSameAddresses(address(newTransferControllerManager), address(transferControllerManager))
    {
        
        TransferControllerManager oldTransferControllerManager = transferControllerManager;
        transferControllerManager = newTransferControllerManager;

        
        emit SetTransferControllerManagerEvent(oldTransferControllerManager, newTransferControllerManager);
    }

    
    function transferController(address currencyCt, string memory standard)
    internal
    view
    returns (TransferController)
    {
        return transferControllerManager.transferController(currencyCt, standard);
    }

    
    
    
    modifier transferControllerManagerInitialized() {
        require(address(transferControllerManager) != address(0), "Transfer controller manager not initialized [TransferControllerManageable.sol:63]");
        _;
    }
}

library TxHistoryLib {
    
    
    
    struct AssetEntry {
        int256 amount;
        uint256 blockNumber;
        address currencyCt;      
        uint256 currencyId;
    }

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

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

    
    
    
    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, "Index ouf of bounds [TxHistoryLib.sol:56]");

        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, "Index out of bounds [TxHistoryLib.sol:77]");

        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, "Index out of bounds [TxHistoryLib.sol:98]");

        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, "Index out of bounds [TxHistoryLib.sol:119]");

        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;
    }
}

contract SecurityBond is Ownable, Configurable, 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;

    
    
    
    string constant public REWARD_ACTION = "reward";
    string constant public DEPRIVE_ACTION = "deprive";

    
    
    
    struct FractionalReward {
        uint256 fraction;
        uint256 nonce;
        uint256 unlockTime;
    }

    struct AbsoluteReward {
        int256 amount;
        uint256 nonce;
        uint256 unlockTime;
    }

    
    
    
    FungibleBalanceLib.Balance private deposited;
    TxHistoryLib.TxHistory private txHistory;
    CurrenciesLib.Currencies private inUseCurrencies;

    mapping(address => FractionalReward) public fractionalRewardByWallet;

    mapping(address => mapping(address => mapping(uint256 => AbsoluteReward))) public absoluteRewardByWallet;

    mapping(address => mapping(address => mapping(uint256 => uint256))) public claimNonceByWalletCurrency;

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

    mapping(address => uint256) public nonceByWallet;

    
    
    
    event ReceiveEvent(address from, int256 amount, address currencyCt, uint256 currencyId);
    event RewardFractionalEvent(address wallet, uint256 fraction, uint256 unlockTimeoutInSeconds);
    event RewardAbsoluteEvent(address wallet, int256 amount, address currencyCt, uint256 currencyId,
        uint256 unlockTimeoutInSeconds);
    event DepriveFractionalEvent(address wallet);
    event DepriveAbsoluteEvent(address wallet, address currencyCt, uint256 currencyId);
    event ClaimAndTransferToBeneficiaryEvent(address from, Beneficiary beneficiary, 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(address deployer) Ownable(deployer) Servable() public {
    }

    
    
    
    
    function() external payable {
        receiveEthersTo(msg.sender, "");
    }

    
    
    function receiveEthersTo(address wallet, string memory)
    public
    payable
    {
        int256 amount = SafeMathIntLib.toNonZeroInt256(msg.value);

        
        deposited.add(amount, address(0), 0);
        txHistory.addDeposit(amount, address(0), 0);

        
        inUseCurrencies.add(address(0), 0);

        
        emit ReceiveEvent(wallet, amount, address(0), 0);
    }

    
    
    
    
    
    function receiveTokens(string memory, int256 amount, address currencyCt,
        uint256 currencyId, string memory standard)
    public
    {
        receiveTokensTo(msg.sender, "", amount, currencyCt, currencyId, standard);
    }

    
    
    
    
    
    
    function receiveTokensTo(address wallet, string memory, int256 amount, address currencyCt,
        uint256 currencyId, string memory standard)
    public
    {
        require(amount.isNonZeroPositiveInt256(), "Amount not strictly positive [SecurityBond.sol:145]");

        
        TransferController controller = transferController(currencyCt, standard);
        (bool success,) = address(controller).delegatecall(
            abi.encodeWithSelector(
                controller.getReceiveSignature(), msg.sender, this, uint256(amount), currencyCt, currencyId
            )
        );
        require(success, "Reception by controller failed [SecurityBond.sol:154]");

        
        deposited.add(amount, currencyCt, currencyId);
        txHistory.addDeposit(amount, currencyCt, currencyId);

        
        inUseCurrencies.add(currencyCt, currencyId);

        
        emit ReceiveEvent(wallet, amount, currencyCt, currencyId);
    }

    
    
    function depositsCount()
    public
    view
    returns (uint256)
    {
        return txHistory.depositsCount();
    }

    
    
    function deposit(uint index)
    public
    view
    returns (int256 amount, uint256 blockNumber, address currencyCt, uint256 currencyId)
    {
        return txHistory.deposit(index);
    }

    
    
    
    
    function depositedBalance(address currencyCt, uint256 currencyId)
    public
    view
    returns (int256)
    {
        return deposited.get(currencyCt, currencyId);
    }

    
    
    
    
    
    function depositedFractionalBalance(address currencyCt, uint256 currencyId, uint256 fraction)
    public
    view
    returns (int256)
    {
        return deposited.get(currencyCt, currencyId)
        .mul(SafeMathIntLib.toInt256(fraction))
        .div(ConstantsLib.PARTS_PER());
    }

    
    
    
    
    function stagedBalance(address wallet, address currencyCt, uint256 currencyId)
    public
    view
    returns (int256)
    {
        return stagedByWallet[wallet].get(currencyCt, currencyId);
    }

    
    
    function inUseCurrenciesCount()
    public
    view
    returns (uint256)
    {
        return inUseCurrencies.count();
    }

    
    
    
    
    function inUseCurrenciesByIndices(uint256 low, uint256 up)
    public
    view
    returns (MonetaryTypesLib.Currency[] memory)
    {
        return inUseCurrencies.getByIndices(low, up);
    }

    
    
    
    
    
    
    function rewardFractional(address wallet, uint256 fraction, uint256 unlockTimeoutInSeconds)
    public
    notNullAddress(wallet)
    onlyEnabledServiceAction(REWARD_ACTION)
    {
        
        fractionalRewardByWallet[wallet].fraction = fraction.clampMax(uint256(ConstantsLib.PARTS_PER()));
        fractionalRewardByWallet[wallet].nonce = ++nonceByWallet[wallet];
        fractionalRewardByWallet[wallet].unlockTime = block.timestamp.add(unlockTimeoutInSeconds);

        
        emit RewardFractionalEvent(wallet, fraction, unlockTimeoutInSeconds);
    }

    
    
    
    
    
    
    
    
    function rewardAbsolute(address wallet, int256 amount, address currencyCt, uint256 currencyId,
        uint256 unlockTimeoutInSeconds)
    public
    notNullAddress(wallet)
    onlyEnabledServiceAction(REWARD_ACTION)
    {
        
        absoluteRewardByWallet[wallet][currencyCt][currencyId].amount = amount;
        absoluteRewardByWallet[wallet][currencyCt][currencyId].nonce = ++nonceByWallet[wallet];
        absoluteRewardByWallet[wallet][currencyCt][currencyId].unlockTime = block.timestamp.add(unlockTimeoutInSeconds);

        
        emit RewardAbsoluteEvent(wallet, amount, currencyCt, currencyId, unlockTimeoutInSeconds);
    }

    
    
    function depriveFractional(address wallet)
    public
    onlyEnabledServiceAction(DEPRIVE_ACTION)
    {
        
        fractionalRewardByWallet[wallet].fraction = 0;
        fractionalRewardByWallet[wallet].nonce = ++nonceByWallet[wallet];
        fractionalRewardByWallet[wallet].unlockTime = 0;

        
        emit DepriveFractionalEvent(wallet);
    }

    
    
    
    
    function depriveAbsolute(address wallet, address currencyCt, uint256 currencyId)
    public
    onlyEnabledServiceAction(DEPRIVE_ACTION)
    {
        
        absoluteRewardByWallet[wallet][currencyCt][currencyId].amount = 0;
        absoluteRewardByWallet[wallet][currencyCt][currencyId].nonce = ++nonceByWallet[wallet];
        absoluteRewardByWallet[wallet][currencyCt][currencyId].unlockTime = 0;

        
        emit DepriveAbsoluteEvent(wallet, currencyCt, currencyId);
    }

    
    
    
    
    
    
    function claimAndTransferToBeneficiary(Beneficiary beneficiary, string memory balanceType, address currencyCt,
        uint256 currencyId, string memory standard)
    public
    {
        
        int256 claimedAmount = _claim(msg.sender, currencyCt, currencyId);

        
        deposited.sub(claimedAmount, currencyCt, currencyId);

        
        if (address(0) == currencyCt && 0 == currencyId)
            beneficiary.receiveEthersTo.value(uint256(claimedAmount))(msg.sender, balanceType);

        else {
            TransferController controller = transferController(currencyCt, standard);
            (bool success,) = address(controller).delegatecall(
                abi.encodeWithSelector(
                    controller.getApproveSignature(), address(beneficiary), uint256(claimedAmount), currencyCt, currencyId
                )
            );
            require(success, "Approval by controller failed [SecurityBond.sol:350]");
            beneficiary.receiveTokensTo(msg.sender, balanceType, claimedAmount, currencyCt, currencyId, standard);
        }

        
        emit ClaimAndTransferToBeneficiaryEvent(msg.sender, beneficiary, balanceType, claimedAmount, currencyCt, currencyId, standard);
    }

    
    
    
    function claimAndStage(address currencyCt, uint256 currencyId)
    public
    {
        
        int256 claimedAmount = _claim(msg.sender, currencyCt, currencyId);

        
        deposited.sub(claimedAmount, currencyCt, currencyId);

        
        stagedByWallet[msg.sender].add(claimedAmount, currencyCt, currencyId);

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

    
    
    
    
    
    function withdraw(int256 amount, address currencyCt, uint256 currencyId, string memory standard)
    public
    {
        
        require(amount.isNonZeroPositiveInt256(), "Amount not strictly positive [SecurityBond.sol:386]");

        
        amount = amount.clampMax(stagedByWallet[msg.sender].get(currencyCt, currencyId));

        
        stagedByWallet[msg.sender].sub(amount, currencyCt, currencyId);

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

        else {
            TransferController controller = transferController(currencyCt, standard);
            (bool success,) = address(controller).delegatecall(
                abi.encodeWithSelector(
                    controller.getDispatchSignature(), address(this), msg.sender, uint256(amount), currencyCt, currencyId
                )
            );
            require(success, "Dispatch by controller failed [SecurityBond.sol:405]");
        }

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

    
    
    
    function _claim(address wallet, address currencyCt, uint256 currencyId)
    private
    returns (int256)
    {
        
        uint256 claimNonce = fractionalRewardByWallet[wallet].nonce.clampMin(
            absoluteRewardByWallet[wallet][currencyCt][currencyId].nonce
        );

        
        require(
            claimNonce > claimNonceByWalletCurrency[wallet][currencyCt][currencyId],
            "Claim nonce not strictly greater than previously claimed nonce [SecurityBond.sol:425]"
        );

        
        int256 claimAmount = _fractionalRewardAmountByWalletCurrency(wallet, currencyCt, currencyId).add(
            _absoluteRewardAmountByWalletCurrency(wallet, currencyCt, currencyId)
        ).clampMax(
            deposited.get(currencyCt, currencyId)
        );

        
        require(claimAmount.isNonZeroPositiveInt256(), "Claim amount not strictly positive [SecurityBond.sol:438]");

        
        claimNonceByWalletCurrency[wallet][currencyCt][currencyId] = claimNonce;

        return claimAmount;
    }

    function _fractionalRewardAmountByWalletCurrency(address wallet, address currencyCt, uint256 currencyId)
    private
    view
    returns (int256)
    {
        if (
            claimNonceByWalletCurrency[wallet][currencyCt][currencyId] < fractionalRewardByWallet[wallet].nonce &&
            block.timestamp >= fractionalRewardByWallet[wallet].unlockTime
        )
            return deposited.get(currencyCt, currencyId)
            .mul(SafeMathIntLib.toInt256(fractionalRewardByWallet[wallet].fraction))
            .div(ConstantsLib.PARTS_PER());

        else
            return 0;
    }

    function _absoluteRewardAmountByWalletCurrency(address wallet, address currencyCt, uint256 currencyId)
    private
    view
    returns (int256)
    {
        if (
            claimNonceByWalletCurrency[wallet][currencyCt][currencyId] < absoluteRewardByWallet[wallet][currencyCt][currencyId].nonce &&
            block.timestamp >= absoluteRewardByWallet[wallet][currencyCt][currencyId].unlockTime
        )
            return absoluteRewardByWallet[wallet][currencyCt][currencyId].amount.clampMax(
                deposited.get(currencyCt, currencyId)
            );

        else
            return 0;
    }
}

contract SecurityBondable is Ownable {
    
    
    
    SecurityBond public securityBond;

    
    
    
    event SetSecurityBondEvent(SecurityBond oldSecurityBond, SecurityBond newSecurityBond);

    
    
    
    
    
    function setSecurityBond(SecurityBond newSecurityBond)
    public
    onlyDeployer
    notNullAddress(address(newSecurityBond))
    notSameAddresses(address(newSecurityBond), address(securityBond))
    {
        
        SecurityBond oldSecurityBond = securityBond;
        securityBond = newSecurityBond;

        
        emit SetSecurityBondEvent(oldSecurityBond, newSecurityBond);
    }

    
    
    
    modifier securityBondInitialized() {
        require(address(securityBond) != address(0), "Security bond not initialized [SecurityBondable.sol:52]");
        _;
    }
}

contract FraudChallenge is Ownable, Servable {
    
    
    
    string constant public ADD_SEIZED_WALLET_ACTION = "add_seized_wallet";
    string constant public ADD_DOUBLE_SPENDER_WALLET_ACTION = "add_double_spender_wallet";
    string constant public ADD_FRAUDULENT_ORDER_ACTION = "add_fraudulent_order";
    string constant public ADD_FRAUDULENT_TRADE_ACTION = "add_fraudulent_trade";
    string constant public ADD_FRAUDULENT_PAYMENT_ACTION = "add_fraudulent_payment";

    
    
    
    address[] public doubleSpenderWallets;
    mapping(address => bool) public doubleSpenderByWallet;

    bytes32[] public fraudulentOrderHashes;
    mapping(bytes32 => bool) public fraudulentByOrderHash;

    bytes32[] public fraudulentTradeHashes;
    mapping(bytes32 => bool) public fraudulentByTradeHash;

    bytes32[] public fraudulentPaymentHashes;
    mapping(bytes32 => bool) public fraudulentByPaymentHash;

    
    
    
    event AddDoubleSpenderWalletEvent(address wallet);
    event AddFraudulentOrderHashEvent(bytes32 hash);
    event AddFraudulentTradeHashEvent(bytes32 hash);
    event AddFraudulentPaymentHashEvent(bytes32 hash);

    
    
    
    constructor(address deployer) Ownable(deployer) public {
    }

    
    
    
    
    
    
    function isDoubleSpenderWallet(address wallet)
    public
    view
    returns (bool)
    {
        return doubleSpenderByWallet[wallet];
    }

    
    
    function doubleSpenderWalletsCount()
    public
    view
    returns (uint256)
    {
        return doubleSpenderWallets.length;
    }

    
    
    function addDoubleSpenderWallet(address wallet)
    public
    onlyEnabledServiceAction(ADD_DOUBLE_SPENDER_WALLET_ACTION) {
        if (!doubleSpenderByWallet[wallet]) {
            doubleSpenderWallets.push(wallet);
            doubleSpenderByWallet[wallet] = true;
            emit AddDoubleSpenderWalletEvent(wallet);
        }
    }

    
    function fraudulentOrderHashesCount()
    public
    view
    returns (uint256)
    {
        return fraudulentOrderHashes.length;
    }

    
    
    function isFraudulentOrderHash(bytes32 hash)
    public
    view returns (bool) {
        return fraudulentByOrderHash[hash];
    }

    
    function addFraudulentOrderHash(bytes32 hash)
    public
    onlyEnabledServiceAction(ADD_FRAUDULENT_ORDER_ACTION)
    {
        if (!fraudulentByOrderHash[hash]) {
            fraudulentByOrderHash[hash] = true;
            fraudulentOrderHashes.push(hash);
            emit AddFraudulentOrderHashEvent(hash);
        }
    }

    
    function fraudulentTradeHashesCount()
    public
    view
    returns (uint256)
    {
        return fraudulentTradeHashes.length;
    }

    
    
    
    function isFraudulentTradeHash(bytes32 hash)
    public
    view
    returns (bool)
    {
        return fraudulentByTradeHash[hash];
    }

    
    function addFraudulentTradeHash(bytes32 hash)
    public
    onlyEnabledServiceAction(ADD_FRAUDULENT_TRADE_ACTION)
    {
        if (!fraudulentByTradeHash[hash]) {
            fraudulentByTradeHash[hash] = true;
            fraudulentTradeHashes.push(hash);
            emit AddFraudulentTradeHashEvent(hash);
        }
    }

    
    function fraudulentPaymentHashesCount()
    public
    view
    returns (uint256)
    {
        return fraudulentPaymentHashes.length;
    }

    
    
    
    function isFraudulentPaymentHash(bytes32 hash)
    public
    view
    returns (bool)
    {
        return fraudulentByPaymentHash[hash];
    }

    
    function addFraudulentPaymentHash(bytes32 hash)
    public
    onlyEnabledServiceAction(ADD_FRAUDULENT_PAYMENT_ACTION)
    {
        if (!fraudulentByPaymentHash[hash]) {
            fraudulentByPaymentHash[hash] = true;
            fraudulentPaymentHashes.push(hash);
            emit AddFraudulentPaymentHashEvent(hash);
        }
    }
}

contract FraudChallengable is Ownable {
    
    
    
    FraudChallenge public fraudChallenge;

    
    
    
    event SetFraudChallengeEvent(FraudChallenge oldFraudChallenge, FraudChallenge newFraudChallenge);

    
    
    
    
    
    function setFraudChallenge(FraudChallenge newFraudChallenge)
    public
    onlyDeployer
    notNullAddress(address(newFraudChallenge))
    notSameAddresses(address(newFraudChallenge), address(fraudChallenge))
    {
        
        FraudChallenge oldFraudChallenge = fraudChallenge;
        fraudChallenge = newFraudChallenge;

        
        emit SetFraudChallengeEvent(oldFraudChallenge, newFraudChallenge);
    }

    
    
    
    modifier fraudChallengeInitialized() {
        require(address(fraudChallenge) != address(0), "Fraud challenge not initialized [FraudChallengable.sol:52]");
        _;
    }
}

library SettlementChallengeTypesLib {
    
    
    
    enum Status {Qualified, Disqualified}

    struct Proposal {
        address wallet;
        uint256 nonce;
        uint256 referenceBlockNumber;
        uint256 definitionBlockNumber;

        uint256 expirationTime;

        
        Status status;

        
        Amounts amounts;

        
        MonetaryTypesLib.Currency currency;

        
        Driip challenged;

        
        bool walletInitiated;

        
        bool terminated;

        
        Disqualification disqualification;
    }

    struct Amounts {
        
        int256 cumulativeTransfer;

        
        int256 stage;

        
        int256 targetBalance;
    }

    struct Driip {
        
        string kind;

        
        bytes32 hash;
    }

    struct Disqualification {
        
        address challenger;
        uint256 nonce;
        uint256 blockNumber;

        
        Driip candidate;
    }
}

contract Upgradable {
    
    
    
    address public upgradeAgent;
    bool public upgradesFrozen;

    
    
    
    event SetUpgradeAgentEvent(address upgradeAgent);
    event FreezeUpgradesEvent();

    
    
    
    
    
    function setUpgradeAgent(address _upgradeAgent)
    public
    onlyWhenUpgradable
    {
        require(address(0) == upgradeAgent, "Upgrade agent has already been set [Upgradable.sol:37]");

        
        upgradeAgent = _upgradeAgent;

        
        emit SetUpgradeAgentEvent(upgradeAgent);
    }

    
    
    function freezeUpgrades()
    public
    onlyWhenUpgrading
    {
        
        upgradesFrozen = true;

        
        emit FreezeUpgradesEvent();
    }

    
    
    
    modifier onlyWhenUpgrading() {
        require(msg.sender == upgradeAgent, "Caller is not upgrade agent [Upgradable.sol:63]");
        require(!upgradesFrozen, "Upgrades have been frozen [Upgradable.sol:64]");
        _;
    }

    modifier onlyWhenUpgradable() {
        require(!upgradesFrozen, "Upgrades have been frozen [Upgradable.sol:69]");
        _;
    }
}

contract DriipSettlementChallengeState is Ownable, Servable, Configurable, Upgradable {
    using SafeMathIntLib for int256;
    using SafeMathUintLib for uint256;

    
    
    
    string constant public INITIATE_PROPOSAL_ACTION = "initiate_proposal";
    string constant public TERMINATE_PROPOSAL_ACTION = "terminate_proposal";
    string constant public REMOVE_PROPOSAL_ACTION = "remove_proposal";
    string constant public DISQUALIFY_PROPOSAL_ACTION = "disqualify_proposal";
    string constant public QUALIFY_PROPOSAL_ACTION = "qualify_proposal";

    
    
    
    SettlementChallengeTypesLib.Proposal[] public proposals;
    mapping(address => mapping(address => mapping(uint256 => uint256))) public proposalIndexByWalletCurrency;
    mapping(address => mapping(uint256 => mapping(address => mapping(uint256 => uint256)))) public proposalIndexByWalletNonceCurrency;

    
    
    
    event InitiateProposalEvent(address wallet, uint256 nonce, int256 cumulativeTransferAmount, int256 stageAmount,
        int256 targetBalanceAmount, MonetaryTypesLib.Currency currency, uint256 blockNumber, bool walletInitiated,
        bytes32 challengedHash, string challengedKind);
    event TerminateProposalEvent(address wallet, uint256 nonce, int256 cumulativeTransferAmount, int256 stageAmount,
        int256 targetBalanceAmount, MonetaryTypesLib.Currency currency, uint256 blockNumber, bool walletInitiated,
        bytes32 challengedHash, string challengedKind);
    event RemoveProposalEvent(address wallet, uint256 nonce, int256 cumulativeTransferAmount, int256 stageAmount,
        int256 targetBalanceAmount, MonetaryTypesLib.Currency currency, uint256 blockNumber, bool walletInitiated,
        bytes32 challengedHash, string challengedKind);
    event DisqualifyProposalEvent(address challengedWallet, uint256 challengedNonce, int256 cumulativeTransferAmount,
        int256 stageAmount, int256 targetBalanceAmount, MonetaryTypesLib.Currency currency, uint256 blockNumber,
        bool walletInitiated, address challengerWallet, uint256 candidateNonce, bytes32 candidateHash,
        string candidateKind);
    event QualifyProposalEvent(address challengedWallet, uint256 challengedNonce, int256 cumulativeTransferAmount,
        int256 stageAmount, int256 targetBalanceAmount, MonetaryTypesLib.Currency currency, uint256 blockNumber,
        bool walletInitiated, address challengerWallet, uint256 candidateNonce, bytes32 candidateHash,
        string candidateKind);
    event UpgradeProposalEvent(SettlementChallengeTypesLib.Proposal proposal);

    
    
    
    constructor(address deployer) Ownable(deployer) public {
    }

    
    
    
    
    
    function proposalsCount()
    public
    view
    returns (uint256)
    {
        return proposals.length;
    }

    
    
    
    
    
    
    
    
    
    
    
    function initiateProposal(address wallet, uint256 nonce, int256 cumulativeTransferAmount, int256 stageAmount,
        int256 targetBalanceAmount, MonetaryTypesLib.Currency memory currency, uint256 blockNumber, bool walletInitiated,
        bytes32 challengedHash, string memory challengedKind)
    public
    onlyEnabledServiceAction(INITIATE_PROPOSAL_ACTION)
    {
        
        _initiateProposal(
            wallet, nonce, cumulativeTransferAmount, stageAmount, targetBalanceAmount,
            currency, blockNumber, walletInitiated, challengedHash, challengedKind
        );

        
        emit InitiateProposalEvent(
            wallet, nonce, cumulativeTransferAmount, stageAmount, targetBalanceAmount, currency,
            blockNumber, walletInitiated, challengedHash, challengedKind
        );
    }

    
    
    
    function terminateProposal(address wallet, MonetaryTypesLib.Currency memory currency, bool clearNonce)
    public
    onlyEnabledServiceAction(TERMINATE_PROPOSAL_ACTION)
    {
        
        uint256 index = proposalIndexByWalletCurrency[wallet][currency.ct][currency.id];

        
        if (0 == index)
            return;

        
        if (clearNonce)
            proposalIndexByWalletNonceCurrency[wallet][proposals[index - 1].nonce][currency.ct][currency.id] = 0;

        
        proposals[index - 1].terminated = true;

        
        emit TerminateProposalEvent(
            wallet, proposals[index - 1].nonce, proposals[index - 1].amounts.cumulativeTransfer,
            proposals[index - 1].amounts.stage, proposals[index - 1].amounts.targetBalance, currency,
            proposals[index - 1].referenceBlockNumber, proposals[index - 1].walletInitiated,
            proposals[index - 1].challenged.hash, proposals[index - 1].challenged.kind
        );
    }

    
    
    
    
    
    function terminateProposal(address wallet, MonetaryTypesLib.Currency memory currency, bool clearNonce,
        bool walletTerminated)
    public
    onlyEnabledServiceAction(TERMINATE_PROPOSAL_ACTION)
    {
        
        uint256 index = proposalIndexByWalletCurrency[wallet][currency.ct][currency.id];

        
        if (0 == index)
            return;

        
        require(walletTerminated == proposals[index - 1].walletInitiated, "Wallet initiation and termination mismatch [DriipSettlementChallengeState.sol:165]");

        
        if (clearNonce)
            proposalIndexByWalletNonceCurrency[wallet][proposals[index - 1].nonce][currency.ct][currency.id] = 0;

        
        proposals[index - 1].terminated = true;

        
        emit TerminateProposalEvent(
            wallet, proposals[index - 1].nonce, proposals[index - 1].amounts.cumulativeTransfer,
            proposals[index - 1].amounts.stage, proposals[index - 1].amounts.targetBalance, currency,
            proposals[index - 1].referenceBlockNumber, proposals[index - 1].walletInitiated,
            proposals[index - 1].challenged.hash, proposals[index - 1].challenged.kind
        );
    }

    
    
    
    function removeProposal(address wallet, MonetaryTypesLib.Currency memory currency)
    public
    onlyEnabledServiceAction(REMOVE_PROPOSAL_ACTION)
    {
        
        uint256 index = proposalIndexByWalletCurrency[wallet][currency.ct][currency.id];

        
        if (0 == index)
            return;

        
        emit RemoveProposalEvent(
            wallet, proposals[index - 1].nonce, proposals[index - 1].amounts.cumulativeTransfer,
            proposals[index - 1].amounts.stage, proposals[index - 1].amounts.targetBalance, currency,
            proposals[index - 1].referenceBlockNumber, proposals[index - 1].walletInitiated,
            proposals[index - 1].challenged.hash, proposals[index - 1].challenged.kind
        );

        
        _removeProposal(index);
    }

    
    
    
    
    function removeProposal(address wallet, MonetaryTypesLib.Currency memory currency, bool walletTerminated)
    public
    onlyEnabledServiceAction(REMOVE_PROPOSAL_ACTION)
    {
        
        uint256 index = proposalIndexByWalletCurrency[wallet][currency.ct][currency.id];

        
        if (0 == index)
            return;

        
        require(walletTerminated == proposals[index - 1].walletInitiated, "Wallet initiation and termination mismatch [DriipSettlementChallengeState.sol:225]");

        
        emit RemoveProposalEvent(
            wallet, proposals[index - 1].nonce, proposals[index - 1].amounts.cumulativeTransfer,
            proposals[index - 1].amounts.stage, proposals[index - 1].amounts.targetBalance, currency,
            proposals[index - 1].referenceBlockNumber, proposals[index - 1].walletInitiated,
            proposals[index - 1].challenged.hash, proposals[index - 1].challenged.kind
        );

        
        _removeProposal(index);
    }

    
    
    
    
    
    
    
    
    
    function disqualifyProposal(address challengedWallet, MonetaryTypesLib.Currency memory currency, address challengerWallet,
        uint256 blockNumber, uint256 candidateNonce, bytes32 candidateHash, string memory candidateKind)
    public
    onlyEnabledServiceAction(DISQUALIFY_PROPOSAL_ACTION)
    {
        
        uint256 index = proposalIndexByWalletCurrency[challengedWallet][currency.ct][currency.id];
        require(0 != index, "No proposal found for wallet and currency [DriipSettlementChallengeState.sol:255]");

        
        proposals[index - 1].status = SettlementChallengeTypesLib.Status.Disqualified;
        proposals[index - 1].expirationTime = block.timestamp.add(configuration.settlementChallengeTimeout());
        proposals[index - 1].disqualification.challenger = challengerWallet;
        proposals[index - 1].disqualification.nonce = candidateNonce;
        proposals[index - 1].disqualification.blockNumber = blockNumber;
        proposals[index - 1].disqualification.candidate.hash = candidateHash;
        proposals[index - 1].disqualification.candidate.kind = candidateKind;

        
        emit DisqualifyProposalEvent(
            challengedWallet, proposals[index - 1].nonce, proposals[index - 1].amounts.cumulativeTransfer,
            proposals[index - 1].amounts.stage, proposals[index - 1].amounts.targetBalance,
            currency, proposals[index - 1].referenceBlockNumber, proposals[index - 1].walletInitiated,
            challengerWallet, candidateNonce, candidateHash, candidateKind
        );
    }

    
    
    
    function qualifyProposal(address wallet, MonetaryTypesLib.Currency memory currency)
    public
    onlyEnabledServiceAction(QUALIFY_PROPOSAL_ACTION)
    {
        
        uint256 index = proposalIndexByWalletCurrency[wallet][currency.ct][currency.id];
        require(0 != index, "No proposal found for wallet and currency [DriipSettlementChallengeState.sol:284]");

        
        emit QualifyProposalEvent(
            wallet, proposals[index - 1].nonce, proposals[index - 1].amounts.cumulativeTransfer,
            proposals[index - 1].amounts.stage, proposals[index - 1].amounts.targetBalance, currency,
            proposals[index - 1].referenceBlockNumber, proposals[index - 1].walletInitiated,
            proposals[index - 1].disqualification.challenger,
            proposals[index - 1].disqualification.nonce,
            proposals[index - 1].disqualification.candidate.hash,
            proposals[index - 1].disqualification.candidate.kind
        );

        
        proposals[index - 1].status = SettlementChallengeTypesLib.Status.Qualified;
        proposals[index - 1].expirationTime = block.timestamp.add(configuration.settlementChallengeTimeout());
        delete proposals[index - 1].disqualification;
    }

    
    
    
    
    
    
    function hasProposal(address wallet, uint256 nonce, MonetaryTypesLib.Currency memory currency)
    public
    view
    returns (bool)
    {
        
        return 0 != proposalIndexByWalletNonceCurrency[wallet][nonce][currency.ct][currency.id];
    }

    
    
    
    
    function hasProposal(address wallet, MonetaryTypesLib.Currency memory currency)
    public
    view
    returns (bool)
    {
        
        return 0 != proposalIndexByWalletCurrency[wallet][currency.ct][currency.id];
    }

    
    
    
    
    function hasProposalTerminated(address wallet, MonetaryTypesLib.Currency memory currency)
    public
    view
    returns (bool)
    {
        
        uint256 index = proposalIndexByWalletCurrency[wallet][currency.ct][currency.id];
        require(0 != index, "No proposal found for wallet and currency [DriipSettlementChallengeState.sol:342]");
        return proposals[index - 1].terminated;
    }

    
    
    
    
    function hasProposalExpired(address wallet, MonetaryTypesLib.Currency memory currency)
    public
    view
    returns (bool)
    {
        
        uint256 index = proposalIndexByWalletCurrency[wallet][currency.ct][currency.id];
        require(0 != index, "No proposal found for wallet and currency [DriipSettlementChallengeState.sol:357]");
        return block.timestamp >= proposals[index - 1].expirationTime;
    }

    
    
    
    
    function proposalNonce(address wallet, MonetaryTypesLib.Currency memory currency)
    public
    view
    returns (uint256)
    {
        uint256 index = proposalIndexByWalletCurrency[wallet][currency.ct][currency.id];
        require(0 != index, "No proposal found for wallet and currency [DriipSettlementChallengeState.sol:371]");
        return proposals[index - 1].nonce;
    }

    
    
    
    
    function proposalReferenceBlockNumber(address wallet, MonetaryTypesLib.Currency memory currency)
    public
    view
    returns (uint256)
    {
        uint256 index = proposalIndexByWalletCurrency[wallet][currency.ct][currency.id];
        require(0 != index, "No proposal found for wallet and currency [DriipSettlementChallengeState.sol:385]");
        return proposals[index - 1].referenceBlockNumber;
    }

    
    
    
    
    function proposalDefinitionBlockNumber(address wallet, MonetaryTypesLib.Currency memory currency)
    public
    view
    returns (uint256)
    {
        uint256 index = proposalIndexByWalletCurrency[wallet][currency.ct][currency.id];
        require(0 != index, "No proposal found for wallet and currency [DriipSettlementChallengeState.sol:399]");
        return proposals[index - 1].definitionBlockNumber;
    }

    
    
    
    
    function proposalExpirationTime(address wallet, MonetaryTypesLib.Currency memory currency)
    public
    view
    returns (uint256)
    {
        uint256 index = proposalIndexByWalletCurrency[wallet][currency.ct][currency.id];
        require(0 != index, "No proposal found for wallet and currency [DriipSettlementChallengeState.sol:413]");
        return proposals[index - 1].expirationTime;
    }

    
    
    
    
    function proposalStatus(address wallet, MonetaryTypesLib.Currency memory currency)
    public
    view
    returns (SettlementChallengeTypesLib.Status)
    {
        uint256 index = proposalIndexByWalletCurrency[wallet][currency.ct][currency.id];
        require(0 != index, "No proposal found for wallet and currency [DriipSettlementChallengeState.sol:427]");
        return proposals[index - 1].status;
    }

    
    
    
    
    function proposalCumulativeTransferAmount(address wallet, MonetaryTypesLib.Currency memory currency)
    public
    view
    returns (int256)
    {
        uint256 index = proposalIndexByWalletCurrency[wallet][currency.ct][currency.id];
        require(0 != index, "No proposal found for wallet and currency [DriipSettlementChallengeState.sol:441]");
        return proposals[index - 1].amounts.cumulativeTransfer;
    }

    
    
    
    
    function proposalStageAmount(address wallet, MonetaryTypesLib.Currency memory currency)
    public
    view
    returns (int256)
    {
        uint256 index = proposalIndexByWalletCurrency[wallet][currency.ct][currency.id];
        require(0 != index, "No proposal found for wallet and currency [DriipSettlementChallengeState.sol:455]");
        return proposals[index - 1].amounts.stage;
    }

    
    
    
    
    function proposalTargetBalanceAmount(address wallet, MonetaryTypesLib.Currency memory currency)
    public
    view
    returns (int256)
    {
        uint256 index = proposalIndexByWalletCurrency[wallet][currency.ct][currency.id];
        require(0 != index, "No proposal found for wallet and currency [DriipSettlementChallengeState.sol:469]");
        return proposals[index - 1].amounts.targetBalance;
    }

    
    
    
    
    function proposalChallengedHash(address wallet, MonetaryTypesLib.Currency memory currency)
    public
    view
    returns (bytes32)
    {
        uint256 index = proposalIndexByWalletCurrency[wallet][currency.ct][currency.id];
        require(0 != index, "No proposal found for wallet and currency [DriipSettlementChallengeState.sol:483]");
        return proposals[index - 1].challenged.hash;
    }

    
    
    
    
    function proposalChallengedKind(address wallet, MonetaryTypesLib.Currency memory currency)
    public
    view
    returns (string memory)
    {
        uint256 index = proposalIndexByWalletCurrency[wallet][currency.ct][currency.id];
        require(0 != index, "No proposal found for wallet and currency [DriipSettlementChallengeState.sol:497]");
        return proposals[index - 1].challenged.kind;
    }

    
    
    
    
    function proposalWalletInitiated(address wallet, MonetaryTypesLib.Currency memory currency)
    public
    view
    returns (bool)
    {
        uint256 index = proposalIndexByWalletCurrency[wallet][currency.ct][currency.id];
        require(0 != index, "No proposal found for wallet and currency [DriipSettlementChallengeState.sol:511]");
        return proposals[index - 1].walletInitiated;
    }

    
    
    
    
    function proposalDisqualificationChallenger(address wallet, MonetaryTypesLib.Currency memory currency)
    public
    view
    returns (address)
    {
        uint256 index = proposalIndexByWalletCurrency[wallet][currency.ct][currency.id];
        require(0 != index, "No proposal found for wallet and currency [DriipSettlementChallengeState.sol:525]");
        return proposals[index - 1].disqualification.challenger;
    }

    
    
    
    
    function proposalDisqualificationNonce(address wallet, MonetaryTypesLib.Currency memory currency)
    public
    view
    returns (uint256)
    {
        uint256 index = proposalIndexByWalletCurrency[wallet][currency.ct][currency.id];
        require(0 != index, "No proposal found for wallet and currency [DriipSettlementChallengeState.sol:539]");
        return proposals[index - 1].disqualification.nonce;
    }

    
    
    
    
    function proposalDisqualificationBlockNumber(address wallet, MonetaryTypesLib.Currency memory currency)
    public
    view
    returns (uint256)
    {
        uint256 index = proposalIndexByWalletCurrency[wallet][currency.ct][currency.id];
        require(0 != index, "No proposal found for wallet and currency [DriipSettlementChallengeState.sol:553]");
        return proposals[index - 1].disqualification.blockNumber;
    }

    
    
    
    
    function proposalDisqualificationCandidateHash(address wallet, MonetaryTypesLib.Currency memory currency)
    public
    view
    returns (bytes32)
    {
        uint256 index = proposalIndexByWalletCurrency[wallet][currency.ct][currency.id];
        require(0 != index, "No proposal found for wallet and currency [DriipSettlementChallengeState.sol:567]");
        return proposals[index - 1].disqualification.candidate.hash;
    }

    
    
    
    
    function proposalDisqualificationCandidateKind(address wallet, MonetaryTypesLib.Currency memory currency)
    public
    view
    returns (string memory)
    {
        uint256 index = proposalIndexByWalletCurrency[wallet][currency.ct][currency.id];
        require(0 != index, "No proposal found for wallet and currency [DriipSettlementChallengeState.sol:581]");
        return proposals[index - 1].disqualification.candidate.kind;
    }

    
    
    function upgradeProposal(SettlementChallengeTypesLib.Proposal memory proposal)
    public
    onlyWhenUpgrading
    {
        
        require(
            0 == proposalIndexByWalletNonceCurrency[proposal.wallet][proposal.nonce][proposal.currency.ct][proposal.currency.id],
            "Proposal exists for wallet, nonce and currency [DriipSettlementChallengeState.sol:592]"
        );

        
        proposals.push(proposal);

        
        uint256 index = proposals.length;

        
        proposalIndexByWalletCurrency[proposal.wallet][proposal.currency.ct][proposal.currency.id] = index;
        proposalIndexByWalletNonceCurrency[proposal.wallet][proposal.nonce][proposal.currency.ct][proposal.currency.id] = index;

        
        emit UpgradeProposalEvent(proposal);
    }

    
    
    
    function _initiateProposal(address wallet, uint256 nonce, int256 cumulativeTransferAmount, int256 stageAmount,
        int256 targetBalanceAmount, MonetaryTypesLib.Currency memory currency, uint256 referenceBlockNumber, bool walletInitiated,
        bytes32 challengedHash, string memory challengedKind)
    private
    {
        
        require(
            0 == proposalIndexByWalletNonceCurrency[wallet][nonce][currency.ct][currency.id],
            "Existing proposal found for wallet, nonce and currency [DriipSettlementChallengeState.sol:620]"
        );

        
        require(stageAmount.isPositiveInt256(), "Stage amount not positive [DriipSettlementChallengeState.sol:626]");
        require(targetBalanceAmount.isPositiveInt256(), "Target balance amount not positive [DriipSettlementChallengeState.sol:627]");

        uint256 index = proposalIndexByWalletCurrency[wallet][currency.ct][currency.id];

        
        if (0 == index) {
            index = ++(proposals.length);
            proposalIndexByWalletCurrency[wallet][currency.ct][currency.id] = index;
        }

        
        proposals[index - 1].wallet = wallet;
        proposals[index - 1].nonce = nonce;
        proposals[index - 1].referenceBlockNumber = referenceBlockNumber;
        proposals[index - 1].definitionBlockNumber = block.number;
        proposals[index - 1].expirationTime = block.timestamp.add(configuration.settlementChallengeTimeout());
        proposals[index - 1].status = SettlementChallengeTypesLib.Status.Qualified;
        proposals[index - 1].currency = currency;
        proposals[index - 1].amounts.cumulativeTransfer = cumulativeTransferAmount;
        proposals[index - 1].amounts.stage = stageAmount;
        proposals[index - 1].amounts.targetBalance = targetBalanceAmount;
        proposals[index - 1].walletInitiated = walletInitiated;
        proposals[index - 1].terminated = false;
        proposals[index - 1].challenged.hash = challengedHash;
        proposals[index - 1].challenged.kind = challengedKind;

        
        proposalIndexByWalletNonceCurrency[wallet][nonce][currency.ct][currency.id] = index;
    }

    function _removeProposal(uint256 index)
    private
    {
        
        proposalIndexByWalletCurrency[proposals[index - 1].wallet][proposals[index - 1].currency.ct][proposals[index - 1].currency.id] = 0;
        proposalIndexByWalletNonceCurrency[proposals[index - 1].wallet][proposals[index - 1].nonce][proposals[index - 1].currency.ct][proposals[index - 1].currency.id] = 0;
        if (index < proposals.length) {
            proposals[index - 1] = proposals[proposals.length - 1];
            proposalIndexByWalletCurrency[proposals[index - 1].wallet][proposals[index - 1].currency.ct][proposals[index - 1].currency.id] = index;
            proposalIndexByWalletNonceCurrency[proposals[index - 1].wallet][proposals[index - 1].nonce][proposals[index - 1].currency.ct][proposals[index - 1].currency.id] = index;
        }
        proposals.length--;
    }
}

contract NullSettlementChallengeState is Ownable, Servable, Configurable, BalanceTrackable, Upgradable {
    using SafeMathIntLib for int256;
    using SafeMathUintLib for uint256;

    
    
    
    string constant public INITIATE_PROPOSAL_ACTION = "initiate_proposal";
    string constant public TERMINATE_PROPOSAL_ACTION = "terminate_proposal";
    string constant public REMOVE_PROPOSAL_ACTION = "remove_proposal";
    string constant public DISQUALIFY_PROPOSAL_ACTION = "disqualify_proposal";

    
    
    
    SettlementChallengeTypesLib.Proposal[] public proposals;
    mapping(address => mapping(address => mapping(uint256 => uint256))) public proposalIndexByWalletCurrency;

    
    
    
    event InitiateProposalEvent(address wallet, uint256 nonce, int256 stageAmount, int256 targetBalanceAmount,
        MonetaryTypesLib.Currency currency, uint256 blockNumber, bool walletInitiated);
    event TerminateProposalEvent(address wallet, uint256 nonce, int256 stageAmount, int256 targetBalanceAmount,
        MonetaryTypesLib.Currency currency, uint256 blockNumber, bool walletInitiated);
    event RemoveProposalEvent(address wallet, uint256 nonce, int256 stageAmount, int256 targetBalanceAmount,
        MonetaryTypesLib.Currency currency, uint256 blockNumber, bool walletInitiated);
    event DisqualifyProposalEvent(address challengedWallet, uint256 challangedNonce, int256 stageAmount,
        int256 targetBalanceAmount, MonetaryTypesLib.Currency currency, uint256 blockNumber, bool walletInitiated,
        address challengerWallet, uint256 candidateNonce, bytes32 candidateHash, string candidateKind);
    event UpgradeProposalEvent(SettlementChallengeTypesLib.Proposal proposal);

    
    
    
    constructor(address deployer) Ownable(deployer) public {
    }

    
    
    
    
    
    function proposalsCount()
    public
    view
    returns (uint256)
    {
        return proposals.length;
    }

    
    
    
    
    
    
    
    
    function initiateProposal(address wallet, uint256 nonce, int256 stageAmount, int256 targetBalanceAmount,
        MonetaryTypesLib.Currency memory currency, uint256 blockNumber, bool walletInitiated)
    public
    onlyEnabledServiceAction(INITIATE_PROPOSAL_ACTION)
    {
        
        _initiateProposal(
            wallet, nonce, stageAmount, targetBalanceAmount,
            currency, blockNumber, walletInitiated
        );

        
        emit InitiateProposalEvent(
            wallet, nonce, stageAmount, targetBalanceAmount, currency,
            blockNumber, walletInitiated
        );
    }

    
    
    
    function terminateProposal(address wallet, MonetaryTypesLib.Currency memory currency)
    public
    onlyEnabledServiceAction(TERMINATE_PROPOSAL_ACTION)
    {
        
        uint256 index = proposalIndexByWalletCurrency[wallet][currency.ct][currency.id];

        
        if (0 == index)
            return;

        
        proposals[index - 1].terminated = true;

        
        emit TerminateProposalEvent(
            wallet, proposals[index - 1].nonce, proposals[index - 1].amounts.stage,
            proposals[index - 1].amounts.targetBalance, currency,
            proposals[index - 1].referenceBlockNumber, proposals[index - 1].walletInitiated
        );
    }

    
    
    
    
    function terminateProposal(address wallet, MonetaryTypesLib.Currency memory currency, bool walletTerminated)
    public
    onlyEnabledServiceAction(TERMINATE_PROPOSAL_ACTION)
    {
        
        uint256 index = proposalIndexByWalletCurrency[wallet][currency.ct][currency.id];

        
        if (0 == index)
            return;

        
        require(walletTerminated == proposals[index - 1].walletInitiated, "Wallet initiation and termination mismatch [NullSettlementChallengeState.sol:145]");

        
        proposals[index - 1].terminated = true;

        
        emit TerminateProposalEvent(
            wallet, proposals[index - 1].nonce, proposals[index - 1].amounts.stage,
            proposals[index - 1].amounts.targetBalance, currency,
            proposals[index - 1].referenceBlockNumber, proposals[index - 1].walletInitiated
        );
    }

    
    
    
    function removeProposal(address wallet, MonetaryTypesLib.Currency memory currency)
    public
    onlyEnabledServiceAction(REMOVE_PROPOSAL_ACTION)
    {
        
        uint256 index = proposalIndexByWalletCurrency[wallet][currency.ct][currency.id];

        
        if (0 == index)
            return;

        
        emit RemoveProposalEvent(
            wallet, proposals[index - 1].nonce, proposals[index - 1].amounts.stage,
            proposals[index - 1].amounts.targetBalance, currency,
            proposals[index - 1].referenceBlockNumber, proposals[index - 1].walletInitiated
        );

        
        _removeProposal(index);
    }

    
    
    
    
    function removeProposal(address wallet, MonetaryTypesLib.Currency memory currency, bool walletTerminated)
    public
    onlyEnabledServiceAction(REMOVE_PROPOSAL_ACTION)
    {
        
        uint256 index = proposalIndexByWalletCurrency[wallet][currency.ct][currency.id];

        
        if (0 == index)
            return;

        
        require(walletTerminated == proposals[index - 1].walletInitiated, "Wallet initiation and termination mismatch [NullSettlementChallengeState.sol:199]");

        
        emit RemoveProposalEvent(
            wallet, proposals[index - 1].nonce, proposals[index - 1].amounts.stage,
            proposals[index - 1].amounts.targetBalance, currency,
            proposals[index - 1].referenceBlockNumber, proposals[index - 1].walletInitiated
        );

        
        _removeProposal(index);
    }

    
    
    
    
    
    
    
    
    
    function disqualifyProposal(address challengedWallet, MonetaryTypesLib.Currency memory currency, address challengerWallet,
        uint256 blockNumber, uint256 candidateNonce, bytes32 candidateHash, string memory candidateKind)
    public
    onlyEnabledServiceAction(DISQUALIFY_PROPOSAL_ACTION)
    {
        
        uint256 index = proposalIndexByWalletCurrency[challengedWallet][currency.ct][currency.id];
        require(0 != index, "No settlement found for wallet and currency [NullSettlementChallengeState.sol:228]");

        
        proposals[index - 1].status = SettlementChallengeTypesLib.Status.Disqualified;
        proposals[index - 1].expirationTime = block.timestamp.add(configuration.settlementChallengeTimeout());
        proposals[index - 1].disqualification.challenger = challengerWallet;
        proposals[index - 1].disqualification.nonce = candidateNonce;
        proposals[index - 1].disqualification.blockNumber = blockNumber;
        proposals[index - 1].disqualification.candidate.hash = candidateHash;
        proposals[index - 1].disqualification.candidate.kind = candidateKind;

        
        emit DisqualifyProposalEvent(
            challengedWallet, proposals[index - 1].nonce, proposals[index - 1].amounts.stage,
            proposals[index - 1].amounts.targetBalance, currency, proposals[index - 1].referenceBlockNumber,
            proposals[index - 1].walletInitiated, challengerWallet, candidateNonce, candidateHash, candidateKind
        );
    }

    
    
    
    
    function hasProposal(address wallet, MonetaryTypesLib.Currency memory currency)
    public
    view
    returns (bool)
    {
        
        return 0 != proposalIndexByWalletCurrency[wallet][currency.ct][currency.id];
    }

    
    
    
    
    function hasProposalTerminated(address wallet, MonetaryTypesLib.Currency memory currency)
    public
    view
    returns (bool)
    {
        
        uint256 index = proposalIndexByWalletCurrency[wallet][currency.ct][currency.id];
        require(0 != index, "No proposal found for wallet and currency [NullSettlementChallengeState.sol:271]");
        return proposals[index - 1].terminated;
    }

    
    
    
    
    function hasProposalExpired(address wallet, MonetaryTypesLib.Currency memory currency)
    public
    view
    returns (bool)
    {
        
        uint256 index = proposalIndexByWalletCurrency[wallet][currency.ct][currency.id];
        require(0 != index, "No proposal found for wallet and currency [NullSettlementChallengeState.sol:286]");
        return block.timestamp >= proposals[index - 1].expirationTime;
    }

    
    
    
    
    function proposalNonce(address wallet, MonetaryTypesLib.Currency memory currency)
    public
    view
    returns (uint256)
    {
        uint256 index = proposalIndexByWalletCurrency[wallet][currency.ct][currency.id];
        require(0 != index, "No proposal found for wallet and currency [NullSettlementChallengeState.sol:300]");
        return proposals[index - 1].nonce;
    }

    
    
    
    
    function proposalReferenceBlockNumber(address wallet, MonetaryTypesLib.Currency memory currency)
    public
    view
    returns (uint256)
    {
        uint256 index = proposalIndexByWalletCurrency[wallet][currency.ct][currency.id];
        require(0 != index, "No proposal found for wallet and currency [NullSettlementChallengeState.sol:314]");
        return proposals[index - 1].referenceBlockNumber;
    }

    
    
    
    
    function proposalDefinitionBlockNumber(address wallet, MonetaryTypesLib.Currency memory currency)
    public
    view
    returns (uint256)
    {
        uint256 index = proposalIndexByWalletCurrency[wallet][currency.ct][currency.id];
        require(0 != index, "No proposal found for wallet and currency [NullSettlementChallengeState.sol:328]");
        return proposals[index - 1].definitionBlockNumber;
    }

    
    
    
    
    function proposalExpirationTime(address wallet, MonetaryTypesLib.Currency memory currency)
    public
    view
    returns (uint256)
    {
        uint256 index = proposalIndexByWalletCurrency[wallet][currency.ct][currency.id];
        require(0 != index, "No proposal found for wallet and currency [NullSettlementChallengeState.sol:342]");
        return proposals[index - 1].expirationTime;
    }

    
    
    
    
    function proposalStatus(address wallet, MonetaryTypesLib.Currency memory currency)
    public
    view
    returns (SettlementChallengeTypesLib.Status)
    {
        uint256 index = proposalIndexByWalletCurrency[wallet][currency.ct][currency.id];
        require(0 != index, "No proposal found for wallet and currency [NullSettlementChallengeState.sol:356]");
        return proposals[index - 1].status;
    }

    
    
    
    
    function proposalStageAmount(address wallet, MonetaryTypesLib.Currency memory currency)
    public
    view
    returns (int256)
    {
        uint256 index = proposalIndexByWalletCurrency[wallet][currency.ct][currency.id];
        require(0 != index, "No proposal found for wallet and currency [NullSettlementChallengeState.sol:370]");
        return proposals[index - 1].amounts.stage;
    }

    
    
    
    
    function proposalTargetBalanceAmount(address wallet, MonetaryTypesLib.Currency memory currency)
    public
    view
    returns (int256)
    {
        uint256 index = proposalIndexByWalletCurrency[wallet][currency.ct][currency.id];
        require(0 != index, "No proposal found for wallet and currency [NullSettlementChallengeState.sol:384]");
        return proposals[index - 1].amounts.targetBalance;
    }

    
    
    
    
    function proposalWalletInitiated(address wallet, MonetaryTypesLib.Currency memory currency)
    public
    view
    returns (bool)
    {
        uint256 index = proposalIndexByWalletCurrency[wallet][currency.ct][currency.id];
        require(0 != index, "No proposal found for wallet and currency [NullSettlementChallengeState.sol:398]");
        return proposals[index - 1].walletInitiated;
    }

    
    
    
    
    function proposalDisqualificationChallenger(address wallet, MonetaryTypesLib.Currency memory currency)
    public
    view
    returns (address)
    {
        uint256 index = proposalIndexByWalletCurrency[wallet][currency.ct][currency.id];
        require(0 != index, "No proposal found for wallet and currency [NullSettlementChallengeState.sol:412]");
        return proposals[index - 1].disqualification.challenger;
    }

    
    
    
    
    function proposalDisqualificationBlockNumber(address wallet, MonetaryTypesLib.Currency memory currency)
    public
    view
    returns (uint256)
    {
        uint256 index = proposalIndexByWalletCurrency[wallet][currency.ct][currency.id];
        require(0 != index, "No proposal found for wallet and currency [NullSettlementChallengeState.sol:426]");
        return proposals[index - 1].disqualification.blockNumber;
    }

    
    
    
    
    function proposalDisqualificationNonce(address wallet, MonetaryTypesLib.Currency memory currency)
    public
    view
    returns (uint256)
    {
        uint256 index = proposalIndexByWalletCurrency[wallet][currency.ct][currency.id];
        require(0 != index, "No proposal found for wallet and currency [NullSettlementChallengeState.sol:440]");
        return proposals[index - 1].disqualification.nonce;
    }

    
    
    
    
    function proposalDisqualificationCandidateHash(address wallet, MonetaryTypesLib.Currency memory currency)
    public
    view
    returns (bytes32)
    {
        uint256 index = proposalIndexByWalletCurrency[wallet][currency.ct][currency.id];
        require(0 != index, "No proposal found for wallet and currency [NullSettlementChallengeState.sol:454]");
        return proposals[index - 1].disqualification.candidate.hash;
    }

    
    
    
    
    function proposalDisqualificationCandidateKind(address wallet, MonetaryTypesLib.Currency memory currency)
    public
    view
    returns (string memory)
    {
        uint256 index = proposalIndexByWalletCurrency[wallet][currency.ct][currency.id];
        require(0 != index, "No proposal found for wallet and currency [NullSettlementChallengeState.sol:468]");
        return proposals[index - 1].disqualification.candidate.kind;
    }

    
    
    function upgradeProposal(SettlementChallengeTypesLib.Proposal memory proposal)
    public
    onlyWhenUpgrading
    {
        
        require(
            0 == proposalIndexByWalletCurrency[proposal.wallet][proposal.currency.ct][proposal.currency.id],
            "Proposal exists for wallet and currency [NullSettlementChallengeState.sol:479]"
        );

        
        proposals.push(proposal);

        
        uint256 index = proposals.length;

        
        proposalIndexByWalletCurrency[proposal.wallet][proposal.currency.ct][proposal.currency.id] = index;

        
        emit UpgradeProposalEvent(proposal);
    }


    
    
    
    function _initiateProposal(address wallet, uint256 nonce, int256 stageAmount, int256 targetBalanceAmount,
        MonetaryTypesLib.Currency memory currency, uint256 referenceBlockNumber, bool walletInitiated)
    private
    {
        
        require(stageAmount.isPositiveInt256(), "Stage amount not positive [NullSettlementChallengeState.sol:506]");
        require(targetBalanceAmount.isPositiveInt256(), "Target balance amount not positive [NullSettlementChallengeState.sol:507]");

        uint256 index = proposalIndexByWalletCurrency[wallet][currency.ct][currency.id];

        
        if (0 == index) {
            index = ++(proposals.length);
            proposalIndexByWalletCurrency[wallet][currency.ct][currency.id] = index;
        }

        
        proposals[index - 1].wallet = wallet;
        proposals[index - 1].nonce = nonce;
        proposals[index - 1].referenceBlockNumber = referenceBlockNumber;
        proposals[index - 1].definitionBlockNumber = block.number;
        proposals[index - 1].expirationTime = block.timestamp.add(configuration.settlementChallengeTimeout());
        proposals[index - 1].status = SettlementChallengeTypesLib.Status.Qualified;
        proposals[index - 1].currency = currency;
        proposals[index - 1].amounts.stage = stageAmount;
        proposals[index - 1].amounts.targetBalance = targetBalanceAmount;
        proposals[index - 1].walletInitiated = walletInitiated;
        proposals[index - 1].terminated = false;
    }

    function _removeProposal(uint256 index)
    private
    returns (bool)
    {
        
        proposalIndexByWalletCurrency[proposals[index - 1].wallet][proposals[index - 1].currency.ct][proposals[index - 1].currency.id] = 0;
        if (index < proposals.length) {
            proposals[index - 1] = proposals[proposals.length - 1];
            proposalIndexByWalletCurrency[proposals[index - 1].wallet][proposals[index - 1].currency.ct][proposals[index - 1].currency.id] = index;
        }
        proposals.length--;
    }

    function _activeBalanceLogEntry(address wallet, address currencyCt, uint256 currencyId)
    private
    view
    returns (int256 amount, uint256 blockNumber)
    {
        
        (int256 depositedAmount, uint256 depositedBlockNumber) = balanceTracker.lastFungibleRecord(
            wallet, balanceTracker.depositedBalanceType(), currencyCt, currencyId
        );
        (int256 settledAmount, uint256 settledBlockNumber) = balanceTracker.lastFungibleRecord(
            wallet, balanceTracker.settledBalanceType(), currencyCt, currencyId
        );

        
        amount = depositedAmount.add(settledAmount);

        
        blockNumber = depositedBlockNumber > settledBlockNumber ? depositedBlockNumber : settledBlockNumber;
    }
}

library BalanceTrackerLib {
    using SafeMathIntLib for int256;
    using SafeMathUintLib for uint256;

    function fungibleActiveRecordByBlockNumber(BalanceTracker self, address wallet,
        MonetaryTypesLib.Currency memory currency, uint256 _blockNumber)
    internal
    view
    returns (int256 amount, uint256 blockNumber)
    {
        
        (int256 depositedAmount, uint256 depositedBlockNumber) = self.fungibleRecordByBlockNumber(
            wallet, self.depositedBalanceType(), currency.ct, currency.id, _blockNumber
        );
        (int256 settledAmount, uint256 settledBlockNumber) = self.fungibleRecordByBlockNumber(
            wallet, self.settledBalanceType(), currency.ct, currency.id, _blockNumber
        );

        
        amount = depositedAmount.add(settledAmount);
        blockNumber = depositedBlockNumber.clampMin(settledBlockNumber);
    }

    function fungibleActiveBalanceAmountByBlockNumber(BalanceTracker self, address wallet,
        MonetaryTypesLib.Currency memory currency, uint256 blockNumber)
    internal
    view
    returns (int256)
    {
        (int256 amount,) = fungibleActiveRecordByBlockNumber(self, wallet, currency, blockNumber);
        return amount;
    }

    function fungibleActiveDeltaBalanceAmountByBlockNumbers(BalanceTracker self, address wallet,
        MonetaryTypesLib.Currency memory currency, uint256 fromBlockNumber, uint256 toBlockNumber)
    internal
    view
    returns (int256)
    {
        return fungibleActiveBalanceAmountByBlockNumber(self, wallet, currency, toBlockNumber) -
        fungibleActiveBalanceAmountByBlockNumber(self, wallet, currency, fromBlockNumber);
    }

    function fungibleActiveRecord(BalanceTracker self, address wallet,
        MonetaryTypesLib.Currency memory currency)
    internal
    view
    returns (int256 amount, uint256 blockNumber)
    {
        
        (int256 depositedAmount, uint256 depositedBlockNumber) = self.lastFungibleRecord(
            wallet, self.depositedBalanceType(), currency.ct, currency.id
        );
        (int256 settledAmount, uint256 settledBlockNumber) = self.lastFungibleRecord(
            wallet, self.settledBalanceType(), currency.ct, currency.id
        );

        
        amount = depositedAmount.add(settledAmount);
        blockNumber = depositedBlockNumber.clampMin(settledBlockNumber);
    }

    function fungibleActiveBalanceAmount(BalanceTracker self, address wallet, MonetaryTypesLib.Currency memory currency)
    internal
    view
    returns (int256)
    {
        return self.get(wallet, self.depositedBalanceType(), currency.ct, currency.id).add(
            self.get(wallet, self.settledBalanceType(), currency.ct, currency.id)
        );
    }
}

contract DriipSettlementDisputeByPayment is Ownable, Configurable, Validatable, SecurityBondable, WalletLockable,
BalanceTrackable, FraudChallengable, Servable {
    using SafeMathIntLib for int256;
    using SafeMathUintLib for uint256;
    using BalanceTrackerLib for BalanceTracker;

    
    
    
    string constant public CHALLENGE_BY_PAYMENT_ACTION = "challenge_by_payment";

    
    
    
    DriipSettlementChallengeState public driipSettlementChallengeState;
    NullSettlementChallengeState public nullSettlementChallengeState;

    
    
    
    event SetDriipSettlementChallengeStateEvent(DriipSettlementChallengeState oldDriipSettlementChallengeState,
        DriipSettlementChallengeState newDriipSettlementChallengeState);
    event SetNullSettlementChallengeStateEvent(NullSettlementChallengeState oldNullSettlementChallengeState,
        NullSettlementChallengeState newNullSettlementChallengeState);
    event ChallengeByPaymentEvent(address wallet, uint256 nonce, PaymentTypesLib.Payment payment,
        address challenger);

    
    
    
    constructor(address deployer) Ownable(deployer) public {
    }

    
    
    function setDriipSettlementChallengeState(DriipSettlementChallengeState newDriipSettlementChallengeState) public
    onlyDeployer
    notNullAddress(address(newDriipSettlementChallengeState))
    {
        DriipSettlementChallengeState oldDriipSettlementChallengeState = driipSettlementChallengeState;
        driipSettlementChallengeState = newDriipSettlementChallengeState;
        emit SetDriipSettlementChallengeStateEvent(oldDriipSettlementChallengeState, driipSettlementChallengeState);
    }

    
    
    function setNullSettlementChallengeState(NullSettlementChallengeState newNullSettlementChallengeState) public
    onlyDeployer
    notNullAddress(address(newNullSettlementChallengeState))
    {
        NullSettlementChallengeState oldNullSettlementChallengeState = nullSettlementChallengeState;
        nullSettlementChallengeState = newNullSettlementChallengeState;
        emit SetNullSettlementChallengeStateEvent(oldNullSettlementChallengeState, nullSettlementChallengeState);
    }

    
    
    
    
    
    function challengeByPayment(address wallet, PaymentTypesLib.Payment memory payment, address challenger)
    public
    onlyEnabledServiceAction(CHALLENGE_BY_PAYMENT_ACTION)
    onlySealedPayment(payment)
    onlyPaymentSender(payment, wallet)
    {
        
        require(
            !fraudChallenge.isFraudulentPaymentHash(payment.seals.operator.hash),
            "Payment deemed fraudulent [DriipSettlementDisputeByPayment.sol:102]"
        );

        
        require(
            driipSettlementChallengeState.hasProposal(wallet, payment.currency),
            "No proposal found [DriipSettlementDisputeByPayment.sol:108]"
        );

        
        require(
            !driipSettlementChallengeState.hasProposalExpired(wallet, payment.currency),
            "Proposal found expired [DriipSettlementDisputeByPayment.sol:114]"
        );

        
        
        require(
            payment.sender.nonce > driipSettlementChallengeState.proposalNonce(wallet, payment.currency),
            "Payment nonce not strictly greater than proposal nonce [DriipSettlementDisputeByPayment.sol:121]"
        );
        require(
            payment.sender.nonce > driipSettlementChallengeState.proposalDisqualificationNonce(wallet, payment.currency),
            "Payment nonce not strictly greater than proposal disqualification nonce [DriipSettlementDisputeByPayment.sol:125]"
        );

        
        require(_overrun(wallet, payment), "No overrun found [DriipSettlementDisputeByPayment.sol:131]");

        
        _settleRewards(wallet, payment.sender.balances.current, payment.currency, challenger);

        
        driipSettlementChallengeState.disqualifyProposal(
            wallet, payment.currency, challenger, payment.blockNumber,
            payment.sender.nonce, payment.seals.operator.hash, PaymentTypesLib.PAYMENT_KIND()
        );

        
        nullSettlementChallengeState.terminateProposal(wallet, payment.currency);

        
        emit ChallengeByPaymentEvent(
            wallet, driipSettlementChallengeState.proposalNonce(wallet, payment.currency), payment, challenger
        );
    }

    
    
    
    function _overrun(address wallet, PaymentTypesLib.Payment memory payment)
    private
    view
    returns (bool)
    {
        
        int targetBalanceAmount = driipSettlementChallengeState.proposalTargetBalanceAmount(
            wallet, payment.currency
        );

        
        int256 deltaBalanceAmountSinceStart = balanceTracker.fungibleActiveDeltaBalanceAmountByBlockNumbers(
            wallet, payment.currency,
            driipSettlementChallengeState.proposalReferenceBlockNumber(wallet, payment.currency),
            block.number
        );

        
        int256 paymentCumulativeTransferAmount = payment.sender.balances.current.sub(
            balanceTracker.fungibleActiveBalanceAmountByBlockNumber(
                wallet, payment.currency, payment.blockNumber
            )
        );

        
        int proposalCumulativeTransferAmount = driipSettlementChallengeState.proposalCumulativeTransferAmount(
            wallet, payment.currency
        );

        return targetBalanceAmount.add(deltaBalanceAmountSinceStart) < proposalCumulativeTransferAmount.sub(paymentCumulativeTransferAmount);
    }

    
    function _settleRewards(address wallet, int256 walletAmount, MonetaryTypesLib.Currency memory currency,
        address challenger)
    private
    {
        if (driipSettlementChallengeState.proposalWalletInitiated(wallet, currency))
            _settleBalanceReward(wallet, walletAmount, currency, challenger);

        else
            _settleSecurityBondReward(wallet, walletAmount, currency, challenger);
    }

    function _settleBalanceReward(address wallet, int256 walletAmount, MonetaryTypesLib.Currency memory currency,
        address challenger)
    private
    {
        
        if (SettlementChallengeTypesLib.Status.Disqualified == driipSettlementChallengeState.proposalStatus(
            wallet, currency
        ))
            walletLocker.unlockFungibleByProxy(
                wallet,
                driipSettlementChallengeState.proposalDisqualificationChallenger(
                    wallet, currency
                ),
                currency.ct, currency.id
            );

        
        walletLocker.lockFungibleByProxy(
            wallet, challenger, walletAmount, currency.ct, currency.id, configuration.settlementChallengeTimeout()
        );
    }

    
    
    
    
    
    function _settleSecurityBondReward(address wallet, int256 walletAmount, MonetaryTypesLib.Currency memory currency,
        address challenger)
    private
    {
        
        if (SettlementChallengeTypesLib.Status.Disqualified == driipSettlementChallengeState.proposalStatus(
            wallet, currency
        ))
            securityBond.depriveAbsolute(
                driipSettlementChallengeState.proposalDisqualificationChallenger(
                    wallet, currency
                ),
                currency.ct, currency.id
            );

        
        MonetaryTypesLib.Figure memory flatReward = _flatReward();
        securityBond.rewardAbsolute(
            challenger, flatReward.amount, flatReward.currency.ct, flatReward.currency.id, 0
        );

        
        int256 progressiveRewardAmount = walletAmount.clampMax(
            securityBond.depositedFractionalBalance(
                currency.ct, currency.id, configuration.operatorSettlementStakeFraction()
            )
        );
        securityBond.rewardAbsolute(
            challenger, progressiveRewardAmount, currency.ct, currency.id, 0
        );
    }

    function _flatReward()
    private
    view
    returns (MonetaryTypesLib.Figure memory)
    {
        (int256 amount, address currencyCt, uint256 currencyId) = configuration.operatorSettlementStake();
        return MonetaryTypesLib.Figure(amount, MonetaryTypesLib.Currency(currencyCt, currencyId));
    }
}

contract CommunityVote is Ownable {
    
    
    
    mapping(address => bool) doubleSpenderByWallet;
    uint256 maxDriipNonce;
    uint256 maxNullNonce;
    bool dataAvailable;

    
    
    
    constructor(address deployer) Ownable(deployer) public {
        dataAvailable = true;
    }

    
    
    
    
    
    
    function isDoubleSpenderWallet(address wallet)
    public
    view
    returns (bool)
    {
        return doubleSpenderByWallet[wallet];
    }

    
    
    function getMaxDriipNonce()
    public
    view
    returns (uint256)
    {
        return maxDriipNonce;
    }

    
    
    function getMaxNullNonce()
    public
    view
    returns (uint256)
    {
        return maxNullNonce;
    }

    
    
    function isDataAvailable()
    public
    view
    returns (bool)
    {
        return dataAvailable;
    }
}

contract CommunityVotable is Ownable {
    
    
    
    CommunityVote public communityVote;
    bool public communityVoteFrozen;

    
    
    
    event SetCommunityVoteEvent(CommunityVote oldCommunityVote, CommunityVote newCommunityVote);
    event FreezeCommunityVoteEvent();

    
    
    
    
    
    function setCommunityVote(CommunityVote newCommunityVote) 
    public 
    onlyDeployer
    notNullAddress(address(newCommunityVote))
    notSameAddresses(address(newCommunityVote), address(communityVote))
    {
        require(!communityVoteFrozen, "Community vote frozen [CommunityVotable.sol:41]");

        
        CommunityVote oldCommunityVote = communityVote;
        communityVote = newCommunityVote;

        
        emit SetCommunityVoteEvent(oldCommunityVote, newCommunityVote);
    }

    
    
    function freezeCommunityVote()
    public
    onlyDeployer
    {
        communityVoteFrozen = true;

        
        emit FreezeCommunityVoteEvent();
    }

    
    
    
    modifier communityVoteInitialized() {
        require(address(communityVote) != address(0), "Community vote not initialized [CommunityVotable.sol:67]");
        _;
    }
}

contract Benefactor is Ownable {
    
    
    
    Beneficiary[] public beneficiaries;
    mapping(address => uint256) public beneficiaryIndexByAddress;

    
    
    
    event RegisterBeneficiaryEvent(Beneficiary beneficiary);
    event DeregisterBeneficiaryEvent(Beneficiary beneficiary);

    
    
    
    
    
    function registerBeneficiary(Beneficiary beneficiary)
    public
    onlyDeployer
    notNullAddress(address(beneficiary))
    returns (bool)
    {
        address _beneficiary = address(beneficiary);

        if (beneficiaryIndexByAddress[_beneficiary] > 0)
            return false;

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

        
        emit RegisterBeneficiaryEvent(beneficiary);

        return true;
    }

    
    
    function deregisterBeneficiary(Beneficiary beneficiary)
    public
    onlyDeployer
    notNullAddress(address(beneficiary))
    returns (bool)
    {
        address _beneficiary = address(beneficiary);

        if (beneficiaryIndexByAddress[_beneficiary] == 0)
            return false;

        uint256 idx = beneficiaryIndexByAddress[_beneficiary] - 1;
        if (idx < beneficiaries.length - 1) {
            
            beneficiaries[idx] = beneficiaries[beneficiaries.length - 1];
            beneficiaryIndexByAddress[address(beneficiaries[idx])] = idx + 1;
        }
        beneficiaries.length--;
        beneficiaryIndexByAddress[_beneficiary] = 0;

        
        emit DeregisterBeneficiaryEvent(beneficiary);

        return true;
    }

    
    
    
    function isRegisteredBeneficiary(Beneficiary beneficiary)
    public
    view
    returns (bool)
    {
        return beneficiaryIndexByAddress[address(beneficiary)] > 0;
    }

    
    
    function registeredBeneficiariesCount()
    public
    view
    returns (uint256)
    {
        return beneficiaries.length;
    }
}

contract AccrualBenefactor is Benefactor {
    using SafeMathIntLib for int256;

    
    
    
    mapping(address => int256) private _beneficiaryFractionMap;
    int256 public totalBeneficiaryFraction;

    
    
    
    event RegisterAccrualBeneficiaryEvent(Beneficiary beneficiary, int256 fraction);
    event DeregisterAccrualBeneficiaryEvent(Beneficiary beneficiary);

    
    
    
    
    
    function registerBeneficiary(Beneficiary beneficiary)
    public
    onlyDeployer
    notNullAddress(address(beneficiary))
    returns (bool)
    {
        return registerFractionalBeneficiary(AccrualBeneficiary(address(beneficiary)), ConstantsLib.PARTS_PER());
    }

    
    
    
    function registerFractionalBeneficiary(AccrualBeneficiary beneficiary, int256 fraction)
    public
    onlyDeployer
    notNullAddress(address(beneficiary))
    returns (bool)
    {
        require(fraction > 0, "Fraction not strictly positive [AccrualBenefactor.sol:59]");
        require(
            totalBeneficiaryFraction.add(fraction) <= ConstantsLib.PARTS_PER(),
            "Total beneficiary fraction out of bounds [AccrualBenefactor.sol:60]"
        );

        if (!super.registerBeneficiary(beneficiary))
            return false;

        _beneficiaryFractionMap[address(beneficiary)] = fraction;
        totalBeneficiaryFraction = totalBeneficiaryFraction.add(fraction);

        
        emit RegisterAccrualBeneficiaryEvent(beneficiary, fraction);

        return true;
    }

    
    
    function deregisterBeneficiary(Beneficiary beneficiary)
    public
    onlyDeployer
    notNullAddress(address(beneficiary))
    returns (bool)
    {
        if (!super.deregisterBeneficiary(beneficiary))
            return false;

        address _beneficiary = address(beneficiary);

        totalBeneficiaryFraction = totalBeneficiaryFraction.sub(_beneficiaryFractionMap[_beneficiary]);
        _beneficiaryFractionMap[_beneficiary] = 0;

        
        emit DeregisterAccrualBeneficiaryEvent(beneficiary);

        return true;
    }

    
    
    
    function beneficiaryFraction(AccrualBeneficiary beneficiary)
    public
    view
    returns (int256)
    {
        return _beneficiaryFractionMap[address(beneficiary)];
    }
}

contract RevenueFund is Ownable, AccrualBeneficiary, AccrualBenefactor, TransferControllerManageable {
    using FungibleBalanceLib for FungibleBalanceLib.Balance;
    using TxHistoryLib for TxHistoryLib.TxHistory;
    using SafeMathIntLib for int256;
    using SafeMathUintLib for uint256;
    using CurrenciesLib for CurrenciesLib.Currencies;

    
    
    
    FungibleBalanceLib.Balance periodAccrual;
    CurrenciesLib.Currencies periodCurrencies;

    FungibleBalanceLib.Balance aggregateAccrual;
    CurrenciesLib.Currencies aggregateCurrencies;

    TxHistoryLib.TxHistory private txHistory;

    
    
    
    event ReceiveEvent(address from, int256 amount, address currencyCt, uint256 currencyId);
    event CloseAccrualPeriodEvent();
    event RegisterServiceEvent(address service);
    event DeregisterServiceEvent(address service);

    
    
    
    constructor(address deployer) Ownable(deployer) public {
    }

    
    
    
    
    function() external payable {
        receiveEthersTo(msg.sender, "");
    }

    
    
    function receiveEthersTo(address wallet, string memory)
    public
    payable
    {
        int256 amount = SafeMathIntLib.toNonZeroInt256(msg.value);

        
        periodAccrual.add(amount, address(0), 0);
        aggregateAccrual.add(amount, address(0), 0);

        
        periodCurrencies.add(address(0), 0);
        aggregateCurrencies.add(address(0), 0);

        
        txHistory.addDeposit(amount, address(0), 0);

        
        emit ReceiveEvent(wallet, amount, address(0), 0);
    }

    
    
    
    
    
    function receiveTokens(string memory balanceType, int256 amount, address currencyCt,
        uint256 currencyId, string memory standard)
    public
    {
        receiveTokensTo(msg.sender, balanceType, amount, currencyCt, currencyId, standard);
    }

    
    
    
    
    
    
    function receiveTokensTo(address wallet, string memory, int256 amount,
        address currencyCt, uint256 currencyId, string memory standard)
    public
    {
        require(amount.isNonZeroPositiveInt256(), "Amount not strictly positive [RevenueFund.sol:115]");

        
        TransferController controller = transferController(currencyCt, standard);
        (bool success,) = address(controller).delegatecall(
            abi.encodeWithSelector(
                controller.getReceiveSignature(), msg.sender, this, uint256(amount), currencyCt, currencyId
            )
        );
        require(success, "Reception by controller failed [RevenueFund.sol:124]");

        
        periodAccrual.add(amount, currencyCt, currencyId);
        aggregateAccrual.add(amount, currencyCt, currencyId);

        
        periodCurrencies.add(currencyCt, currencyId);
        aggregateCurrencies.add(currencyCt, currencyId);

        
        txHistory.addDeposit(amount, currencyCt, currencyId);

        
        emit ReceiveEvent(wallet, amount, currencyCt, currencyId);
    }

    
    
    
    
    function periodAccrualBalance(address currencyCt, uint256 currencyId)
    public
    view
    returns (int256)
    {
        return periodAccrual.get(currencyCt, currencyId);
    }

    
    
    
    
    
    function aggregateAccrualBalance(address currencyCt, uint256 currencyId)
    public
    view
    returns (int256)
    {
        return aggregateAccrual.get(currencyCt, currencyId);
    }

    
    
    function periodCurrenciesCount()
    public
    view
    returns (uint256)
    {
        return periodCurrencies.count();
    }

    
    
    
    
    function periodCurrenciesByIndices(uint256 low, uint256 up)
    public
    view
    returns (MonetaryTypesLib.Currency[] memory)
    {
        return periodCurrencies.getByIndices(low, up);
    }

    
    
    function aggregateCurrenciesCount()
    public
    view
    returns (uint256)
    {
        return aggregateCurrencies.count();
    }

    
    
    
    
    function aggregateCurrenciesByIndices(uint256 low, uint256 up)
    public
    view
    returns (MonetaryTypesLib.Currency[] memory)
    {
        return aggregateCurrencies.getByIndices(low, up);
    }

    
    
    function depositsCount()
    public
    view
    returns (uint256)
    {
        return txHistory.depositsCount();
    }

    
    
    function deposit(uint index)
    public
    view
    returns (int256 amount, uint256 blockNumber, address currencyCt, uint256 currencyId)
    {
        return txHistory.deposit(index);
    }

    
    
    function closeAccrualPeriod(MonetaryTypesLib.Currency[] memory currencies)
    public
    onlyOperator
    {
        require(
            ConstantsLib.PARTS_PER() == totalBeneficiaryFraction,
            "Total beneficiary fraction out of bounds [RevenueFund.sol:236]"
        );

        
        for (uint256 i = 0; i < currencies.length; i++) {
            MonetaryTypesLib.Currency memory currency = currencies[i];

            int256 remaining = periodAccrual.get(currency.ct, currency.id);

            if (0 >= remaining)
                continue;

            for (uint256 j = 0; j < beneficiaries.length; j++) {
                AccrualBeneficiary beneficiary = AccrualBeneficiary(address(beneficiaries[j]));

                if (beneficiaryFraction(beneficiary) > 0) {
                    int256 transferable = periodAccrual.get(currency.ct, currency.id)
                    .mul(beneficiaryFraction(beneficiary))
                    .div(ConstantsLib.PARTS_PER());

                    if (transferable > remaining)
                        transferable = remaining;

                    if (transferable > 0) {
                        
                        if (currency.ct == address(0))
                            beneficiary.receiveEthersTo.value(uint256(transferable))(address(0), "");

                        
                        else {
                            TransferController controller = transferController(currency.ct, "");
                            (bool success,) = address(controller).delegatecall(
                                abi.encodeWithSelector(
                                    controller.getApproveSignature(), address(beneficiary), uint256(transferable), currency.ct, currency.id
                                )
                            );
                            require(success, "Approval by controller failed [RevenueFund.sol:274]");

                            beneficiary.receiveTokensTo(address(0), "", transferable, currency.ct, currency.id, "");
                        }

                        remaining = remaining.sub(transferable);
                    }
                }
            }

            
            periodAccrual.set(remaining, currency.ct, currency.id);
        }

        
        for (uint256 j = 0; j < beneficiaries.length; j++) {
            AccrualBeneficiary beneficiary = AccrualBeneficiary(address(beneficiaries[j]));

            
            if (0 >= beneficiaryFraction(beneficiary))
                continue;

            
            beneficiary.closeAccrualPeriod(currencies);
        }

        
        emit CloseAccrualPeriodEvent();
    }
}

library Strings {

    
    function concat(string memory _base, string memory _value)
        internal
        pure
        returns (string memory) {
        bytes memory _baseBytes = bytes(_base);
        bytes memory _valueBytes = bytes(_value);

        assert(_valueBytes.length > 0);

        string memory _tmpValue = new string(_baseBytes.length +
            _valueBytes.length);
        bytes memory _newValue = bytes(_tmpValue);

        uint i;
        uint j;

        for (i = 0; i < _baseBytes.length; i++) {
            _newValue[j++] = _baseBytes[i];
        }

        for (i = 0; i < _valueBytes.length; i++) {
            _newValue[j++] = _valueBytes[i];
        }

        return string(_newValue);
    }

    
    function indexOf(string memory _base, string memory _value)
        internal
        pure
        returns (int) {
        return _indexOf(_base, _value, 0);
    }

    
    function _indexOf(string memory _base, string memory _value, uint _offset)
        internal
        pure
        returns (int) {
        bytes memory _baseBytes = bytes(_base);
        bytes memory _valueBytes = bytes(_value);

        assert(_valueBytes.length == 1);

        for (uint i = _offset; i < _baseBytes.length; i++) {
            if (_baseBytes[i] == _valueBytes[0]) {
                return int(i);
            }
        }

        return -1;
    }

    
    function length(string memory _base)
        internal
        pure
        returns (uint) {
        bytes memory _baseBytes = bytes(_base);
        return _baseBytes.length;
    }

    
    function substring(string memory _base, int _length)
        internal
        pure
        returns (string memory) {
        return _substring(_base, _length, 0);
    }

    
    function _substring(string memory _base, int _length, int _offset)
        internal
        pure
        returns (string memory) {
        bytes memory _baseBytes = bytes(_base);

        assert(uint(_offset + _length) <= _baseBytes.length);

        string memory _tmp = new string(uint(_length));
        bytes memory _tmpBytes = bytes(_tmp);

        uint j = 0;
        for (uint i = uint(_offset); i < uint(_offset + _length); i++) {
            _tmpBytes[j++] = _baseBytes[i];
        }

        return string(_tmpBytes);
    }

    
    function split(string memory _base, string memory _value)
        internal
        pure
        returns (string[] memory splitArr) {
        bytes memory _baseBytes = bytes(_base);

        uint _offset = 0;
        uint _splitsCount = 1;
        while (_offset < _baseBytes.length - 1) {
            int _limit = _indexOf(_base, _value, _offset);
            if (_limit == -1)
                break;
            else {
                _splitsCount++;
                _offset = uint(_limit) + 1;
            }
        }

        splitArr = new string[](_splitsCount);

        _offset = 0;
        _splitsCount = 0;
        while (_offset < _baseBytes.length - 1) {

            int _limit = _indexOf(_base, _value, _offset);
            if (_limit == - 1) {
                _limit = int(_baseBytes.length);
            }

            string memory _tmp = new string(uint(_limit) - _offset);
            bytes memory _tmpBytes = bytes(_tmp);

            uint j = 0;
            for (uint i = _offset; i < uint(_limit); i++) {
                _tmpBytes[j++] = _baseBytes[i];
            }
            _offset = uint(_limit) + 1;
            splitArr[_splitsCount++] = string(_tmpBytes);
        }
        return splitArr;
    }

    
    function compareTo(string memory _base, string memory _value)
        internal
        pure
        returns (bool) {
        bytes memory _baseBytes = bytes(_base);
        bytes memory _valueBytes = bytes(_value);

        if (_baseBytes.length != _valueBytes.length) {
            return false;
        }

        for (uint i = 0; i < _baseBytes.length; i++) {
            if (_baseBytes[i] != _valueBytes[i]) {
                return false;
            }
        }

        return true;
    }

    
    function compareToIgnoreCase(string memory _base, string memory _value)
        internal
        pure
        returns (bool) {
        bytes memory _baseBytes = bytes(_base);
        bytes memory _valueBytes = bytes(_value);

        if (_baseBytes.length != _valueBytes.length) {
            return false;
        }

        for (uint i = 0; i < _baseBytes.length; i++) {
            if (_baseBytes[i] != _valueBytes[i] &&
            _upper(_baseBytes[i]) != _upper(_valueBytes[i])) {
                return false;
            }
        }

        return true;
    }

    
    function upper(string memory _base)
        internal
        pure
        returns (string memory) {
        bytes memory _baseBytes = bytes(_base);
        for (uint i = 0; i < _baseBytes.length; i++) {
            _baseBytes[i] = _upper(_baseBytes[i]);
        }
        return string(_baseBytes);
    }

    
    function lower(string memory _base)
        internal
        pure
        returns (string memory) {
        bytes memory _baseBytes = bytes(_base);
        for (uint i = 0; i < _baseBytes.length; i++) {
            _baseBytes[i] = _lower(_baseBytes[i]);
        }
        return string(_baseBytes);
    }

    
    function _upper(bytes1 _b1)
        private
        pure
        returns (bytes1) {

        if (_b1 >= 0x61 && _b1 <= 0x7A) {
            return bytes1(uint8(_b1) - 32);
        }

        return _b1;
    }

    
    function _lower(bytes1 _b1)
        private
        pure
        returns (bytes1) {

        if (_b1 >= 0x41 && _b1 <= 0x5A) {
            return bytes1(uint8(_b1) + 32);
        }

        return _b1;
    }
}

contract PartnerFund is Ownable, Beneficiary, TransferControllerManageable {
    using FungibleBalanceLib for FungibleBalanceLib.Balance;
    using TxHistoryLib for TxHistoryLib.TxHistory;
    using SafeMathIntLib for int256;
    using Strings for string;

    
    
    
    struct Partner {
        bytes32 nameHash;

        uint256 fee;
        address wallet;
        uint256 index;

        bool operatorCanUpdate;
        bool partnerCanUpdate;

        FungibleBalanceLib.Balance active;
        FungibleBalanceLib.Balance staged;

        TxHistoryLib.TxHistory txHistory;
        FullBalanceHistory[] fullBalanceHistory;
    }

    struct FullBalanceHistory {
        uint256 listIndex;
        int256 balance;
        uint256 blockNumber;
    }

    
    
    
    Partner[] private partners;

    mapping(bytes32 => uint256) private _indexByNameHash;
    mapping(address => uint256) private _indexByWallet;

    
    
    
    event ReceiveEvent(address from, int256 amount, address currencyCt, uint256 currencyId);
    event RegisterPartnerByNameEvent(string name, uint256 fee, address wallet);
    event RegisterPartnerByNameHashEvent(bytes32 nameHash, uint256 fee, address wallet);
    event SetFeeByIndexEvent(uint256 index, uint256 oldFee, uint256 newFee);
    event SetFeeByNameEvent(string name, uint256 oldFee, uint256 newFee);
    event SetFeeByNameHashEvent(bytes32 nameHash, uint256 oldFee, uint256 newFee);
    event SetFeeByWalletEvent(address wallet, uint256 oldFee, uint256 newFee);
    event SetPartnerWalletByIndexEvent(uint256 index, address oldWallet, address newWallet);
    event SetPartnerWalletByNameEvent(string name, address oldWallet, address newWallet);
    event SetPartnerWalletByNameHashEvent(bytes32 nameHash, address oldWallet, address newWallet);
    event SetPartnerWalletByWalletEvent(address oldWallet, address newWallet);
    event StageEvent(address from, int256 amount, address currencyCt, uint256 currencyId);
    event WithdrawEvent(address to, int256 amount, address currencyCt, uint256 currencyId);

    
    
    
    constructor(address deployer) Ownable(deployer) public {
    }

    
    
    
    
    function() external payable {
        _receiveEthersTo(
            indexByWallet(msg.sender) - 1, SafeMathIntLib.toNonZeroInt256(msg.value)
        );
    }

    
    
    function receiveEthersTo(address tag, string memory)
    public
    payable
    {
        _receiveEthersTo(
            uint256(tag) - 1, SafeMathIntLib.toNonZeroInt256(msg.value)
        );
    }

    
    
    
    
    
    function receiveTokens(string memory, int256 amount, address currencyCt,
        uint256 currencyId, string memory standard)
    public
    {
        _receiveTokensTo(
            indexByWallet(msg.sender) - 1, amount, currencyCt, currencyId, standard
        );
    }

    
    
    
    
    
    
    function receiveTokensTo(address tag, string memory, int256 amount, address currencyCt,
        uint256 currencyId, string memory standard)
    public
    {
        _receiveTokensTo(
            uint256(tag) - 1, amount, currencyCt, currencyId, standard
        );
    }

    
    
    
    function hashName(string memory name)
    public
    pure
    returns (bytes32)
    {
        return keccak256(abi.encodePacked(name.upper()));
    }

    
    
    
    
    function depositByIndices(uint256 partnerIndex, uint256 depositIndex)
    public
    view
    returns (int256 balance, uint256 blockNumber, address currencyCt, uint256 currencyId)
    {
        
        require(0 < partnerIndex && partnerIndex <= partners.length, "Some error message when require fails [PartnerFund.sol:160]");

        return _depositByIndices(partnerIndex - 1, depositIndex);
    }

    
    
    
    
    function depositByName(string memory name, uint depositIndex)
    public
    view
    returns (int256 balance, uint256 blockNumber, address currencyCt, uint256 currencyId)
    {
        
        return _depositByIndices(indexByName(name) - 1, depositIndex);
    }

    
    
    
    
    function depositByNameHash(bytes32 nameHash, uint depositIndex)
    public
    view
    returns (int256 balance, uint256 blockNumber, address currencyCt, uint256 currencyId)
    {
        
        return _depositByIndices(indexByNameHash(nameHash) - 1, depositIndex);
    }

    
    
    
    
    function depositByWallet(address wallet, uint depositIndex)
    public
    view
    returns (int256 balance, uint256 blockNumber, address currencyCt, uint256 currencyId)
    {
        
        return _depositByIndices(indexByWallet(wallet) - 1, depositIndex);
    }

    
    
    
    function depositsCountByIndex(uint256 index)
    public
    view
    returns (uint256)
    {
        
        require(0 < index && index <= partners.length, "Some error message when require fails [PartnerFund.sol:213]");

        return _depositsCountByIndex(index - 1);
    }

    
    
    
    function depositsCountByName(string memory name)
    public
    view
    returns (uint256)
    {
        
        return _depositsCountByIndex(indexByName(name) - 1);
    }

    
    
    
    function depositsCountByNameHash(bytes32 nameHash)
    public
    view
    returns (uint256)
    {
        
        return _depositsCountByIndex(indexByNameHash(nameHash) - 1);
    }

    
    
    
    function depositsCountByWallet(address wallet)
    public
    view
    returns (uint256)
    {
        
        return _depositsCountByIndex(indexByWallet(wallet) - 1);
    }

    
    
    
    
    
    function activeBalanceByIndex(uint256 index, address currencyCt, uint256 currencyId)
    public
    view
    returns (int256)
    {
        
        require(0 < index && index <= partners.length, "Some error message when require fails [PartnerFund.sol:265]");

        return _activeBalanceByIndex(index - 1, currencyCt, currencyId);
    }

    
    
    
    
    
    function activeBalanceByName(string memory name, address currencyCt, uint256 currencyId)
    public
    view
    returns (int256)
    {
        
        return _activeBalanceByIndex(indexByName(name) - 1, currencyCt, currencyId);
    }

    
    
    
    
    
    function activeBalanceByNameHash(bytes32 nameHash, address currencyCt, uint256 currencyId)
    public
    view
    returns (int256)
    {
        
        return _activeBalanceByIndex(indexByNameHash(nameHash) - 1, currencyCt, currencyId);
    }

    
    
    
    
    
    function activeBalanceByWallet(address wallet, address currencyCt, uint256 currencyId)
    public
    view
    returns (int256)
    {
        
        return _activeBalanceByIndex(indexByWallet(wallet) - 1, currencyCt, currencyId);
    }

    
    
    
    
    
    function stagedBalanceByIndex(uint256 index, address currencyCt, uint256 currencyId)
    public
    view
    returns (int256)
    {
        
        require(0 < index && index <= partners.length, "Some error message when require fails [PartnerFund.sol:323]");

        return _stagedBalanceByIndex(index - 1, currencyCt, currencyId);
    }

    
    
    
    
    
    function stagedBalanceByName(string memory name, address currencyCt, uint256 currencyId)
    public
    view
    returns (int256)
    {
        
        return _stagedBalanceByIndex(indexByName(name) - 1, currencyCt, currencyId);
    }

    
    
    
    
    
    function stagedBalanceByNameHash(bytes32 nameHash, address currencyCt, uint256 currencyId)
    public
    view
    returns (int256)
    {
        
        return _stagedBalanceByIndex(indexByNameHash(nameHash) - 1, currencyCt, currencyId);
    }

    
    
    
    
    
    function stagedBalanceByWallet(address wallet, address currencyCt, uint256 currencyId)
    public
    view
    returns (int256)
    {
        
        return _stagedBalanceByIndex(indexByWallet(wallet) - 1, currencyCt, currencyId);
    }

    
    
    function partnersCount()
    public
    view
    returns (uint256)
    {
        return partners.length;
    }

    
    
    
    
    
    
    function registerByName(string memory name, uint256 fee, address wallet,
        bool partnerCanUpdate, bool operatorCanUpdate)
    public
    onlyOperator
    {
        
        require(bytes(name).length > 0, "Some error message when require fails [PartnerFund.sol:392]");

        
        bytes32 nameHash = hashName(name);

        
        _registerPartnerByNameHash(nameHash, fee, wallet, partnerCanUpdate, operatorCanUpdate);

        
        emit RegisterPartnerByNameEvent(name, fee, wallet);
    }

    
    
    
    
    
    
    function registerByNameHash(bytes32 nameHash, uint256 fee, address wallet,
        bool partnerCanUpdate, bool operatorCanUpdate)
    public
    onlyOperator
    {
        
        _registerPartnerByNameHash(nameHash, fee, wallet, partnerCanUpdate, operatorCanUpdate);

        
        emit RegisterPartnerByNameHashEvent(nameHash, fee, wallet);
    }

    
    
    
    function indexByNameHash(bytes32 nameHash)
    public
    view
    returns (uint256)
    {
        uint256 index = _indexByNameHash[nameHash];
        require(0 < index, "Some error message when require fails [PartnerFund.sol:431]");
        return index;
    }

    
    
    
    function indexByName(string memory name)
    public
    view
    returns (uint256)
    {
        return indexByNameHash(hashName(name));
    }

    
    
    
    function indexByWallet(address wallet)
    public
    view
    returns (uint256)
    {
        uint256 index = _indexByWallet[wallet];
        require(0 < index, "Some error message when require fails [PartnerFund.sol:455]");
        return index;
    }

    
    
    
    function isRegisteredByName(string memory name)
    public
    view
    returns (bool)
    {
        return (0 < _indexByNameHash[hashName(name)]);
    }

    
    
    
    function isRegisteredByNameHash(bytes32 nameHash)
    public
    view
    returns (bool)
    {
        return (0 < _indexByNameHash[nameHash]);
    }

    
    
    
    function isRegisteredByWallet(address wallet)
    public
    view
    returns (bool)
    {
        return (0 < _indexByWallet[wallet]);
    }

    
    
    
    function feeByIndex(uint256 index)
    public
    view
    returns (uint256)
    {
        
        require(0 < index && index <= partners.length, "Some error message when require fails [PartnerFund.sol:501]");

        return _partnerFeeByIndex(index - 1);
    }

    
    
    
    function feeByName(string memory name)
    public
    view
    returns (uint256)
    {
        
        return _partnerFeeByIndex(indexByName(name) - 1);
    }

    
    
    
    function feeByNameHash(bytes32 nameHash)
    public
    view
    returns (uint256)
    {
        
        return _partnerFeeByIndex(indexByNameHash(nameHash) - 1);
    }

    
    
    
    function feeByWallet(address wallet)
    public
    view
    returns (uint256)
    {
        
        return _partnerFeeByIndex(indexByWallet(wallet) - 1);
    }

    
    
    
    function setFeeByIndex(uint256 index, uint256 newFee)
    public
    {
        
        require(0 < index && index <= partners.length, "Some error message when require fails [PartnerFund.sol:549]");

        
        uint256 oldFee = _setPartnerFeeByIndex(index - 1, newFee);

        
        emit SetFeeByIndexEvent(index, oldFee, newFee);
    }

    
    
    
    function setFeeByName(string memory name, uint256 newFee)
    public
    {
        
        uint256 oldFee = _setPartnerFeeByIndex(indexByName(name) - 1, newFee);

        
        emit SetFeeByNameEvent(name, oldFee, newFee);
    }

    
    
    
    function setFeeByNameHash(bytes32 nameHash, uint256 newFee)
    public
    {
        
        uint256 oldFee = _setPartnerFeeByIndex(indexByNameHash(nameHash) - 1, newFee);

        
        emit SetFeeByNameHashEvent(nameHash, oldFee, newFee);
    }

    
    
    
    function setFeeByWallet(address wallet, uint256 newFee)
    public
    {
        
        uint256 oldFee = _setPartnerFeeByIndex(indexByWallet(wallet) - 1, newFee);

        
        emit SetFeeByWalletEvent(wallet, oldFee, newFee);
    }

    
    
    
    function walletByIndex(uint256 index)
    public
    view
    returns (address)
    {
        
        require(0 < index && index <= partners.length, "Some error message when require fails [PartnerFund.sol:606]");

        return partners[index - 1].wallet;
    }

    
    
    
    function walletByName(string memory name)
    public
    view
    returns (address)
    {
        
        return partners[indexByName(name) - 1].wallet;
    }

    
    
    
    function walletByNameHash(bytes32 nameHash)
    public
    view
    returns (address)
    {
        
        return partners[indexByNameHash(nameHash) - 1].wallet;
    }

    
    
    
    function setWalletByIndex(uint256 index, address newWallet)
    public
    {
        
        require(0 < index && index <= partners.length, "Some error message when require fails [PartnerFund.sol:642]");

        
        address oldWallet = _setPartnerWalletByIndex(index - 1, newWallet);

        
        emit SetPartnerWalletByIndexEvent(index, oldWallet, newWallet);
    }

    
    
    
    function setWalletByName(string memory name, address newWallet)
    public
    {
        
        address oldWallet = _setPartnerWalletByIndex(indexByName(name) - 1, newWallet);

        
        emit SetPartnerWalletByNameEvent(name, oldWallet, newWallet);
    }

    
    
    
    function setWalletByNameHash(bytes32 nameHash, address newWallet)
    public
    {
        
        address oldWallet = _setPartnerWalletByIndex(indexByNameHash(nameHash) - 1, newWallet);

        
        emit SetPartnerWalletByNameHashEvent(nameHash, oldWallet, newWallet);
    }

    
    
    
    function setWalletByWallet(address oldWallet, address newWallet)
    public
    {
        
        _setPartnerWalletByIndex(indexByWallet(oldWallet) - 1, newWallet);

        
        emit SetPartnerWalletByWalletEvent(oldWallet, newWallet);
    }

    
    
    
    
    function stage(int256 amount, address currencyCt, uint256 currencyId)
    public
    {
        
        uint256 index = indexByWallet(msg.sender);

        
        require(amount.isPositiveInt256(), "Some error message when require fails [PartnerFund.sol:701]");

        
        amount = amount.clampMax(partners[index - 1].active.get(currencyCt, currencyId));

        partners[index - 1].active.sub(amount, currencyCt, currencyId);
        partners[index - 1].staged.add(amount, currencyCt, currencyId);

        partners[index - 1].txHistory.addDeposit(amount, currencyCt, currencyId);

        
        partners[index - 1].fullBalanceHistory.push(
            FullBalanceHistory(
                partners[index - 1].txHistory.depositsCount() - 1,
                partners[index - 1].active.get(currencyCt, currencyId),
                block.number
            )
        );

        
        emit StageEvent(msg.sender, amount, currencyCt, currencyId);
    }

    
    
    
    
    
    function withdraw(int256 amount, address currencyCt, uint256 currencyId, string memory standard)
    public
    {
        
        uint256 index = indexByWallet(msg.sender);

        
        require(amount.isPositiveInt256(), "Some error message when require fails [PartnerFund.sol:736]");

        
        amount = amount.clampMax(partners[index - 1].staged.get(currencyCt, currencyId));

        partners[index - 1].staged.sub(amount, currencyCt, currencyId);

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

        else {
            TransferController controller = transferController(currencyCt, standard);
            (bool success,) = address(controller).delegatecall(
                abi.encodeWithSelector(
                    controller.getDispatchSignature(), address(this), msg.sender, uint256(amount), currencyCt, currencyId
                )
            );
            require(success, "Some error message when require fails [PartnerFund.sol:754]");
        }

        
        emit WithdrawEvent(msg.sender, amount, currencyCt, currencyId);
    }

    
    
    
    
    function _receiveEthersTo(uint256 index, int256 amount)
    private
    {
        
        require(index < partners.length, "Some error message when require fails [PartnerFund.sol:769]");

        
        partners[index].active.add(amount, address(0), 0);
        partners[index].txHistory.addDeposit(amount, address(0), 0);

        
        partners[index].fullBalanceHistory.push(
            FullBalanceHistory(
                partners[index].txHistory.depositsCount() - 1,
                partners[index].active.get(address(0), 0),
                block.number
            )
        );

        
        emit ReceiveEvent(msg.sender, amount, address(0), 0);
    }

    
    function _receiveTokensTo(uint256 index, int256 amount, address currencyCt,
        uint256 currencyId, string memory standard)
    private
    {
        
        require(index < partners.length, "Some error message when require fails [PartnerFund.sol:794]");

        require(amount.isNonZeroPositiveInt256(), "Some error message when require fails [PartnerFund.sol:796]");

        
        TransferController controller = transferController(currencyCt, standard);
        (bool success,) = address(controller).delegatecall(
            abi.encodeWithSelector(
                controller.getReceiveSignature(), msg.sender, this, uint256(amount), currencyCt, currencyId
            )
        );
        require(success, "Some error message when require fails [PartnerFund.sol:805]");

        
        partners[index].active.add(amount, currencyCt, currencyId);
        partners[index].txHistory.addDeposit(amount, currencyCt, currencyId);

        
        partners[index].fullBalanceHistory.push(
            FullBalanceHistory(
                partners[index].txHistory.depositsCount() - 1,
                partners[index].active.get(currencyCt, currencyId),
                block.number
            )
        );

        
        emit ReceiveEvent(msg.sender, amount, currencyCt, currencyId);
    }

    
    function _depositByIndices(uint256 partnerIndex, uint256 depositIndex)
    private
    view
    returns (int256 balance, uint256 blockNumber, address currencyCt, uint256 currencyId)
    {
        require(depositIndex < partners[partnerIndex].fullBalanceHistory.length, "Some error message when require fails [PartnerFund.sol:830]");

        FullBalanceHistory storage entry = partners[partnerIndex].fullBalanceHistory[depositIndex];
        (,, currencyCt, currencyId) = partners[partnerIndex].txHistory.deposit(entry.listIndex);

        balance = entry.balance;
        blockNumber = entry.blockNumber;
    }

    
    function _depositsCountByIndex(uint256 index)
    private
    view
    returns (uint256)
    {
        return partners[index].fullBalanceHistory.length;
    }

    
    function _activeBalanceByIndex(uint256 index, address currencyCt, uint256 currencyId)
    private
    view
    returns (int256)
    {
        return partners[index].active.get(currencyCt, currencyId);
    }

    
    function _stagedBalanceByIndex(uint256 index, address currencyCt, uint256 currencyId)
    private
    view
    returns (int256)
    {
        return partners[index].staged.get(currencyCt, currencyId);
    }

    function _registerPartnerByNameHash(bytes32 nameHash, uint256 fee, address wallet,
        bool partnerCanUpdate, bool operatorCanUpdate)
    private
    {
        
        require(0 == _indexByNameHash[nameHash], "Some error message when require fails [PartnerFund.sol:871]");

        
        require(partnerCanUpdate || operatorCanUpdate, "Some error message when require fails [PartnerFund.sol:874]");

        
        partners.length++;

        
        uint256 index = partners.length;

        
        partners[index - 1].nameHash = nameHash;
        partners[index - 1].fee = fee;
        partners[index - 1].wallet = wallet;
        partners[index - 1].partnerCanUpdate = partnerCanUpdate;
        partners[index - 1].operatorCanUpdate = operatorCanUpdate;
        partners[index - 1].index = index;

        
        _indexByNameHash[nameHash] = index;

        
        _indexByWallet[wallet] = index;
    }

    
    function _setPartnerFeeByIndex(uint256 index, uint256 fee)
    private
    returns (uint256)
    {
        uint256 oldFee = partners[index].fee;

        
        if (isOperator())
            require(partners[index].operatorCanUpdate, "Some error message when require fails [PartnerFund.sol:906]");

        else {
            
            require(msg.sender == partners[index].wallet, "Some error message when require fails [PartnerFund.sol:910]");

            
            require(partners[index].partnerCanUpdate, "Some error message when require fails [PartnerFund.sol:913]");
        }

        
        partners[index].fee = fee;

        return oldFee;
    }

    
    function _setPartnerWalletByIndex(uint256 index, address newWallet)
    private
    returns (address)
    {
        address oldWallet = partners[index].wallet;

        
        if (oldWallet == address(0))
            require(isOperator(), "Some error message when require fails [PartnerFund.sol:931]");

        
        else if (isOperator())
            require(partners[index].operatorCanUpdate, "Some error message when require fails [PartnerFund.sol:935]");

        else {
            
            require(msg.sender == oldWallet, "Some error message when require fails [PartnerFund.sol:939]");

            
            require(partners[index].partnerCanUpdate, "Some error message when require fails [PartnerFund.sol:942]");

            
            require(partners[index].operatorCanUpdate || newWallet != address(0), "Some error message when require fails [PartnerFund.sol:945]");
        }

        
        partners[index].wallet = newWallet;

        
        if (oldWallet != address(0))
            _indexByWallet[oldWallet] = 0;
        if (newWallet != address(0))
            _indexByWallet[newWallet] = index;

        return oldWallet;
    }

    
    function _partnerFeeByIndex(uint256 index)
    private
    view
    returns (uint256)
    {
        return partners[index].fee;
    }
}

library DriipSettlementTypesLib {
    
    
    
    enum SettlementRole {Origin, Target}

    struct SettlementParty {
        uint256 nonce;
        address wallet;
        uint256 doneBlockNumber;
    }

    struct Settlement {
        string settledKind;
        bytes32 settledHash;
        SettlementParty origin;
        SettlementParty target;
    }
}

contract DriipSettlementState is Ownable, Servable, CommunityVotable, Upgradable {
    using SafeMathIntLib for int256;
    using SafeMathUintLib for uint256;

    
    
    
    string constant public INIT_SETTLEMENT_ACTION = "init_settlement";
    string constant public COMPLETE_SETTLEMENT_ACTION = "complete_settlement";
    string constant public SET_MAX_NONCE_ACTION = "set_max_nonce";
    string constant public ADD_SETTLED_AMOUNT_ACTION = "add_settled_amount";
    string constant public SET_TOTAL_FEE_ACTION = "set_total_fee";

    
    
    
    uint256 public maxDriipNonce;

    DriipSettlementTypesLib.Settlement[] public settlements;
    mapping(address => uint256[]) public walletSettlementIndices;
    mapping(address => mapping(uint256 => uint256)) public walletNonceSettlementIndex;

    mapping(address => mapping(address => mapping(uint256 => uint256))) public walletCurrencyMaxNonce;

    mapping(address => mapping(address => mapping(uint256 => mapping(uint256 => int256)))) public walletCurrencyBlockNumberSettledAmount;
    mapping(address => mapping(address => mapping(uint256 => uint256[]))) public walletCurrencySettledBlockNumbers;

    mapping(address => mapping(address => mapping(address => mapping(address => mapping(uint256 => MonetaryTypesLib.NoncedAmount))))) public totalFeesMap;

    
    
    
    event InitSettlementEvent(DriipSettlementTypesLib.Settlement settlement);
    event CompleteSettlementPartyEvent(address wallet, uint256 nonce, DriipSettlementTypesLib.SettlementRole settlementRole,
        uint256 doneBlockNumber);
    event SetMaxDriipNonceEvent(uint256 maxDriipNonce);
    event UpdateMaxDriipNonceFromCommunityVoteEvent(uint256 maxDriipNonce);
    event SetMaxNonceByWalletAndCurrencyEvent(address wallet, MonetaryTypesLib.Currency currency,
        uint256 maxNonce);
    event AddSettledAmountEvent(address wallet, int256 amount, MonetaryTypesLib.Currency currency,
        uint256 blockNumber);
    event SetTotalFeeEvent(address wallet, Beneficiary beneficiary, address destination,
        MonetaryTypesLib.Currency currency, MonetaryTypesLib.NoncedAmount totalFee);
    event UpgradeSettlementEvent(DriipSettlementTypesLib.Settlement settlement);
    event UpgradeSettledAmountEvent(address wallet, int256 amount, MonetaryTypesLib.Currency currency,
        uint256 blockNumber);

    
    
    
    constructor(address deployer) Ownable(deployer) public {
    }

    
    
    
    
    function settlementsCount()
    public
    view
    returns (uint256)
    {
        return settlements.length;
    }

    
    
    
    function settlementsCountByWallet(address wallet)
    public
    view
    returns (uint256)
    {
        return walletSettlementIndices[wallet].length;
    }

    
    
    
    
    function settlementByWalletAndIndex(address wallet, uint256 index)
    public
    view
    returns (DriipSettlementTypesLib.Settlement memory)
    {
        require(walletSettlementIndices[wallet].length > index, "Index out of bounds [DriipSettlementState.sol:114]");
        return settlements[walletSettlementIndices[wallet][index] - 1];
    }

    
    
    
    
    function settlementByWalletAndNonce(address wallet, uint256 nonce)
    public
    view
    returns (DriipSettlementTypesLib.Settlement memory)
    {
        require(0 != walletNonceSettlementIndex[wallet][nonce], "No settlement found for wallet and nonce [DriipSettlementState.sol:127]");
        return settlements[walletNonceSettlementIndex[wallet][nonce] - 1];
    }

    
    
    
    
    
    
    
    
    function initSettlement(string memory settledKind, bytes32 settledHash, address originWallet,
        uint256 originNonce, address targetWallet, uint256 targetNonce)
    public
    onlyEnabledServiceAction(INIT_SETTLEMENT_ACTION)
    {
        if (
            0 == walletNonceSettlementIndex[originWallet][originNonce] &&
            0 == walletNonceSettlementIndex[targetWallet][targetNonce]
        ) {
            
            settlements.length++;

            
            uint256 index = settlements.length - 1;

            
            settlements[index].settledKind = settledKind;
            settlements[index].settledHash = settledHash;
            settlements[index].origin.nonce = originNonce;
            settlements[index].origin.wallet = originWallet;
            settlements[index].target.nonce = targetNonce;
            settlements[index].target.wallet = targetWallet;

            
            emit InitSettlementEvent(settlements[index]);

            
            index++;
            walletSettlementIndices[originWallet].push(index);
            walletSettlementIndices[targetWallet].push(index);
            walletNonceSettlementIndex[originWallet][originNonce] = index;
            walletNonceSettlementIndex[targetWallet][targetNonce] = index;
        }
    }

    
    
    
    
    
    function completeSettlement(address wallet, uint256 nonce,
        DriipSettlementTypesLib.SettlementRole settlementRole, bool done)
    public
    onlyEnabledServiceAction(COMPLETE_SETTLEMENT_ACTION)
    {
        
        uint256 index = walletNonceSettlementIndex[wallet][nonce];

        
        require(0 != index, "No settlement found for wallet and nonce [DriipSettlementState.sol:188]");

        
        DriipSettlementTypesLib.SettlementParty storage party =
        DriipSettlementTypesLib.SettlementRole.Origin == settlementRole ?
        settlements[index - 1].origin :
        settlements[index - 1].target;

        
        party.doneBlockNumber = done ? block.number : 0;

        
        emit CompleteSettlementPartyEvent(wallet, nonce, settlementRole, party.doneBlockNumber);
    }

    
    
    
    
    function isSettlementPartyDone(address wallet, uint256 nonce)
    public
    view
    returns (bool)
    {
        
        uint256 index = walletNonceSettlementIndex[wallet][nonce];

        
        if (0 == index)
            return false;

        
        return (
        wallet == settlements[index - 1].origin.wallet ?
        0 != settlements[index - 1].origin.doneBlockNumber :
        0 != settlements[index - 1].target.doneBlockNumber
        );
    }

    
    
    
    
    
    
    function isSettlementPartyDone(address wallet, uint256 nonce,
        DriipSettlementTypesLib.SettlementRole settlementRole)
    public
    view
    returns (bool)
    {
        
        uint256 index = walletNonceSettlementIndex[wallet][nonce];

        
        if (0 == index)
            return false;

        
        DriipSettlementTypesLib.SettlementParty storage settlementParty =
        DriipSettlementTypesLib.SettlementRole.Origin == settlementRole ?
        settlements[index - 1].origin : settlements[index - 1].target;

        
        require(wallet == settlementParty.wallet, "Wallet has wrong settlement role [DriipSettlementState.sol:252]");

        
        return 0 != settlementParty.doneBlockNumber;
    }

    
    
    
    
    function settlementPartyDoneBlockNumber(address wallet, uint256 nonce)
    public
    view
    returns (uint256)
    {
        
        uint256 index = walletNonceSettlementIndex[wallet][nonce];

        
        require(0 != index, "No settlement found for wallet and nonce [DriipSettlementState.sol:271]");

        
        return (
        wallet == settlements[index - 1].origin.wallet ?
        settlements[index - 1].origin.doneBlockNumber :
        settlements[index - 1].target.doneBlockNumber
        );
    }

    
    
    
    
    
    function settlementPartyDoneBlockNumber(address wallet, uint256 nonce,
        DriipSettlementTypesLib.SettlementRole settlementRole)
    public
    view
    returns (uint256)
    {
        
        uint256 index = walletNonceSettlementIndex[wallet][nonce];

        
        require(0 != index, "No settlement found for wallet and nonce [DriipSettlementState.sol:296]");

        
        DriipSettlementTypesLib.SettlementParty storage settlementParty =
        DriipSettlementTypesLib.SettlementRole.Origin == settlementRole ?
        settlements[index - 1].origin : settlements[index - 1].target;

        
        require(wallet == settlementParty.wallet, "Wallet has wrong settlement role [DriipSettlementState.sol:304]");

        
        return settlementParty.doneBlockNumber;
    }

    
    
    function setMaxDriipNonce(uint256 _maxDriipNonce)
    public
    onlyEnabledServiceAction(SET_MAX_NONCE_ACTION)
    {
        maxDriipNonce = _maxDriipNonce;

        
        emit SetMaxDriipNonceEvent(maxDriipNonce);
    }

    
    function updateMaxDriipNonceFromCommunityVote()
    public
    {
        uint256 _maxDriipNonce = communityVote.getMaxDriipNonce();
        if (0 == _maxDriipNonce)
            return;

        maxDriipNonce = _maxDriipNonce;

        
        emit UpdateMaxDriipNonceFromCommunityVoteEvent(maxDriipNonce);
    }

    
    
    
    
    function maxNonceByWalletAndCurrency(address wallet, MonetaryTypesLib.Currency memory currency)
    public
    view
    returns (uint256)
    {
        return walletCurrencyMaxNonce[wallet][currency.ct][currency.id];
    }

    
    
    
    
    function setMaxNonceByWalletAndCurrency(address wallet, MonetaryTypesLib.Currency memory currency,
        uint256 maxNonce)
    public
    onlyEnabledServiceAction(SET_MAX_NONCE_ACTION)
    {
        
        walletCurrencyMaxNonce[wallet][currency.ct][currency.id] = maxNonce;

        
        emit SetMaxNonceByWalletAndCurrencyEvent(wallet, currency, maxNonce);
    }

    
    
    
    
    function settledAmountByBlockNumber(address wallet, MonetaryTypesLib.Currency memory currency,
        uint256 blockNumber)
    public
    view
    returns (int256)
    {
        uint256 settledBlockNumber = _walletSettledBlockNumber(wallet, currency, blockNumber);
        return walletCurrencyBlockNumberSettledAmount[wallet][currency.ct][currency.id][settledBlockNumber];
    }

    
    
    
    
    
    function addSettledAmountByBlockNumber(address wallet, int256 amount, MonetaryTypesLib.Currency memory currency,
        uint256 blockNumber)
    public
    onlyEnabledServiceAction(ADD_SETTLED_AMOUNT_ACTION)
    {
        
        uint256 settledBlockNumber = _walletSettledBlockNumber(wallet, currency, blockNumber);

        
        walletCurrencyBlockNumberSettledAmount[wallet][currency.ct][currency.id][settledBlockNumber] =
        walletCurrencyBlockNumberSettledAmount[wallet][currency.ct][currency.id][settledBlockNumber].add(amount);

        
        walletCurrencySettledBlockNumbers[wallet][currency.ct][currency.id].push(block.number);

        
        emit AddSettledAmountEvent(wallet, amount, currency, blockNumber);
    }

    
    
    
    
    
    
    
    function totalFee(address wallet, Beneficiary beneficiary, address destination,
        MonetaryTypesLib.Currency memory currency)
    public
    view
    returns (MonetaryTypesLib.NoncedAmount memory)
    {
        return totalFeesMap[wallet][address(beneficiary)][destination][currency.ct][currency.id];
    }

    
    
    
    
    
    
    function setTotalFee(address wallet, Beneficiary beneficiary, address destination,
        MonetaryTypesLib.Currency memory currency, MonetaryTypesLib.NoncedAmount memory _totalFee)
    public
    onlyEnabledServiceAction(SET_TOTAL_FEE_ACTION)
    {
        
        totalFeesMap[wallet][address(beneficiary)][destination][currency.ct][currency.id] = _totalFee;

        
        emit SetTotalFeeEvent(wallet, beneficiary, destination, currency, _totalFee);
    }

    
    
    function upgradeSettlement(DriipSettlementTypesLib.Settlement memory settlement)
    public
    onlyWhenUpgrading
    {
        
        require(
            0 == walletNonceSettlementIndex[settlement.origin.wallet][settlement.origin.nonce],
            "Settlement exists for origin wallet and nonce [DriipSettlementState.sol:443]"
        );
        require(
            0 == walletNonceSettlementIndex[settlement.target.wallet][settlement.target.nonce],
            "Settlement exists for target wallet and nonce [DriipSettlementState.sol:447]"
        );

        
        settlements.push(settlement);

        
        uint256 index = settlements.length;

        
        walletSettlementIndices[settlement.origin.wallet].push(index);
        walletSettlementIndices[settlement.target.wallet].push(index);
        walletNonceSettlementIndex[settlement.origin.wallet][settlement.origin.nonce] = index;
        walletNonceSettlementIndex[settlement.target.wallet][settlement.target.nonce] = index;

        
        emit UpgradeSettlementEvent(settlement);
    }

    
    
    
    
    
    function upgradeSettledAmount(address wallet, int256 amount, MonetaryTypesLib.Currency memory currency,
        uint256 blockNumber)
    public
    onlyWhenUpgrading
    {
        
        require(0 == walletCurrencyBlockNumberSettledAmount[wallet][currency.ct][currency.id][blockNumber], "[DriipSettlementState.sol:479]");

        
        walletCurrencyBlockNumberSettledAmount[wallet][currency.ct][currency.id][blockNumber] = amount;

        
        walletCurrencySettledBlockNumbers[wallet][currency.ct][currency.id].push(blockNumber);

        
        emit UpgradeSettledAmountEvent(wallet, amount, currency, blockNumber);
    }

    
    
    
    function _walletSettledBlockNumber(address wallet, MonetaryTypesLib.Currency memory currency,
        uint256 blockNumber)
    private
    view
    returns (uint256)
    {
        for (uint256 i = walletCurrencySettledBlockNumbers[wallet][currency.ct][currency.id].length; i > 0; i--)
            if (walletCurrencySettledBlockNumbers[wallet][currency.ct][currency.id][i - 1] <= blockNumber)
                return walletCurrencySettledBlockNumbers[wallet][currency.ct][currency.id][i - 1];
        return 0;
    }
}

contract DriipSettlementChallengeByPayment is Ownable, ConfigurableOperational, Validatable, WalletLockable,
BalanceTrackable {
    using SafeMathIntLib for int256;
    using SafeMathUintLib for uint256;
    using BalanceTrackerLib for BalanceTracker;

    
    
    
    DriipSettlementDisputeByPayment public driipSettlementDisputeByPayment;
    DriipSettlementChallengeState public driipSettlementChallengeState;
    NullSettlementChallengeState public nullSettlementChallengeState;
    DriipSettlementState public driipSettlementState;

    
    
    
    event SetDriipSettlementDisputeByPaymentEvent(DriipSettlementDisputeByPayment oldDriipSettlementDisputeByPayment,
        DriipSettlementDisputeByPayment newDriipSettlementDisputeByPayment);
    event SetDriipSettlementChallengeStateEvent(DriipSettlementChallengeState oldDriipSettlementChallengeState,
        DriipSettlementChallengeState newDriipSettlementChallengeState);
    event SetNullSettlementChallengeStateEvent(NullSettlementChallengeState oldNullSettlementChallengeState,
        NullSettlementChallengeState newNullSettlementChallengeState);
    event SetDriipSettlementStateEvent(DriipSettlementState oldDriipSettlementState,
        DriipSettlementState newDriipSettlementState);
    event StartChallengeFromPaymentEvent(address wallet, uint256 nonce, int256 cumulativeTransferAmount, int256 stageAmount,
        int256 targetBalanceAmount, address currencyCt, uint256 currencyId);
    event StartChallengeFromPaymentByProxyEvent(address wallet, uint256 nonce, int256 cumulativeTransferAmount, int256 stageAmount,
        int256 targetBalanceAmount, address currencyCt, uint256 currencyId, address proxy);
    event StopChallengeEvent(address wallet, uint256 nonce, int256 cumulativeTransferAmount, int256 stageAmount,
        int256 targetBalanceAmount, address currencyCt, uint256 currencyId);
    event StopChallengeByProxyEvent(address wallet, uint256 nonce, int256 cumulativeTransferAmount, int256 stageAmount,
        int256 targetBalanceAmount, address currencyCt, uint256 currencyId, address proxy);
    event ChallengeByPaymentEvent(address challengedWallet, uint256 nonce, int256 cumulativeTransferAmount, int256 stageAmount,
        int256 targetBalanceAmount, address currencyCt, uint256 currencyId, address challengerWallet);

    
    
    
    constructor(address deployer) Ownable(deployer) public {
    }

    
    
    
    
    
    function setDriipSettlementDisputeByPayment(DriipSettlementDisputeByPayment newDriipSettlementDisputeByPayment)
    public
    onlyDeployer
    notNullAddress(address(newDriipSettlementDisputeByPayment))
    {
        DriipSettlementDisputeByPayment oldDriipSettlementDisputeByPayment = driipSettlementDisputeByPayment;
        driipSettlementDisputeByPayment = newDriipSettlementDisputeByPayment;
        emit SetDriipSettlementDisputeByPaymentEvent(oldDriipSettlementDisputeByPayment, driipSettlementDisputeByPayment);
    }

    
    
    function setDriipSettlementChallengeState(DriipSettlementChallengeState newDriipSettlementChallengeState)
    public
    onlyDeployer
    notNullAddress(address(newDriipSettlementChallengeState))
    {
        DriipSettlementChallengeState oldDriipSettlementChallengeState = driipSettlementChallengeState;
        driipSettlementChallengeState = newDriipSettlementChallengeState;
        emit SetDriipSettlementChallengeStateEvent(oldDriipSettlementChallengeState, driipSettlementChallengeState);
    }

    
    
    function setNullSettlementChallengeState(NullSettlementChallengeState newNullSettlementChallengeState)
    public
    onlyDeployer
    notNullAddress(address(newNullSettlementChallengeState))
    {
        NullSettlementChallengeState oldNullSettlementChallengeState = nullSettlementChallengeState;
        nullSettlementChallengeState = newNullSettlementChallengeState;
        emit SetNullSettlementChallengeStateEvent(oldNullSettlementChallengeState, nullSettlementChallengeState);
    }

    
    
    function setDriipSettlementState(DriipSettlementState newDriipSettlementState)
    public
    onlyDeployer
    notNullAddress(address(newDriipSettlementState))
    {
        DriipSettlementState oldDriipSettlementState = driipSettlementState;
        driipSettlementState = newDriipSettlementState;
        emit SetDriipSettlementStateEvent(oldDriipSettlementState, driipSettlementState);
    }

    
    
    
    function startChallengeFromPayment(PaymentTypesLib.Payment memory payment, int256 stageAmount)
    public
    {
        
        require(!walletLocker.isLocked(msg.sender), "Wallet found locked [DriipSettlementChallengeByPayment.sol:134]");

        
        _startChallengeFromPayment(msg.sender, payment, stageAmount, true);

        
        emit StartChallengeFromPaymentEvent(
            msg.sender,
            driipSettlementChallengeState.proposalNonce(msg.sender, payment.currency),
            driipSettlementChallengeState.proposalCumulativeTransferAmount(msg.sender, payment.currency),
            stageAmount,
            driipSettlementChallengeState.proposalTargetBalanceAmount(msg.sender, payment.currency),
            payment.currency.ct, payment.currency.id
        );
    }

    
    
    
    
    function startChallengeFromPaymentByProxy(address wallet, PaymentTypesLib.Payment memory payment, int256 stageAmount)
    public
    onlyOperator
    {
        
        _startChallengeFromPayment(wallet, payment, stageAmount, false);

        
        emit StartChallengeFromPaymentByProxyEvent(
            wallet,
            driipSettlementChallengeState.proposalNonce(wallet, payment.currency),
            driipSettlementChallengeState.proposalCumulativeTransferAmount(wallet, payment.currency),
            stageAmount,
            driipSettlementChallengeState.proposalTargetBalanceAmount(wallet, payment.currency),
            payment.currency.ct, payment.currency.id, msg.sender
        );
    }

    
    
    
    function stopChallenge(address currencyCt, uint256 currencyId)
    public
    {
        
        MonetaryTypesLib.Currency memory currency = MonetaryTypesLib.Currency(currencyCt, currencyId);

        
        _stopChallenge(msg.sender, currency, true, true);

        
        emit StopChallengeEvent(
            msg.sender,
            driipSettlementChallengeState.proposalNonce(msg.sender, currency),
            driipSettlementChallengeState.proposalCumulativeTransferAmount(msg.sender, currency),
            driipSettlementChallengeState.proposalStageAmount(msg.sender, currency),
            driipSettlementChallengeState.proposalTargetBalanceAmount(msg.sender, currency),
            currencyCt, currencyId
        );
    }

    
    
    
    
    function stopChallengeByProxy(address wallet, address currencyCt, uint256 currencyId)
    public
    onlyOperator
    {
        
        MonetaryTypesLib.Currency memory currency = MonetaryTypesLib.Currency(currencyCt, currencyId);

        
        _stopChallenge(wallet, currency, true, false);

        
        emit StopChallengeByProxyEvent(
            wallet,
            driipSettlementChallengeState.proposalNonce(wallet, currency),
            driipSettlementChallengeState.proposalCumulativeTransferAmount(wallet, currency),
            driipSettlementChallengeState.proposalStageAmount(wallet, currency),
            driipSettlementChallengeState.proposalTargetBalanceAmount(wallet, currency),
            currencyCt, currencyId, msg.sender
        );
    }

    
    
    
    
    
    function hasProposal(address wallet, address currencyCt, uint256 currencyId)
    public
    view
    returns (bool)
    {
        return driipSettlementChallengeState.hasProposal(
            wallet, MonetaryTypesLib.Currency(currencyCt, currencyId)
        );
    }

    
    
    
    
    
    function hasProposalTerminated(address wallet, address currencyCt, uint256 currencyId)
    public
    view
    returns (bool)
    {
        return driipSettlementChallengeState.hasProposalTerminated(
            wallet, MonetaryTypesLib.Currency(currencyCt, currencyId)
        );
    }

    
    
    
    
    
    function hasProposalExpired(address wallet, address currencyCt, uint256 currencyId)
    public
    view
    returns (bool)
    {
        return driipSettlementChallengeState.hasProposalExpired(
            wallet, MonetaryTypesLib.Currency(currencyCt, currencyId)
        );
    }

    
    
    
    
    
    function proposalNonce(address wallet, address currencyCt, uint256 currencyId)
    public
    view
    returns (uint256)
    {
        return driipSettlementChallengeState.proposalNonce(
            wallet, MonetaryTypesLib.Currency(currencyCt, currencyId)
        );
    }

    
    
    
    
    
    function proposalReferenceBlockNumber(address wallet, address currencyCt, uint256 currencyId)
    public
    view
    returns (uint256)
    {
        return driipSettlementChallengeState.proposalReferenceBlockNumber(
            wallet, MonetaryTypesLib.Currency(currencyCt, currencyId)
        );
    }

    
    
    
    
    
    function proposalExpirationTime(address wallet, address currencyCt, uint256 currencyId)
    public
    view
    returns (uint256)
    {
        return driipSettlementChallengeState.proposalExpirationTime(
            wallet, MonetaryTypesLib.Currency(currencyCt, currencyId)
        );
    }

    
    
    
    
    
    function proposalStatus(address wallet, address currencyCt, uint256 currencyId)
    public
    view
    returns (SettlementChallengeTypesLib.Status)
    {
        return driipSettlementChallengeState.proposalStatus(
            wallet, MonetaryTypesLib.Currency(currencyCt, currencyId)
        );
    }

    
    
    
    
    
    function proposalStageAmount(address wallet, address currencyCt, uint256 currencyId)
    public
    view
    returns (int256)
    {
        return driipSettlementChallengeState.proposalStageAmount(
            wallet, MonetaryTypesLib.Currency(currencyCt, currencyId)
        );
    }

    
    
    
    
    
    function proposalTargetBalanceAmount(address wallet, address currencyCt, uint256 currencyId)
    public
    view
    returns (int256)
    {
        return driipSettlementChallengeState.proposalTargetBalanceAmount(
            wallet, MonetaryTypesLib.Currency(currencyCt, currencyId)
        );
    }

    
    
    
    
    
    function proposalChallengedHash(address wallet, address currencyCt, uint256 currencyId)
    public
    view
    returns (bytes32)
    {
        return driipSettlementChallengeState.proposalChallengedHash(
            wallet, MonetaryTypesLib.Currency(currencyCt, currencyId)
        );
    }

    
    
    
    
    
    function proposalChallengedKind(address wallet, address currencyCt, uint256 currencyId)
    public
    view
    returns (string memory)
    {
        return driipSettlementChallengeState.proposalChallengedKind(
            wallet, MonetaryTypesLib.Currency(currencyCt, currencyId)
        );
    }

    
    
    
    
    
    function proposalWalletInitiated(address wallet, address currencyCt, uint256 currencyId)
    public
    view
    returns (bool)
    {
        return driipSettlementChallengeState.proposalWalletInitiated(
            wallet, MonetaryTypesLib.Currency(currencyCt, currencyId)
        );
    }

    
    
    
    
    
    function proposalDisqualificationChallenger(address wallet, address currencyCt, uint256 currencyId)
    public
    view
    returns (address)
    {
        return driipSettlementChallengeState.proposalDisqualificationChallenger(
            wallet, MonetaryTypesLib.Currency(currencyCt, currencyId)
        );
    }
    
    
    
    
    
    function proposalDisqualificationBlockNumber(address wallet, address currencyCt, uint256 currencyId)
    public
    view
    returns (uint256)
    {
        return driipSettlementChallengeState.proposalDisqualificationBlockNumber(
            wallet, MonetaryTypesLib.Currency(currencyCt, currencyId)
        );
    }

    
    
    
    
    
    function proposalDisqualificationCandidateKind(address wallet, address currencyCt, uint256 currencyId)
    public
    view
    returns (string memory)
    {
        return driipSettlementChallengeState.proposalDisqualificationCandidateKind(
            wallet, MonetaryTypesLib.Currency(currencyCt, currencyId)
        );
    }

    
    
    
    
    
    function proposalDisqualificationCandidateHash(address wallet, address currencyCt, uint256 currencyId)
    public
    view
    returns (bytes32)
    {
        return driipSettlementChallengeState.proposalDisqualificationCandidateHash(
            wallet, MonetaryTypesLib.Currency(currencyCt, currencyId)
        );
    }

    
    
    
    function challengeByPayment(address wallet, PaymentTypesLib.Payment memory payment)
    public
    onlyOperationalModeNormal
    {
        
        driipSettlementDisputeByPayment.challengeByPayment(wallet, payment, msg.sender);

        
        emit ChallengeByPaymentEvent(
            wallet,
            driipSettlementChallengeState.proposalNonce(wallet, payment.currency),
            driipSettlementChallengeState.proposalCumulativeTransferAmount(wallet, payment.currency),
            driipSettlementChallengeState.proposalStageAmount(wallet, payment.currency),
            driipSettlementChallengeState.proposalTargetBalanceAmount(wallet, payment.currency),
            payment.currency.ct, payment.currency.id, msg.sender
        );
    }

    
    
    
    function _startChallengeFromPayment(address wallet, PaymentTypesLib.Payment memory payment,
        int256 stageAmount, bool walletInitiated)
    private
    onlySealedPayment(payment)
    {
        
        require(
            block.number >= configuration.earliestSettlementBlockNumber(),
            "Current block number below earliest settlement block number [DriipSettlementChallengeByPayment.sol:489]"
        );

        
        require(
            validator.isPaymentParty(payment, wallet),
            "Wallet is not payment party [DriipSettlementChallengeByPayment.sol:495]"
        );

        
        require(
            !driipSettlementChallengeState.hasProposal(wallet, payment.currency) ||
        driipSettlementChallengeState.hasProposalTerminated(wallet, payment.currency),
            "Overlapping driip settlement challenge proposal found [DriipSettlementChallengeByPayment.sol:501]"
        );

        
        require(
            !nullSettlementChallengeState.hasProposal(wallet, payment.currency) ||
        nullSettlementChallengeState.hasProposalTerminated(wallet, payment.currency),
            "Overlapping null settlement challenge proposal found [DriipSettlementChallengeByPayment.sol:508]"
        );

        
        (uint256 nonce, int256 correctedCumulativeTransferAmount) = _paymentPartyProperties(payment, wallet);

        
        require(
            driipSettlementState.maxNonceByWalletAndCurrency(wallet, payment.currency) < nonce,
            "Wallet's nonce below highest settled nonce [DriipSettlementChallengeByPayment.sol:518]"
        );

        
        
        driipSettlementChallengeState.initiateProposal(
            wallet, nonce, correctedCumulativeTransferAmount, stageAmount,
            balanceTracker.fungibleActiveBalanceAmount(wallet, payment.currency)
            .add(correctedCumulativeTransferAmount.sub(stageAmount)),
            payment.currency, payment.blockNumber,
            walletInitiated, payment.seals.operator.hash, PaymentTypesLib.PAYMENT_KIND()
        );
    }

    function _stopChallenge(address wallet, MonetaryTypesLib.Currency memory currency, bool clearNonce, bool walletTerminated)
    private
    {
        
        require(
            driipSettlementChallengeState.hasProposal(wallet, currency),
            "No proposal found [DriipSettlementChallengeByPayment.sol:538]"
        );
        require(
            !driipSettlementChallengeState.hasProposalTerminated(wallet, currency),
            "Proposal found terminated [DriipSettlementChallengeByPayment.sol:542]"
        );

        
        driipSettlementChallengeState.terminateProposal(wallet, currency, clearNonce, walletTerminated);

        
        nullSettlementChallengeState.terminateProposal(wallet, currency);
    }

    function _paymentPartyProperties(PaymentTypesLib.Payment memory payment, address wallet)
    private
    view
    returns (uint256 nonce, int256 correctedCumulativeTransferAmount)
    {
        
        int256 activeBalanceAmountAtPaymentBlock = balanceTracker.fungibleActiveBalanceAmountByBlockNumber(
            wallet, payment.currency, payment.blockNumber
        );

        
        int256 deltaSettledBalanceAmount = driipSettlementState.settledAmountByBlockNumber(
            wallet, payment.currency, payment.blockNumber
        );

        
        
        if (validator.isPaymentSender(payment, wallet)) {
            nonce = payment.sender.nonce;
            correctedCumulativeTransferAmount = payment.sender.balances.current
            .sub(activeBalanceAmountAtPaymentBlock)
            .sub(deltaSettledBalanceAmount);
        } else {
            nonce = payment.recipient.nonce;
            correctedCumulativeTransferAmount = payment.recipient.balances.current
            .sub(activeBalanceAmountAtPaymentBlock)
            .sub(deltaSettledBalanceAmount);
        }
    }
}

Contract Security Audit

Contract ABI

[{"constant":true,"inputs":[{"internalType":"address","name":"wallet","type":"address"},{"internalType":"address","name":"currencyCt","type":"address"},{"internalType":"uint256","name":"currencyId","type":"uint256"}],"name":"proposalChallengedKind","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"wallet","type":"address"},{"internalType":"address","name":"currencyCt","type":"address"},{"internalType":"uint256","name":"currencyId","type":"uint256"}],"name":"proposalStatus","outputs":[{"internalType":"enum SettlementChallengeTypesLib.Status","name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"driipSettlementDisputeByPayment","outputs":[{"internalType":"contract DriipSettlementDisputeByPayment","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"contract Validator","name":"newValidator","type":"address"}],"name":"setValidator","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"wallet","type":"address"},{"internalType":"address","name":"currencyCt","type":"address"},{"internalType":"uint256","name":"currencyId","type":"uint256"}],"name":"proposalTargetBalanceAmount","outputs":[{"internalType":"int256","name":"","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"balanceTracker","outputs":[{"internalType":"contract BalanceTracker","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"walletLockerFrozen","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"driipSettlementState","outputs":[{"internalType":"contract DriipSettlementState","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"triggerSelfDestruction","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"contract DriipSettlementChallengeState","name":"newDriipSettlementChallengeState","type":"address"}],"name":"setDriipSettlementChallengeState","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"selfDestructionDisabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"wallet","type":"address"},{"components":[{"internalType":"int256","name":"amount","type":"int256"},{"components":[{"internalType":"address","name":"ct","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"internalType":"struct MonetaryTypesLib.Currency","name":"currency","type":"tuple"},{"components":[{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"address","name":"wallet","type":"address"},{"components":[{"internalType":"int256","name":"current","type":"int256"},{"internalType":"int256","name":"previous","type":"int256"}],"internalType":"struct NahmiiTypesLib.CurrentPreviousInt256","name":"balances","type":"tuple"},{"components":[{"components":[{"internalType":"int256","name":"amount","type":"int256"},{"components":[{"internalType":"address","name":"ct","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"internalType":"struct MonetaryTypesLib.Currency","name":"currency","type":"tuple"}],"internalType":"struct MonetaryTypesLib.Figure","name":"single","type":"tuple"},{"components":[{"internalType":"uint256","name":"originId","type":"uint256"},{"components":[{"internalType":"int256","name":"amount","type":"int256"},{"components":[{"internalType":"address","name":"ct","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"internalType":"struct MonetaryTypesLib.Currency","name":"currency","type":"tuple"}],"internalType":"struct MonetaryTypesLib.Figure","name":"figure","type":"tuple"}],"internalType":"struct NahmiiTypesLib.OriginFigure[]","name":"total","type":"tuple[]"}],"internalType":"struct NahmiiTypesLib.SingleFigureTotalOriginFigures","name":"fees","type":"tuple"},{"internalType":"string","name":"data","type":"string"}],"internalType":"struct PaymentTypesLib.PaymentSenderParty","name":"sender","type":"tuple"},{"components":[{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"address","name":"wallet","type":"address"},{"components":[{"internalType":"int256","name":"current","type":"int256"},{"internalType":"int256","name":"previous","type":"int256"}],"internalType":"struct NahmiiTypesLib.CurrentPreviousInt256","name":"balances","type":"tuple"},{"components":[{"components":[{"internalType":"uint256","name":"originId","type":"uint256"},{"components":[{"internalType":"int256","name":"amount","type":"int256"},{"components":[{"internalType":"address","name":"ct","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"internalType":"struct MonetaryTypesLib.Currency","name":"currency","type":"tuple"}],"internalType":"struct MonetaryTypesLib.Figure","name":"figure","type":"tuple"}],"internalType":"struct NahmiiTypesLib.OriginFigure[]","name":"total","type":"tuple[]"}],"internalType":"struct NahmiiTypesLib.TotalOriginFigures","name":"fees","type":"tuple"}],"internalType":"struct PaymentTypesLib.PaymentRecipientParty","name":"recipient","type":"tuple"},{"components":[{"internalType":"int256","name":"single","type":"int256"},{"internalType":"int256","name":"total","type":"int256"}],"internalType":"struct NahmiiTypesLib.SingleTotalInt256","name":"transfers","type":"tuple"},{"components":[{"components":[{"internalType":"bytes32","name":"hash","type":"bytes32"},{"components":[{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"},{"internalType":"uint8","name":"v","type":"uint8"}],"internalType":"struct NahmiiTypesLib.Signature","name":"signature","type":"tuple"}],"internalType":"struct NahmiiTypesLib.Seal","name":"wallet","type":"tuple"},{"components":[{"internalType":"bytes32","name":"hash","type":"bytes32"},{"components":[{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"},{"internalType":"uint8","name":"v","type":"uint8"}],"internalType":"struct NahmiiTypesLib.Signature","name":"signature","type":"tuple"}],"internalType":"struct NahmiiTypesLib.Seal","name":"operator","type":"tuple"}],"internalType":"struct NahmiiTypesLib.WalletOperatorSeal","name":"seals","type":"tuple"},{"internalType":"uint256","name":"blockNumber","type":"uint256"},{"components":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"string","name":"data","type":"string"}],"internalType":"struct PaymentTypesLib.Operator","name":"operator","type":"tuple"}],"internalType":"struct PaymentTypesLib.Payment","name":"payment","type":"tuple"},{"internalType":"int256","name":"stageAmount","type":"int256"}],"name":"startChallengeFromPaymentByProxy","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"validator","outputs":[{"internalType":"contract Validator","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"wallet","type":"address"},{"internalType":"address","name":"currencyCt","type":"address"},{"internalType":"uint256","name":"currencyId","type":"uint256"}],"name":"hasProposalExpired","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"balanceTrackerFrozen","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"contract DriipSettlementState","name":"newDriipSettlementState","type":"address"}],"name":"setDriipSettlementState","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"wallet","type":"address"},{"internalType":"address","name":"currencyCt","type":"address"},{"internalType":"uint256","name":"currencyId","type":"uint256"}],"name":"proposalStageAmount","outputs":[{"internalType":"int256","name":"","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"destructor","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"wallet","type":"address"},{"internalType":"address","name":"currencyCt","type":"address"},{"internalType":"uint256","name":"currencyId","type":"uint256"}],"name":"hasProposalTerminated","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"components":[{"internalType":"int256","name":"amount","type":"int256"},{"components":[{"internalType":"address","name":"ct","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"internalType":"struct MonetaryTypesLib.Currency","name":"currency","type":"tuple"},{"components":[{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"address","name":"wallet","type":"address"},{"components":[{"internalType":"int256","name":"current","type":"int256"},{"internalType":"int256","name":"previous","type":"int256"}],"internalType":"struct NahmiiTypesLib.CurrentPreviousInt256","name":"balances","type":"tuple"},{"components":[{"components":[{"internalType":"int256","name":"amount","type":"int256"},{"components":[{"internalType":"address","name":"ct","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"internalType":"struct MonetaryTypesLib.Currency","name":"currency","type":"tuple"}],"internalType":"struct MonetaryTypesLib.Figure","name":"single","type":"tuple"},{"components":[{"internalType":"uint256","name":"originId","type":"uint256"},{"components":[{"internalType":"int256","name":"amount","type":"int256"},{"components":[{"internalType":"address","name":"ct","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"internalType":"struct MonetaryTypesLib.Currency","name":"currency","type":"tuple"}],"internalType":"struct MonetaryTypesLib.Figure","name":"figure","type":"tuple"}],"internalType":"struct NahmiiTypesLib.OriginFigure[]","name":"total","type":"tuple[]"}],"internalType":"struct NahmiiTypesLib.SingleFigureTotalOriginFigures","name":"fees","type":"tuple"},{"internalType":"string","name":"data","type":"string"}],"internalType":"struct PaymentTypesLib.PaymentSenderParty","name":"sender","type":"tuple"},{"components":[{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"address","name":"wallet","type":"address"},{"components":[{"internalType":"int256","name":"current","type":"int256"},{"internalType":"int256","name":"previous","type":"int256"}],"internalType":"struct NahmiiTypesLib.CurrentPreviousInt256","name":"balances","type":"tuple"},{"components":[{"components":[{"internalType":"uint256","name":"originId","type":"uint256"},{"components":[{"internalType":"int256","name":"amount","type":"int256"},{"components":[{"internalType":"address","name":"ct","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"internalType":"struct MonetaryTypesLib.Currency","name":"currency","type":"tuple"}],"internalType":"struct MonetaryTypesLib.Figure","name":"figure","type":"tuple"}],"internalType":"struct NahmiiTypesLib.OriginFigure[]","name":"total","type":"tuple[]"}],"internalType":"struct NahmiiTypesLib.TotalOriginFigures","name":"fees","type":"tuple"}],"internalType":"struct PaymentTypesLib.PaymentRecipientParty","name":"recipient","type":"tuple"},{"components":[{"internalType":"int256","name":"single","type":"int256"},{"internalType":"int256","name":"total","type":"int256"}],"internalType":"struct NahmiiTypesLib.SingleTotalInt256","name":"transfers","type":"tuple"},{"components":[{"components":[{"internalType":"bytes32","name":"hash","type":"bytes32"},{"components":[{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"},{"internalType":"uint8","name":"v","type":"uint8"}],"internalType":"struct NahmiiTypesLib.Signature","name":"signature","type":"tuple"}],"internalType":"struct NahmiiTypesLib.Seal","name":"wallet","type":"tuple"},{"components":[{"internalType":"bytes32","name":"hash","type":"bytes32"},{"components":[{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"},{"internalType":"uint8","name":"v","type":"uint8"}],"internalType":"struct NahmiiTypesLib.Signature","name":"signature","type":"tuple"}],"internalType":"struct NahmiiTypesLib.Seal","name":"operator","type":"tuple"}],"internalType":"struct NahmiiTypesLib.WalletOperatorSeal","name":"seals","type":"tuple"},{"internalType":"uint256","name":"blockNumber","type":"uint256"},{"components":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"string","name":"data","type":"string"}],"internalType":"struct PaymentTypesLib.Operator","name":"operator","type":"tuple"}],"internalType":"struct PaymentTypesLib.Payment","name":"payment","type":"tuple"},{"internalType":"int256","name":"stageAmount","type":"int256"}],"name":"startChallengeFromPayment","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"contract DriipSettlementDisputeByPayment","name":"newDriipSettlementDisputeByPayment","type":"address"}],"name":"setDriipSettlementDisputeByPayment","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"driipSettlementChallengeState","outputs":[{"internalType":"contract DriipSettlementChallengeState","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"operator","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"contract BalanceTracker","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":false,"inputs":[{"internalType":"contract Configuration","name":"newConfiguration","type":"address"}],"name":"setConfiguration","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"configuration","outputs":[{"internalType":"contract Configuration","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"contract NullSettlementChallengeState","name":"newNullSettlementChallengeState","type":"address"}],"name":"setNullSettlementChallengeState","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"wallet","type":"address"},{"internalType":"address","name":"currencyCt","type":"address"},{"internalType":"uint256","name":"currencyId","type":"uint256"}],"name":"proposalReferenceBlockNumber","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"disableSelfDestruction","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"wallet","type":"address"},{"internalType":"address","name":"currencyCt","type":"address"},{"internalType":"uint256","name":"currencyId","type":"uint256"}],"name":"proposalWalletInitiated","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"contract WalletLocker","name":"newWalletLocker","type":"address"}],"name":"setWalletLocker","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"wallet","type":"address"},{"components":[{"internalType":"int256","name":"amount","type":"int256"},{"components":[{"internalType":"address","name":"ct","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"internalType":"struct MonetaryTypesLib.Currency","name":"currency","type":"tuple"},{"components":[{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"address","name":"wallet","type":"address"},{"components":[{"internalType":"int256","name":"current","type":"int256"},{"internalType":"int256","name":"previous","type":"int256"}],"internalType":"struct NahmiiTypesLib.CurrentPreviousInt256","name":"balances","type":"tuple"},{"components":[{"components":[{"internalType":"int256","name":"amount","type":"int256"},{"components":[{"internalType":"address","name":"ct","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"internalType":"struct MonetaryTypesLib.Currency","name":"currency","type":"tuple"}],"internalType":"struct MonetaryTypesLib.Figure","name":"single","type":"tuple"},{"components":[{"internalType":"uint256","name":"originId","type":"uint256"},{"components":[{"internalType":"int256","name":"amount","type":"int256"},{"components":[{"internalType":"address","name":"ct","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"internalType":"struct MonetaryTypesLib.Currency","name":"currency","type":"tuple"}],"internalType":"struct MonetaryTypesLib.Figure","name":"figure","type":"tuple"}],"internalType":"struct NahmiiTypesLib.OriginFigure[]","name":"total","type":"tuple[]"}],"internalType":"struct NahmiiTypesLib.SingleFigureTotalOriginFigures","name":"fees","type":"tuple"},{"internalType":"string","name":"data","type":"string"}],"internalType":"struct PaymentTypesLib.PaymentSenderParty","name":"sender","type":"tuple"},{"components":[{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"address","name":"wallet","type":"address"},{"components":[{"internalType":"int256","name":"current","type":"int256"},{"internalType":"int256","name":"previous","type":"int256"}],"internalType":"struct NahmiiTypesLib.CurrentPreviousInt256","name":"balances","type":"tuple"},{"components":[{"components":[{"internalType":"uint256","name":"originId","type":"uint256"},{"components":[{"internalType":"int256","name":"amount","type":"int256"},{"components":[{"internalType":"address","name":"ct","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"internalType":"struct MonetaryTypesLib.Currency","name":"currency","type":"tuple"}],"internalType":"struct MonetaryTypesLib.Figure","name":"figure","type":"tuple"}],"internalType":"struct NahmiiTypesLib.OriginFigure[]","name":"total","type":"tuple[]"}],"internalType":"struct NahmiiTypesLib.TotalOriginFigures","name":"fees","type":"tuple"}],"internalType":"struct PaymentTypesLib.PaymentRecipientParty","name":"recipient","type":"tuple"},{"components":[{"internalType":"int256","name":"single","type":"int256"},{"internalType":"int256","name":"total","type":"int256"}],"internalType":"struct NahmiiTypesLib.SingleTotalInt256","name":"transfers","type":"tuple"},{"components":[{"components":[{"internalType":"bytes32","name":"hash","type":"bytes32"},{"components":[{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"},{"internalType":"uint8","name":"v","type":"uint8"}],"internalType":"struct NahmiiTypesLib.Signature","name":"signature","type":"tuple"}],"internalType":"struct NahmiiTypesLib.Seal","name":"wallet","type":"tuple"},{"components":[{"internalType":"bytes32","name":"hash","type":"bytes32"},{"components":[{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"},{"internalType":"uint8","name":"v","type":"uint8"}],"internalType":"struct NahmiiTypesLib.Signature","name":"signature","type":"tuple"}],"internalType":"struct NahmiiTypesLib.Seal","name":"operator","type":"tuple"}],"internalType":"struct NahmiiTypesLib.WalletOperatorSeal","name":"seals","type":"tuple"},{"internalType":"uint256","name":"blockNumber","type":"uint256"},{"components":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"string","name":"data","type":"string"}],"internalType":"struct PaymentTypesLib.Operator","name":"operator","type":"tuple"}],"internalType":"struct PaymentTypesLib.Payment","name":"payment","type":"tuple"}],"name":"challengeByPayment","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"newDeployer","type":"address"}],"name":"setDeployer","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"wallet","type":"address"},{"internalType":"address","name":"currencyCt","type":"address"},{"internalType":"uint256","name":"currencyId","type":"uint256"}],"name":"stopChallengeByProxy","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"wallet","type":"address"},{"internalType":"address","name":"currencyCt","type":"address"},{"internalType":"uint256","name":"currencyId","type":"uint256"}],"name":"proposalChallengedHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"wallet","type":"address"},{"internalType":"address","name":"currencyCt","type":"address"},{"internalType":"uint256","name":"currencyId","type":"uint256"}],"name":"proposalDisqualificationCandidateHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","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":[{"internalType":"address","name":"wallet","type":"address"},{"internalType":"address","name":"currencyCt","type":"address"},{"internalType":"uint256","name":"currencyId","type":"uint256"}],"name":"hasProposal","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"currencyCt","type":"address"},{"internalType":"uint256","name":"currencyId","type":"uint256"}],"name":"stopChallenge","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"wallet","type":"address"},{"internalType":"address","name":"currencyCt","type":"address"},{"internalType":"uint256","name":"currencyId","type":"uint256"}],"name":"proposalDisqualificationCandidateKind","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"wallet","type":"address"},{"internalType":"address","name":"currencyCt","type":"address"},{"internalType":"uint256","name":"currencyId","type":"uint256"}],"name":"proposalNonce","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"wallet","type":"address"},{"internalType":"address","name":"currencyCt","type":"address"},{"internalType":"uint256","name":"currencyId","type":"uint256"}],"name":"proposalDisqualificationBlockNumber","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"deployer","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"walletLocker","outputs":[{"internalType":"contract WalletLocker","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"nullSettlementChallengeState","outputs":[{"internalType":"contract NullSettlementChallengeState","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"wallet","type":"address"},{"internalType":"address","name":"currencyCt","type":"address"},{"internalType":"uint256","name":"currencyId","type":"uint256"}],"name":"proposalExpirationTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"wallet","type":"address"},{"internalType":"address","name":"currencyCt","type":"address"},{"internalType":"uint256","name":"currencyId","type":"uint256"}],"name":"proposalDisqualificationChallenger","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"deployer","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract DriipSettlementDisputeByPayment","name":"oldDriipSettlementDisputeByPayment","type":"address"},{"indexed":false,"internalType":"contract DriipSettlementDisputeByPayment","name":"newDriipSettlementDisputeByPayment","type":"address"}],"name":"SetDriipSettlementDisputeByPaymentEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract DriipSettlementChallengeState","name":"oldDriipSettlementChallengeState","type":"address"},{"indexed":false,"internalType":"contract DriipSettlementChallengeState","name":"newDriipSettlementChallengeState","type":"address"}],"name":"SetDriipSettlementChallengeStateEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract NullSettlementChallengeState","name":"oldNullSettlementChallengeState","type":"address"},{"indexed":false,"internalType":"contract NullSettlementChallengeState","name":"newNullSettlementChallengeState","type":"address"}],"name":"SetNullSettlementChallengeStateEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract DriipSettlementState","name":"oldDriipSettlementState","type":"address"},{"indexed":false,"internalType":"contract DriipSettlementState","name":"newDriipSettlementState","type":"address"}],"name":"SetDriipSettlementStateEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"wallet","type":"address"},{"indexed":false,"internalType":"uint256","name":"nonce","type":"uint256"},{"indexed":false,"internalType":"int256","name":"cumulativeTransferAmount","type":"int256"},{"indexed":false,"internalType":"int256","name":"stageAmount","type":"int256"},{"indexed":false,"internalType":"int256","name":"targetBalanceAmount","type":"int256"},{"indexed":false,"internalType":"address","name":"currencyCt","type":"address"},{"indexed":false,"internalType":"uint256","name":"currencyId","type":"uint256"}],"name":"StartChallengeFromPaymentEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"wallet","type":"address"},{"indexed":false,"internalType":"uint256","name":"nonce","type":"uint256"},{"indexed":false,"internalType":"int256","name":"cumulativeTransferAmount","type":"int256"},{"indexed":false,"internalType":"int256","name":"stageAmount","type":"int256"},{"indexed":false,"internalType":"int256","name":"targetBalanceAmount","type":"int256"},{"indexed":false,"internalType":"address","name":"currencyCt","type":"address"},{"indexed":false,"internalType":"uint256","name":"currencyId","type":"uint256"},{"indexed":false,"internalType":"address","name":"proxy","type":"address"}],"name":"StartChallengeFromPaymentByProxyEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"wallet","type":"address"},{"indexed":false,"internalType":"uint256","name":"nonce","type":"uint256"},{"indexed":false,"internalType":"int256","name":"cumulativeTransferAmount","type":"int256"},{"indexed":false,"internalType":"int256","name":"stageAmount","type":"int256"},{"indexed":false,"internalType":"int256","name":"targetBalanceAmount","type":"int256"},{"indexed":false,"internalType":"address","name":"currencyCt","type":"address"},{"indexed":false,"internalType":"uint256","name":"currencyId","type":"uint256"}],"name":"StopChallengeEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"wallet","type":"address"},{"indexed":false,"internalType":"uint256","name":"nonce","type":"uint256"},{"indexed":false,"internalType":"int256","name":"cumulativeTransferAmount","type":"int256"},{"indexed":false,"internalType":"int256","name":"stageAmount","type":"int256"},{"indexed":false,"internalType":"int256","name":"targetBalanceAmount","type":"int256"},{"indexed":false,"internalType":"address","name":"currencyCt","type":"address"},{"indexed":false,"internalType":"uint256","name":"currencyId","type":"uint256"},{"indexed":false,"internalType":"address","name":"proxy","type":"address"}],"name":"StopChallengeByProxyEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"challengedWallet","type":"address"},{"indexed":false,"internalType":"uint256","name":"nonce","type":"uint256"},{"indexed":false,"internalType":"int256","name":"cumulativeTransferAmount","type":"int256"},{"indexed":false,"internalType":"int256","name":"stageAmount","type":"int256"},{"indexed":false,"internalType":"int256","name":"targetBalanceAmount","type":"int256"},{"indexed":false,"internalType":"address","name":"currencyCt","type":"address"},{"indexed":false,"internalType":"uint256","name":"currencyId","type":"uint256"},{"indexed":false,"internalType":"address","name":"challengerWallet","type":"address"}],"name":"ChallengeByPaymentEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract BalanceTracker","name":"oldBalanceTracker","type":"address"},{"indexed":false,"internalType":"contract BalanceTracker","name":"newBalanceTracker","type":"address"}],"name":"SetBalanceTrackerEvent","type":"event"},{"anonymous":false,"inputs":[],"name":"FreezeBalanceTrackerEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract WalletLocker","name":"oldWalletLocker","type":"address"},{"indexed":false,"internalType":"contract WalletLocker","name":"newWalletLocker","type":"address"}],"name":"SetWalletLockerEvent","type":"event"},{"anonymous":false,"inputs":[],"name":"FreezeWalletLockerEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract Validator","name":"oldValidator","type":"address"},{"indexed":false,"internalType":"contract Validator","name":"newValidator","type":"address"}],"name":"SetValidatorEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract Configuration","name":"oldConfiguration","type":"address"},{"indexed":false,"internalType":"contract Configuration","name":"newConfiguration","type":"address"}],"name":"SetConfigurationEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldDeployer","type":"address"},{"indexed":false,"internalType":"address","name":"newDeployer","type":"address"}],"name":"SetDeployerEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldOperator","type":"address"},{"indexed":false,"internalType":"address","name":"newOperator","type":"address"}],"name":"SetOperatorEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"wallet","type":"address"}],"name":"SelfDestructionDisabledEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"wallet","type":"address"}],"name":"TriggerSelfDestructionEvent","type":"event"}]

60806040523480156200001157600080fd5b506040516200490c3803806200490c8339810160408190526200003491620000b3565b80806001600160a01b0381166200004a57600080fd5b6001600160a01b0381163014156200006157600080fd5b5060008054610100600160a81b0319166101006001600160a01b0393909316928302179055600180546001600160a01b03191690911790555062000108565b8051620000ad81620000ee565b92915050565b600060208284031215620000c657600080fd5b6000620000d48484620000a0565b949350505050565b60006001600160a01b038216620000ad565b620000f981620000dc565b81146200010557600080fd5b50565b6147f480620001186000396000f3fe608060405234801561001057600080fd5b506004361061023b5760003560e01c8063039b9fb1146102405780630712abb0146102695780630fa6f23d146102895780631327d3d81461029e57806316987929146102b357806319139092146102d3578063240625d8146102db57806325c84441146102f05780632738a112146102f85780632aa1c9d9146103005780632f013a001461031357806334033fc11461031b5780633a5381b51461032e5780633bebdcb5146103365780633e59b70614610349578063404e896f1461035157806342cb4587146103645780634476d23b1461037757806346d1d3c41461038c5780634d3d826c1461039f5780634dc8eb79146103b257806351d7cd61146103c5578063570ca735146103cd578063598b75ad146103d55780635dc60def146103e8578063627f09c3146103f05780636c70bee9146104035780636ccb43c81461040b5780636de6a5d21461041e57806370327ea1146104315780637da5b11c14610439578063892860b21461044c578063911eb9651461045f5780639621473514610472578063972fcee0146104855780639882da6c14610498578063adfb6778146104ab578063b3ab15fb146104be578063b52d470a146104d1578063bb9357de146104d9578063c19df1e4146104ec578063ca7f47ed146104ff578063cdbf941914610512578063d5b68c6214610525578063d5f3948814610538578063e8ae41e314610540578063fa7424f214610548578063fb1a825e14610550578063fcd30b0214610563575b600080fd5b61025361024e3660046136f4565b610576565b604051610260919061450b565b60405180910390f35b61027c6102773660046136f4565b61061d565b60405161026091906144fd565b6102916106b6565b60405161026091906144d4565b6102b16102ac366004613847565b6106c5565b005b6102c66102c13660046136f4565b610766565b60405161026091906144c6565b6102916107ff565b6102e361080e565b60405161026091906144b8565b61029161081e565b6102b161082d565b6102b161030e366004613847565b610893565b6102e361091a565b6102b1610329366004613792565b610923565b610291610b16565b6102e36103443660046136f4565b610b25565b6102e3610bbe565b6102b161035f366004613847565b610bce565b6102c66103723660046136f4565b610c48565b61037f610c91565b60405161026091906141d0565b6102e361039a3660046136f4565b610ca5565b6102b16103ad3660046138e7565b610cee565b6102b16103c0366004613847565b610f7f565b610291610ff9565b61037f611008565b6102b16103e3366004613847565b611017565b6102b16110d2565b6102b16103fe366004613847565b611121565b6102916111b2565b6102b1610419366004613847565b6111c1565b6102c661042c3660046136f4565b61123b565b6102b1611284565b6102e36104473660046136f4565b6112e7565b6102b161045a366004613847565b611330565b6102b161046d366004613741565b6113eb565b6102b16104803660046136b8565b61174f565b6102b16104933660046136f4565b611805565b6102c66104a63660046136f4565b611a9d565b6102c66104b93660046136f4565b611ae6565b6102b16104cc3660046136b8565b611b2f565b6102b1611bd2565b6102e36104e73660046136f4565b611c21565b6102b16104fa3660046137db565b611c6a565b61025361050d3660046136f4565b611ee0565b6102c66105203660046136f4565b611f29565b6102c66105333660046136f4565b611f72565b61037f611fbb565b610291611fcf565b610291611fde565b6102c661055e3660046136f4565b611fed565b61037f6105713660046136f4565b612036565b6007546040805180820182526001600160a01b0385811682526020820185905291516394da13eb60e01b815260609392909216916394da13eb916105bf9188919060040161430b565b60006040518083038186803b1580156105d757600080fd5b505afa1580156105eb573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261061391908101906138b3565b90505b9392505050565b6007546040805180820182526001600160a01b03858116825260208201859052915163b6e2fc3360e01b8152600093929092169163b6e2fc33916106669188919060040161430b565b60206040518083038186803b15801561067e57600080fd5b505afa158015610692573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506106139190810190613865565b6006546001600160a01b031681565b6106cd6120cf565b6106d657600080fd5b806001600160a01b0381166106ea57600080fd5b60035482906001600160a01b03908116908216811461076057600380546001600160a01b038681166001600160a01b03198316179092556040519116907f1882af944a16549c3d4f60e3cd26f158b0c7aac3222cf32971fc21375ce05f609061075690839088906144e2565b60405180910390a1505b50505050565b6007546040805180820182526001600160a01b03858116825260208201859052915163c46fcd7560e01b8152600093929092169163c46fcd75916107af9188919060040161430b565b60206040518083038186803b1580156107c757600080fd5b505afa1580156107db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506106139190810190613829565b6005546001600160a01b031681565b600454600160a01b900460ff1681565b6009546001600160a01b031681565b33610836610c91565b6001600160a01b03161461084957600080fd5b60005460ff161561085957600080fd5b7f787a5d936e74f4b564b9153575886059829c78cd9927b1be5e0d976b317ef7363360405161088891906141de565b60405180910390a133ff5b61089b6120cf565b6108a457600080fd5b806001600160a01b0381166108b857600080fd5b600780546001600160a01b038481166001600160a01b031983161792839055604051918116927f4f7125332801996cdeaabf9cff1ac89ddf4b52c673558936affe853ab64a88da9261090d92859216906144e2565b60405180910390a1505050565b60005460ff1681565b61092b6120e5565b61093457600080fd5b61094183838360006120f6565b6007546020830151604051632ee2ed2560e01b81527f505b0ba83acb520eb622fae274a079c14f40dd43f70fd9f1f8d2312fd4e76caf9286926001600160a01b0390911691632ee2ed259161099b9185919060040161430b565b60206040518083038186803b1580156109b357600080fd5b505afa1580156109c7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506109eb9190810190613829565b6007546020860151604051638ab3e96560e01b81526001600160a01b0390921691638ab3e96591610a21918a919060040161430b565b60206040518083038186803b158015610a3957600080fd5b505afa158015610a4d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610a719190810190613829565b600754602087015160405163c46fcd7560e01b815287926001600160a01b03169163c46fcd7591610aa6918c9160040161430b565b60206040518083038186803b158015610abe57600080fd5b505afa158015610ad2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610af69190810190613829565b602080890151805191015160405161090d979695949392919033906143a5565b6003546001600160a01b031681565b6007546040805180820182526001600160a01b038581168252602082018590529151635482c73560e01b81526000939290921691635482c73591610b6e9188919060040161430b565b60206040518083038186803b158015610b8657600080fd5b505afa158015610b9a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610613919081019061380b565b600554600160a01b900460ff1681565b610bd66120cf565b610bdf57600080fd5b806001600160a01b038116610bf357600080fd5b600980546001600160a01b038481166001600160a01b031983161792839055604051918116927f6d4735156f5fb1047cfcd4a9edf19e3291aec19d53916f8fee650ca3c2d739109261090d92859216906144e2565b6007546040805180820182526001600160a01b038581168252602082018590529151633007230f60e11b8152600093929092169163600e461e916107af9188919060040161430b565b60005461010090046001600160a01b031690565b6007546040805180820182526001600160a01b038581168252602082018590529151637ff81c3760e01b81526000939290921691637ff81c3791610b6e9188919060040161430b565b60048054604051631293efbb60e21b81526001600160a01b0390911691634a4fbeec91610d1d913391016141de565b60206040518083038186803b158015610d3557600080fd5b505afa158015610d49573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610d6d919081019061380b565b15610d935760405162461bcd60e51b8152600401610d8a9061455c565b60405180910390fd5b610da033838360016120f6565b6007546020830151604051632ee2ed2560e01b81527f1d8a75ed090cb569da62b746afdb83f0969831d5d9b9bb1a11320bc11c438c9f9233926001600160a01b0390911691632ee2ed2591610dfa918591906004016141ec565b60206040518083038186803b158015610e1257600080fd5b505afa158015610e26573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610e4a9190810190613829565b6007546020860151604051638ab3e96560e01b81526001600160a01b0390921691638ab3e96591610e80913391906004016141ec565b60206040518083038186803b158015610e9857600080fd5b505afa158015610eac573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610ed09190810190613829565b600754602087015160405163c46fcd7560e01b815287926001600160a01b03169163c46fcd7591610f059133916004016141ec565b60206040518083038186803b158015610f1d57600080fd5b505afa158015610f31573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610f559190810190613829565b6020808901518051910151604051610f739796959493929190614207565b60405180910390a15050565b610f876120cf565b610f9057600080fd5b806001600160a01b038116610fa457600080fd5b600680546001600160a01b038481166001600160a01b031983161792839055604051918116927f663141f76be1f253f0e608edd55c18b0816521f3b635e072c1c2f710dde6ed199261090d92859216906144e2565b6007546001600160a01b031681565b6001546001600160a01b031681565b61101f6120cf565b61102857600080fd5b806001600160a01b03811661103c57600080fd5b60055482906001600160a01b03908116908216811461076057600554600160a01b900460ff161561107f5760405162461bcd60e51b8152600401610d8a9061452c565b600580546001600160a01b038681166001600160a01b03198316179092556040519116907fb2a91d3a71b0c5bc7c083153b3474378e489506ba98bd4ddb1b9056fdc594bb59061075690839088906144e2565b6110da6120cf565b6110e357600080fd5b6004805460ff60a01b1916600160a01b1790556040517f56ec8900b9c4bf84f4b715a53068ca06961dd49084c07b481931e2c2045346e690600090a1565b6111296120cf565b61113257600080fd5b806001600160a01b03811661114657600080fd5b60025482906001600160a01b03908116908216811461076057600280546001600160a01b038681166001600160a01b03198316179092556040519116907f634f61bf00e14adedce330c80c2823e16e184f189ebe853e1ddecc4a268477ff9061075690839088906144e2565b6002546001600160a01b031681565b6111c96120cf565b6111d257600080fd5b806001600160a01b0381166111e657600080fd5b600880546001600160a01b038481166001600160a01b031983161792839055604051918116927ff21c127205310467822e10c64b2d9ffae588e019194d6af71f2bdddc0b7ef5269261090d92859216906144e2565b6007546040805180820182526001600160a01b03858116825260208201859052915163374852cb60e11b81526000939290921691636e90a596916107af9188919060040161430b565b3361128d610c91565b6001600160a01b0316146112a057600080fd5b6000805460ff191660011790556040517fd5a2a04a775c741c2ca0dc46ea7ce4835190e1aaf1ca018def0e82568ec33616906112dd9033906141de565b60405180910390a1565b6007546040805180820182526001600160a01b0385811682526020820185905291516333ca4eab60e21b8152600093929092169163cf293aac91610b6e9188919060040161430b565b6113386120cf565b61134157600080fd5b806001600160a01b03811661135557600080fd5b60045482906001600160a01b03908116908216811461076057600454600160a01b900460ff16156113985760405162461bcd60e51b8152600401610d8a906145cc565b600480546001600160a01b038681166001600160a01b03198316179092556040519116907fa44d361e26327b72a7ccbeae801b3c5cd7677ea4fa74168b289e273c46bfecfc9061075690839088906144e2565b600260009054906101000a90046001600160a01b03166001600160a01b031663f71e860f6040518163ffffffff1660e01b815260040160206040518083038186803b15801561143957600080fd5b505afa15801561144d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611471919081019061380b565b61148d5760405162461bcd60e51b8152600401610d8a9061451c565b600654604051630eae657960e11b81526001600160a01b0390911690631d5ccaf2906114c190859085903390600401614376565b600060405180830381600087803b1580156114db57600080fd5b505af11580156114ef573d6000803e3d6000fd5b50506007546020840151604051632ee2ed2560e01b81527fffa28bfeaa617be065ef4f9cbd4448b8ac8e99c98f00741cd2d78fa4732cf9cf94508693506001600160a01b0390921691632ee2ed259161154d9185919060040161430b565b60206040518083038186803b15801561156557600080fd5b505afa158015611579573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061159d9190810190613829565b6007546020850151604051638ab3e96560e01b81526001600160a01b0390921691638ab3e965916115d39189919060040161430b565b60206040518083038186803b1580156115eb57600080fd5b505afa1580156115ff573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506116239190810190613829565b6007546020860151604051633007230f60e11b81526001600160a01b039092169163600e461e91611659918a919060040161430b565b60206040518083038186803b15801561167157600080fd5b505afa158015611685573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506116a99190810190613829565b600754602087015160405163c46fcd7560e01b81526001600160a01b039092169163c46fcd75916116df918b919060040161430b565b60206040518083038186803b1580156116f757600080fd5b505afa15801561170b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061172f9190810190613829565b6020808801518051910151604051610f73979695949392919033906143a5565b6117576120cf565b61176057600080fd5b806001600160a01b03811661177457600080fd5b6001600160a01b03811630141561178a57600080fd5b6000546001600160a01b03838116610100909204161461180157600080546001600160a01b03848116610100908102610100600160a81b03198416179093556040519290910416907f977e5fa58e458501775e0008d275006294c5249e3c08d1d0e3a9f3acad14f6e49061090d908390869061426f565b5050565b61180d6120e5565b61181657600080fd5b61181e613053565b6040518060400160405280846001600160a01b0316815260200183815250905061184c84826001600061274d565b600754604051632ee2ed2560e01b81527f55681e18b795929fa6b2ebef09244e035285788d963bf5d6e3968dc2bf8addf09186916001600160a01b0390911690632ee2ed25906118a2908490879060040161430b565b60206040518083038186803b1580156118ba57600080fd5b505afa1580156118ce573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506118f29190810190613829565b600754604051638ab3e96560e01b81526001600160a01b0390911690638ab3e96590611924908a90889060040161430b565b60206040518083038186803b15801561193c57600080fd5b505afa158015611950573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506119749190810190613829565b600754604051633007230f60e11b81526001600160a01b039091169063600e461e906119a6908b90899060040161430b565b60206040518083038186803b1580156119be57600080fd5b505afa1580156119d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506119f69190810190613829565b60075460405163c46fcd7560e01b81526001600160a01b039091169063c46fcd7590611a28908c908a9060040161430b565b60206040518083038186803b158015611a4057600080fd5b505afa158015611a54573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611a789190810190613829565b888833604051611a8f9897969594939291906143a5565b60405180910390a150505050565b6007546040805180820182526001600160a01b03858116825260208201859052915163da473c5760e01b8152600093929092169163da473c57916107af9188919060040161430b565b6007546040805180820182526001600160a01b038581168252602082018590529151631b90e3c160e01b81526000939290921691631b90e3c1916107af9188919060040161430b565b611b376120e5565b611b4057600080fd5b806001600160a01b038116611b5457600080fd5b6001600160a01b038116301415611b6a57600080fd5b6001546001600160a01b0383811691161461180157600180546001600160a01b038481166001600160a01b03198316179092556040519116907f9f611b789425d0d5b90b920f1b2852907dd865c80074a30b1629aaa041d1812c9061090d908390869061426f565b611bda6120cf565b611be357600080fd5b6005805460ff60a01b1916600160a01b1790556040517f9c567b65fe8caab5ec7bc979a498a1322c1d4baf01a30727cbca137187560ea290600090a1565b6007546040805180820182526001600160a01b03858116825260208201859052915163076470a560e31b81526000939290921691633b23852891610b6e9188919060040161430b565b611c72613053565b6040518060400160405280846001600160a01b03168152602001838152509050611c9f338260018061274d565b600754604051632ee2ed2560e01b81527f46e3d3c7067c83d22286a7cf49b1ac45b28d128a51b1d4a3b9bdc3927a6da0be9133916001600160a01b0390911690632ee2ed2590611cf590849087906004016141ec565b60206040518083038186803b158015611d0d57600080fd5b505afa158015611d21573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611d459190810190613829565b600754604051638ab3e96560e01b81526001600160a01b0390911690638ab3e96590611d7790339088906004016141ec565b60206040518083038186803b158015611d8f57600080fd5b505afa158015611da3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611dc79190810190613829565b600754604051633007230f60e11b81526001600160a01b039091169063600e461e90611df990339089906004016141ec565b60206040518083038186803b158015611e1157600080fd5b505afa158015611e25573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611e499190810190613829565b60075460405163c46fcd7560e01b81526001600160a01b039091169063c46fcd7590611e7b9033908a906004016141ec565b60206040518083038186803b158015611e9357600080fd5b505afa158015611ea7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611ecb9190810190613829565b888860405161090d9796959493929190614207565b6007546040805180820182526001600160a01b03858116825260208201859052915163160ed0cd60e11b81526060939290921691632c1da19a916105bf9188919060040161430b565b6007546040805180820182526001600160a01b038581168252602082018590529151632ee2ed2560e01b81526000939290921691632ee2ed25916107af9188919060040161430b565b6007546040805180820182526001600160a01b0385811682526020820185905291516345a76a6160e11b81526000939290921691638b4ed4c2916107af9188919060040161430b565b60005461010090046001600160a01b031681565b6004546001600160a01b031681565b6008546001600160a01b031681565b6007546040805180820182526001600160a01b0385811682526020820185905291516356ec907560e01b815260009392909216916356ec9075916107af9188919060040161430b565b6007546040805180820182526001600160a01b038581168252602082018590529151635658722960e01b8152600093929092169163565872299161207f9188919060040161430b565b60206040518083038186803b15801561209757600080fd5b505afa1580156120ab573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061061391908101906136d6565b60005461010090046001600160a01b0316331490565b6001546001600160a01b0316331490565b60035460405163273f8b6560e11b815284916001600160a01b031690634e7f16ca906121269084906004016145dc565b60206040518083038186803b15801561213e57600080fd5b505afa158015612152573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612176919081019061380b565b6121925760405162461bcd60e51b8152600401610d8a906145bc565b600260009054906101000a90046001600160a01b03166001600160a01b031663b6bf41766040518163ffffffff1660e01b815260040160206040518083038186803b1580156121e057600080fd5b505afa1580156121f4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506122189190810190613829565b4310156122375760405162461bcd60e51b8152600401610d8a9061453c565b60035460405163f59d1a7560e01b81526001600160a01b039091169063f59d1a759061226990879089906004016145ed565b60206040518083038186803b15801561228157600080fd5b505afa158015612295573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506122b9919081019061380b565b6122d55760405162461bcd60e51b8152600401610d8a9061457c565b600754602085015160405163076470a560e31b81526001600160a01b0390921691633b2385289161230b9189919060040161430b565b60206040518083038186803b15801561232357600080fd5b505afa158015612337573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061235b919081019061380b565b15806123e857506007546020850151604051637ff81c3760e01b81526001600160a01b0390921691637ff81c37916123989189919060040161430b565b60206040518083038186803b1580156123b057600080fd5b505afa1580156123c4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506123e8919081019061380b565b6124045760405162461bcd60e51b8152600401610d8a9061456c565b600854602085015160405163076470a560e31b81526001600160a01b0390921691633b2385289161243a9189919060040161430b565b60206040518083038186803b15801561245257600080fd5b505afa158015612466573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061248a919081019061380b565b158061251757506008546020850151604051637ff81c3760e01b81526001600160a01b0390921691637ff81c37916124c79189919060040161430b565b60206040518083038186803b1580156124df57600080fd5b505afa1580156124f3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612517919081019061380b565b6125335760405162461bcd60e51b8152600401610d8a9061459c565b600080612540868861295c565b6009546020890151604051633858654b60e01b815293955091935084926001600160a01b0390911691633858654b9161257d918c9160040161430b565b60206040518083038186803b15801561259557600080fd5b505afa1580156125a9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506125cd9190810190613829565b106125ea5760405162461bcd60e51b8152600401610d8a9061458c565b6007546001600160a01b031663bb3bffe088848489612655612612838363ffffffff612b1116565b6126498f8f60200151600560009054906101000a90046001600160a01b03166001600160a01b0316612b509092919063ffffffff16565b9063ffffffff612d4d16565b8c602001518d60c001518c8f60a00151602001516000015173b99f3f4aacb6e1197a623919103b99f4b41aaef0635174abe96040518163ffffffff1660e01b815260040160006040518083038186803b1580156126b157600080fd5b505af41580156126c5573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526126ed91908101906138b3565b6040518b63ffffffff1660e01b81526004016127129a9998979695949392919061441c565b600060405180830381600087803b15801561272c57600080fd5b505af1158015612740573d6000803e3d6000fd5b5050505050505050505050565b60075460405163076470a560e31b81526001600160a01b0390911690633b2385289061277f908790879060040161430b565b60206040518083038186803b15801561279757600080fd5b505afa1580156127ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506127cf919081019061380b565b6127eb5760405162461bcd60e51b8152600401610d8a906145ac565b600754604051637ff81c3760e01b81526001600160a01b0390911690637ff81c379061281d908790879060040161430b565b60206040518083038186803b15801561283557600080fd5b505afa158015612849573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061286d919081019061380b565b1561288a5760405162461bcd60e51b8152600401610d8a9061454c565b6007546040516335a4238360e01b81526001600160a01b03909116906335a42383906128c0908790879087908790600401614319565b600060405180830381600087803b1580156128da57600080fd5b505af11580156128ee573d6000803e3d6000fd5b5050600854604051632ce4e29b60e21b81526001600160a01b03909116925063b3938a6c9150612924908790879060040161430b565b600060405180830381600087803b15801561293e57600080fd5b505af1158015612952573d6000803e3d6000fd5b5050505050505050565b602082015160c08301516005546000928392839261298e926001600160a01b039091169187919063ffffffff612d8016565b600954602087015160c088015160405163bc8dd4cd60e01b81529394506000936001600160a01b039093169263bc8dd4cd926129ce928a9260040161434e565b60206040518083038186803b1580156129e657600080fd5b505afa1580156129fa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612a1e9190810190613829565b60035460405163d308b9db60e01b81529192506001600160a01b03169063d308b9db90612a5190899089906004016145ed565b60206040518083038186803b158015612a6957600080fd5b505afa158015612a7d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612aa1919081019061380b565b15612ade57604080870151805191015151909450612ad7908290612acb908563ffffffff612b1116565b9063ffffffff612b1116565b9250612b08565b6060860151805160409091015151909450612b05908290612acb908563ffffffff612b1116565b92505b50509250929050565b6000808212158015612b2557508282840313155b80612b3c5750600082128015612b3c575082828403135b612b4557600080fd5b508082035b92915050565b6000610613846001600160a01b03166371f4ec5185876001600160a01b0316634652ec746040518163ffffffff1660e01b815260040160206040518083038186803b158015612b9e57600080fd5b505afa158015612bb2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612bd69190810190613829565b865160208801516040516001600160e01b031960e087901b168152612c01949392919060040161428a565b60206040518083038186803b158015612c1957600080fd5b505afa158015612c2d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612c519190810190613829565b856001600160a01b03166371f4ec5186886001600160a01b031663ded113106040518163ffffffff1660e01b815260040160206040518083038186803b158015612c9a57600080fd5b505afa158015612cae573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612cd29190810190613829565b875160208901516040516001600160e01b031960e087901b168152612cfd949392919060040161428a565b60206040518083038186803b158015612d1557600080fd5b505afa158015612d29573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506126499190810190613829565b6000828201818312801590612d625750838112155b80612d775750600083128015612d7757508381125b61061657600080fd5b600080612d8f86868686612d9c565b509150505b949350505050565b600080600080876001600160a01b031663a246138c888a6001600160a01b031663ded113106040518163ffffffff1660e01b815260040160206040518083038186803b158015612deb57600080fd5b505afa158015612dff573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612e239190810190613829565b895160208b01516040516001600160e01b031960e087901b168152612e5094939291908c906004016142bf565b604080518083038186803b158015612e6757600080fd5b505afa158015612e7b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612e9f9190810190613883565b91509150600080896001600160a01b031663a246138c8a8c6001600160a01b0316634652ec746040518163ffffffff1660e01b815260040160206040518083038186803b158015612eef57600080fd5b505afa158015612f03573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612f279190810190613829565b8b5160208d01516040516001600160e01b031960e087901b168152612f5494939291908e906004016142bf565b604080518083038186803b158015612f6b57600080fd5b505afa158015612f7f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612fa39190810190613883565b9092509050612fb8848363ffffffff612d4d16565b60405163a682d5ad60e01b8152909650730ff948c236c8d4dfcd0168bf243314c8ff8ec9679063a682d5ad90612ff4908690859060040161460d565b60206040518083038186803b15801561300c57600080fd5b505af4158015613020573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506130449190810190613829565b94505050505094509492505050565b604080518082019091526000808252602082015290565b8035612b4a8161474c565b8051612b4a8161474c565b600082601f83011261309157600080fd5b81356130a461309f8261464e565b614628565b915081818352602084019350602081019050838560808402820111156130c957600080fd5b60005b838110156130f757816130df88826132cb565b845250602090920191608091909101906001016130cc565b5050505092915050565b8051612b4a81614760565b8035612b4a81614769565b8051612b4a81614769565b8035612b4a81614772565b8051612b4a8161477b565b600082601f83011261314957600080fd5b813561315761309f8261466e565b9150808252602083016020830185838301111561317357600080fd5b61317e8382846146fd565b50505092915050565b600082601f83011261319857600080fd5b81516131a661309f8261466e565b915080825260208301602083018583830111156131c257600080fd5b61317e838284614709565b6000604082840312156131df57600080fd5b6131e96040614628565b905060006131f7848461306a565b82525060206132088484830161310c565b60208301525092915050565b60006040828403121561322657600080fd5b6132306040614628565b905060006131f7848461310c565b60006060828403121561325057600080fd5b61325a6040614628565b90506000613268848461310c565b8252506020613208848483016131cd565b60006040828403121561328b57600080fd5b6132956040614628565b905060006132a3848461310c565b82525060208201356001600160401b038111156132bf57600080fd5b61320884828501613138565b6000608082840312156132dd57600080fd5b6132e76040614628565b905060006132f5848461310c565b82525060206132088484830161323e565b600060a0828403121561331857600080fd5b6133226080614628565b90506000613330848461310c565b82525060206133418484830161306a565b602083015250604061335584828501613214565b60408301525060808201356001600160401b0381111561337457600080fd5b61338084828501613628565b60608301525092915050565b600060c0828403121561339e57600080fd5b6133a860a0614628565b905060006133b6848461310c565b82525060206133c78484830161306a565b60208301525060406133db84828501613214565b60408301525060808201356001600160401b038111156133fa57600080fd5b613406848285016135d6565b60608301525060a08201356001600160401b0381111561342557600080fd5b61343184828501613138565b60808301525092915050565b6000610220828403121561345057600080fd5b61345b610100614628565b90506000613469848461310c565b825250602061347a848483016131cd565b60208301525060608201356001600160401b0381111561349957600080fd5b6134a58482850161338c565b60408301525060808201356001600160401b038111156134c457600080fd5b6134d084828501613306565b60608301525060a06134e484828501613214565b60808301525060e06134f884828501613671565b60a0830152506101e061350d8482850161310c565b60c0830152506102008201356001600160401b0381111561352d57600080fd5b61353984828501613279565b60e08301525092915050565b60006080828403121561355757600080fd5b6135616040614628565b9050600061356f848461310c565b82525060206132088484830160006060828403121561358d57600080fd5b6135976060614628565b905060006135a5848461310c565b82525060206135b68484830161310c565b60208301525060406135ca848285016136ad565b60408301525092915050565b6000608082840312156135e857600080fd5b6135f26040614628565b90506000613600848461323e565b82525060608201356001600160401b0381111561361c57600080fd5b61320884828501613080565b60006020828403121561363a57600080fd5b6136446020614628565b905081356001600160401b0381111561365c57600080fd5b61366884828501613080565b82525092915050565b6000610100828403121561368457600080fd5b61368e6040614628565b9050600061369c8484613545565b825250608061320884848301613545565b8035612b4a81614788565b6000602082840312156136ca57600080fd5b6000612d94848461306a565b6000602082840312156136e857600080fd5b6000612d948484613075565b60008060006060848603121561370957600080fd5b6000613715868661306a565b93505060206137268682870161306a565b92505060406137378682870161310c565b9150509250925092565b6000806040838503121561375457600080fd5b6000613760858561306a565b92505060208301356001600160401b0381111561377c57600080fd5b6137888582860161343d565b9150509250929050565b6000806000606084860312156137a757600080fd5b60006137b3868661306a565b93505060208401356001600160401b038111156137cf57600080fd5b6137268682870161343d565b600080604083850312156137ee57600080fd5b60006137fa858561306a565b92505060206137888582860161310c565b60006020828403121561381d57600080fd5b6000612d948484613101565b60006020828403121561383b57600080fd5b6000612d948484613117565b60006020828403121561385957600080fd5b6000612d948484613122565b60006020828403121561387757600080fd5b6000612d94848461312d565b6000806040838503121561389657600080fd5b60006138a28585613117565b925050602061378885828601613117565b6000602082840312156138c557600080fd5b81516001600160401b038111156138db57600080fd5b612d9484828501613187565b600080604083850312156138fa57600080fd5b82356001600160401b0381111561391057600080fd5b6137fa8582860161343d565b60006139288383613f77565b505060800190565b613939816146e7565b82525050565b613939816146a8565b60006139538261469b565b61395d818561469f565b935061396883614695565b8060005b83811015613996578151613980888261391c565b975061398b83614695565b92505060010161396c565b509495945050505050565b613939816146b3565b613939816146b8565b613939816146bb565b613939816146f2565b60006139d08261469b565b6139da818561469f565b93506139ea818560208601614709565b6139f381614735565b9093019392505050565b6000613a0a603f8361469f565b7f4f7065726174696f6e616c206d6f6465206973206e6f74206e6f726d616c205b81527f436f6e666967757261626c654f7065726174696f6e616c2e736f6c3a32325d00602082015260400192915050565b6000613a6960308361469f565b7f42616c616e636520747261636b65722066726f7a656e205b42616c616e63655481526f7261636b61626c652e736f6c3a34335d60801b602082015260400192915050565b6000613abb60678361469f565b7f43757272656e7420626c6f636b206e756d6265722062656c6f77206561726c6981527f65737420736574746c656d656e7420626c6f636b206e756d626572205b44726960208201526000805160206147928339815191526040820152666f6c3a3438395d60c81b606082015260800192915050565b6000613b3e60458361469f565b7f50726f706f73616c20666f756e64207465726d696e61746564205b447269697081527f536574746c656d656e744368616c6c656e676542795061796d656e742e736f6c6020820152643a3534325d60d81b604082015260600192915050565b6000613bab603f8361469f565b7f57616c6c657420666f756e64206c6f636b6564205b4472696970536574746c6581527f6d656e744368616c6c656e676542795061796d656e742e736f6c3a3133345d00602082015260400192915050565b6000613c0a60618361469f565b7f4f7665726c617070696e6720647269697020736574746c656d656e742063686181527f6c6c656e67652070726f706f73616c20666f756e64205b44726969705365747460208201527f6c656d656e744368616c6c656e676542795061796d656e742e736f6c3a3530316040820152605d60f81b606082015260800192915050565b6000613c9960478361469f565b7f57616c6c6574206973206e6f74207061796d656e74207061727479205b44726981526000805160206147928339815191526020820152666f6c3a3439355d60c81b604082015260600192915050565b6000613cf660568361469f565b7f57616c6c65742773206e6f6e63652062656c6f7720686967686573742073657481527f746c6564206e6f6e6365205b4472696970536574746c656d656e744368616c6c602082015275656e676542795061796d656e742e736f6c3a3531385d60501b604082015260600192915050565b6000613d7460608361469f565b7f4f7665726c617070696e67206e756c6c20736574746c656d656e74206368616c81527f6c656e67652070726f706f73616c20666f756e64205b4472696970536574746c60208201527f656d656e744368616c6c656e676542795061796d656e742e736f6c3a3530385d604082015260600192915050565b6000613df9603d8361469f565b7f4e6f2070726f706f73616c20666f756e64205b4472696970536574746c656d6581527f6e744368616c6c656e676542795061796d656e742e736f6c3a3533385d000000602082015260400192915050565b6000613e58602e8361469f565b7f5061796d656e74207365616c73206e6f742067656e75696e65205b56616c696481526d617461626c652e736f6c3a36355d60901b602082015260400192915050565b6000613ea8602c8361469f565b7f57616c6c6574206c6f636b65722066726f7a656e205b57616c6c65744c6f636b81526b61626c652e736f6c3a34335d60a01b602082015260400192915050565b80516040830190613efa848261393f565b50602082015161076060208501826139aa565b80516040830190613efa84826139aa565b80516060830190613f2f84826139aa565b5060208201516107606020850182613ee9565b80516000906040840190613f5685826139aa565b5060208301518482036020860152613f6e82826139c5565b95945050505050565b80516080830190613f8884826139aa565b5060208201516107606020850182613f1e565b805160009060a0840190613faf85826139aa565b506020830151613fc2602086018261393f565b506040830151613fd56040860182613f0d565b5060608301518482036080860152613f6e828261418a565b805160009060c084019061400185826139aa565b506020830151614014602086018261393f565b5060408301516140276040860182613f0d565b506060830151848203608086015261403f828261415e565b915050608083015184820360a0860152613f6e82826139c5565b805160009061022084019061406e85826139aa565b5060208301516140816020860182613ee9565b50604083015184820360608601526140998282613fed565b915050606083015184820360808601526140b38282613f9b565b91505060808301516140c860a0860182613f0d565b5060a08301516140db60e08601826141a2565b5060c08301516140ef6101e08601826139aa565b5060e0830151848203610200860152613f6e8282613f42565b8051608083019061411984826139aa565b50602082015161076060208501828051606083019061413884826139aa565b50602082015161414b60208501826139aa565b50604082015161076060408501826141c7565b805160009060808401906141728582613f1e565b5060208301518482036060860152613f6e8282613948565b8051602080845260009190840190613f6e8282613948565b80516101008301906141b48482614108565b5060208201516107606080850182614108565b613939816146e1565b60208101612b4a828461393f565b60208101612b4a8284613930565b606081016141fa8285613930565b6106166020830184613ee9565b60e08101614215828a613930565b61422260208301896139aa565b61422f60408301886139aa565b61423c60608301876139aa565b61424960808301866139aa565b61425660a083018561393f565b61426360c08301846139aa565b98975050505050505050565b6040810161427d828561393f565b610616602083018461393f565b60808101614298828761393f565b6142a560208301866139aa565b6142b2604083018561393f565b613f6e60608301846139aa565b60a081016142cd828861393f565b6142da60208301876139aa565b6142e7604083018661393f565b6142f460608301856139aa565b61430160808301846139aa565b9695505050505050565b606081016141fa828561393f565b60a08101614327828761393f565b6143346020830186613ee9565b61434160608301856139a1565b613f6e60808301846139a1565b6080810161435c828661393f565b6143696020830185613ee9565b612d9460608301846139aa565b60608101614384828661393f565b81810360208301526143968185614059565b9050612d946040830184613930565b61010081016143b4828b61393f565b6143c1602083018a6139aa565b6143ce60408301896139aa565b6143db60608301886139aa565b6143e860808301876139aa565b6143f560a083018661393f565b61440260c08301856139aa565b61440f60e0830184613930565b9998505050505050505050565b610160810161442b828d61393f565b614438602083018c6139aa565b614445604083018b6139aa565b614452606083018a6139aa565b61445f60808301896139aa565b61446c60a0830188613ee9565b61447960e08301876139aa565b6144876101008301866139a1565b6144956101208301856139aa565b8181036101408301526144a881846139c5565b9c9b505050505050505050505050565b60208101612b4a82846139a1565b60208101612b4a82846139aa565b60208101612b4a82846139b3565b604081016144f082856139b3565b61061660208301846139b3565b60208101612b4a82846139bc565b6020808252810161061681846139c5565b60208082528101612b4a816139fd565b60208082528101612b4a81613a5c565b60208082528101612b4a81613aae565b60208082528101612b4a81613b31565b60208082528101612b4a81613b9e565b60208082528101612b4a81613bfd565b60208082528101612b4a81613c8c565b60208082528101612b4a81613ce9565b60208082528101612b4a81613d67565b60208082528101612b4a81613dec565b60208082528101612b4a81613e4b565b60208082528101612b4a81613e9b565b602080825281016106168184614059565b604080825281016145fe8185614059565b9050610616602083018461393f565b6040810161461b82856139aa565b61061660208301846139aa565b6040518181016001600160401b038111828210171561464657600080fd5b604052919050565b60006001600160401b0382111561466457600080fd5b5060209081020190565b60006001600160401b0382111561468457600080fd5b506020601f91909101601f19160190565b60200190565b5190565b90815260200190565b6000612b4a826146d5565b151590565b90565b6000612b4a826146a8565b806146d08161473f565b919050565b6001600160a01b031690565b60ff1690565b6000612b4a826146bb565b6000612b4a826146c6565b82818337506000910152565b60005b8381101561472457818101518382015260200161470c565b838111156107605750506000910152565b601f01601f191690565b6002811061474957fe5b50565b614755816146a8565b811461474957600080fd5b614755816146b3565b614755816146b8565b614755816146bb565b6002811061474957600080fd5b614755816146e156fe6970536574746c656d656e744368616c6c656e676542795061796d656e742e73a365627a7a723158208377fca6c152a24ff05ca78673a209831b0e8caf71a351e7a734ab34c4fd3ad06c6578706572696d656e74616cf564736f6c634300050b0040000000000000000000000000f05179bac3d1fbef58a2fcd7ad0f769840027cc6

Deployed Bytecode

0x608060405234801561001057600080fd5b506004361061023b5760003560e01c8063039b9fb1146102405780630712abb0146102695780630fa6f23d146102895780631327d3d81461029e57806316987929146102b357806319139092146102d3578063240625d8146102db57806325c84441146102f05780632738a112146102f85780632aa1c9d9146103005780632f013a001461031357806334033fc11461031b5780633a5381b51461032e5780633bebdcb5146103365780633e59b70614610349578063404e896f1461035157806342cb4587146103645780634476d23b1461037757806346d1d3c41461038c5780634d3d826c1461039f5780634dc8eb79146103b257806351d7cd61146103c5578063570ca735146103cd578063598b75ad146103d55780635dc60def146103e8578063627f09c3146103f05780636c70bee9146104035780636ccb43c81461040b5780636de6a5d21461041e57806370327ea1146104315780637da5b11c14610439578063892860b21461044c578063911eb9651461045f5780639621473514610472578063972fcee0146104855780639882da6c14610498578063adfb6778146104ab578063b3ab15fb146104be578063b52d470a146104d1578063bb9357de146104d9578063c19df1e4146104ec578063ca7f47ed146104ff578063cdbf941914610512578063d5b68c6214610525578063d5f3948814610538578063e8ae41e314610540578063fa7424f214610548578063fb1a825e14610550578063fcd30b0214610563575b600080fd5b61025361024e3660046136f4565b610576565b604051610260919061450b565b60405180910390f35b61027c6102773660046136f4565b61061d565b60405161026091906144fd565b6102916106b6565b60405161026091906144d4565b6102b16102ac366004613847565b6106c5565b005b6102c66102c13660046136f4565b610766565b60405161026091906144c6565b6102916107ff565b6102e361080e565b60405161026091906144b8565b61029161081e565b6102b161082d565b6102b161030e366004613847565b610893565b6102e361091a565b6102b1610329366004613792565b610923565b610291610b16565b6102e36103443660046136f4565b610b25565b6102e3610bbe565b6102b161035f366004613847565b610bce565b6102c66103723660046136f4565b610c48565b61037f610c91565b60405161026091906141d0565b6102e361039a3660046136f4565b610ca5565b6102b16103ad3660046138e7565b610cee565b6102b16103c0366004613847565b610f7f565b610291610ff9565b61037f611008565b6102b16103e3366004613847565b611017565b6102b16110d2565b6102b16103fe366004613847565b611121565b6102916111b2565b6102b1610419366004613847565b6111c1565b6102c661042c3660046136f4565b61123b565b6102b1611284565b6102e36104473660046136f4565b6112e7565b6102b161045a366004613847565b611330565b6102b161046d366004613741565b6113eb565b6102b16104803660046136b8565b61174f565b6102b16104933660046136f4565b611805565b6102c66104a63660046136f4565b611a9d565b6102c66104b93660046136f4565b611ae6565b6102b16104cc3660046136b8565b611b2f565b6102b1611bd2565b6102e36104e73660046136f4565b611c21565b6102b16104fa3660046137db565b611c6a565b61025361050d3660046136f4565b611ee0565b6102c66105203660046136f4565b611f29565b6102c66105333660046136f4565b611f72565b61037f611fbb565b610291611fcf565b610291611fde565b6102c661055e3660046136f4565b611fed565b61037f6105713660046136f4565b612036565b6007546040805180820182526001600160a01b0385811682526020820185905291516394da13eb60e01b815260609392909216916394da13eb916105bf9188919060040161430b565b60006040518083038186803b1580156105d757600080fd5b505afa1580156105eb573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261061391908101906138b3565b90505b9392505050565b6007546040805180820182526001600160a01b03858116825260208201859052915163b6e2fc3360e01b8152600093929092169163b6e2fc33916106669188919060040161430b565b60206040518083038186803b15801561067e57600080fd5b505afa158015610692573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506106139190810190613865565b6006546001600160a01b031681565b6106cd6120cf565b6106d657600080fd5b806001600160a01b0381166106ea57600080fd5b60035482906001600160a01b03908116908216811461076057600380546001600160a01b038681166001600160a01b03198316179092556040519116907f1882af944a16549c3d4f60e3cd26f158b0c7aac3222cf32971fc21375ce05f609061075690839088906144e2565b60405180910390a1505b50505050565b6007546040805180820182526001600160a01b03858116825260208201859052915163c46fcd7560e01b8152600093929092169163c46fcd75916107af9188919060040161430b565b60206040518083038186803b1580156107c757600080fd5b505afa1580156107db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506106139190810190613829565b6005546001600160a01b031681565b600454600160a01b900460ff1681565b6009546001600160a01b031681565b33610836610c91565b6001600160a01b03161461084957600080fd5b60005460ff161561085957600080fd5b7f787a5d936e74f4b564b9153575886059829c78cd9927b1be5e0d976b317ef7363360405161088891906141de565b60405180910390a133ff5b61089b6120cf565b6108a457600080fd5b806001600160a01b0381166108b857600080fd5b600780546001600160a01b038481166001600160a01b031983161792839055604051918116927f4f7125332801996cdeaabf9cff1ac89ddf4b52c673558936affe853ab64a88da9261090d92859216906144e2565b60405180910390a1505050565b60005460ff1681565b61092b6120e5565b61093457600080fd5b61094183838360006120f6565b6007546020830151604051632ee2ed2560e01b81527f505b0ba83acb520eb622fae274a079c14f40dd43f70fd9f1f8d2312fd4e76caf9286926001600160a01b0390911691632ee2ed259161099b9185919060040161430b565b60206040518083038186803b1580156109b357600080fd5b505afa1580156109c7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506109eb9190810190613829565b6007546020860151604051638ab3e96560e01b81526001600160a01b0390921691638ab3e96591610a21918a919060040161430b565b60206040518083038186803b158015610a3957600080fd5b505afa158015610a4d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610a719190810190613829565b600754602087015160405163c46fcd7560e01b815287926001600160a01b03169163c46fcd7591610aa6918c9160040161430b565b60206040518083038186803b158015610abe57600080fd5b505afa158015610ad2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610af69190810190613829565b602080890151805191015160405161090d979695949392919033906143a5565b6003546001600160a01b031681565b6007546040805180820182526001600160a01b038581168252602082018590529151635482c73560e01b81526000939290921691635482c73591610b6e9188919060040161430b565b60206040518083038186803b158015610b8657600080fd5b505afa158015610b9a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610613919081019061380b565b600554600160a01b900460ff1681565b610bd66120cf565b610bdf57600080fd5b806001600160a01b038116610bf357600080fd5b600980546001600160a01b038481166001600160a01b031983161792839055604051918116927f6d4735156f5fb1047cfcd4a9edf19e3291aec19d53916f8fee650ca3c2d739109261090d92859216906144e2565b6007546040805180820182526001600160a01b038581168252602082018590529151633007230f60e11b8152600093929092169163600e461e916107af9188919060040161430b565b60005461010090046001600160a01b031690565b6007546040805180820182526001600160a01b038581168252602082018590529151637ff81c3760e01b81526000939290921691637ff81c3791610b6e9188919060040161430b565b60048054604051631293efbb60e21b81526001600160a01b0390911691634a4fbeec91610d1d913391016141de565b60206040518083038186803b158015610d3557600080fd5b505afa158015610d49573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610d6d919081019061380b565b15610d935760405162461bcd60e51b8152600401610d8a9061455c565b60405180910390fd5b610da033838360016120f6565b6007546020830151604051632ee2ed2560e01b81527f1d8a75ed090cb569da62b746afdb83f0969831d5d9b9bb1a11320bc11c438c9f9233926001600160a01b0390911691632ee2ed2591610dfa918591906004016141ec565b60206040518083038186803b158015610e1257600080fd5b505afa158015610e26573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610e4a9190810190613829565b6007546020860151604051638ab3e96560e01b81526001600160a01b0390921691638ab3e96591610e80913391906004016141ec565b60206040518083038186803b158015610e9857600080fd5b505afa158015610eac573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610ed09190810190613829565b600754602087015160405163c46fcd7560e01b815287926001600160a01b03169163c46fcd7591610f059133916004016141ec565b60206040518083038186803b158015610f1d57600080fd5b505afa158015610f31573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610f559190810190613829565b6020808901518051910151604051610f739796959493929190614207565b60405180910390a15050565b610f876120cf565b610f9057600080fd5b806001600160a01b038116610fa457600080fd5b600680546001600160a01b038481166001600160a01b031983161792839055604051918116927f663141f76be1f253f0e608edd55c18b0816521f3b635e072c1c2f710dde6ed199261090d92859216906144e2565b6007546001600160a01b031681565b6001546001600160a01b031681565b61101f6120cf565b61102857600080fd5b806001600160a01b03811661103c57600080fd5b60055482906001600160a01b03908116908216811461076057600554600160a01b900460ff161561107f5760405162461bcd60e51b8152600401610d8a9061452c565b600580546001600160a01b038681166001600160a01b03198316179092556040519116907fb2a91d3a71b0c5bc7c083153b3474378e489506ba98bd4ddb1b9056fdc594bb59061075690839088906144e2565b6110da6120cf565b6110e357600080fd5b6004805460ff60a01b1916600160a01b1790556040517f56ec8900b9c4bf84f4b715a53068ca06961dd49084c07b481931e2c2045346e690600090a1565b6111296120cf565b61113257600080fd5b806001600160a01b03811661114657600080fd5b60025482906001600160a01b03908116908216811461076057600280546001600160a01b038681166001600160a01b03198316179092556040519116907f634f61bf00e14adedce330c80c2823e16e184f189ebe853e1ddecc4a268477ff9061075690839088906144e2565b6002546001600160a01b031681565b6111c96120cf565b6111d257600080fd5b806001600160a01b0381166111e657600080fd5b600880546001600160a01b038481166001600160a01b031983161792839055604051918116927ff21c127205310467822e10c64b2d9ffae588e019194d6af71f2bdddc0b7ef5269261090d92859216906144e2565b6007546040805180820182526001600160a01b03858116825260208201859052915163374852cb60e11b81526000939290921691636e90a596916107af9188919060040161430b565b3361128d610c91565b6001600160a01b0316146112a057600080fd5b6000805460ff191660011790556040517fd5a2a04a775c741c2ca0dc46ea7ce4835190e1aaf1ca018def0e82568ec33616906112dd9033906141de565b60405180910390a1565b6007546040805180820182526001600160a01b0385811682526020820185905291516333ca4eab60e21b8152600093929092169163cf293aac91610b6e9188919060040161430b565b6113386120cf565b61134157600080fd5b806001600160a01b03811661135557600080fd5b60045482906001600160a01b03908116908216811461076057600454600160a01b900460ff16156113985760405162461bcd60e51b8152600401610d8a906145cc565b600480546001600160a01b038681166001600160a01b03198316179092556040519116907fa44d361e26327b72a7ccbeae801b3c5cd7677ea4fa74168b289e273c46bfecfc9061075690839088906144e2565b600260009054906101000a90046001600160a01b03166001600160a01b031663f71e860f6040518163ffffffff1660e01b815260040160206040518083038186803b15801561143957600080fd5b505afa15801561144d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611471919081019061380b565b61148d5760405162461bcd60e51b8152600401610d8a9061451c565b600654604051630eae657960e11b81526001600160a01b0390911690631d5ccaf2906114c190859085903390600401614376565b600060405180830381600087803b1580156114db57600080fd5b505af11580156114ef573d6000803e3d6000fd5b50506007546020840151604051632ee2ed2560e01b81527fffa28bfeaa617be065ef4f9cbd4448b8ac8e99c98f00741cd2d78fa4732cf9cf94508693506001600160a01b0390921691632ee2ed259161154d9185919060040161430b565b60206040518083038186803b15801561156557600080fd5b505afa158015611579573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061159d9190810190613829565b6007546020850151604051638ab3e96560e01b81526001600160a01b0390921691638ab3e965916115d39189919060040161430b565b60206040518083038186803b1580156115eb57600080fd5b505afa1580156115ff573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506116239190810190613829565b6007546020860151604051633007230f60e11b81526001600160a01b039092169163600e461e91611659918a919060040161430b565b60206040518083038186803b15801561167157600080fd5b505afa158015611685573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506116a99190810190613829565b600754602087015160405163c46fcd7560e01b81526001600160a01b039092169163c46fcd75916116df918b919060040161430b565b60206040518083038186803b1580156116f757600080fd5b505afa15801561170b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061172f9190810190613829565b6020808801518051910151604051610f73979695949392919033906143a5565b6117576120cf565b61176057600080fd5b806001600160a01b03811661177457600080fd5b6001600160a01b03811630141561178a57600080fd5b6000546001600160a01b03838116610100909204161461180157600080546001600160a01b03848116610100908102610100600160a81b03198416179093556040519290910416907f977e5fa58e458501775e0008d275006294c5249e3c08d1d0e3a9f3acad14f6e49061090d908390869061426f565b5050565b61180d6120e5565b61181657600080fd5b61181e613053565b6040518060400160405280846001600160a01b0316815260200183815250905061184c84826001600061274d565b600754604051632ee2ed2560e01b81527f55681e18b795929fa6b2ebef09244e035285788d963bf5d6e3968dc2bf8addf09186916001600160a01b0390911690632ee2ed25906118a2908490879060040161430b565b60206040518083038186803b1580156118ba57600080fd5b505afa1580156118ce573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506118f29190810190613829565b600754604051638ab3e96560e01b81526001600160a01b0390911690638ab3e96590611924908a90889060040161430b565b60206040518083038186803b15801561193c57600080fd5b505afa158015611950573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506119749190810190613829565b600754604051633007230f60e11b81526001600160a01b039091169063600e461e906119a6908b90899060040161430b565b60206040518083038186803b1580156119be57600080fd5b505afa1580156119d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506119f69190810190613829565b60075460405163c46fcd7560e01b81526001600160a01b039091169063c46fcd7590611a28908c908a9060040161430b565b60206040518083038186803b158015611a4057600080fd5b505afa158015611a54573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611a789190810190613829565b888833604051611a8f9897969594939291906143a5565b60405180910390a150505050565b6007546040805180820182526001600160a01b03858116825260208201859052915163da473c5760e01b8152600093929092169163da473c57916107af9188919060040161430b565b6007546040805180820182526001600160a01b038581168252602082018590529151631b90e3c160e01b81526000939290921691631b90e3c1916107af9188919060040161430b565b611b376120e5565b611b4057600080fd5b806001600160a01b038116611b5457600080fd5b6001600160a01b038116301415611b6a57600080fd5b6001546001600160a01b0383811691161461180157600180546001600160a01b038481166001600160a01b03198316179092556040519116907f9f611b789425d0d5b90b920f1b2852907dd865c80074a30b1629aaa041d1812c9061090d908390869061426f565b611bda6120cf565b611be357600080fd5b6005805460ff60a01b1916600160a01b1790556040517f9c567b65fe8caab5ec7bc979a498a1322c1d4baf01a30727cbca137187560ea290600090a1565b6007546040805180820182526001600160a01b03858116825260208201859052915163076470a560e31b81526000939290921691633b23852891610b6e9188919060040161430b565b611c72613053565b6040518060400160405280846001600160a01b03168152602001838152509050611c9f338260018061274d565b600754604051632ee2ed2560e01b81527f46e3d3c7067c83d22286a7cf49b1ac45b28d128a51b1d4a3b9bdc3927a6da0be9133916001600160a01b0390911690632ee2ed2590611cf590849087906004016141ec565b60206040518083038186803b158015611d0d57600080fd5b505afa158015611d21573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611d459190810190613829565b600754604051638ab3e96560e01b81526001600160a01b0390911690638ab3e96590611d7790339088906004016141ec565b60206040518083038186803b158015611d8f57600080fd5b505afa158015611da3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611dc79190810190613829565b600754604051633007230f60e11b81526001600160a01b039091169063600e461e90611df990339089906004016141ec565b60206040518083038186803b158015611e1157600080fd5b505afa158015611e25573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611e499190810190613829565b60075460405163c46fcd7560e01b81526001600160a01b039091169063c46fcd7590611e7b9033908a906004016141ec565b60206040518083038186803b158015611e9357600080fd5b505afa158015611ea7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611ecb9190810190613829565b888860405161090d9796959493929190614207565b6007546040805180820182526001600160a01b03858116825260208201859052915163160ed0cd60e11b81526060939290921691632c1da19a916105bf9188919060040161430b565b6007546040805180820182526001600160a01b038581168252602082018590529151632ee2ed2560e01b81526000939290921691632ee2ed25916107af9188919060040161430b565b6007546040805180820182526001600160a01b0385811682526020820185905291516345a76a6160e11b81526000939290921691638b4ed4c2916107af9188919060040161430b565b60005461010090046001600160a01b031681565b6004546001600160a01b031681565b6008546001600160a01b031681565b6007546040805180820182526001600160a01b0385811682526020820185905291516356ec907560e01b815260009392909216916356ec9075916107af9188919060040161430b565b6007546040805180820182526001600160a01b038581168252602082018590529151635658722960e01b8152600093929092169163565872299161207f9188919060040161430b565b60206040518083038186803b15801561209757600080fd5b505afa1580156120ab573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061061391908101906136d6565b60005461010090046001600160a01b0316331490565b6001546001600160a01b0316331490565b60035460405163273f8b6560e11b815284916001600160a01b031690634e7f16ca906121269084906004016145dc565b60206040518083038186803b15801561213e57600080fd5b505afa158015612152573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612176919081019061380b565b6121925760405162461bcd60e51b8152600401610d8a906145bc565b600260009054906101000a90046001600160a01b03166001600160a01b031663b6bf41766040518163ffffffff1660e01b815260040160206040518083038186803b1580156121e057600080fd5b505afa1580156121f4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506122189190810190613829565b4310156122375760405162461bcd60e51b8152600401610d8a9061453c565b60035460405163f59d1a7560e01b81526001600160a01b039091169063f59d1a759061226990879089906004016145ed565b60206040518083038186803b15801561228157600080fd5b505afa158015612295573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506122b9919081019061380b565b6122d55760405162461bcd60e51b8152600401610d8a9061457c565b600754602085015160405163076470a560e31b81526001600160a01b0390921691633b2385289161230b9189919060040161430b565b60206040518083038186803b15801561232357600080fd5b505afa158015612337573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061235b919081019061380b565b15806123e857506007546020850151604051637ff81c3760e01b81526001600160a01b0390921691637ff81c37916123989189919060040161430b565b60206040518083038186803b1580156123b057600080fd5b505afa1580156123c4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506123e8919081019061380b565b6124045760405162461bcd60e51b8152600401610d8a9061456c565b600854602085015160405163076470a560e31b81526001600160a01b0390921691633b2385289161243a9189919060040161430b565b60206040518083038186803b15801561245257600080fd5b505afa158015612466573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061248a919081019061380b565b158061251757506008546020850151604051637ff81c3760e01b81526001600160a01b0390921691637ff81c37916124c79189919060040161430b565b60206040518083038186803b1580156124df57600080fd5b505afa1580156124f3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612517919081019061380b565b6125335760405162461bcd60e51b8152600401610d8a9061459c565b600080612540868861295c565b6009546020890151604051633858654b60e01b815293955091935084926001600160a01b0390911691633858654b9161257d918c9160040161430b565b60206040518083038186803b15801561259557600080fd5b505afa1580156125a9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506125cd9190810190613829565b106125ea5760405162461bcd60e51b8152600401610d8a9061458c565b6007546001600160a01b031663bb3bffe088848489612655612612838363ffffffff612b1116565b6126498f8f60200151600560009054906101000a90046001600160a01b03166001600160a01b0316612b509092919063ffffffff16565b9063ffffffff612d4d16565b8c602001518d60c001518c8f60a00151602001516000015173b99f3f4aacb6e1197a623919103b99f4b41aaef0635174abe96040518163ffffffff1660e01b815260040160006040518083038186803b1580156126b157600080fd5b505af41580156126c5573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526126ed91908101906138b3565b6040518b63ffffffff1660e01b81526004016127129a9998979695949392919061441c565b600060405180830381600087803b15801561272c57600080fd5b505af1158015612740573d6000803e3d6000fd5b5050505050505050505050565b60075460405163076470a560e31b81526001600160a01b0390911690633b2385289061277f908790879060040161430b565b60206040518083038186803b15801561279757600080fd5b505afa1580156127ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506127cf919081019061380b565b6127eb5760405162461bcd60e51b8152600401610d8a906145ac565b600754604051637ff81c3760e01b81526001600160a01b0390911690637ff81c379061281d908790879060040161430b565b60206040518083038186803b15801561283557600080fd5b505afa158015612849573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061286d919081019061380b565b1561288a5760405162461bcd60e51b8152600401610d8a9061454c565b6007546040516335a4238360e01b81526001600160a01b03909116906335a42383906128c0908790879087908790600401614319565b600060405180830381600087803b1580156128da57600080fd5b505af11580156128ee573d6000803e3d6000fd5b5050600854604051632ce4e29b60e21b81526001600160a01b03909116925063b3938a6c9150612924908790879060040161430b565b600060405180830381600087803b15801561293e57600080fd5b505af1158015612952573d6000803e3d6000fd5b5050505050505050565b602082015160c08301516005546000928392839261298e926001600160a01b039091169187919063ffffffff612d8016565b600954602087015160c088015160405163bc8dd4cd60e01b81529394506000936001600160a01b039093169263bc8dd4cd926129ce928a9260040161434e565b60206040518083038186803b1580156129e657600080fd5b505afa1580156129fa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612a1e9190810190613829565b60035460405163d308b9db60e01b81529192506001600160a01b03169063d308b9db90612a5190899089906004016145ed565b60206040518083038186803b158015612a6957600080fd5b505afa158015612a7d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612aa1919081019061380b565b15612ade57604080870151805191015151909450612ad7908290612acb908563ffffffff612b1116565b9063ffffffff612b1116565b9250612b08565b6060860151805160409091015151909450612b05908290612acb908563ffffffff612b1116565b92505b50509250929050565b6000808212158015612b2557508282840313155b80612b3c5750600082128015612b3c575082828403135b612b4557600080fd5b508082035b92915050565b6000610613846001600160a01b03166371f4ec5185876001600160a01b0316634652ec746040518163ffffffff1660e01b815260040160206040518083038186803b158015612b9e57600080fd5b505afa158015612bb2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612bd69190810190613829565b865160208801516040516001600160e01b031960e087901b168152612c01949392919060040161428a565b60206040518083038186803b158015612c1957600080fd5b505afa158015612c2d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612c519190810190613829565b856001600160a01b03166371f4ec5186886001600160a01b031663ded113106040518163ffffffff1660e01b815260040160206040518083038186803b158015612c9a57600080fd5b505afa158015612cae573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612cd29190810190613829565b875160208901516040516001600160e01b031960e087901b168152612cfd949392919060040161428a565b60206040518083038186803b158015612d1557600080fd5b505afa158015612d29573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506126499190810190613829565b6000828201818312801590612d625750838112155b80612d775750600083128015612d7757508381125b61061657600080fd5b600080612d8f86868686612d9c565b509150505b949350505050565b600080600080876001600160a01b031663a246138c888a6001600160a01b031663ded113106040518163ffffffff1660e01b815260040160206040518083038186803b158015612deb57600080fd5b505afa158015612dff573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612e239190810190613829565b895160208b01516040516001600160e01b031960e087901b168152612e5094939291908c906004016142bf565b604080518083038186803b158015612e6757600080fd5b505afa158015612e7b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612e9f9190810190613883565b91509150600080896001600160a01b031663a246138c8a8c6001600160a01b0316634652ec746040518163ffffffff1660e01b815260040160206040518083038186803b158015612eef57600080fd5b505afa158015612f03573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612f279190810190613829565b8b5160208d01516040516001600160e01b031960e087901b168152612f5494939291908e906004016142bf565b604080518083038186803b158015612f6b57600080fd5b505afa158015612f7f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612fa39190810190613883565b9092509050612fb8848363ffffffff612d4d16565b60405163a682d5ad60e01b8152909650730ff948c236c8d4dfcd0168bf243314c8ff8ec9679063a682d5ad90612ff4908690859060040161460d565b60206040518083038186803b15801561300c57600080fd5b505af4158015613020573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506130449190810190613829565b94505050505094509492505050565b604080518082019091526000808252602082015290565b8035612b4a8161474c565b8051612b4a8161474c565b600082601f83011261309157600080fd5b81356130a461309f8261464e565b614628565b915081818352602084019350602081019050838560808402820111156130c957600080fd5b60005b838110156130f757816130df88826132cb565b845250602090920191608091909101906001016130cc565b5050505092915050565b8051612b4a81614760565b8035612b4a81614769565b8051612b4a81614769565b8035612b4a81614772565b8051612b4a8161477b565b600082601f83011261314957600080fd5b813561315761309f8261466e565b9150808252602083016020830185838301111561317357600080fd5b61317e8382846146fd565b50505092915050565b600082601f83011261319857600080fd5b81516131a661309f8261466e565b915080825260208301602083018583830111156131c257600080fd5b61317e838284614709565b6000604082840312156131df57600080fd5b6131e96040614628565b905060006131f7848461306a565b82525060206132088484830161310c565b60208301525092915050565b60006040828403121561322657600080fd5b6132306040614628565b905060006131f7848461310c565b60006060828403121561325057600080fd5b61325a6040614628565b90506000613268848461310c565b8252506020613208848483016131cd565b60006040828403121561328b57600080fd5b6132956040614628565b905060006132a3848461310c565b82525060208201356001600160401b038111156132bf57600080fd5b61320884828501613138565b6000608082840312156132dd57600080fd5b6132e76040614628565b905060006132f5848461310c565b82525060206132088484830161323e565b600060a0828403121561331857600080fd5b6133226080614628565b90506000613330848461310c565b82525060206133418484830161306a565b602083015250604061335584828501613214565b60408301525060808201356001600160401b0381111561337457600080fd5b61338084828501613628565b60608301525092915050565b600060c0828403121561339e57600080fd5b6133a860a0614628565b905060006133b6848461310c565b82525060206133c78484830161306a565b60208301525060406133db84828501613214565b60408301525060808201356001600160401b038111156133fa57600080fd5b613406848285016135d6565b60608301525060a08201356001600160401b0381111561342557600080fd5b61343184828501613138565b60808301525092915050565b6000610220828403121561345057600080fd5b61345b610100614628565b90506000613469848461310c565b825250602061347a848483016131cd565b60208301525060608201356001600160401b0381111561349957600080fd5b6134a58482850161338c565b60408301525060808201356001600160401b038111156134c457600080fd5b6134d084828501613306565b60608301525060a06134e484828501613214565b60808301525060e06134f884828501613671565b60a0830152506101e061350d8482850161310c565b60c0830152506102008201356001600160401b0381111561352d57600080fd5b61353984828501613279565b60e08301525092915050565b60006080828403121561355757600080fd5b6135616040614628565b9050600061356f848461310c565b82525060206132088484830160006060828403121561358d57600080fd5b6135976060614628565b905060006135a5848461310c565b82525060206135b68484830161310c565b60208301525060406135ca848285016136ad565b60408301525092915050565b6000608082840312156135e857600080fd5b6135f26040614628565b90506000613600848461323e565b82525060608201356001600160401b0381111561361c57600080fd5b61320884828501613080565b60006020828403121561363a57600080fd5b6136446020614628565b905081356001600160401b0381111561365c57600080fd5b61366884828501613080565b82525092915050565b6000610100828403121561368457600080fd5b61368e6040614628565b9050600061369c8484613545565b825250608061320884848301613545565b8035612b4a81614788565b6000602082840312156136ca57600080fd5b6000612d94848461306a565b6000602082840312156136e857600080fd5b6000612d948484613075565b60008060006060848603121561370957600080fd5b6000613715868661306a565b93505060206137268682870161306a565b92505060406137378682870161310c565b9150509250925092565b6000806040838503121561375457600080fd5b6000613760858561306a565b92505060208301356001600160401b0381111561377c57600080fd5b6137888582860161343d565b9150509250929050565b6000806000606084860312156137a757600080fd5b60006137b3868661306a565b93505060208401356001600160401b038111156137cf57600080fd5b6137268682870161343d565b600080604083850312156137ee57600080fd5b60006137fa858561306a565b92505060206137888582860161310c565b60006020828403121561381d57600080fd5b6000612d948484613101565b60006020828403121561383b57600080fd5b6000612d948484613117565b60006020828403121561385957600080fd5b6000612d948484613122565b60006020828403121561387757600080fd5b6000612d94848461312d565b6000806040838503121561389657600080fd5b60006138a28585613117565b925050602061378885828601613117565b6000602082840312156138c557600080fd5b81516001600160401b038111156138db57600080fd5b612d9484828501613187565b600080604083850312156138fa57600080fd5b82356001600160401b0381111561391057600080fd5b6137fa8582860161343d565b60006139288383613f77565b505060800190565b613939816146e7565b82525050565b613939816146a8565b60006139538261469b565b61395d818561469f565b935061396883614695565b8060005b83811015613996578151613980888261391c565b975061398b83614695565b92505060010161396c565b509495945050505050565b613939816146b3565b613939816146b8565b613939816146bb565b613939816146f2565b60006139d08261469b565b6139da818561469f565b93506139ea818560208601614709565b6139f381614735565b9093019392505050565b6000613a0a603f8361469f565b7f4f7065726174696f6e616c206d6f6465206973206e6f74206e6f726d616c205b81527f436f6e666967757261626c654f7065726174696f6e616c2e736f6c3a32325d00602082015260400192915050565b6000613a6960308361469f565b7f42616c616e636520747261636b65722066726f7a656e205b42616c616e63655481526f7261636b61626c652e736f6c3a34335d60801b602082015260400192915050565b6000613abb60678361469f565b7f43757272656e7420626c6f636b206e756d6265722062656c6f77206561726c6981527f65737420736574746c656d656e7420626c6f636b206e756d626572205b44726960208201526000805160206147928339815191526040820152666f6c3a3438395d60c81b606082015260800192915050565b6000613b3e60458361469f565b7f50726f706f73616c20666f756e64207465726d696e61746564205b447269697081527f536574746c656d656e744368616c6c656e676542795061796d656e742e736f6c6020820152643a3534325d60d81b604082015260600192915050565b6000613bab603f8361469f565b7f57616c6c657420666f756e64206c6f636b6564205b4472696970536574746c6581527f6d656e744368616c6c656e676542795061796d656e742e736f6c3a3133345d00602082015260400192915050565b6000613c0a60618361469f565b7f4f7665726c617070696e6720647269697020736574746c656d656e742063686181527f6c6c656e67652070726f706f73616c20666f756e64205b44726969705365747460208201527f6c656d656e744368616c6c656e676542795061796d656e742e736f6c3a3530316040820152605d60f81b606082015260800192915050565b6000613c9960478361469f565b7f57616c6c6574206973206e6f74207061796d656e74207061727479205b44726981526000805160206147928339815191526020820152666f6c3a3439355d60c81b604082015260600192915050565b6000613cf660568361469f565b7f57616c6c65742773206e6f6e63652062656c6f7720686967686573742073657481527f746c6564206e6f6e6365205b4472696970536574746c656d656e744368616c6c602082015275656e676542795061796d656e742e736f6c3a3531385d60501b604082015260600192915050565b6000613d7460608361469f565b7f4f7665726c617070696e67206e756c6c20736574746c656d656e74206368616c81527f6c656e67652070726f706f73616c20666f756e64205b4472696970536574746c60208201527f656d656e744368616c6c656e676542795061796d656e742e736f6c3a3530385d604082015260600192915050565b6000613df9603d8361469f565b7f4e6f2070726f706f73616c20666f756e64205b4472696970536574746c656d6581527f6e744368616c6c656e676542795061796d656e742e736f6c3a3533385d000000602082015260400192915050565b6000613e58602e8361469f565b7f5061796d656e74207365616c73206e6f742067656e75696e65205b56616c696481526d617461626c652e736f6c3a36355d60901b602082015260400192915050565b6000613ea8602c8361469f565b7f57616c6c6574206c6f636b65722066726f7a656e205b57616c6c65744c6f636b81526b61626c652e736f6c3a34335d60a01b602082015260400192915050565b80516040830190613efa848261393f565b50602082015161076060208501826139aa565b80516040830190613efa84826139aa565b80516060830190613f2f84826139aa565b5060208201516107606020850182613ee9565b80516000906040840190613f5685826139aa565b5060208301518482036020860152613f6e82826139c5565b95945050505050565b80516080830190613f8884826139aa565b5060208201516107606020850182613f1e565b805160009060a0840190613faf85826139aa565b506020830151613fc2602086018261393f565b506040830151613fd56040860182613f0d565b5060608301518482036080860152613f6e828261418a565b805160009060c084019061400185826139aa565b506020830151614014602086018261393f565b5060408301516140276040860182613f0d565b506060830151848203608086015261403f828261415e565b915050608083015184820360a0860152613f6e82826139c5565b805160009061022084019061406e85826139aa565b5060208301516140816020860182613ee9565b50604083015184820360608601526140998282613fed565b915050606083015184820360808601526140b38282613f9b565b91505060808301516140c860a0860182613f0d565b5060a08301516140db60e08601826141a2565b5060c08301516140ef6101e08601826139aa565b5060e0830151848203610200860152613f6e8282613f42565b8051608083019061411984826139aa565b50602082015161076060208501828051606083019061413884826139aa565b50602082015161414b60208501826139aa565b50604082015161076060408501826141c7565b805160009060808401906141728582613f1e565b5060208301518482036060860152613f6e8282613948565b8051602080845260009190840190613f6e8282613948565b80516101008301906141b48482614108565b5060208201516107606080850182614108565b613939816146e1565b60208101612b4a828461393f565b60208101612b4a8284613930565b606081016141fa8285613930565b6106166020830184613ee9565b60e08101614215828a613930565b61422260208301896139aa565b61422f60408301886139aa565b61423c60608301876139aa565b61424960808301866139aa565b61425660a083018561393f565b61426360c08301846139aa565b98975050505050505050565b6040810161427d828561393f565b610616602083018461393f565b60808101614298828761393f565b6142a560208301866139aa565b6142b2604083018561393f565b613f6e60608301846139aa565b60a081016142cd828861393f565b6142da60208301876139aa565b6142e7604083018661393f565b6142f460608301856139aa565b61430160808301846139aa565b9695505050505050565b606081016141fa828561393f565b60a08101614327828761393f565b6143346020830186613ee9565b61434160608301856139a1565b613f6e60808301846139a1565b6080810161435c828661393f565b6143696020830185613ee9565b612d9460608301846139aa565b60608101614384828661393f565b81810360208301526143968185614059565b9050612d946040830184613930565b61010081016143b4828b61393f565b6143c1602083018a6139aa565b6143ce60408301896139aa565b6143db60608301886139aa565b6143e860808301876139aa565b6143f560a083018661393f565b61440260c08301856139aa565b61440f60e0830184613930565b9998505050505050505050565b610160810161442b828d61393f565b614438602083018c6139aa565b614445604083018b6139aa565b614452606083018a6139aa565b61445f60808301896139aa565b61446c60a0830188613ee9565b61447960e08301876139aa565b6144876101008301866139a1565b6144956101208301856139aa565b8181036101408301526144a881846139c5565b9c9b505050505050505050505050565b60208101612b4a82846139a1565b60208101612b4a82846139aa565b60208101612b4a82846139b3565b604081016144f082856139b3565b61061660208301846139b3565b60208101612b4a82846139bc565b6020808252810161061681846139c5565b60208082528101612b4a816139fd565b60208082528101612b4a81613a5c565b60208082528101612b4a81613aae565b60208082528101612b4a81613b31565b60208082528101612b4a81613b9e565b60208082528101612b4a81613bfd565b60208082528101612b4a81613c8c565b60208082528101612b4a81613ce9565b60208082528101612b4a81613d67565b60208082528101612b4a81613dec565b60208082528101612b4a81613e4b565b60208082528101612b4a81613e9b565b602080825281016106168184614059565b604080825281016145fe8185614059565b9050610616602083018461393f565b6040810161461b82856139aa565b61061660208301846139aa565b6040518181016001600160401b038111828210171561464657600080fd5b604052919050565b60006001600160401b0382111561466457600080fd5b5060209081020190565b60006001600160401b0382111561468457600080fd5b506020601f91909101601f19160190565b60200190565b5190565b90815260200190565b6000612b4a826146d5565b151590565b90565b6000612b4a826146a8565b806146d08161473f565b919050565b6001600160a01b031690565b60ff1690565b6000612b4a826146bb565b6000612b4a826146c6565b82818337506000910152565b60005b8381101561472457818101518382015260200161470c565b838111156107605750506000910152565b601f01601f191690565b6002811061474957fe5b50565b614755816146a8565b811461474957600080fd5b614755816146b3565b614755816146b8565b614755816146bb565b6002811061474957600080fd5b614755816146e156fe6970536574746c656d656e744368616c6c656e676542795061796d656e742e73a365627a7a723158208377fca6c152a24ff05ca78673a209831b0e8caf71a351e7a734ab34c4fd3ad06c6578706572696d656e74616cf564736f6c634300050b0040

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


Swarm Source

bzzr://8377fca6c152a24ff05ca78673a209831b0e8caf71a351e7a734ab34c4fd3ad0

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  ]

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.