ETH Price: $1,877.34 (+0.31%)
Gas: 0.77 Gwei

Transaction Decoder

Block:
13566596 at Nov-07-2021 01:59:25 AM +UTC
Transaction Fee:
0.016106685276959604 ETH $30.24
Gas Used:
190,396 Gas / 84.595712499 Gwei

Emitted Events:

162 TokenProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x000000000000000000000000513fb60037240205a0cf17c260257097d747bd46, 0x00000000000000000000000013aa17dc704f634a9c897689fff86ac302d3cc38, 0000000000000000000000000000000000000000000000679da9454f19568a6d )
163 RewardDelegatorsProxy.0x643d0f768d20758fe82686d7de201866faaee92f36542c9b1ea68fe26217dc90( 0x643d0f768d20758fe82686d7de201866faaee92f36542c9b1ea68fe26217dc90, 00000000000000000000000013aa17dc704f634a9c897689fff86ac302d3cc38 )
164 TokenProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x000000000000000000000000513fb60037240205a0cf17c260257097d747bd46, 0x00000000000000000000000084b9eee80beb6e618d677343c6eff6f3240326e7, 0000000000000000000000000000000000000000000002ee0aaad352e75f9f77 )
165 RewardDelegatorsProxy.0x54e8f034636217314f27bfc2c4e0aec174ceab3a2b1196d2e00f8bae09ac52fd( 0x54e8f034636217314f27bfc2c4e0aec174ceab3a2b1196d2e00f8bae09ac52fd, 00000000000000000000000013aa17dc704f634a9c897689fff86ac302d3cc38, 00000000000000000000000084b9eee80beb6e618d677343c6eff6f3240326e7, 0000000000000000000000000000000000000000000000000000000000000080, 0000000000000000000000000000000000000000000002ee0aaad352e75f9f77, 0000000000000000000000000000000000000000000000000000000000000002, 5802add45f8ec0a524470683e7295faacc853f97cf4a8d3ffbaaf25ce0fd87c4, 1635815984abab0dbb9afd77984dad69c24bf3d711bc0ddb1e2d53ef2d523e5e )
166 StakeManagerProxy.0x910aaad05eef8babc75a5bbd3987092b49f6dcd0fd06c860d9af177b650c5625( 0x910aaad05eef8babc75a5bbd3987092b49f6dcd0fd06c860d9af177b650c5625, 363a63c83fb1b082d2626fb079e7d74d92caf27fec08dcc75278f5de8d9db6d7, 00000000000000000000000013aa17dc704f634a9c897689fff86ac302d3cc38, 0000000000000000000000000000000000000000000000000000000000cfb634 )

Account State Difference:

  Address   Before After State Difference Code
(Hiveon Pool)
7,880.891331917973092816 Eth7,880.891617511973092816 Eth0.000285594
0x5124324e...Ef0297cd7
0x513FB600...7D747BD46
0x57B94600...10e58D26C
0x84b9eee8...3240326E7
0.467937084219774429 Eth
Nonce: 273
0.451830398942814825 Eth
Nonce: 274
0.016106685276959604
0xCBB94D13...8ae4B6c82
(Marlin: Stake Manager Proxy)

Execution Trace

StakeManagerProxy.5dc47b57( )
  • StakeManager.undelegateStash( _stashId=363A63C83FB1B082D2626FB079E7D74D92CAF27FEC08DCC75278F5DE8D9DB6D7 )
    • RewardDelegatorsProxy.STATICCALL( )
      • RewardDelegators.DELEGATECALL( )
      • RewardDelegatorsProxy.9a943e00( )
        • RewardDelegators.undelegate( )
          • ClusterRewardsProxy.d279c191( )
            • ClusterRewards.claimReward( _cluster=0x13aa17dc704f634a9C897689FfF86ac302d3cc38 ) => ( 47784383183328310953132 )
            • ClusterRegistryProxy.06032d74( )
              • ClusterRegistry.getRewardInfo( _cluster=0x13aa17dc704f634a9C897689FfF86ac302d3cc38 ) => ( 4, 0x13aa17dc704f634a9C897689FfF86ac302d3cc38 )
              • TokenProxy.a9059cbb( )
                • TokenLogic.transfer( to=0x13aa17dc704f634a9C897689FfF86ac302d3cc38, value=1911375327333132438125 ) => ( True )
                • TokenProxy.a9059cbb( )
                  • TokenLogic.transfer( to=0x84b9eee80BeB6e618d677343C6eFF6f3240326E7, value=13835826714321606385527 ) => ( True )
                    File 1 of 10: StakeManagerProxy
                    pragma solidity >=0.4.21 <0.7.0;
                    
                    
                    /// @title Contract to reward overlapping stakes
                    /// @author Marlin
                    /// @notice Use this contract only for testing
                    /// @dev Contract may or may not change in future (depending upon the new slots in proxy-store)
                    contract StakeManagerProxy {
                        bytes32 internal constant IMPLEMENTATION_SLOT = bytes32(
                            uint256(keccak256("eip1967.proxy.implementation")) - 1
                        );
                        bytes32 internal constant PROXY_ADMIN_SLOT = bytes32(
                            uint256(keccak256("eip1967.proxy.admin")) - 1
                        );
                    
                        constructor(address contractLogic, address proxyAdmin) public {
                            // save the code address
                            bytes32 slot = IMPLEMENTATION_SLOT;
                            assembly {
                                sstore(slot, contractLogic)
                            }
                            // save the proxy admin
                            slot = PROXY_ADMIN_SLOT;
                            address sender = proxyAdmin;
                            assembly {
                                sstore(slot, sender)
                            }
                        }
                    
                        function updateAdmin(address _newAdmin) public {
                            require(
                                msg.sender == getAdmin(),
                                "Only the current admin should be able to new admin"
                            );
                            bytes32 slot = PROXY_ADMIN_SLOT;
                            assembly {
                                sstore(slot, _newAdmin)
                            }
                        }
                    
                        /// @author Marlin
                        /// @dev Only admin can update the contract
                        /// @param _newLogic address is the address of the contract that has to updated to
                        function updateLogic(address _newLogic) public {
                            require(
                                msg.sender == getAdmin(),
                                "Only Admin should be able to update the contracts"
                            );
                            bytes32 slot = IMPLEMENTATION_SLOT;
                            assembly {
                                sstore(slot, _newLogic)
                            }
                        }
                    
                        /// @author Marlin
                        /// @dev use assembly as contract store slot is manually decided
                        function getAdmin() internal view returns (address result) {
                            bytes32 slot = PROXY_ADMIN_SLOT;
                            assembly {
                                result := sload(slot)
                            }
                        }
                    
                        /// @author Marlin
                        /// @dev add functionality to forward the balance as well.
                        function() external payable {
                            bytes32 slot = IMPLEMENTATION_SLOT;
                            assembly {
                                let contractLogic := sload(slot)
                                calldatacopy(0x0, 0x0, calldatasize())
                                let success := delegatecall(
                                    sub(gas(), 10000),
                                    contractLogic,
                                    0x0,
                                    calldatasize(),
                                    0,
                                    0
                                )
                                let retSz := returndatasize()
                                returndatacopy(0, 0, retSz)
                    
                                switch success
                                    case 0 {
                                        revert(0, retSz)
                                    }
                                    default {
                                        return(0, retSz)
                                    }
                            }
                        }
                    }

                    File 2 of 10: TokenProxy
                    pragma solidity >=0.4.21 <0.7.0;
                    
                    
                    /// @title Contract to reward overlapping stakes
                    /// @author Marlin
                    /// @notice Use this contract only for testing
                    /// @dev Contract may or may not change in future (depending upon the new slots in proxy-store)
                    contract TokenProxy {
                        bytes32 internal constant IMPLEMENTATION_SLOT = bytes32(
                            uint256(keccak256("eip1967.proxy.implementation")) - 1
                        );
                        bytes32 internal constant PROXY_ADMIN_SLOT = bytes32(
                            uint256(keccak256("eip1967.proxy.admin")) - 1
                        );
                    
                        constructor(address contractLogic, address proxyAdmin) public {
                            // save the code address
                            bytes32 slot = IMPLEMENTATION_SLOT;
                            assembly {
                                sstore(slot, contractLogic)
                            }
                            // save the proxy admin
                            slot = PROXY_ADMIN_SLOT;
                            address sender = proxyAdmin;
                            assembly {
                                sstore(slot, sender)
                            }
                        }
                    
                        function updateAdmin(address _newAdmin) public {
                            require(
                                msg.sender == getAdmin(),
                                "Only the current admin should be able to new admin"
                            );
                            bytes32 slot = PROXY_ADMIN_SLOT;
                            assembly {
                                sstore(slot, _newAdmin)
                            }
                        }
                    
                        /// @author Marlin
                        /// @dev Only admin can update the contract
                        /// @param _newLogic address is the address of the contract that has to updated to
                        function updateLogic(address _newLogic) public {
                            require(
                                msg.sender == getAdmin(),
                                "Only Admin should be able to update the contracts"
                            );
                            bytes32 slot = IMPLEMENTATION_SLOT;
                            assembly {
                                sstore(slot, _newLogic)
                            }
                        }
                    
                        /// @author Marlin
                        /// @dev use assembly as contract store slot is manually decided
                        function getAdmin() internal view returns (address result) {
                            bytes32 slot = PROXY_ADMIN_SLOT;
                            assembly {
                                result := sload(slot)
                            }
                        }
                    
                        /// @author Marlin
                        /// @dev add functionality to forward the balance as well.
                        function() external payable {
                            bytes32 slot = IMPLEMENTATION_SLOT;
                            assembly {
                                let contractLogic := sload(slot)
                                calldatacopy(0x0, 0x0, calldatasize())
                                let success := delegatecall(
                                    sub(gas(), 10000),
                                    contractLogic,
                                    0x0,
                                    calldatasize(),
                                    0,
                                    0
                                )
                                let retSz := returndatasize()
                                returndatacopy(0, 0, retSz)
                    
                                switch success
                                    case 0 {
                                        revert(0, retSz)
                                    }
                                    default {
                                        return(0, retSz)
                                    }
                            }
                        }
                    }

                    File 3 of 10: RewardDelegatorsProxy
                    pragma solidity >=0.4.21 <0.7.0;
                    
                    
                    /// @title Contract to reward overlapping stakes
                    /// @author Marlin
                    /// @notice Use this contract only for testing
                    /// @dev Contract may or may not change in future (depending upon the new slots in proxy-store)
                    contract RewardDelegatorsProxy {
                        bytes32 internal constant IMPLEMENTATION_SLOT = bytes32(
                            uint256(keccak256("eip1967.proxy.implementation")) - 1
                        );
                        bytes32 internal constant PROXY_ADMIN_SLOT = bytes32(
                            uint256(keccak256("eip1967.proxy.admin")) - 1
                        );
                    
                        constructor(address contractLogic, address proxyAdmin) public {
                            // save the code address
                            bytes32 slot = IMPLEMENTATION_SLOT;
                            assembly {
                                sstore(slot, contractLogic)
                            }
                            // save the proxy admin
                            slot = PROXY_ADMIN_SLOT;
                            address sender = proxyAdmin;
                            assembly {
                                sstore(slot, sender)
                            }
                        }
                    
                        function updateAdmin(address _newAdmin) public {
                            require(
                                msg.sender == getAdmin(),
                                "Only the current admin should be able to new admin"
                            );
                            bytes32 slot = PROXY_ADMIN_SLOT;
                            assembly {
                                sstore(slot, _newAdmin)
                            }
                        }
                    
                        /// @author Marlin
                        /// @dev Only admin can update the contract
                        /// @param _newLogic address is the address of the contract that has to updated to
                        function updateLogic(address _newLogic) public {
                            require(
                                msg.sender == getAdmin(),
                                "Only Admin should be able to update the contracts"
                            );
                            bytes32 slot = IMPLEMENTATION_SLOT;
                            assembly {
                                sstore(slot, _newLogic)
                            }
                        }
                    
                        /// @author Marlin
                        /// @dev use assembly as contract store slot is manually decided
                        function getAdmin() internal view returns (address result) {
                            bytes32 slot = PROXY_ADMIN_SLOT;
                            assembly {
                                result := sload(slot)
                            }
                        }
                    
                        /// @author Marlin
                        /// @dev add functionality to forward the balance as well.
                        function() external payable {
                            bytes32 slot = IMPLEMENTATION_SLOT;
                            assembly {
                                let contractLogic := sload(slot)
                                calldatacopy(0x0, 0x0, calldatasize())
                                let success := delegatecall(
                                    sub(gas(), 10000),
                                    contractLogic,
                                    0x0,
                                    calldatasize(),
                                    0,
                                    0
                                )
                                let retSz := returndatasize()
                                returndatacopy(0, 0, retSz)
                    
                                switch success
                                    case 0 {
                                        revert(0, retSz)
                                    }
                                    default {
                                        return(0, retSz)
                                    }
                            }
                        }
                    }

                    File 4 of 10: StakeManager
                    pragma solidity >=0.4.24 <0.7.0;
                    
                    
                    /**
                     * @title Initializable
                     *
                     * @dev Helper contract to support initializer functions. To use it, replace
                     * the constructor with a function that has the `initializer` modifier.
                     * WARNING: Unlike constructors, initializer functions must be manually
                     * invoked. This applies both to deploying an Initializable contract, as well
                     * as extending an Initializable contract via inheritance.
                     * WARNING: When used with inheritance, manual care must be taken to not invoke
                     * a parent initializer twice, or ensure that all initializers are idempotent,
                     * because this is not dealt with automatically as with constructors.
                     */
                    contract Initializable {
                    
                      /**
                       * @dev Indicates that the contract has been initialized.
                       */
                      bool private initialized;
                    
                      /**
                       * @dev Indicates that the contract is in the process of being initialized.
                       */
                      bool private initializing;
                    
                      /**
                       * @dev Modifier to use in the initializer function of a contract.
                       */
                      modifier initializer() {
                        require(initializing || isConstructor() || !initialized, "Contract instance has already been initialized");
                    
                        bool isTopLevelCall = !initializing;
                        if (isTopLevelCall) {
                          initializing = true;
                          initialized = true;
                        }
                    
                        _;
                    
                        if (isTopLevelCall) {
                          initializing = false;
                        }
                      }
                    
                      /// @dev Returns true if and only if the function is running in the constructor
                      function isConstructor() private view returns (bool) {
                        // extcodesize checks the size of the code stored in an address, and
                        // address returns the current address. Since the code is still not
                        // deployed when running a constructor, any checks on its code size will
                        // yield zero, making it an effective way to detect if a contract is
                        // under construction or not.
                        address self = address(this);
                        uint256 cs;
                        assembly { cs := extcodesize(self) }
                        return cs == 0;
                      }
                    
                      // Reserved storage space to allow for layout changes in the future.
                      uint256[50] private ______gap;
                    }
                    
                    
                    
                    
                                
                    pragma solidity ^0.5.0;
                    
                    /*
                     * @dev Provides information about the current execution context, including the
                     * sender of the transaction and its data. While these are generally available
                     * via msg.sender and msg.data, they should not be accessed in such a direct
                     * manner, since when dealing with GSN meta-transactions the account sending and
                     * paying for execution may not be the actual sender (as far as an application
                     * is concerned).
                     *
                     * This contract is only required for intermediate, library-like contracts.
                     */
                    contract Context is Initializable {
                        // Empty internal constructor, to prevent people from mistakenly deploying
                        // an instance of this contract, which should be used via inheritance.
                        constructor () internal { }
                        // solhint-disable-previous-line no-empty-blocks
                    
                        function _msgSender() internal view returns (address payable) {
                            return msg.sender;
                        }
                    
                        function _msgData() internal view returns (bytes memory) {
                            this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                            return msg.data;
                        }
                    }
                    
                    
                    
                    
                                
                    pragma solidity ^0.5.0;
                    
                    /**
                     * @dev Wrappers over Solidity's arithmetic operations with added overflow
                     * checks.
                     *
                     * Arithmetic operations in Solidity wrap on overflow. This can easily result
                     * in bugs, because programmers usually assume that an overflow raises an
                     * error, which is the standard behavior in high level programming languages.
                     * `SafeMath` restores this intuition by reverting the transaction when an
                     * operation overflows.
                     *
                     * Using this library instead of the unchecked operations eliminates an entire
                     * class of bugs, so it's recommended to use it always.
                     */
                    library SafeMath {
                        /**
                         * @dev Returns the addition of two unsigned integers, reverting on
                         * overflow.
                         *
                         * Counterpart to Solidity's `+` operator.
                         *
                         * Requirements:
                         * - Addition cannot overflow.
                         */
                        function add(uint256 a, uint256 b) internal pure returns (uint256) {
                            uint256 c = a + b;
                            require(c >= a, "SafeMath: addition overflow");
                    
                            return c;
                        }
                    
                        /**
                         * @dev Returns the subtraction of two unsigned integers, reverting on
                         * overflow (when the result is negative).
                         *
                         * Counterpart to Solidity's `-` operator.
                         *
                         * Requirements:
                         * - Subtraction cannot overflow.
                         */
                        function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                            return sub(a, b, "SafeMath: subtraction overflow");
                        }
                    
                        /**
                         * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
                         * overflow (when the result is negative).
                         *
                         * Counterpart to Solidity's `-` operator.
                         *
                         * Requirements:
                         * - Subtraction cannot overflow.
                         *
                         * _Available since v2.4.0._
                         */
                        function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                            require(b <= a, errorMessage);
                            uint256 c = a - b;
                    
                            return c;
                        }
                    
                        /**
                         * @dev Returns the multiplication of two unsigned integers, reverting on
                         * overflow.
                         *
                         * Counterpart to Solidity's `*` operator.
                         *
                         * Requirements:
                         * - Multiplication cannot overflow.
                         */
                        function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                            // benefit is lost if 'b' is also tested.
                            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                            if (a == 0) {
                                return 0;
                            }
                    
                            uint256 c = a * b;
                            require(c / a == b, "SafeMath: multiplication overflow");
                    
                            return c;
                        }
                    
                        /**
                         * @dev Returns the integer division of two unsigned integers. Reverts on
                         * division by zero. The result is rounded towards zero.
                         *
                         * Counterpart to Solidity's `/` operator. Note: this function uses a
                         * `revert` opcode (which leaves remaining gas untouched) while Solidity
                         * uses an invalid opcode to revert (consuming all remaining gas).
                         *
                         * Requirements:
                         * - The divisor cannot be zero.
                         */
                        function div(uint256 a, uint256 b) internal pure returns (uint256) {
                            return div(a, b, "SafeMath: division by zero");
                        }
                    
                        /**
                         * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
                         * division by zero. The result is rounded towards zero.
                         *
                         * Counterpart to Solidity's `/` operator. Note: this function uses a
                         * `revert` opcode (which leaves remaining gas untouched) while Solidity
                         * uses an invalid opcode to revert (consuming all remaining gas).
                         *
                         * Requirements:
                         * - The divisor cannot be zero.
                         *
                         * _Available since v2.4.0._
                         */
                        function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                            // Solidity only automatically asserts when dividing by 0
                            require(b > 0, errorMessage);
                            uint256 c = a / b;
                            // assert(a == b * c + a % b); // There is no case in which this doesn't hold
                    
                            return c;
                        }
                    
                        /**
                         * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                         * Reverts when dividing by zero.
                         *
                         * Counterpart to Solidity's `%` operator. This function uses a `revert`
                         * opcode (which leaves remaining gas untouched) while Solidity uses an
                         * invalid opcode to revert (consuming all remaining gas).
                         *
                         * Requirements:
                         * - The divisor cannot be zero.
                         */
                        function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                            return mod(a, b, "SafeMath: modulo by zero");
                        }
                    
                        /**
                         * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                         * Reverts with custom message when dividing by zero.
                         *
                         * Counterpart to Solidity's `%` operator. This function uses a `revert`
                         * opcode (which leaves remaining gas untouched) while Solidity uses an
                         * invalid opcode to revert (consuming all remaining gas).
                         *
                         * Requirements:
                         * - The divisor cannot be zero.
                         *
                         * _Available since v2.4.0._
                         */
                        function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                            require(b != 0, errorMessage);
                            return a % b;
                        }
                    }
                    
                    
                    
                    
                                
                    pragma solidity ^0.5.0;
                    
                    /**
                     * @dev Interface of the ERC20 standard as defined in the EIP. Does not include
                     * the optional functions; to access them see {ERC20Detailed}.
                     */
                    interface IERC20 {
                        /**
                         * @dev Returns the amount of tokens in existence.
                         */
                        function totalSupply() external view returns (uint256);
                    
                        /**
                         * @dev Returns the amount of tokens owned by `account`.
                         */
                        function balanceOf(address account) external view returns (uint256);
                    
                        /**
                         * @dev Moves `amount` tokens from the caller's account to `recipient`.
                         *
                         * Returns a boolean value indicating whether the operation succeeded.
                         *
                         * Emits a {Transfer} event.
                         */
                        function transfer(address recipient, uint256 amount) external returns (bool);
                    
                        /**
                         * @dev Returns the remaining number of tokens that `spender` will be
                         * allowed to spend on behalf of `owner` through {transferFrom}. This is
                         * zero by default.
                         *
                         * This value changes when {approve} or {transferFrom} are called.
                         */
                        function allowance(address owner, address spender) external view returns (uint256);
                    
                        /**
                         * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
                         *
                         * Returns a boolean value indicating whether the operation succeeded.
                         *
                         * ////IMPORTANT: Beware that changing an allowance with this method brings the risk
                         * that someone may use both the old and the new allowance by unfortunate
                         * transaction ordering. One possible solution to mitigate this race
                         * condition is to first reduce the spender's allowance to 0 and set the
                         * desired value afterwards:
                         * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                         *
                         * Emits an {Approval} event.
                         */
                        function approve(address spender, uint256 amount) external returns (bool);
                    
                        /**
                         * @dev Moves `amount` tokens from `sender` to `recipient` using the
                         * allowance mechanism. `amount` is then deducted from the caller's
                         * allowance.
                         *
                         * Returns a boolean value indicating whether the operation succeeded.
                         *
                         * Emits a {Transfer} event.
                         */
                        function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
                    
                        /**
                         * @dev Emitted when `value` tokens are moved from one account (`from`) to
                         * another (`to`).
                         *
                         * Note that `value` may be zero.
                         */
                        event Transfer(address indexed from, address indexed to, uint256 value);
                    
                        /**
                         * @dev Emitted when the allowance of a `spender` for an `owner` is set by
                         * a call to {approve}. `value` is the new allowance.
                         */
                        event Approval(address indexed owner, address indexed spender, uint256 value);
                    }
                    
                    
                    
                    
                                
                    pragma solidity 0.5.17;
                    pragma experimental ABIEncoderV2;
                    
                    
                    contract MPondLogic is Initializable {
                        /// @notice EIP-20 token name for this token
                        string public name;
                    
                        /// @notice EIP-20 token symbol for this token
                        string public symbol;
                    
                        /// @notice EIP-20 token decimals for this token
                        uint8 public decimals;
                    
                        /// @notice Total number of tokens in circulation
                        uint256 public totalSupply; // 10k MPond
                        uint256 public bridgeSupply; // 3k MPond
                    
                        address public dropBridge;
                        /// @notice Allowance amounts on behalf of others
                        mapping(address => mapping(address => uint96)) internal allowances;
                    
                        /// @notice Official record of token balances for each account
                        mapping(address => uint96) internal balances;
                    
                        /// @notice A record of each accounts delegate
                        mapping(address => mapping(address => uint96)) public delegates;
                    
                        /// @notice A checkpoint for marking number of votes from a given block
                        struct Checkpoint {
                            uint32 fromBlock;
                            uint96 votes;
                        }
                    
                        /// @notice A record of votes checkpoints for each account, by index
                        mapping(address => mapping(uint32 => Checkpoint)) public checkpoints;
                    
                        /// @notice The number of checkpoints for each account
                        mapping(address => uint32) public numCheckpoints;
                    
                        /// @notice The EIP-712 typehash for the contract's domain
                        bytes32 public DOMAIN_TYPEHASH;
                    
                        /// @notice The EIP-712 typehash for the delegation struct used by the contract
                        bytes32 public DELEGATION_TYPEHASH;
                    
                        /// @notice The EIP-712 typehash for the delegation struct used by the contract
                        bytes32 public UNDELEGATION_TYPEHASH;
                    
                        /// @notice A record of states for signing / validating signatures
                        mapping(address => uint256) public nonces;
                    
                        /// customized params
                        address public admin;
                        mapping(address => bool) public isWhiteListed;
                        bool public enableAllTranfers;
                    
                        /// @notice An event thats emitted when an account changes its delegate
                        event DelegateChanged(
                            address indexed delegator,
                            address indexed fromDelegate,
                            address indexed toDelegate
                        );
                    
                        /// @notice An event thats emitted when a delegate account's vote balance changes
                        event DelegateVotesChanged(
                            address indexed delegate,
                            uint256 previousBalance,
                            uint256 newBalance
                        );
                    
                        /// @notice The standard EIP-20 transfer event
                        event Transfer(address indexed from, address indexed to, uint256 amount);
                    
                        /// @notice The standard EIP-20 approval event
                        event Approval(
                            address indexed owner,
                            address indexed spender,
                            uint256 amount
                        );
                    
                        /**
                         * @notice Initializer a new MPond token
                         * @param account The initial account to grant all the tokens
                         */
                        function initialize(
                            address account,
                            address bridge,
                            address dropBridgeAddress
                        ) public initializer {
                            createConstants();
                            require(
                                account != bridge,
                                "Bridge and account should not be the same address"
                            );
                            balances[bridge] = uint96(bridgeSupply);
                            delegates[bridge][address(0)] = uint96(bridgeSupply);
                            isWhiteListed[bridge] = true;
                            emit Transfer(address(0), bridge, bridgeSupply);
                    
                            uint96 remainingSupply = sub96(
                                uint96(totalSupply),
                                uint96(bridgeSupply),
                                "MPond: Subtraction overflow in the constructor"
                            );
                            balances[account] = remainingSupply;
                            delegates[account][address(0)] = remainingSupply;
                            isWhiteListed[account] = true;
                            dropBridge = dropBridgeAddress;
                            emit Transfer(address(0), account, uint256(remainingSupply));
                        }
                    
                        function createConstants() internal {
                            name = "Marlin";
                            symbol = "MPond";
                            decimals = 18;
                            totalSupply = 10000e18;
                            bridgeSupply = 7000e18;
                            DOMAIN_TYPEHASH = keccak256(
                                "EIP712Domain(string name,uint256 chainId,address verifyingContract)"
                            );
                            DELEGATION_TYPEHASH = keccak256(
                                "Delegation(address delegatee,uint256 nonce,uint256 expiry,uint96 amount)"
                            );
                            UNDELEGATION_TYPEHASH = keccak256(
                                "Unelegation(address delegatee,uint256 nonce,uint256 expiry,uint96 amount)"
                            );
                            admin = msg.sender;
                            // enableAllTranfers = true; //This is only for testing, will be false
                        }
                    
                        function addWhiteListAddress(address _address)
                            external
                            onlyAdmin("Only admin can whitelist")
                            returns (bool)
                        {
                            isWhiteListed[_address] = true;
                            return true;
                        }
                    
                        function removeWhiteListAddress(address _address)
                            external
                            onlyAdmin("Only admin can remove from whitelist")
                            returns (bool)
                        {
                            isWhiteListed[_address] = false;
                            return true;
                        }
                    
                        function enableAllTransfers()
                            external
                            onlyAdmin("Only admin can enable all transfers")
                            returns (bool)
                        {
                            enableAllTranfers = true;
                            return true;
                        }
                    
                        function disableAllTransfers()
                            external
                            onlyAdmin("Only admin can disable all transfers")
                            returns (bool)
                        {
                            enableAllTranfers = false;
                            return true;
                        }
                    
                        function changeDropBridge(address _updatedBridge)
                            public
                            onlyAdmin("Only admin can change drop bridge")
                        {
                            dropBridge = _updatedBridge;
                        }
                    
                        function isWhiteListedTransfer(address _address1, address _address2)
                            public
                            view
                            returns (bool)
                        {
                            if (
                                enableAllTranfers ||
                                isWhiteListed[_address1] ||
                                isWhiteListed[_address2]
                            ) {
                                return true;
                            } else if (_address1 == dropBridge) {
                                return true;
                            }
                            return false;
                        }
                    
                        /**
                         * @notice Get the number of tokens `spender` is approved to spend on behalf of `account`
                         * @param account The address of the account holding the funds
                         * @param spender The address of the account spending the funds
                         * @return The number of tokens approved
                         */
                        function allowance(address account, address spender)
                            external
                            view
                            returns (uint256)
                        {
                            return allowances[account][spender];
                        }
                    
                        /**
                         * @notice Approve `spender` to transfer up to `amount` from `src`
                         * @dev This will overwrite the approval amount for `spender`
                         *  and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)
                         * @param spender The address of the account which may transfer tokens
                         * @param rawAmount The number of tokens that are approved (2^256-1 means infinite)
                         * @return Whether or not the approval succeeded
                         */
                        function approve(address spender, uint256 rawAmount)
                            external
                            returns (bool)
                        {
                            uint96 amount;
                            if (rawAmount == uint256(-1)) {
                                amount = uint96(-1);
                            } else {
                                amount = safe96(
                                    rawAmount,
                                    "MPond::approve: amount exceeds 96 bits"
                                );
                            }
                    
                            allowances[msg.sender][spender] = amount;
                    
                            emit Approval(msg.sender, spender, amount);
                            return true;
                        }
                    
                        function increaseAllowance(address spender, uint256 addedAmount)
                            external
                            returns (bool)
                        {
                            uint96 amount;
                            if (addedAmount == uint256(-1)) {
                                amount = uint96(-1);
                            } else {
                                amount = safe96(
                                    addedAmount,
                                    "MPond::approve: addedAmount exceeds 96 bits"
                                );
                            }
                    
                            allowances[msg.sender][spender] = add96(
                                allowances[msg.sender][spender],
                                amount,
                                "MPond: increaseAllowance allowance value overflows"
                            );
                            emit Approval(msg.sender, spender, allowances[msg.sender][spender]);
                            return true;
                        }
                    
                        function decreaseAllowance(address spender, uint256 removedAmount)
                            external
                            returns (bool)
                        {
                            uint96 amount;
                            if (removedAmount == uint256(-1)) {
                                amount = uint96(-1);
                            } else {
                                amount = safe96(
                                    removedAmount,
                                    "MPond::approve: removedAmount exceeds 96 bits"
                                );
                            }
                    
                            allowances[msg.sender][spender] = sub96(
                                allowances[msg.sender][spender],
                                amount,
                                "MPond: decreaseAllowance allowance value underflows"
                            );
                            emit Approval(msg.sender, spender, allowances[msg.sender][spender]);
                            return true;
                        }
                    
                        /**
                         * @notice Get the number of tokens held by the `account`
                         * @param account The address of the account to get the balance of
                         * @return The number of tokens held
                         */
                        function balanceOf(address account) external view returns (uint256) {
                            return balances[account];
                        }
                    
                        /**
                         * @notice Transfer `amount` tokens from `msg.sender` to `dst`
                         * @param dst The address of the destination account
                         * @param rawAmount The number of tokens to transfer
                         * @return Whether or not the transfer succeeded
                         */
                        function transfer(address dst, uint256 rawAmount) external returns (bool) {
                            require(
                                isWhiteListedTransfer(msg.sender, dst),
                                "Atleast one of the address (src or dst) should be whitelisted or all transfers must be enabled via enableAllTransfers()"
                            );
                            uint96 amount = safe96(
                                rawAmount,
                                "MPond::transfer: amount exceeds 96 bits"
                            );
                            _transferTokens(msg.sender, dst, amount);
                            return true;
                        }
                    
                        /**
                         * @notice Transfer `amount` tokens from `src` to `dst`
                         * @param src The address of the source account
                         * @param dst The address of the destination account
                         * @param rawAmount The number of tokens to transfer
                         * @return Whether or not the transfer succeeded
                         */
                        function transferFrom(
                            address src,
                            address dst,
                            uint256 rawAmount
                        ) external returns (bool) {
                            require(
                                isWhiteListedTransfer(src, dst),
                                "Atleast one of the address (src or dst) should be whitelisted or all transfers must be enabled via enableAllTransfers()"
                            );
                            address spender = msg.sender;
                            uint96 spenderAllowance = allowances[src][spender];
                            uint96 amount = safe96(
                                rawAmount,
                                "MPond::approve: amount exceeds 96 bits"
                            );
                    
                            if (spender != src && spenderAllowance != uint96(-1)) {
                                uint96 newAllowance = sub96(
                                    spenderAllowance,
                                    amount,
                                    "MPond::transferFrom: transfer amount exceeds spender allowance"
                                );
                                allowances[src][spender] = newAllowance;
                    
                                emit Approval(src, spender, newAllowance);
                            }
                    
                            _transferTokens(src, dst, amount);
                            return true;
                        }
                    
                        /**
                         * @notice Delegate votes from `msg.sender` to `delegatee`
                         * @param delegatee The address to delegate votes to
                         */
                        function delegate(address delegatee, uint96 amount) public {
                            return _delegate(msg.sender, delegatee, amount);
                        }
                    
                        function undelegate(address delegatee, uint96 amount) public {
                            return _undelegate(msg.sender, delegatee, amount);
                        }
                    
                        /**
                         * @notice Delegates votes from signatory to `delegatee`
                         * @param delegatee The address to delegate votes to
                         * @param nonce The contract state required to match the signature
                         * @param expiry The time at which to expire the signature
                         * @param v The recovery byte of the signature
                         * @param r Half of the ECDSA signature pair
                         * @param s Half of the ECDSA signature pair
                         */
                        function delegateBySig(
                            address delegatee,
                            uint256 nonce,
                            uint256 expiry,
                            uint8 v,
                            bytes32 r,
                            bytes32 s,
                            uint96 amount
                        ) public {
                            bytes32 domainSeparator = keccak256(
                                abi.encode(
                                    DOMAIN_TYPEHASH,
                                    keccak256(bytes(name)),
                                    getChainId(),
                                    address(this)
                                )
                            );
                            bytes32 structHash = keccak256(
                                abi.encode(DELEGATION_TYPEHASH, delegatee, nonce, expiry, amount)
                            );
                            bytes32 digest = keccak256(
                                abi.encodePacked("\x19\x01", domainSeparator, structHash)
                            );
                            address signatory = ecrecover(digest, v, r, s);
                            require(
                                signatory != address(0),
                                "MPond::delegateBySig: invalid signature"
                            );
                            require(
                                nonce == nonces[signatory]++,
                                "MPond::delegateBySig: invalid nonce"
                            );
                            require(now <= expiry, "MPond::delegateBySig: signature expired");
                            return _delegate(signatory, delegatee, amount);
                        }
                    
                        function undelegateBySig(
                            address delegatee,
                            uint256 nonce,
                            uint256 expiry,
                            uint8 v,
                            bytes32 r,
                            bytes32 s,
                            uint96 amount
                        ) public {
                            bytes32 domainSeparator = keccak256(
                                abi.encode(
                                    DOMAIN_TYPEHASH,
                                    keccak256(bytes(name)),
                                    getChainId(),
                                    address(this)
                                )
                            );
                            bytes32 structHash = keccak256(
                                abi.encode(UNDELEGATION_TYPEHASH, delegatee, nonce, expiry, amount)
                            );
                            bytes32 digest = keccak256(
                                abi.encodePacked("\x19\x01", domainSeparator, structHash)
                            );
                            address signatory = ecrecover(digest, v, r, s);
                            require(
                                signatory != address(0),
                                "MPond::undelegateBySig: invalid signature"
                            );
                            require(
                                nonce == nonces[signatory]++,
                                "MPond::undelegateBySig: invalid nonce"
                            );
                            require(now <= expiry, "MPond::undelegateBySig: signature expired");
                            return _undelegate(signatory, delegatee, amount);
                        }
                    
                        /**
                         * @notice Gets the current votes balance for `account`
                         * @param account The address to get votes balance
                         * @return The number of current votes for `account`
                         */
                        function getCurrentVotes(address account) external view returns (uint96) {
                            uint32 nCheckpoints = numCheckpoints[account];
                            return
                                nCheckpoints != 0
                                    ? checkpoints[account][nCheckpoints - 1].votes
                                    : 0;
                        }
                    
                        /**
                         * @notice Determine the prior number of votes for an account as of a block number
                         * @dev Block number must be a finalized block or else this function will revert to prevent misinformation.
                         * @param account The address of the account to check
                         * @param blockNumber The block number to get the vote balance at
                         * @return The number of votes the account had as of the given block
                         */
                        function getPriorVotes(address account, uint256 blockNumber)
                            public
                            view
                            returns (uint96)
                        {
                            require(
                                blockNumber < block.number,
                                "MPond::getPriorVotes: not yet determined"
                            );
                    
                            uint32 nCheckpoints = numCheckpoints[account];
                            if (nCheckpoints == 0) {
                                return 0;
                            }
                    
                            // First check most recent balance
                            if (checkpoints[account][nCheckpoints - 1].fromBlock <= blockNumber) {
                                return checkpoints[account][nCheckpoints - 1].votes;
                            }
                    
                            // Next check implicit zero balance
                            if (checkpoints[account][0].fromBlock > blockNumber) {
                                return 0;
                            }
                    
                            uint32 lower = 0;
                            uint32 upper = nCheckpoints - 1;
                            while (upper > lower) {
                                uint32 center = upper - (upper - lower) / 2; // ceil, avoiding overflow
                                Checkpoint memory cp = checkpoints[account][center];
                                if (cp.fromBlock == blockNumber) {
                                    return cp.votes;
                                } else if (cp.fromBlock < blockNumber) {
                                    lower = center;
                                } else {
                                    upper = center - 1;
                                }
                            }
                            return checkpoints[account][lower].votes;
                        }
                    
                        function _delegate(
                            address delegator,
                            address delegatee,
                            uint96 amount
                        ) internal {
                            delegates[delegator][address(0)] = sub96(
                                delegates[delegator][address(0)],
                                amount,
                                "MPond: delegates underflow"
                            );
                            delegates[delegator][delegatee] = add96(
                                delegates[delegator][delegatee],
                                amount,
                                "MPond: delegates overflow"
                            );
                    
                            emit DelegateChanged(delegator, address(0), delegatee);
                    
                            _moveDelegates(address(0), delegatee, amount);
                        }
                    
                        function _undelegate(
                            address delegator,
                            address delegatee,
                            uint96 amount
                        ) internal {
                            delegates[delegator][delegatee] = sub96(
                                delegates[delegator][delegatee],
                                amount,
                                "MPond: undelegates underflow"
                            );
                            delegates[delegator][address(0)] = add96(
                                delegates[delegator][address(0)],
                                amount,
                                "MPond: delegates underflow"
                            );
                            emit DelegateChanged(delegator, delegatee, address(0));
                            _moveDelegates(delegatee, address(0), amount);
                        }
                    
                        function _transferTokens(
                            address src,
                            address dst,
                            uint96 amount
                        ) internal {
                            require(
                                src != address(0),
                                "MPond::_transferTokens: cannot transfer from the zero address"
                            );
                            require(
                                delegates[src][address(0)] >= amount,
                                "MPond: _transferTokens: undelegated amount should be greater than transfer amount"
                            );
                            require(
                                dst != address(0),
                                "MPond::_transferTokens: cannot transfer to the zero address"
                            );
                    
                            balances[src] = sub96(
                                balances[src],
                                amount,
                                "MPond::_transferTokens: transfer amount exceeds balance"
                            );
                            delegates[src][address(0)] = sub96(
                                delegates[src][address(0)],
                                amount,
                                "MPond: _tranferTokens: undelegate subtraction error"
                            );
                    
                            balances[dst] = add96(
                                balances[dst],
                                amount,
                                "MPond::_transferTokens: transfer amount overflows"
                            );
                            delegates[dst][address(0)] = add96(
                                delegates[dst][address(0)],
                                amount,
                                "MPond: _transferTokens: undelegate addition error"
                            );
                            emit Transfer(src, dst, amount);
                    
                            // _moveDelegates(delegates[src], delegates[dst], amount);
                        }
                    
                        function _moveDelegates(
                            address srcRep,
                            address dstRep,
                            uint96 amount
                        ) internal {
                            if (srcRep != dstRep && amount != 0) {
                                if (srcRep != address(0)) {
                                    uint32 srcRepNum = numCheckpoints[srcRep];
                                    uint96 srcRepOld = srcRepNum != 0
                                        ? checkpoints[srcRep][srcRepNum - 1].votes
                                        : 0;
                                    uint96 srcRepNew = sub96(
                                        srcRepOld,
                                        amount,
                                        "MPond::_moveVotes: vote amount underflows"
                                    );
                                    _writeCheckpoint(srcRep, srcRepNum, srcRepOld, srcRepNew);
                                }
                    
                                if (dstRep != address(0)) {
                                    uint32 dstRepNum = numCheckpoints[dstRep];
                                    uint96 dstRepOld = dstRepNum != 0
                                        ? checkpoints[dstRep][dstRepNum - 1].votes
                                        : 0;
                                    uint96 dstRepNew = add96(
                                        dstRepOld,
                                        amount,
                                        "MPond::_moveVotes: vote amount overflows"
                                    );
                                    _writeCheckpoint(dstRep, dstRepNum, dstRepOld, dstRepNew);
                                }
                            }
                        }
                    
                        function _writeCheckpoint(
                            address delegatee,
                            uint32 nCheckpoints,
                            uint96 oldVotes,
                            uint96 newVotes
                        ) internal {
                            uint32 blockNumber = safe32(
                                block.number,
                                "MPond::_writeCheckpoint: block number exceeds 32 bits"
                            );
                    
                            if (
                                nCheckpoints != 0 &&
                                checkpoints[delegatee][nCheckpoints - 1].fromBlock == blockNumber
                            ) {
                                checkpoints[delegatee][nCheckpoints - 1].votes = newVotes;
                            } else {
                                checkpoints[delegatee][nCheckpoints] = Checkpoint(
                                    blockNumber,
                                    newVotes
                                );
                                numCheckpoints[delegatee] = nCheckpoints + 1;
                            }
                    
                            emit DelegateVotesChanged(delegatee, oldVotes, newVotes);
                        }
                    
                        function safe32(uint256 n, string memory errorMessage)
                            internal
                            pure
                            returns (uint32)
                        {
                            require(n < 2**32, errorMessage);
                            return uint32(n);
                        }
                    
                        function safe96(uint256 n, string memory errorMessage)
                            internal
                            pure
                            returns (uint96)
                        {
                            require(n < 2**96, errorMessage);
                            return uint96(n);
                        }
                    
                        function add96(
                            uint96 a,
                            uint96 b,
                            string memory errorMessage
                        ) internal pure returns (uint96) {
                            uint96 c = a + b;
                            require(c >= a, errorMessage);
                            return c;
                        }
                    
                        function sub96(
                            uint96 a,
                            uint96 b,
                            string memory errorMessage
                        ) internal pure returns (uint96) {
                            require(b <= a, errorMessage);
                            return a - b;
                        }
                    
                        function getChainId() internal pure returns (uint256) {
                            uint256 chainId;
                            assembly {
                                chainId := chainid()
                            }
                            return chainId;
                        }
                    
                        modifier onlyAdmin(string memory _error) {
                            require(msg.sender == admin, _error);
                            _;
                        }
                    }
                    
                    
                    
                    
                                
                    pragma solidity ^0.5.17;
                    
                    interface IRewardDelegators {
                        // there's no undelegationWaitTime in rewardDelegators contract
                        function undelegationWaitTime() external returns(uint256);
                        function minMPONDStake() external returns(uint256);
                        function MPONDTokenId() external returns(bytes32);
                        function updateMPONDTokenId(bytes32 _updatedMPONDTokenId) external;
                        function addRewardFactor(bytes32 _tokenId, uint256 _rewardFactor) external;
                        function removeRewardFactor(bytes32 _tokenId) external;
                        function updateRewardFactor(bytes32 _tokenId, uint256 _updatedRewardFactor) external;
                        function _updateRewards(address _cluster) external;
                        function delegate(
                            address _delegator,
                            address _cluster,
                            bytes32[] calldata _tokens,
                            uint256[] calldata _amounts
                        ) external;
                        function undelegate(
                            address _delegator,
                            address _cluster,
                            bytes32[] calldata _tokens,
                            uint256[] calldata _amounts
                        ) external;
                        function withdrawRewards(address _delegator, address _cluster) external returns(uint256);
                        function isClusterActive(address _cluster) external returns(bool);
                        function getClusterDelegation(address _cluster, bytes32 _tokenId) external view returns(uint256);
                        function getDelegation(address _cluster, address _delegator, bytes32 _tokenId) external view returns(uint256);
                        function updateUndelegationWaitTime(uint256 _undelegationWaitTime) external;
                        function updateMinMPONDStake(uint256 _minMPONDStake) external;
                        function updateStakeAddress(address _updatedStakeAddress) external;
                        function updateClusterRewards(address _updatedClusterRewards) external;
                        function updateClusterRegistry(address _updatedClusterRegistry) external;
                        function updatePONDAddress(address _updatedPOND) external;
                        function getFullTokenList() external view returns (bytes32[] memory);
                        function getAccRewardPerShare(address _cluster, bytes32 _tokenId) external view returns(uint256);
                    }
                    
                    
                    
                                
                    pragma solidity ^0.5.0;
                    
                    /**
                     * @dev Contract module which provides a basic access control mechanism, where
                     * there is an account (an owner) that can be granted exclusive access to
                     * specific functions.
                     *
                     * This module is used through inheritance. It will make available the modifier
                     * `onlyOwner`, which can be aplied to your functions to restrict their use to
                     * the owner.
                     */
                    contract Ownable is Initializable, Context {
                        address private _owner;
                    
                        event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                    
                        /**
                         * @dev Initializes the contract setting the deployer as the initial owner.
                         */
                        function initialize(address sender) public initializer {
                            _owner = sender;
                            emit OwnershipTransferred(address(0), _owner);
                        }
                    
                        /**
                         * @dev Returns the address of the current owner.
                         */
                        function owner() public view returns (address) {
                            return _owner;
                        }
                    
                        /**
                         * @dev Throws if called by any account other than the owner.
                         */
                        modifier onlyOwner() {
                            require(isOwner(), "Ownable: caller is not the owner");
                            _;
                        }
                    
                        /**
                         * @dev Returns true if the caller is the current owner.
                         */
                        function isOwner() public view returns (bool) {
                            return _msgSender() == _owner;
                        }
                    
                        /**
                         * @dev Leaves the contract without owner. It will not be possible to call
                         * `onlyOwner` functions anymore. Can only be called by the current owner.
                         *
                         * > Note: Renouncing ownership will leave the contract without an owner,
                         * thereby removing any functionality that is only available to the owner.
                         */
                        function renounceOwnership() public onlyOwner {
                            emit OwnershipTransferred(_owner, address(0));
                            _owner = address(0);
                        }
                    
                        /**
                         * @dev Transfers ownership of the contract to a new account (`newOwner`).
                         * Can only be called by the current owner.
                         */
                        function transferOwnership(address newOwner) public onlyOwner {
                            _transferOwnership(newOwner);
                        }
                    
                        /**
                         * @dev Transfers ownership of the contract to a new account (`newOwner`).
                         */
                        function _transferOwnership(address newOwner) internal {
                            require(newOwner != address(0), "Ownable: new owner is the zero address");
                            emit OwnershipTransferred(_owner, newOwner);
                            _owner = newOwner;
                        }
                    
                        uint256[50] private ______gap;
                    }
                    
                    
                    
                    
                                
                    pragma solidity ^0.5.0;
                    
                    /**
                     * @dev Implementation of the {IERC20} interface.
                     *
                     * This implementation is agnostic to the way tokens are created. This means
                     * that a supply mechanism has to be added in a derived contract using {_mint}.
                     * For a generic mechanism see {ERC20Mintable}.
                     *
                     * TIP: For a detailed writeup see our guide
                     * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
                     * to implement supply mechanisms].
                     *
                     * We have followed general OpenZeppelin guidelines: functions revert instead
                     * of returning `false` on failure. This behavior is nonetheless conventional
                     * and does not conflict with the expectations of ERC20 applications.
                     *
                     * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
                     * This allows applications to reconstruct the allowance for all accounts just
                     * by listening to said events. Other implementations of the EIP may not emit
                     * these events, as it isn't required by the specification.
                     *
                     * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
                     * functions have been added to mitigate the well-known issues around setting
                     * allowances. See {IERC20-approve}.
                     */
                    contract ERC20 is Initializable, Context, IERC20 {
                        using SafeMath for uint256;
                    
                        mapping (address => uint256) private _balances;
                    
                        mapping (address => mapping (address => uint256)) private _allowances;
                    
                        uint256 private _totalSupply;
                    
                        /**
                         * @dev See {IERC20-totalSupply}.
                         */
                        function totalSupply() public view returns (uint256) {
                            return _totalSupply;
                        }
                    
                        /**
                         * @dev See {IERC20-balanceOf}.
                         */
                        function balanceOf(address account) public view returns (uint256) {
                            return _balances[account];
                        }
                    
                        /**
                         * @dev See {IERC20-transfer}.
                         *
                         * Requirements:
                         *
                         * - `recipient` cannot be the zero address.
                         * - the caller must have a balance of at least `amount`.
                         */
                        function transfer(address recipient, uint256 amount) public returns (bool) {
                            _transfer(_msgSender(), recipient, amount);
                            return true;
                        }
                    
                        /**
                         * @dev See {IERC20-allowance}.
                         */
                        function allowance(address owner, address spender) public view returns (uint256) {
                            return _allowances[owner][spender];
                        }
                    
                        /**
                         * @dev See {IERC20-approve}.
                         *
                         * Requirements:
                         *
                         * - `spender` cannot be the zero address.
                         */
                        function approve(address spender, uint256 amount) public returns (bool) {
                            _approve(_msgSender(), spender, amount);
                            return true;
                        }
                    
                        /**
                         * @dev See {IERC20-transferFrom}.
                         *
                         * Emits an {Approval} event indicating the updated allowance. This is not
                         * required by the EIP. See the note at the beginning of {ERC20};
                         *
                         * Requirements:
                         * - `sender` and `recipient` cannot be the zero address.
                         * - `sender` must have a balance of at least `amount`.
                         * - the caller must have allowance for `sender`'s tokens of at least
                         * `amount`.
                         */
                        function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) {
                            _transfer(sender, recipient, amount);
                            _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
                            return true;
                        }
                    
                        /**
                         * @dev Atomically increases the allowance granted to `spender` by the caller.
                         *
                         * This is an alternative to {approve} that can be used as a mitigation for
                         * problems described in {IERC20-approve}.
                         *
                         * Emits an {Approval} event indicating the updated allowance.
                         *
                         * Requirements:
                         *
                         * - `spender` cannot be the zero address.
                         */
                        function increaseAllowance(address spender, uint256 addedValue) public returns (bool) {
                            _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
                            return true;
                        }
                    
                        /**
                         * @dev Atomically decreases the allowance granted to `spender` by the caller.
                         *
                         * This is an alternative to {approve} that can be used as a mitigation for
                         * problems described in {IERC20-approve}.
                         *
                         * Emits an {Approval} event indicating the updated allowance.
                         *
                         * Requirements:
                         *
                         * - `spender` cannot be the zero address.
                         * - `spender` must have allowance for the caller of at least
                         * `subtractedValue`.
                         */
                        function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) {
                            _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
                            return true;
                        }
                    
                        /**
                         * @dev Moves tokens `amount` from `sender` to `recipient`.
                         *
                         * This is internal function is equivalent to {transfer}, and can be used to
                         * e.g. implement automatic token fees, slashing mechanisms, etc.
                         *
                         * Emits a {Transfer} event.
                         *
                         * Requirements:
                         *
                         * - `sender` cannot be the zero address.
                         * - `recipient` cannot be the zero address.
                         * - `sender` must have a balance of at least `amount`.
                         */
                        function _transfer(address sender, address recipient, uint256 amount) internal {
                            require(sender != address(0), "ERC20: transfer from the zero address");
                            require(recipient != address(0), "ERC20: transfer to the zero address");
                    
                            _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
                            _balances[recipient] = _balances[recipient].add(amount);
                            emit Transfer(sender, recipient, amount);
                        }
                    
                        /** @dev Creates `amount` tokens and assigns them to `account`, increasing
                         * the total supply.
                         *
                         * Emits a {Transfer} event with `from` set to the zero address.
                         *
                         * Requirements
                         *
                         * - `to` cannot be the zero address.
                         */
                        function _mint(address account, uint256 amount) internal {
                            require(account != address(0), "ERC20: mint to the zero address");
                    
                            _totalSupply = _totalSupply.add(amount);
                            _balances[account] = _balances[account].add(amount);
                            emit Transfer(address(0), account, amount);
                        }
                    
                        /**
                         * @dev Destroys `amount` tokens from `account`, reducing the
                         * total supply.
                         *
                         * Emits a {Transfer} event with `to` set to the zero address.
                         *
                         * Requirements
                         *
                         * - `account` cannot be the zero address.
                         * - `account` must have at least `amount` tokens.
                         */
                        function _burn(address account, uint256 amount) internal {
                            require(account != address(0), "ERC20: burn from the zero address");
                    
                            _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
                            _totalSupply = _totalSupply.sub(amount);
                            emit Transfer(account, address(0), amount);
                        }
                    
                        /**
                         * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens.
                         *
                         * This is internal function is equivalent to `approve`, and can be used to
                         * e.g. set automatic allowances for certain subsystems, etc.
                         *
                         * Emits an {Approval} event.
                         *
                         * Requirements:
                         *
                         * - `owner` cannot be the zero address.
                         * - `spender` cannot be the zero address.
                         */
                        function _approve(address owner, address spender, uint256 amount) internal {
                            require(owner != address(0), "ERC20: approve from the zero address");
                            require(spender != address(0), "ERC20: approve to the zero address");
                    
                            _allowances[owner][spender] = amount;
                            emit Approval(owner, spender, amount);
                        }
                    
                        /**
                         * @dev Destroys `amount` tokens from `account`.`amount` is then deducted
                         * from the caller's allowance.
                         *
                         * See {_burn} and {_approve}.
                         */
                        function _burnFrom(address account, uint256 amount) internal {
                            _burn(account, amount);
                            _approve(account, _msgSender(), _allowances[account][_msgSender()].sub(amount, "ERC20: burn amount exceeds allowance"));
                        }
                    
                        uint256[50] private ______gap;
                    }
                    
                    
                    pragma solidity >=0.4.21 <0.7.0;
                    
                    
                    
                    contract StakeManager is Initializable, Ownable {
                    
                        using SafeMath for uint256;
                    
                        struct Stash {
                            address staker;
                            address delegatedCluster;
                            mapping(bytes32 => uint256) amount;   // name is not intuitive
                            uint256 undelegatesAt;
                        }
                    
                        struct Token {
                            address addr;
                            bool isActive;
                        }
                        // stashId to stash
                        // stashId = keccak256(index)
                        mapping(bytes32 => Stash) public stashes;
                        // Stash index for unique id generation
                        uint256 public stashIndex;
                        // tokenId to token address - tokenId = keccak256(tokenTicker)
                        mapping(bytes32 => Token) tokenAddresses;
                        MPondLogic MPOND;
                        MPondLogic prevMPOND;
                        address _unused_1;
                        IRewardDelegators public rewardDelegators;
                        // new variables
                        struct Lock {
                            uint256 unlockBlock;
                            uint256 iValue;
                        }
                    
                        mapping(bytes32 => Lock) public locks;
                        mapping(bytes32 => uint256) public lockWaitTime;
                        bytes32 constant REDELEGATION_LOCK_SELECTOR = keccak256("REDELEGATION_LOCK");
                        uint256 public undelegationWaitTime;
                    
                        event StashCreated(
                            address indexed creator,
                            bytes32 stashId,
                            uint256 stashIndex,
                            bytes32[] tokens,
                            uint256[] amounts
                        );
                        event StashDelegated(bytes32 stashId, address delegatedCluster);
                        event StashUndelegated(bytes32 stashId, address undelegatedCluster, uint256 undelegatesAt);
                        event StashWithdrawn(bytes32 stashId, bytes32[] tokens, uint256[] amounts);
                        event StashClosed(bytes32 stashId, address indexed staker);
                        event AddedToStash(bytes32 stashId, address delegatedCluster, bytes32[] tokens, uint256[] amounts);
                        event TokenAdded(bytes32 tokenId, address tokenAddress);
                        event TokenRemoved(bytes32 tokenId);
                        event TokenUpdated(bytes32 tokenId, address tokenAddress);
                        event RedelegationRequested(bytes32 stashId, address currentCluster, address updatedCluster, uint256 redelegatesAt);
                        event Redelegated(bytes32 stashId, address updatedCluster);
                        event LockTimeUpdated(bytes32 selector, uint256 prevLockTime, uint256 updatedLockTime);
                        event StashSplit(
                            bytes32 _newStashId,
                            bytes32 _stashId,
                            uint256 _stashIndex,
                            bytes32[] _splitTokens,
                            uint256[] _splitAmounts
                        );
                        event StashesMerged(bytes32 _stashId1, bytes32 _stashId2);
                        event StashUndelegationCancelled(bytes32 _stashId);
                        event UndelegationWaitTimeUpdated(uint256 undelegationWaitTime);
                        event RedelegationCancelled(bytes32 indexed _stashId);
                    
                        function initialize(
                            bytes32[] memory _tokenIds,
                            address[] memory _tokenAddresses,
                            address _MPONDTokenAddress,
                            address _rewardDelegatorsAddress,
                            address _owner,
                            uint256 _undelegationWaitTime)
                            initializer
                            public
                        {
                            require(
                                _tokenIds.length == _tokenAddresses.length
                            );
                            for(uint256 i=0; i < _tokenIds.length; i++) {
                                tokenAddresses[_tokenIds[i]] = Token(_tokenAddresses[i], true);
                                emit TokenAdded(_tokenIds[i], _tokenAddresses[i]);
                            }
                            MPOND = MPondLogic(_MPONDTokenAddress);
                            rewardDelegators = IRewardDelegators(_rewardDelegatorsAddress);
                            undelegationWaitTime = _undelegationWaitTime;
                            super.initialize(_owner);
                        }
                    
                        function updateLockWaitTime(bytes32 _selector, uint256 _updatedWaitTime) external onlyOwner {
                            emit LockTimeUpdated(_selector, lockWaitTime[_selector], _updatedWaitTime);
                            lockWaitTime[_selector] = _updatedWaitTime;
                        }
                    
                        function changeMPONDTokenAddress(
                            address _MPONDTokenAddress
                        ) external onlyOwner {
                            prevMPOND = MPOND;
                            MPOND = MPondLogic(_MPONDTokenAddress);
                            emit TokenUpdated(keccak256("MPOND"), _MPONDTokenAddress);
                        }
                    
                        function updateRewardDelegators(
                            address _updatedRewardDelegator
                        ) external onlyOwner {
                            require(
                                _updatedRewardDelegator != address(0)
                            );
                            rewardDelegators = IRewardDelegators(_updatedRewardDelegator);
                        }
                    
                        function updateUndelegationWaitTime(
                            uint256 _undelegationWaitTime
                        ) external onlyOwner {
                            undelegationWaitTime = _undelegationWaitTime;
                            emit UndelegationWaitTimeUpdated(_undelegationWaitTime);
                        }
                    
                        function enableToken(
                            bytes32 _tokenId,
                            address _address
                        ) external onlyOwner {
                            require(
                                !tokenAddresses[_tokenId].isActive
                            );
                            require(_address != address(0));
                            tokenAddresses[_tokenId] = Token(_address, true);
                            emit TokenAdded(_tokenId, _address);
                        }
                    
                        function disableToken(
                            bytes32 _tokenId
                        ) external onlyOwner {
                            require(
                                tokenAddresses[_tokenId].isActive
                            );
                            tokenAddresses[_tokenId].isActive = false;
                            emit TokenRemoved(_tokenId);
                        }
                    
                        function createStashAndDelegate(
                            bytes32[] memory _tokens,
                            uint256[] memory _amounts,
                            address _delegatedCluster
                        ) public {
                            bytes32 stashId = createStash(_tokens, _amounts);
                            delegateStash(stashId, _delegatedCluster);
                        }
                    
                        function createStash(
                            bytes32[] memory _tokens,
                            uint256[] memory _amounts
                        ) public returns(bytes32) {
                            require(
                                _tokens.length == _amounts.length,
                                "CS1"
                            );
                            require(
                                _tokens.length != 0,
                                "CS2"
                            );
                            uint256 _stashIndex = stashIndex;
                            bytes32 _stashId = keccak256(abi.encodePacked(_stashIndex));
                            for(uint256 _index=0; _index < _tokens.length; _index++) {
                                bytes32 _tokenId = _tokens[_index];
                                uint256 _amount = _amounts[_index];
                                require(
                                    tokenAddresses[_tokenId].isActive,
                                    "CS3"
                                );
                                require(
                                    stashes[_stashId].amount[_tokenId] == 0,
                                    "CS4"
                                );
                                require(
                                    _amount != 0,
                                    "CS5"
                                );
                                stashes[_stashId].amount[_tokenId] = _amount;
                                _lockTokens(_tokenId, _amount, msg.sender);
                            }
                            stashes[_stashId].staker = msg.sender;
                            emit StashCreated(msg.sender, _stashId, _stashIndex, _tokens, _amounts);
                            stashIndex = _stashIndex + 1;  // Can't overflow
                            return _stashId;
                        }
                    
                        function addToStash(
                            bytes32 _stashId,
                            bytes32[] calldata _tokens,
                            uint256[] calldata _amounts
                        ) external {
                            Stash memory _stash = stashes[_stashId];
                            require(
                                _stash.staker == msg.sender,
                                "AS1"
                            );
                            require(
                                _stash.undelegatesAt <= block.number,
                                "AS2"
                            );
                            require(
                                _tokens.length == _amounts.length,
                                "AS3"
                            );
                            if(
                                _stash.delegatedCluster != address(0)
                            ) {
                                rewardDelegators.delegate(msg.sender, _stash.delegatedCluster, _tokens, _amounts);
                            }
                            for(uint256 i = 0; i < _tokens.length; i++) {
                                bytes32 _tokenId = _tokens[i];
                                require(
                                    tokenAddresses[_tokenId].isActive,
                                    "AS4"
                                );
                                if(_amounts[i] != 0) {
                                    stashes[_stashId].amount[_tokenId] = stashes[_stashId].amount[_tokenId].add(_amounts[i]);
                                    _lockTokens(_tokenId, _amounts[i], msg.sender);
                                }
                            }
                            
                            emit AddedToStash(_stashId, _stash.delegatedCluster, _tokens, _amounts);
                        }
                    
                        function delegateStash(bytes32 _stashId, address _delegatedCluster) public {
                            Stash memory _stash = stashes[_stashId];
                            require(
                                _stash.staker == msg.sender,
                                "DS1"
                            );
                            require(
                                _delegatedCluster != address(0),
                                "DS2"
                            );
                            require(
                                _stash.delegatedCluster == address(0),
                                "DS3"
                            );
                            require(
                                _stash.undelegatesAt <= block.number,
                                "DS4"
                            );
                            stashes[_stashId].delegatedCluster = _delegatedCluster;
                            delete stashes[_stashId].undelegatesAt;
                            bytes32[] memory _tokens = rewardDelegators.getFullTokenList();
                            uint256[] memory _amounts = new uint256[](_tokens.length);
                            for(uint256 i = 0; i < _tokens.length; i++) {
                                _amounts[i] = stashes[_stashId].amount[_tokens[i]];
                            }
                            rewardDelegators.delegate(msg.sender, _delegatedCluster, _tokens, _amounts);
                            emit StashDelegated(_stashId, _delegatedCluster);
                        }
                    
                        function requestStashRedelegation(bytes32 _stashId, address _newCluster) public {
                            Stash memory _stash = stashes[_stashId];
                            require(
                                _stash.staker == msg.sender,
                                "RSR1"
                            );
                            require(
                                _stash.delegatedCluster != address(0),
                                "RSR2"
                            );
                            require(
                                _newCluster != address(0),
                                "RSR3"
                            );
                            uint256 _redelegationBlock = _requestStashRedelegation(_stashId, _newCluster);
                            emit RedelegationRequested(_stashId, _stash.delegatedCluster, _newCluster, _redelegationBlock);
                        }
                    
                        function _requestStashRedelegation(bytes32 _stashId, address _newCluster) internal returns(uint256) {
                            bytes32 _lockId = keccak256(abi.encodePacked(REDELEGATION_LOCK_SELECTOR, _stashId));
                            uint256 _unlockBlock = locks[_lockId].unlockBlock;
                            require(
                                _unlockBlock == 0,
                                "IRSR1"
                            );
                            uint256 _redelegationBlock = block.number.add(lockWaitTime[REDELEGATION_LOCK_SELECTOR]);
                            locks[_lockId] = Lock(_redelegationBlock, uint256(_newCluster));
                            return _redelegationBlock;
                        }
                    
                        function requestStashRedelegations(bytes32[] memory _stashIds, address[] memory _newClusters) public {
                            require(_stashIds.length == _newClusters.length, "SM:RSRs - Invalid input data");
                            for(uint256 i=0; i < _stashIds.length; i++) {
                                requestStashRedelegation(_stashIds[i], _newClusters[i]);
                            }
                        }
                    
                        function redelegateStash(bytes32 _stashId) public {
                            Stash memory _stash = stashes[_stashId];
                            require(
                                _stash.delegatedCluster != address(0),
                                "RS1"
                            );
                            bytes32 _lockId = keccak256(abi.encodePacked(REDELEGATION_LOCK_SELECTOR, _stashId));
                            uint256 _unlockBlock = locks[_lockId].unlockBlock;
                            require(
                                _unlockBlock != 0 && _unlockBlock <= block.number,
                                "RS2"
                            );
                            address _updatedCluster = address(locks[_lockId].iValue);
                            _redelegateStash(_stashId, _stash.staker, _stash.delegatedCluster, _updatedCluster);
                            delete locks[_lockId];
                        }
                    
                        function _redelegateStash(
                            bytes32 _stashId,
                            address _staker,
                            address _delegatedCluster,
                            address _updatedCluster
                        ) internal {
                            bytes32[] memory _tokens = rewardDelegators.getFullTokenList();
                            uint256[] memory _amounts = new uint256[](_tokens.length);
                            for(uint256 i=0; i < _tokens.length; i++) {
                                _amounts[i] = stashes[_stashId].amount[_tokens[i]];
                            }
                            if(_delegatedCluster != address(0)) {
                                rewardDelegators.undelegate(_staker, _delegatedCluster, _tokens, _amounts);
                            }
                            rewardDelegators.delegate(_staker, _updatedCluster, _tokens, _amounts);
                            stashes[_stashId].delegatedCluster = _updatedCluster;
                            emit Redelegated(_stashId, _updatedCluster);
                        }
                    
                        function splitStash(bytes32 _stashId, bytes32[] calldata _tokens, uint256[] calldata _amounts) external {
                            Stash memory _stash = stashes[_stashId];
                            require(
                                _stash.staker == msg.sender,
                                "SS1"
                            );
                            require(
                                _tokens.length != 0,
                                "SS2"
                            );
                            require(
                                _tokens.length == _amounts.length,
                                "SS3"
                            );
                            uint256 _stashIndex = stashIndex;
                            bytes32 _newStashId = keccak256(abi.encodePacked(_stashIndex));
                            for(uint256 _index=0; _index < _tokens.length; _index++) {
                                bytes32 _tokenId = _tokens[_index];
                                uint256 _amount = _amounts[_index];
                                require(
                                    stashes[_newStashId].amount[_tokenId] == 0,
                                    "SS4"
                                );
                                require(
                                    _amount != 0,
                                    "SS5"
                                );
                                stashes[_stashId].amount[_tokenId] = stashes[_stashId].amount[_tokenId].sub(
                                    _amount,
                                    "SS6"
                                );
                                stashes[_newStashId].amount[_tokenId] = _amount;
                            }
                            stashes[_newStashId].staker = msg.sender;
                            stashes[_newStashId].delegatedCluster = _stash.delegatedCluster;
                            stashes[_newStashId].undelegatesAt = _stash.undelegatesAt;
                            emit StashSplit(_newStashId, _stashId, _stashIndex, _tokens, _amounts);
                            stashIndex = _stashIndex + 1;
                        }
                    
                        function mergeStash(bytes32 _stashId1, bytes32 _stashId2) external {
                            require(_stashId1 != _stashId2, "MS1");
                            Stash memory _stash1 = stashes[_stashId1];
                            Stash memory _stash2 = stashes[_stashId2];
                            require(
                                _stash1.staker == msg.sender && _stash2.staker == msg.sender,
                                "MS2"
                            );
                            require(
                                _stash1.delegatedCluster == _stash2.delegatedCluster,
                                "MS3"
                            );
                            require(
                                (_stash1.undelegatesAt <= block.number) &&
                                (_stash2.undelegatesAt <= block.number),
                                "MS4"
                            );
                            bytes32 _lockId1 = keccak256(abi.encodePacked(REDELEGATION_LOCK_SELECTOR, _stashId1));
                            uint256 _unlockBlock1 = locks[_lockId1].unlockBlock;
                            bytes32 _lockId2 = keccak256(abi.encodePacked(REDELEGATION_LOCK_SELECTOR, _stashId2));
                            uint256 _unlockBlock2 = locks[_lockId2].unlockBlock;
                            require(
                                _unlockBlock1 == 0 && _unlockBlock2 == 0,
                                "MS5"
                            );
                            bytes32[] memory _tokens = rewardDelegators.getFullTokenList();
                            for(uint256 i=0; i < _tokens.length; i++) {
                                uint256 _amount = stashes[_stashId2].amount[_tokens[i]];
                                if(_amount == 0) {
                                    continue;
                                }
                                delete stashes[_stashId2].amount[_tokens[i]];
                                stashes[_stashId1].amount[_tokens[i]] = stashes[_stashId1].amount[_tokens[i]].add(_amount);
                            }
                            delete stashes[_stashId2];
                            emit StashesMerged(_stashId1, _stashId2);
                        }
                    
                        function redelegateStashes(bytes32[] memory _stashIds) public {
                            for(uint256 i=0; i < _stashIds.length; i++) {
                                redelegateStash(_stashIds[i]);
                            }
                        }
                        
                        function cancelRedelegation(bytes32 _stashId) public {
                            require(
                                msg.sender == stashes[_stashId].staker,
                                "CR1"
                            );
                            require(_cancelRedelegation(_stashId), "CR2");
                        }
                    
                        function _cancelRedelegation(bytes32 _stashId) internal returns(bool) {
                            bytes32 _lockId = keccak256(abi.encodePacked(REDELEGATION_LOCK_SELECTOR, _stashId));
                            if(locks[_lockId].unlockBlock != 0) {
                                delete locks[_lockId];
                                emit RedelegationCancelled(_stashId);
                                return true;
                            }
                            return false;
                        }
                    
                        function undelegateStash(bytes32 _stashId) public {
                            Stash memory _stash = stashes[_stashId];
                            require(
                                _stash.staker == msg.sender,
                                "US1"
                            );
                            require(
                                _stash.delegatedCluster != address(0),
                                "US2"
                            );
                            uint256 _waitTime = undelegationWaitTime;
                            uint256 _undelegationBlock = block.number.add(_waitTime);
                            stashes[_stashId].undelegatesAt = _undelegationBlock;
                            delete stashes[_stashId].delegatedCluster;
                            _cancelRedelegation(_stashId);
                            bytes32[] memory _tokens = rewardDelegators.getFullTokenList();
                            uint256[] memory _amounts = new uint256[](_tokens.length);
                            for(uint256 i=0; i < _tokens.length; i++) {
                                _amounts[i] = stashes[_stashId].amount[_tokens[i]];
                            }
                            rewardDelegators.undelegate(msg.sender, _stash.delegatedCluster, _tokens, _amounts);
                            emit StashUndelegated(_stashId, _stash.delegatedCluster, _undelegationBlock);
                        }
                    
                        function undelegateStashes(bytes32[] memory _stashIds) public {
                            for(uint256 i=0; i < _stashIds.length; i++) {
                                undelegateStash(_stashIds[i]);
                            }
                        }
                        
                        function cancelUndelegation(bytes32 _stashId, address _delegatedCluster) public {
                            address _staker = stashes[_stashId].staker;
                            uint256 _undelegatesAt = stashes[_stashId].undelegatesAt;
                            require(
                                _staker == msg.sender,
                                "CU1"
                            );
                            require(
                                _undelegatesAt > block.number,
                                "CU2"
                            );
                            require(
                                _undelegatesAt < block.number
                                                .add(undelegationWaitTime)
                                                .sub(lockWaitTime[REDELEGATION_LOCK_SELECTOR]),
                                "CU3"
                            );
                            delete stashes[_stashId].undelegatesAt;
                            emit StashUndelegationCancelled(_stashId);
                            _redelegateStash(_stashId, _staker, address(0), _delegatedCluster);
                        }
                    
                        function withdrawStash(bytes32 _stashId) external {
                            Stash memory _stash = stashes[_stashId];
                            require(
                                _stash.staker == msg.sender,
                                "WS1"
                            );
                            require(
                                _stash.delegatedCluster == address(0),
                                "WS2"
                            );
                            require(
                                _stash.undelegatesAt <= block.number,
                                "WS3"
                            );
                            bytes32[] memory _tokens = rewardDelegators.getFullTokenList();
                            uint256[] memory _amounts = new uint256[](_tokens.length);
                            for(uint256 i=0; i < _tokens.length; i++) {
                                _amounts[i] = stashes[_stashId].amount[_tokens[i]];
                                if(_amounts[i] == 0) continue;
                                delete stashes[_stashId].amount[_tokens[i]];
                                _unlockTokens(_tokens[i], _amounts[i], msg.sender);
                            }
                            // Other items already zeroed
                            delete stashes[_stashId].staker;
                            delete stashes[_stashId].undelegatesAt;
                            emit StashWithdrawn(_stashId, _tokens, _amounts);
                            emit StashClosed(_stashId, msg.sender);
                        }
                    
                        function withdrawStash(
                            bytes32 _stashId,
                            bytes32[] calldata _tokens,
                            uint256[] calldata _amounts
                        ) external {
                            Stash memory _stash = stashes[_stashId];
                            require(
                                _stash.staker == msg.sender,
                                "WSC1"
                            );
                            require(
                                _stash.delegatedCluster == address(0),
                                "WSC2"
                            );
                            require(
                                _stash.undelegatesAt <= block.number,
                                "WSC3"
                            );
                            require(
                                _tokens.length == _amounts.length,
                                "WSC4"
                            );
                            for(uint256 i=0; i < _tokens.length; i++) {
                                uint256 _balance = stashes[_stashId].amount[_tokens[i]];
                                require(
                                    _balance >= _amounts[i],
                                    "WSC5"
                                );
                                if(_balance == _amounts[i]) {
                                    delete stashes[_stashId].amount[_tokens[i]];
                                } else {
                                    stashes[_stashId].amount[_tokens[i]] = _balance.sub(_amounts[i]);
                                }
                                _unlockTokens(_tokens[i], _amounts[i], msg.sender);
                            }
                            emit StashWithdrawn(_stashId, _tokens, _amounts);
                        }
                    
                        function _lockTokens(bytes32 _tokenId, uint256 _amount, address _delegator) internal {
                            if(_amount == 0) {
                                return;
                            }
                            address tokenAddress = tokenAddresses[_tokenId].addr;
                            // pull tokens from mpond/pond contract
                            // if mpond transfer the governance rights back
                            require(
                                ERC20(tokenAddress).transferFrom(
                                    _delegator,
                                    address(this),
                                    _amount
                                ), "LT1"
                            );
                            if (tokenAddress == address(MPOND)) {
                                // send a request to delegate governance rights for the amount to delegator
                                MPOND.delegate(
                                    _delegator,
                                    uint96(_amount)
                                );
                            }
                        }
                    
                        function _unlockTokens(bytes32 _tokenId, uint256 _amount, address _delegator) internal {
                            if(_amount == 0) {
                                return;
                            }
                            address tokenAddress = tokenAddresses[_tokenId].addr;
                            if(tokenAddress == address(MPOND)) {
                                // send a request to undelegate governacne rights for the amount to previous delegator
                                MPOND.undelegate(
                                    _delegator,
                                    uint96(_amount)
                                );
                            } else if(tokenAddress == address(prevMPOND)) {
                                prevMPOND.undelegate(
                                    _delegator,
                                    uint96(_amount)
                                );
                            }
                            require(
                                ERC20(tokenAddress).transfer(
                                    _delegator,
                                    _amount
                                ), "UT1"
                            );
                        }
                    
                        function getTokenAmountInStash(bytes32 _stashId, bytes32 _tokenId) external view returns(uint256) {
                            return stashes[_stashId].amount[_tokenId];
                        }
                    }

                    File 5 of 10: RewardDelegators
                    /** 
                     *  SourceUnit: /Users/prateekyammanuru/work/marlin/contracts_github/Contracts/contracts/Stake/RewardDelegators.sol
                    */
                                
                    pragma solidity >=0.4.24 <0.7.0;
                    
                    
                    /**
                     * @title Initializable
                     *
                     * @dev Helper contract to support initializer functions. To use it, replace
                     * the constructor with a function that has the `initializer` modifier.
                     * WARNING: Unlike constructors, initializer functions must be manually
                     * invoked. This applies both to deploying an Initializable contract, as well
                     * as extending an Initializable contract via inheritance.
                     * WARNING: When used with inheritance, manual care must be taken to not invoke
                     * a parent initializer twice, or ensure that all initializers are idempotent,
                     * because this is not dealt with automatically as with constructors.
                     */
                    contract Initializable {
                    
                      /**
                       * @dev Indicates that the contract has been initialized.
                       */
                      bool private initialized;
                    
                      /**
                       * @dev Indicates that the contract is in the process of being initialized.
                       */
                      bool private initializing;
                    
                      /**
                       * @dev Modifier to use in the initializer function of a contract.
                       */
                      modifier initializer() {
                        require(initializing || isConstructor() || !initialized, "Contract instance has already been initialized");
                    
                        bool isTopLevelCall = !initializing;
                        if (isTopLevelCall) {
                          initializing = true;
                          initialized = true;
                        }
                    
                        _;
                    
                        if (isTopLevelCall) {
                          initializing = false;
                        }
                      }
                    
                      /// @dev Returns true if and only if the function is running in the constructor
                      function isConstructor() private view returns (bool) {
                        // extcodesize checks the size of the code stored in an address, and
                        // address returns the current address. Since the code is still not
                        // deployed when running a constructor, any checks on its code size will
                        // yield zero, making it an effective way to detect if a contract is
                        // under construction or not.
                        address self = address(this);
                        uint256 cs;
                        assembly { cs := extcodesize(self) }
                        return cs == 0;
                      }
                    
                      // Reserved storage space to allow for layout changes in the future.
                      uint256[50] private ______gap;
                    }
                    
                    
                    
                    
                    /** 
                     *  SourceUnit: /Users/prateekyammanuru/work/marlin/contracts_github/Contracts/contracts/Stake/RewardDelegators.sol
                    */
                                
                    pragma solidity ^0.5.0;
                    
                    /**
                     * @dev Wrappers over Solidity's arithmetic operations with added overflow
                     * checks.
                     *
                     * Arithmetic operations in Solidity wrap on overflow. This can easily result
                     * in bugs, because programmers usually assume that an overflow raises an
                     * error, which is the standard behavior in high level programming languages.
                     * `SafeMath` restores this intuition by reverting the transaction when an
                     * operation overflows.
                     *
                     * Using this library instead of the unchecked operations eliminates an entire
                     * class of bugs, so it's recommended to use it always.
                     */
                    library SafeMath {
                        /**
                         * @dev Returns the addition of two unsigned integers, reverting on
                         * overflow.
                         *
                         * Counterpart to Solidity's `+` operator.
                         *
                         * Requirements:
                         * - Addition cannot overflow.
                         */
                        function add(uint256 a, uint256 b) internal pure returns (uint256) {
                            uint256 c = a + b;
                            require(c >= a, "SafeMath: addition overflow");
                    
                            return c;
                        }
                    
                        /**
                         * @dev Returns the subtraction of two unsigned integers, reverting on
                         * overflow (when the result is negative).
                         *
                         * Counterpart to Solidity's `-` operator.
                         *
                         * Requirements:
                         * - Subtraction cannot overflow.
                         */
                        function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                            return sub(a, b, "SafeMath: subtraction overflow");
                        }
                    
                        /**
                         * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
                         * overflow (when the result is negative).
                         *
                         * Counterpart to Solidity's `-` operator.
                         *
                         * Requirements:
                         * - Subtraction cannot overflow.
                         *
                         * _Available since v2.4.0._
                         */
                        function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                            require(b <= a, errorMessage);
                            uint256 c = a - b;
                    
                            return c;
                        }
                    
                        /**
                         * @dev Returns the multiplication of two unsigned integers, reverting on
                         * overflow.
                         *
                         * Counterpart to Solidity's `*` operator.
                         *
                         * Requirements:
                         * - Multiplication cannot overflow.
                         */
                        function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                            // benefit is lost if 'b' is also tested.
                            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                            if (a == 0) {
                                return 0;
                            }
                    
                            uint256 c = a * b;
                            require(c / a == b, "SafeMath: multiplication overflow");
                    
                            return c;
                        }
                    
                        /**
                         * @dev Returns the integer division of two unsigned integers. Reverts on
                         * division by zero. The result is rounded towards zero.
                         *
                         * Counterpart to Solidity's `/` operator. Note: this function uses a
                         * `revert` opcode (which leaves remaining gas untouched) while Solidity
                         * uses an invalid opcode to revert (consuming all remaining gas).
                         *
                         * Requirements:
                         * - The divisor cannot be zero.
                         */
                        function div(uint256 a, uint256 b) internal pure returns (uint256) {
                            return div(a, b, "SafeMath: division by zero");
                        }
                    
                        /**
                         * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
                         * division by zero. The result is rounded towards zero.
                         *
                         * Counterpart to Solidity's `/` operator. Note: this function uses a
                         * `revert` opcode (which leaves remaining gas untouched) while Solidity
                         * uses an invalid opcode to revert (consuming all remaining gas).
                         *
                         * Requirements:
                         * - The divisor cannot be zero.
                         *
                         * _Available since v2.4.0._
                         */
                        function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                            // Solidity only automatically asserts when dividing by 0
                            require(b > 0, errorMessage);
                            uint256 c = a / b;
                            // assert(a == b * c + a % b); // There is no case in which this doesn't hold
                    
                            return c;
                        }
                    
                        /**
                         * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                         * Reverts when dividing by zero.
                         *
                         * Counterpart to Solidity's `%` operator. This function uses a `revert`
                         * opcode (which leaves remaining gas untouched) while Solidity uses an
                         * invalid opcode to revert (consuming all remaining gas).
                         *
                         * Requirements:
                         * - The divisor cannot be zero.
                         */
                        function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                            return mod(a, b, "SafeMath: modulo by zero");
                        }
                    
                        /**
                         * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                         * Reverts with custom message when dividing by zero.
                         *
                         * Counterpart to Solidity's `%` operator. This function uses a `revert`
                         * opcode (which leaves remaining gas untouched) while Solidity uses an
                         * invalid opcode to revert (consuming all remaining gas).
                         *
                         * Requirements:
                         * - The divisor cannot be zero.
                         *
                         * _Available since v2.4.0._
                         */
                        function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                            require(b != 0, errorMessage);
                            return a % b;
                        }
                    }
                    
                    
                    
                    
                    /** 
                     *  SourceUnit: /Users/prateekyammanuru/work/marlin/contracts_github/Contracts/contracts/Stake/RewardDelegators.sol
                    */
                                
                    pragma solidity ^0.5.0;
                    
                    /**
                     * @dev Interface of the ERC20 standard as defined in the EIP. Does not include
                     * the optional functions; to access them see {ERC20Detailed}.
                     */
                    interface IERC20 {
                        /**
                         * @dev Returns the amount of tokens in existence.
                         */
                        function totalSupply() external view returns (uint256);
                    
                        /**
                         * @dev Returns the amount of tokens owned by `account`.
                         */
                        function balanceOf(address account) external view returns (uint256);
                    
                        /**
                         * @dev Moves `amount` tokens from the caller's account to `recipient`.
                         *
                         * Returns a boolean value indicating whether the operation succeeded.
                         *
                         * Emits a {Transfer} event.
                         */
                        function transfer(address recipient, uint256 amount) external returns (bool);
                    
                        /**
                         * @dev Returns the remaining number of tokens that `spender` will be
                         * allowed to spend on behalf of `owner` through {transferFrom}. This is
                         * zero by default.
                         *
                         * This value changes when {approve} or {transferFrom} are called.
                         */
                        function allowance(address owner, address spender) external view returns (uint256);
                    
                        /**
                         * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
                         *
                         * Returns a boolean value indicating whether the operation succeeded.
                         *
                         * ////IMPORTANT: Beware that changing an allowance with this method brings the risk
                         * that someone may use both the old and the new allowance by unfortunate
                         * transaction ordering. One possible solution to mitigate this race
                         * condition is to first reduce the spender's allowance to 0 and set the
                         * desired value afterwards:
                         * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                         *
                         * Emits an {Approval} event.
                         */
                        function approve(address spender, uint256 amount) external returns (bool);
                    
                        /**
                         * @dev Moves `amount` tokens from `sender` to `recipient` using the
                         * allowance mechanism. `amount` is then deducted from the caller's
                         * allowance.
                         *
                         * Returns a boolean value indicating whether the operation succeeded.
                         *
                         * Emits a {Transfer} event.
                         */
                        function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
                    
                        /**
                         * @dev Emitted when `value` tokens are moved from one account (`from`) to
                         * another (`to`).
                         *
                         * Note that `value` may be zero.
                         */
                        event Transfer(address indexed from, address indexed to, uint256 value);
                    
                        /**
                         * @dev Emitted when the allowance of a `spender` for an `owner` is set by
                         * a call to {approve}. `value` is the new allowance.
                         */
                        event Approval(address indexed owner, address indexed spender, uint256 value);
                    }
                    
                    
                    
                    
                    /** 
                     *  SourceUnit: /Users/prateekyammanuru/work/marlin/contracts_github/Contracts/contracts/Stake/RewardDelegators.sol
                    */
                                
                    pragma solidity ^0.5.0;
                    
                    
                    /*
                     * @dev Provides information about the current execution context, including the
                     * sender of the transaction and its data. While these are generally available
                     * via msg.sender and msg.data, they should not be accessed in such a direct
                     * manner, since when dealing with GSN meta-transactions the account sending and
                     * paying for execution may not be the actual sender (as far as an application
                     * is concerned).
                     *
                     * This contract is only required for intermediate, library-like contracts.
                     */
                    contract Context is Initializable {
                        // Empty internal constructor, to prevent people from mistakenly deploying
                        // an instance of this contract, which should be used via inheritance.
                        constructor () internal { }
                        // solhint-disable-previous-line no-empty-blocks
                    
                        function _msgSender() internal view returns (address payable) {
                            return msg.sender;
                        }
                    
                        function _msgData() internal view returns (bytes memory) {
                            this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                            return msg.data;
                        }
                    }
                    
                    
                    
                    
                    /** 
                     *  SourceUnit: /Users/prateekyammanuru/work/marlin/contracts_github/Contracts/contracts/Stake/RewardDelegators.sol
                    */
                                
                    pragma solidity ^0.5.17;
                    interface IClusterRegistry {
                        function locks(bytes32 _lockId) external returns(uint256, uint256);
                        function lockWaitTime(bytes32 _selectorId) external returns(uint256);
                        function updateLockWaitTime(bytes32 _selector, uint256 _updatedWaitTime) external;
                        function register(
                            bytes32 _networkId, 
                            uint256 _commission, 
                            address _rewardAddress, 
                            address _clientKey
                        ) external returns(bool);
                        function updateCluster(
                            uint256 _commission, 
                            bytes32 _networkId, 
                            address _rewardAddress, 
                            address _clientKey
                        ) external;
                        function updateCommission(uint256 _commission) external;
                        function switchNetwork(bytes32 _networkId) external;
                        function updateRewardAddress(address _rewardAddress) external;
                        function updateClientKey(address _clientKey) external;
                        function unregister() external;
                        function isClusterValid(address _cluster) external returns(bool);
                        function getCommission(address _cluster) external returns(uint256);
                        function getNetwork(address _cluster) external returns(bytes32);
                        function getRewardAddress(address _cluster) external view returns(address);
                        function getClientKey(address _cluster) external view returns(address);
                        function getCluster(address _cluster) external;
                        function getRewardInfo(address _cluster) external returns(uint256, address);
                    }
                    
                    
                    
                    /** 
                     *  SourceUnit: /Users/prateekyammanuru/work/marlin/contracts_github/Contracts/contracts/Stake/RewardDelegators.sol
                    */
                                
                    pragma solidity ^0.5.17;
                    
                    interface IClusterRewards {
                        function clusterRewards(address _cluster) external returns(uint256);
                        function rewardWeight(bytes32 _networkId) external returns(uint256);
                        function totalRewardsPerEpoch() external returns(uint256);
                        function feeder() external returns(address);
                        function rewardDistributionWaitTime() external returns(uint256);
                        function changeFeeder(address _newFeeder) external;
                        function addNetwork(bytes32 _networkId, uint256 _rewardWeight) external;
                        function removeNetwork(bytes32 _networkId) external;
                        function changeNetworkReward(bytes32 _networkId, uint256 _updatedRewardWeight) external;
                        function feed(bytes32 _networkId, address[] calldata _clusters, uint256[] calldata _payouts, uint256 _epoch) external;
                        function getRewardPerEpoch(bytes32 _networkId) external view returns(uint256);
                        function claimReward(address _cluster) external returns(uint256);
                        function updateRewardDelegatorAddress(address _updatedRewardDelegator) external;
                        function updatePONDAddress(address _updatedPOND) external;
                        function changeRewardPerEpoch(uint256 _updatedRewardPerEpoch) external;
                        function changePayoutDenomination(uint256 _updatedPayoutDenomination) external;
                        function updateRewardDistributionWaitTime(uint256 _updatedRewardDistributionWaitTime) external;
                    }
                    
                    
                    
                    /** 
                     *  SourceUnit: /Users/prateekyammanuru/work/marlin/contracts_github/Contracts/contracts/Stake/RewardDelegators.sol
                    */
                                
                    pragma solidity ^0.5.0;
                    
                    
                    ////import "../../GSN/Context.sol";
                    ////import "./IERC20.sol";
                    ////import "../../math/SafeMath.sol";
                    
                    /**
                     * @dev Implementation of the {IERC20} interface.
                     *
                     * This implementation is agnostic to the way tokens are created. This means
                     * that a supply mechanism has to be added in a derived contract using {_mint}.
                     * For a generic mechanism see {ERC20Mintable}.
                     *
                     * TIP: For a detailed writeup see our guide
                     * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
                     * to implement supply mechanisms].
                     *
                     * We have followed general OpenZeppelin guidelines: functions revert instead
                     * of returning `false` on failure. This behavior is nonetheless conventional
                     * and does not conflict with the expectations of ERC20 applications.
                     *
                     * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
                     * This allows applications to reconstruct the allowance for all accounts just
                     * by listening to said events. Other implementations of the EIP may not emit
                     * these events, as it isn't required by the specification.
                     *
                     * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
                     * functions have been added to mitigate the well-known issues around setting
                     * allowances. See {IERC20-approve}.
                     */
                    contract ERC20 is Initializable, Context, IERC20 {
                        using SafeMath for uint256;
                    
                        mapping (address => uint256) private _balances;
                    
                        mapping (address => mapping (address => uint256)) private _allowances;
                    
                        uint256 private _totalSupply;
                    
                        /**
                         * @dev See {IERC20-totalSupply}.
                         */
                        function totalSupply() public view returns (uint256) {
                            return _totalSupply;
                        }
                    
                        /**
                         * @dev See {IERC20-balanceOf}.
                         */
                        function balanceOf(address account) public view returns (uint256) {
                            return _balances[account];
                        }
                    
                        /**
                         * @dev See {IERC20-transfer}.
                         *
                         * Requirements:
                         *
                         * - `recipient` cannot be the zero address.
                         * - the caller must have a balance of at least `amount`.
                         */
                        function transfer(address recipient, uint256 amount) public returns (bool) {
                            _transfer(_msgSender(), recipient, amount);
                            return true;
                        }
                    
                        /**
                         * @dev See {IERC20-allowance}.
                         */
                        function allowance(address owner, address spender) public view returns (uint256) {
                            return _allowances[owner][spender];
                        }
                    
                        /**
                         * @dev See {IERC20-approve}.
                         *
                         * Requirements:
                         *
                         * - `spender` cannot be the zero address.
                         */
                        function approve(address spender, uint256 amount) public returns (bool) {
                            _approve(_msgSender(), spender, amount);
                            return true;
                        }
                    
                        /**
                         * @dev See {IERC20-transferFrom}.
                         *
                         * Emits an {Approval} event indicating the updated allowance. This is not
                         * required by the EIP. See the note at the beginning of {ERC20};
                         *
                         * Requirements:
                         * - `sender` and `recipient` cannot be the zero address.
                         * - `sender` must have a balance of at least `amount`.
                         * - the caller must have allowance for `sender`'s tokens of at least
                         * `amount`.
                         */
                        function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) {
                            _transfer(sender, recipient, amount);
                            _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
                            return true;
                        }
                    
                        /**
                         * @dev Atomically increases the allowance granted to `spender` by the caller.
                         *
                         * This is an alternative to {approve} that can be used as a mitigation for
                         * problems described in {IERC20-approve}.
                         *
                         * Emits an {Approval} event indicating the updated allowance.
                         *
                         * Requirements:
                         *
                         * - `spender` cannot be the zero address.
                         */
                        function increaseAllowance(address spender, uint256 addedValue) public returns (bool) {
                            _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
                            return true;
                        }
                    
                        /**
                         * @dev Atomically decreases the allowance granted to `spender` by the caller.
                         *
                         * This is an alternative to {approve} that can be used as a mitigation for
                         * problems described in {IERC20-approve}.
                         *
                         * Emits an {Approval} event indicating the updated allowance.
                         *
                         * Requirements:
                         *
                         * - `spender` cannot be the zero address.
                         * - `spender` must have allowance for the caller of at least
                         * `subtractedValue`.
                         */
                        function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) {
                            _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
                            return true;
                        }
                    
                        /**
                         * @dev Moves tokens `amount` from `sender` to `recipient`.
                         *
                         * This is internal function is equivalent to {transfer}, and can be used to
                         * e.g. implement automatic token fees, slashing mechanisms, etc.
                         *
                         * Emits a {Transfer} event.
                         *
                         * Requirements:
                         *
                         * - `sender` cannot be the zero address.
                         * - `recipient` cannot be the zero address.
                         * - `sender` must have a balance of at least `amount`.
                         */
                        function _transfer(address sender, address recipient, uint256 amount) internal {
                            require(sender != address(0), "ERC20: transfer from the zero address");
                            require(recipient != address(0), "ERC20: transfer to the zero address");
                    
                            _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
                            _balances[recipient] = _balances[recipient].add(amount);
                            emit Transfer(sender, recipient, amount);
                        }
                    
                        /** @dev Creates `amount` tokens and assigns them to `account`, increasing
                         * the total supply.
                         *
                         * Emits a {Transfer} event with `from` set to the zero address.
                         *
                         * Requirements
                         *
                         * - `to` cannot be the zero address.
                         */
                        function _mint(address account, uint256 amount) internal {
                            require(account != address(0), "ERC20: mint to the zero address");
                    
                            _totalSupply = _totalSupply.add(amount);
                            _balances[account] = _balances[account].add(amount);
                            emit Transfer(address(0), account, amount);
                        }
                    
                        /**
                         * @dev Destroys `amount` tokens from `account`, reducing the
                         * total supply.
                         *
                         * Emits a {Transfer} event with `to` set to the zero address.
                         *
                         * Requirements
                         *
                         * - `account` cannot be the zero address.
                         * - `account` must have at least `amount` tokens.
                         */
                        function _burn(address account, uint256 amount) internal {
                            require(account != address(0), "ERC20: burn from the zero address");
                    
                            _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
                            _totalSupply = _totalSupply.sub(amount);
                            emit Transfer(account, address(0), amount);
                        }
                    
                        /**
                         * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens.
                         *
                         * This is internal function is equivalent to `approve`, and can be used to
                         * e.g. set automatic allowances for certain subsystems, etc.
                         *
                         * Emits an {Approval} event.
                         *
                         * Requirements:
                         *
                         * - `owner` cannot be the zero address.
                         * - `spender` cannot be the zero address.
                         */
                        function _approve(address owner, address spender, uint256 amount) internal {
                            require(owner != address(0), "ERC20: approve from the zero address");
                            require(spender != address(0), "ERC20: approve to the zero address");
                    
                            _allowances[owner][spender] = amount;
                            emit Approval(owner, spender, amount);
                        }
                    
                        /**
                         * @dev Destroys `amount` tokens from `account`.`amount` is then deducted
                         * from the caller's allowance.
                         *
                         * See {_burn} and {_approve}.
                         */
                        function _burnFrom(address account, uint256 amount) internal {
                            _burn(account, amount);
                            _approve(account, _msgSender(), _allowances[account][_msgSender()].sub(amount, "ERC20: burn amount exceeds allowance"));
                        }
                    
                        uint256[50] private ______gap;
                    }
                    
                    
                    
                    
                    /** 
                     *  SourceUnit: /Users/prateekyammanuru/work/marlin/contracts_github/Contracts/contracts/Stake/RewardDelegators.sol
                    */
                                
                    pragma solidity ^0.5.0;
                    
                    
                    ////import "../GSN/Context.sol";
                    
                    /**
                     * @dev Contract module which provides a basic access control mechanism, where
                     * there is an account (an owner) that can be granted exclusive access to
                     * specific functions.
                     *
                     * This module is used through inheritance. It will make available the modifier
                     * `onlyOwner`, which can be aplied to your functions to restrict their use to
                     * the owner.
                     */
                    contract Ownable is Initializable, Context {
                        address private _owner;
                    
                        event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                    
                        /**
                         * @dev Initializes the contract setting the deployer as the initial owner.
                         */
                        function initialize(address sender) public initializer {
                            _owner = sender;
                            emit OwnershipTransferred(address(0), _owner);
                        }
                    
                        /**
                         * @dev Returns the address of the current owner.
                         */
                        function owner() public view returns (address) {
                            return _owner;
                        }
                    
                        /**
                         * @dev Throws if called by any account other than the owner.
                         */
                        modifier onlyOwner() {
                            require(isOwner(), "Ownable: caller is not the owner");
                            _;
                        }
                    
                        /**
                         * @dev Returns true if the caller is the current owner.
                         */
                        function isOwner() public view returns (bool) {
                            return _msgSender() == _owner;
                        }
                    
                        /**
                         * @dev Leaves the contract without owner. It will not be possible to call
                         * `onlyOwner` functions anymore. Can only be called by the current owner.
                         *
                         * > Note: Renouncing ownership will leave the contract without an owner,
                         * thereby removing any functionality that is only available to the owner.
                         */
                        function renounceOwnership() public onlyOwner {
                            emit OwnershipTransferred(_owner, address(0));
                            _owner = address(0);
                        }
                    
                        /**
                         * @dev Transfers ownership of the contract to a new account (`newOwner`).
                         * Can only be called by the current owner.
                         */
                        function transferOwnership(address newOwner) public onlyOwner {
                            _transferOwnership(newOwner);
                        }
                    
                        /**
                         * @dev Transfers ownership of the contract to a new account (`newOwner`).
                         */
                        function _transferOwnership(address newOwner) internal {
                            require(newOwner != address(0), "Ownable: new owner is the zero address");
                            emit OwnershipTransferred(_owner, newOwner);
                            _owner = newOwner;
                        }
                    
                        uint256[50] private ______gap;
                    }
                    
                    
                    /** 
                     *  SourceUnit: /Users/prateekyammanuru/work/marlin/contracts_github/Contracts/contracts/Stake/RewardDelegators.sol
                    */
                    
                    pragma solidity >=0.4.21 <0.7.0;
                    
                    
                    contract RewardDelegators is Initializable, Ownable {
                    
                        using SafeMath for uint256;
                    
                        struct Cluster {
                            mapping(bytes32 => uint256) totalDelegations;
                            mapping(address => mapping(bytes32 => uint256)) delegators;
                            mapping(address => mapping(bytes32 => uint256)) rewardDebt;
                            mapping(bytes32 => uint256) accRewardPerShare;
                        }
                    
                        mapping(address => Cluster) clusters;
                    
                        uint256 __unused_2;
                        address stakeAddress;
                        uint256 public minMPONDStake;
                        bytes32 public MPONDTokenId;
                        mapping(bytes32 => uint256) rewardFactor;
                        mapping(bytes32 => uint256) tokenIndex;
                        mapping(bytes32 => bytes32) __unused_1;
                        bytes32[] tokenList;
                        IClusterRewards clusterRewards;
                        IClusterRegistry clusterRegistry;
                        ERC20 PONDToken;
                    
                        event AddReward(bytes32 tokenId, uint256 rewardFactor);
                        event RemoveReward(bytes32 tokenId);
                        event MPONDTokenIdUpdated(bytes32 MPONDTokenId);
                        event RewardsUpdated(bytes32 tokenId, uint256 rewardFactor);
                        event ClusterRewardDistributed(address cluster);
                        event RewardsWithdrawn(address cluster, address delegator, bytes32[] tokenIds, uint256 rewards);
                        event MinMPONDStakeUpdated(uint256 minMPONDStake);
                        event StakeAddressUpdated(address _updatedStakeAddress);
                        event ClusterRewardsAddressUpdated(address _updatedClusterRewards);
                        event ClusterRegistryUpdated(address _updatedClusterRegistry);
                        event PONDAddressUpdated(address _updatedPOND);
                    
                        modifier onlyStake() {
                            require(msg.sender == stakeAddress, "RD:OS-only stake contract can invoke");
                            _;
                        }
                    
                        function initialize(
                            address _stakeAddress,
                            address _clusterRewardsAddress,
                            address _clusterRegistry,
                            address _rewardDelegatorsAdmin,
                            uint256 _minMPONDStake,
                            bytes32 _MPONDTokenId,
                            address _PONDAddress,
                            bytes32[] memory _tokenIds,
                            uint256[] memory _rewardFactors
                        ) public initializer {
                            require(
                                _tokenIds.length == _rewardFactors.length,
                                "RD:I-Each TokenId should have a corresponding Reward Factor and vice versa"
                            );
                            stakeAddress = _stakeAddress;
                            clusterRegistry = IClusterRegistry(_clusterRegistry);
                            clusterRewards = IClusterRewards(_clusterRewardsAddress);
                            PONDToken = ERC20(_PONDAddress);
                            minMPONDStake = _minMPONDStake;
                            emit MinMPONDStakeUpdated(_minMPONDStake);
                            MPONDTokenId = _MPONDTokenId;
                            emit MPONDTokenIdUpdated(_MPONDTokenId);
                            for(uint256 i=0; i < _tokenIds.length; i++) {
                                rewardFactor[_tokenIds[i]] = _rewardFactors[i];
                                tokenIndex[_tokenIds[i]] = tokenList.length;
                                tokenList.push(_tokenIds[i]);
                                emit AddReward(_tokenIds[i], _rewardFactors[i]);
                            }
                            super.initialize(_rewardDelegatorsAdmin);
                        }
                    
                        function updateMPONDTokenId(bytes32 _updatedMPONDTokenId) external onlyOwner {
                            MPONDTokenId = _updatedMPONDTokenId;
                            emit MPONDTokenIdUpdated(_updatedMPONDTokenId);
                        }
                    
                        function addRewardFactor(bytes32 _tokenId, uint256 _rewardFactor) external onlyOwner {
                            require(rewardFactor[_tokenId] == 0, "RD:AR-Reward already exists");
                            require(_rewardFactor != 0, "RD:AR-Reward cant be 0");
                            rewardFactor[_tokenId] = _rewardFactor;
                            tokenIndex[_tokenId] = tokenList.length;
                            tokenList.push(_tokenId);
                            emit AddReward(_tokenId, _rewardFactor);
                        }
                    
                        function removeRewardFactor(bytes32 _tokenId) external onlyOwner {
                            require(rewardFactor[_tokenId] != 0, "RD:RR-Reward doesnt exist");
                            bytes32 tokenToReplace = tokenList[tokenList.length - 1];
                            uint256 originalTokenIndex = tokenIndex[_tokenId];
                            tokenList[originalTokenIndex] = tokenToReplace;
                            tokenIndex[tokenToReplace] = originalTokenIndex;
                            tokenList.pop();
                            delete rewardFactor[_tokenId];
                            delete tokenIndex[_tokenId];
                            emit RemoveReward(_tokenId);
                        }
                    
                        function updateRewardFactor(bytes32 _tokenId, uint256 _updatedRewardFactor) external onlyOwner {
                            require(rewardFactor[_tokenId] != 0, "RD:UR-Cant update reward that doesnt exist");
                            require(_updatedRewardFactor != 0, "RD:UR-Reward cant be 0");
                            rewardFactor[_tokenId] = _updatedRewardFactor;
                            emit RewardsUpdated(_tokenId, _updatedRewardFactor);
                        }
                    
                        function _updateRewards(address _cluster) public {
                            uint256 reward = clusterRewards.claimReward(_cluster);
                            if(reward == 0) {
                                return;
                            }
                    
                            (uint256 _commission, address _rewardAddress) = clusterRegistry.getRewardInfo(_cluster);
                    
                            uint256 commissionReward = reward.mul(_commission).div(100);
                            uint256 delegatorReward = reward.sub(commissionReward);
                            bytes32[] memory tokens = tokenList;
                            uint256[] memory delegations = new uint256[](tokens.length);
                            uint256 delegatedTokens = 0;
                            for(uint i=0; i < tokens.length; i++) {
                                delegations[i] = clusters[_cluster].totalDelegations[tokens[i]];
                                if(delegations[i] != 0) {
                                    delegatedTokens++;
                                }
                            }
                            for(uint i=0; i < tokens.length; i++) {
                                // clusters[_cluster].accRewardPerShare[tokens[i]] = clusters[_cluster].accRewardPerShare[tokens[i]].add(
                                //                                                         delegatorReward
                                //                                                         .mul(rewardFactor[tokens[i]])
                                //                                                         .mul(10**30)
                                //                                                         .div(weightedStake)
                                //                                                     );
                                if(delegations[i] != 0) {
                                    clusters[_cluster].accRewardPerShare[tokens[i]] = clusters[_cluster].accRewardPerShare[tokens[i]].add(
                                                                                        delegatorReward
                                                                                        .mul(10**30)
                                                                                        .div(delegatedTokens)
                                                                                        .div(delegations[i])
                                                                                    );
                                }
                            }
                            if(commissionReward != 0) {
                                transferRewards(_rewardAddress, commissionReward);
                            }
                            emit ClusterRewardDistributed(_cluster);
                        }
                    
                        function delegate(
                            address _delegator,
                            address _cluster,
                            bytes32[] memory _tokens,
                            uint256[] memory _amounts
                        ) public onlyStake {
                            _updateTokens(_delegator, _cluster, _tokens, _amounts, true);
                        }
                    
                        function _updateTokens(
                            address _delegator,
                            address _cluster,
                            bytes32[] memory _tokens,
                            uint256[] memory _amounts,
                            bool _isDelegation
                        ) internal returns(uint256 _aggregateReward) {
                            _updateRewards(_cluster);
                    
                            for(uint256 i = 0; i < _tokens.length; i++) {
                                bytes32 _tokenId = _tokens[i];
                                uint256 _amount = _amounts[i];
                    
                                (uint256 _oldBalance, uint256 _newBalance) = _updateBalances(
                                    _cluster,
                                    _delegator,
                                    _tokenId,
                                    _amount,
                                    _isDelegation
                                );
                    
                                uint256 _reward = _updateDelegatorRewards(
                                    _cluster,
                                    _delegator,
                                    _tokenId,
                                    _oldBalance,
                                    _newBalance
                                );
                    
                                _aggregateReward = _aggregateReward.add(_reward);
                            }
                    
                            if(_aggregateReward != 0) {
                                transferRewards(_delegator, _aggregateReward);
                                emit RewardsWithdrawn(_cluster, _delegator, _tokens, _aggregateReward);
                            }
                        }
                    
                        function _updateBalances(
                            address _cluster,
                            address _delegator,
                            bytes32 _tokenId,
                            uint256 _amount,
                            bool _isDelegation
                        ) internal returns(uint256 _oldBalance, uint256 _newBalance) {
                            _oldBalance = clusters[_cluster].delegators[_delegator][_tokenId];
                    
                            // short circuit
                            if(_amount == 0) {
                                _newBalance = _oldBalance;
                                return (_oldBalance, _newBalance);
                            }
                    
                            // update balances
                            if(_isDelegation) {
                                _newBalance =  _oldBalance.add(_amount);
                                clusters[_cluster].totalDelegations[_tokenId] = clusters[_cluster].totalDelegations[_tokenId]
                                                                                .add(_amount);
                            } else {
                                _newBalance =  _oldBalance.sub(_amount);
                                clusters[_cluster].totalDelegations[_tokenId] = clusters[_cluster].totalDelegations[_tokenId]
                                                                                .sub(_amount);
                            }
                            clusters[_cluster].delegators[_delegator][_tokenId] = _newBalance;
                        }
                    
                        function _updateDelegatorRewards(
                            address _cluster,
                            address _delegator,
                            bytes32 _tokenId,
                            uint256 _oldBalance,
                            uint256 _newBalance
                        ) internal returns(uint256 _reward) {
                            uint256 _accRewardPerShare = clusters[_cluster].accRewardPerShare[_tokenId];
                            uint256 _rewardDebt = clusters[_cluster].rewardDebt[_delegator][_tokenId];
                    
                            // pending rewards
                            uint256 _tokenPendingRewards =  _accRewardPerShare.mul(_oldBalance).div(10**30);
                    
                            // calculating pending rewards for the delegator if any
                            _reward = _tokenPendingRewards.sub(_rewardDebt);
                    
                            // short circuit
                            if(_oldBalance == _newBalance && _reward == 0) {
                                return _reward;
                            }
                    
                            // update the debt for next reward calculation
                            clusters[_cluster].rewardDebt[_delegator][_tokenId] = _accRewardPerShare.mul(_newBalance).div(10**30);
                        }
                    
                        function undelegate(
                            address _delegator,
                            address _cluster,
                            bytes32[] memory _tokens,
                            uint256[] memory _amounts
                        ) public onlyStake {
                            _updateTokens(_delegator, _cluster, _tokens, _amounts, false);
                        }
                    
                        function withdrawRewards(address _delegator, address _cluster) public returns(uint256) {
                            return _updateTokens(_delegator, _cluster, tokenList, new uint256[](tokenList.length), true);
                        }
                    
                        function withdrawRewards(address _delegator, address[] calldata _clusters) external {
                            for(uint256 i=0; i < _clusters.length; i++) {
                                withdrawRewards(_delegator, _clusters[i]);
                            }
                        }
                    
                        function transferRewards(address _to, uint256 _amount) internal {
                            PONDToken.transfer(_to, _amount);
                        }
                    
                        function isClusterActive(address _cluster) external returns(bool) {
                            if(
                                clusterRegistry.isClusterValid(_cluster)
                                && clusters[_cluster].totalDelegations[MPONDTokenId] > minMPONDStake
                            ) {
                                return true;
                            }
                            return false;
                        }
                    
                        function getClusterDelegation(address _cluster, bytes32 _tokenId)
                            external
                            view
                            returns(uint256)
                        {
                            return clusters[_cluster].totalDelegations[_tokenId];
                        }
                    
                        function getDelegation(address _cluster, address _delegator, bytes32 _tokenId)
                            external
                            view
                            returns(uint256)
                        {
                            return clusters[_cluster].delegators[_delegator][_tokenId];
                        }
                    
                        function updateMinMPONDStake(uint256 _minMPONDStake) external onlyOwner {
                            minMPONDStake = _minMPONDStake;
                            emit MinMPONDStakeUpdated(_minMPONDStake);
                        }
                    
                        function updateStakeAddress(address _updatedStakeAddress) external onlyOwner {
                            require(
                                _updatedStakeAddress != address(0),
                                "RD:USA-Stake contract address cant be 0"
                            );
                            stakeAddress = _updatedStakeAddress;
                            emit StakeAddressUpdated(_updatedStakeAddress);
                        }
                    
                        function updateClusterRewards(
                            address _updatedClusterRewards
                        ) external onlyOwner {
                            require(
                                _updatedClusterRewards != address(0),
                                "RD:UCR-ClusterRewards address cant be 0"
                            );
                            clusterRewards = IClusterRewards(_updatedClusterRewards);
                            emit ClusterRewardsAddressUpdated(_updatedClusterRewards);
                        }
                    
                        function updateClusterRegistry(
                            address _updatedClusterRegistry
                        ) external onlyOwner {
                            require(
                                _updatedClusterRegistry != address(0),
                                "RD:UCR-Cluster Registry address cant be 0"
                            );
                            clusterRegistry = IClusterRegistry(_updatedClusterRegistry);
                            emit ClusterRegistryUpdated(_updatedClusterRegistry);
                        }
                    
                        function updatePONDAddress(address _updatedPOND) external onlyOwner {
                            require(
                                _updatedPOND != address(0),
                                "RD:UPA-Updated POND token address cant be 0"
                            );
                            PONDToken = ERC20(_updatedPOND);
                            emit PONDAddressUpdated(_updatedPOND);
                        }
                    
                        function getFullTokenList() external view returns (bytes32[] memory) {
                            return tokenList;
                        }
                    
                        function getAccRewardPerShare(address _cluster, bytes32 _tokenId) external view returns(uint256) {
                            return clusters[_cluster].accRewardPerShare[_tokenId];
                        }
                    }

                    File 6 of 10: ClusterRewardsProxy
                    pragma solidity >=0.4.21 <0.7.0;
                    
                    
                    /// @title Contract to reward overlapping stakes
                    /// @author Marlin
                    /// @notice Use this contract only for testing
                    /// @dev Contract may or may not change in future (depending upon the new slots in proxy-store)
                    contract ClusterRewardsProxy {
                        bytes32 internal constant IMPLEMENTATION_SLOT = bytes32(
                            uint256(keccak256("eip1967.proxy.implementation")) - 1
                        );
                        bytes32 internal constant PROXY_ADMIN_SLOT = bytes32(
                            uint256(keccak256("eip1967.proxy.admin")) - 1
                        );
                    
                        constructor(address contractLogic, address proxyAdmin) public {
                            // save the code address
                            bytes32 slot = IMPLEMENTATION_SLOT;
                            assembly {
                                sstore(slot, contractLogic)
                            }
                            // save the proxy admin
                            slot = PROXY_ADMIN_SLOT;
                            address sender = proxyAdmin;
                            assembly {
                                sstore(slot, sender)
                            }
                        }
                    
                        function updateAdmin(address _newAdmin) public {
                            require(
                                msg.sender == getAdmin(),
                                "Only the current admin should be able to new admin"
                            );
                            bytes32 slot = PROXY_ADMIN_SLOT;
                            assembly {
                                sstore(slot, _newAdmin)
                            }
                        }
                    
                        /// @author Marlin
                        /// @dev Only admin can update the contract
                        /// @param _newLogic address is the address of the contract that has to updated to
                        function updateLogic(address _newLogic) public {
                            require(
                                msg.sender == getAdmin(),
                                "Only Admin should be able to update the contracts"
                            );
                            bytes32 slot = IMPLEMENTATION_SLOT;
                            assembly {
                                sstore(slot, _newLogic)
                            }
                        }
                    
                        /// @author Marlin
                        /// @dev use assembly as contract store slot is manually decided
                        function getAdmin() internal view returns (address result) {
                            bytes32 slot = PROXY_ADMIN_SLOT;
                            assembly {
                                result := sload(slot)
                            }
                        }
                    
                        /// @author Marlin
                        /// @dev add functionality to forward the balance as well.
                        function() external payable {
                            bytes32 slot = IMPLEMENTATION_SLOT;
                            assembly {
                                let contractLogic := sload(slot)
                                calldatacopy(0x0, 0x0, calldatasize())
                                let success := delegatecall(
                                    sub(gas(), 10000),
                                    contractLogic,
                                    0x0,
                                    calldatasize(),
                                    0,
                                    0
                                )
                                let retSz := returndatasize()
                                returndatacopy(0, 0, retSz)
                    
                                switch success
                                    case 0 {
                                        revert(0, retSz)
                                    }
                                    default {
                                        return(0, retSz)
                                    }
                            }
                        }
                    }

                    File 7 of 10: ClusterRewards
                    /** 
                     *  SourceUnit: /Users/prateekyammanuru/work/marlin/contracts_github/Contracts/contracts/Stake/ClusterRewards.sol
                    */
                                
                    pragma solidity >=0.4.24 <0.7.0;
                    
                    
                    /**
                     * @title Initializable
                     *
                     * @dev Helper contract to support initializer functions. To use it, replace
                     * the constructor with a function that has the `initializer` modifier.
                     * WARNING: Unlike constructors, initializer functions must be manually
                     * invoked. This applies both to deploying an Initializable contract, as well
                     * as extending an Initializable contract via inheritance.
                     * WARNING: When used with inheritance, manual care must be taken to not invoke
                     * a parent initializer twice, or ensure that all initializers are idempotent,
                     * because this is not dealt with automatically as with constructors.
                     */
                    contract Initializable {
                    
                      /**
                       * @dev Indicates that the contract has been initialized.
                       */
                      bool private initialized;
                    
                      /**
                       * @dev Indicates that the contract is in the process of being initialized.
                       */
                      bool private initializing;
                    
                      /**
                       * @dev Modifier to use in the initializer function of a contract.
                       */
                      modifier initializer() {
                        require(initializing || isConstructor() || !initialized, "Contract instance has already been initialized");
                    
                        bool isTopLevelCall = !initializing;
                        if (isTopLevelCall) {
                          initializing = true;
                          initialized = true;
                        }
                    
                        _;
                    
                        if (isTopLevelCall) {
                          initializing = false;
                        }
                      }
                    
                      /// @dev Returns true if and only if the function is running in the constructor
                      function isConstructor() private view returns (bool) {
                        // extcodesize checks the size of the code stored in an address, and
                        // address returns the current address. Since the code is still not
                        // deployed when running a constructor, any checks on its code size will
                        // yield zero, making it an effective way to detect if a contract is
                        // under construction or not.
                        address self = address(this);
                        uint256 cs;
                        assembly { cs := extcodesize(self) }
                        return cs == 0;
                      }
                    
                      // Reserved storage space to allow for layout changes in the future.
                      uint256[50] private ______gap;
                    }
                    
                    
                    
                    
                    /** 
                     *  SourceUnit: /Users/prateekyammanuru/work/marlin/contracts_github/Contracts/contracts/Stake/ClusterRewards.sol
                    */
                                
                    pragma solidity ^0.5.0;
                    
                    /**
                     * @dev Wrappers over Solidity's arithmetic operations with added overflow
                     * checks.
                     *
                     * Arithmetic operations in Solidity wrap on overflow. This can easily result
                     * in bugs, because programmers usually assume that an overflow raises an
                     * error, which is the standard behavior in high level programming languages.
                     * `SafeMath` restores this intuition by reverting the transaction when an
                     * operation overflows.
                     *
                     * Using this library instead of the unchecked operations eliminates an entire
                     * class of bugs, so it's recommended to use it always.
                     */
                    library SafeMath {
                        /**
                         * @dev Returns the addition of two unsigned integers, reverting on
                         * overflow.
                         *
                         * Counterpart to Solidity's `+` operator.
                         *
                         * Requirements:
                         * - Addition cannot overflow.
                         */
                        function add(uint256 a, uint256 b) internal pure returns (uint256) {
                            uint256 c = a + b;
                            require(c >= a, "SafeMath: addition overflow");
                    
                            return c;
                        }
                    
                        /**
                         * @dev Returns the subtraction of two unsigned integers, reverting on
                         * overflow (when the result is negative).
                         *
                         * Counterpart to Solidity's `-` operator.
                         *
                         * Requirements:
                         * - Subtraction cannot overflow.
                         */
                        function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                            return sub(a, b, "SafeMath: subtraction overflow");
                        }
                    
                        /**
                         * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
                         * overflow (when the result is negative).
                         *
                         * Counterpart to Solidity's `-` operator.
                         *
                         * Requirements:
                         * - Subtraction cannot overflow.
                         *
                         * _Available since v2.4.0._
                         */
                        function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                            require(b <= a, errorMessage);
                            uint256 c = a - b;
                    
                            return c;
                        }
                    
                        /**
                         * @dev Returns the multiplication of two unsigned integers, reverting on
                         * overflow.
                         *
                         * Counterpart to Solidity's `*` operator.
                         *
                         * Requirements:
                         * - Multiplication cannot overflow.
                         */
                        function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                            // benefit is lost if 'b' is also tested.
                            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                            if (a == 0) {
                                return 0;
                            }
                    
                            uint256 c = a * b;
                            require(c / a == b, "SafeMath: multiplication overflow");
                    
                            return c;
                        }
                    
                        /**
                         * @dev Returns the integer division of two unsigned integers. Reverts on
                         * division by zero. The result is rounded towards zero.
                         *
                         * Counterpart to Solidity's `/` operator. Note: this function uses a
                         * `revert` opcode (which leaves remaining gas untouched) while Solidity
                         * uses an invalid opcode to revert (consuming all remaining gas).
                         *
                         * Requirements:
                         * - The divisor cannot be zero.
                         */
                        function div(uint256 a, uint256 b) internal pure returns (uint256) {
                            return div(a, b, "SafeMath: division by zero");
                        }
                    
                        /**
                         * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
                         * division by zero. The result is rounded towards zero.
                         *
                         * Counterpart to Solidity's `/` operator. Note: this function uses a
                         * `revert` opcode (which leaves remaining gas untouched) while Solidity
                         * uses an invalid opcode to revert (consuming all remaining gas).
                         *
                         * Requirements:
                         * - The divisor cannot be zero.
                         *
                         * _Available since v2.4.0._
                         */
                        function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                            // Solidity only automatically asserts when dividing by 0
                            require(b > 0, errorMessage);
                            uint256 c = a / b;
                            // assert(a == b * c + a % b); // There is no case in which this doesn't hold
                    
                            return c;
                        }
                    
                        /**
                         * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                         * Reverts when dividing by zero.
                         *
                         * Counterpart to Solidity's `%` operator. This function uses a `revert`
                         * opcode (which leaves remaining gas untouched) while Solidity uses an
                         * invalid opcode to revert (consuming all remaining gas).
                         *
                         * Requirements:
                         * - The divisor cannot be zero.
                         */
                        function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                            return mod(a, b, "SafeMath: modulo by zero");
                        }
                    
                        /**
                         * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                         * Reverts with custom message when dividing by zero.
                         *
                         * Counterpart to Solidity's `%` operator. This function uses a `revert`
                         * opcode (which leaves remaining gas untouched) while Solidity uses an
                         * invalid opcode to revert (consuming all remaining gas).
                         *
                         * Requirements:
                         * - The divisor cannot be zero.
                         *
                         * _Available since v2.4.0._
                         */
                        function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                            require(b != 0, errorMessage);
                            return a % b;
                        }
                    }
                    
                    
                    
                    
                    /** 
                     *  SourceUnit: /Users/prateekyammanuru/work/marlin/contracts_github/Contracts/contracts/Stake/ClusterRewards.sol
                    */
                                
                    pragma solidity ^0.5.0;
                    
                    /**
                     * @dev Interface of the ERC20 standard as defined in the EIP. Does not include
                     * the optional functions; to access them see {ERC20Detailed}.
                     */
                    interface IERC20 {
                        /**
                         * @dev Returns the amount of tokens in existence.
                         */
                        function totalSupply() external view returns (uint256);
                    
                        /**
                         * @dev Returns the amount of tokens owned by `account`.
                         */
                        function balanceOf(address account) external view returns (uint256);
                    
                        /**
                         * @dev Moves `amount` tokens from the caller's account to `recipient`.
                         *
                         * Returns a boolean value indicating whether the operation succeeded.
                         *
                         * Emits a {Transfer} event.
                         */
                        function transfer(address recipient, uint256 amount) external returns (bool);
                    
                        /**
                         * @dev Returns the remaining number of tokens that `spender` will be
                         * allowed to spend on behalf of `owner` through {transferFrom}. This is
                         * zero by default.
                         *
                         * This value changes when {approve} or {transferFrom} are called.
                         */
                        function allowance(address owner, address spender) external view returns (uint256);
                    
                        /**
                         * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
                         *
                         * Returns a boolean value indicating whether the operation succeeded.
                         *
                         * ////IMPORTANT: Beware that changing an allowance with this method brings the risk
                         * that someone may use both the old and the new allowance by unfortunate
                         * transaction ordering. One possible solution to mitigate this race
                         * condition is to first reduce the spender's allowance to 0 and set the
                         * desired value afterwards:
                         * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                         *
                         * Emits an {Approval} event.
                         */
                        function approve(address spender, uint256 amount) external returns (bool);
                    
                        /**
                         * @dev Moves `amount` tokens from `sender` to `recipient` using the
                         * allowance mechanism. `amount` is then deducted from the caller's
                         * allowance.
                         *
                         * Returns a boolean value indicating whether the operation succeeded.
                         *
                         * Emits a {Transfer} event.
                         */
                        function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
                    
                        /**
                         * @dev Emitted when `value` tokens are moved from one account (`from`) to
                         * another (`to`).
                         *
                         * Note that `value` may be zero.
                         */
                        event Transfer(address indexed from, address indexed to, uint256 value);
                    
                        /**
                         * @dev Emitted when the allowance of a `spender` for an `owner` is set by
                         * a call to {approve}. `value` is the new allowance.
                         */
                        event Approval(address indexed owner, address indexed spender, uint256 value);
                    }
                    
                    
                    
                    
                    /** 
                     *  SourceUnit: /Users/prateekyammanuru/work/marlin/contracts_github/Contracts/contracts/Stake/ClusterRewards.sol
                    */
                                
                    pragma solidity ^0.5.0;
                    
                    
                    /*
                     * @dev Provides information about the current execution context, including the
                     * sender of the transaction and its data. While these are generally available
                     * via msg.sender and msg.data, they should not be accessed in such a direct
                     * manner, since when dealing with GSN meta-transactions the account sending and
                     * paying for execution may not be the actual sender (as far as an application
                     * is concerned).
                     *
                     * This contract is only required for intermediate, library-like contracts.
                     */
                    contract Context is Initializable {
                        // Empty internal constructor, to prevent people from mistakenly deploying
                        // an instance of this contract, which should be used via inheritance.
                        constructor () internal { }
                        // solhint-disable-previous-line no-empty-blocks
                    
                        function _msgSender() internal view returns (address payable) {
                            return msg.sender;
                        }
                    
                        function _msgData() internal view returns (bytes memory) {
                            this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                            return msg.data;
                        }
                    }
                    
                    
                    
                    
                    /** 
                     *  SourceUnit: /Users/prateekyammanuru/work/marlin/contracts_github/Contracts/contracts/Stake/ClusterRewards.sol
                    */
                                
                    pragma solidity ^0.5.0;
                    
                    
                    
                    /**
                     * @dev Implementation of the {IERC20} interface.
                     *
                     * This implementation is agnostic to the way tokens are created. This means
                     * that a supply mechanism has to be added in a derived contract using {_mint}.
                     * For a generic mechanism see {ERC20Mintable}.
                     *
                     * TIP: For a detailed writeup see our guide
                     * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
                     * to implement supply mechanisms].
                     *
                     * We have followed general OpenZeppelin guidelines: functions revert instead
                     * of returning `false` on failure. This behavior is nonetheless conventional
                     * and does not conflict with the expectations of ERC20 applications.
                     *
                     * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
                     * This allows applications to reconstruct the allowance for all accounts just
                     * by listening to said events. Other implementations of the EIP may not emit
                     * these events, as it isn't required by the specification.
                     *
                     * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
                     * functions have been added to mitigate the well-known issues around setting
                     * allowances. See {IERC20-approve}.
                     */
                    contract ERC20 is Initializable, Context, IERC20 {
                        using SafeMath for uint256;
                    
                        mapping (address => uint256) private _balances;
                    
                        mapping (address => mapping (address => uint256)) private _allowances;
                    
                        uint256 private _totalSupply;
                    
                        /**
                         * @dev See {IERC20-totalSupply}.
                         */
                        function totalSupply() public view returns (uint256) {
                            return _totalSupply;
                        }
                    
                        /**
                         * @dev See {IERC20-balanceOf}.
                         */
                        function balanceOf(address account) public view returns (uint256) {
                            return _balances[account];
                        }
                    
                        /**
                         * @dev See {IERC20-transfer}.
                         *
                         * Requirements:
                         *
                         * - `recipient` cannot be the zero address.
                         * - the caller must have a balance of at least `amount`.
                         */
                        function transfer(address recipient, uint256 amount) public returns (bool) {
                            _transfer(_msgSender(), recipient, amount);
                            return true;
                        }
                    
                        /**
                         * @dev See {IERC20-allowance}.
                         */
                        function allowance(address owner, address spender) public view returns (uint256) {
                            return _allowances[owner][spender];
                        }
                    
                        /**
                         * @dev See {IERC20-approve}.
                         *
                         * Requirements:
                         *
                         * - `spender` cannot be the zero address.
                         */
                        function approve(address spender, uint256 amount) public returns (bool) {
                            _approve(_msgSender(), spender, amount);
                            return true;
                        }
                    
                        /**
                         * @dev See {IERC20-transferFrom}.
                         *
                         * Emits an {Approval} event indicating the updated allowance. This is not
                         * required by the EIP. See the note at the beginning of {ERC20};
                         *
                         * Requirements:
                         * - `sender` and `recipient` cannot be the zero address.
                         * - `sender` must have a balance of at least `amount`.
                         * - the caller must have allowance for `sender`'s tokens of at least
                         * `amount`.
                         */
                        function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) {
                            _transfer(sender, recipient, amount);
                            _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
                            return true;
                        }
                    
                        /**
                         * @dev Atomically increases the allowance granted to `spender` by the caller.
                         *
                         * This is an alternative to {approve} that can be used as a mitigation for
                         * problems described in {IERC20-approve}.
                         *
                         * Emits an {Approval} event indicating the updated allowance.
                         *
                         * Requirements:
                         *
                         * - `spender` cannot be the zero address.
                         */
                        function increaseAllowance(address spender, uint256 addedValue) public returns (bool) {
                            _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
                            return true;
                        }
                    
                        /**
                         * @dev Atomically decreases the allowance granted to `spender` by the caller.
                         *
                         * This is an alternative to {approve} that can be used as a mitigation for
                         * problems described in {IERC20-approve}.
                         *
                         * Emits an {Approval} event indicating the updated allowance.
                         *
                         * Requirements:
                         *
                         * - `spender` cannot be the zero address.
                         * - `spender` must have allowance for the caller of at least
                         * `subtractedValue`.
                         */
                        function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) {
                            _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
                            return true;
                        }
                    
                        /**
                         * @dev Moves tokens `amount` from `sender` to `recipient`.
                         *
                         * This is internal function is equivalent to {transfer}, and can be used to
                         * e.g. implement automatic token fees, slashing mechanisms, etc.
                         *
                         * Emits a {Transfer} event.
                         *
                         * Requirements:
                         *
                         * - `sender` cannot be the zero address.
                         * - `recipient` cannot be the zero address.
                         * - `sender` must have a balance of at least `amount`.
                         */
                        function _transfer(address sender, address recipient, uint256 amount) internal {
                            require(sender != address(0), "ERC20: transfer from the zero address");
                            require(recipient != address(0), "ERC20: transfer to the zero address");
                    
                            _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
                            _balances[recipient] = _balances[recipient].add(amount);
                            emit Transfer(sender, recipient, amount);
                        }
                    
                        /** @dev Creates `amount` tokens and assigns them to `account`, increasing
                         * the total supply.
                         *
                         * Emits a {Transfer} event with `from` set to the zero address.
                         *
                         * Requirements
                         *
                         * - `to` cannot be the zero address.
                         */
                        function _mint(address account, uint256 amount) internal {
                            require(account != address(0), "ERC20: mint to the zero address");
                    
                            _totalSupply = _totalSupply.add(amount);
                            _balances[account] = _balances[account].add(amount);
                            emit Transfer(address(0), account, amount);
                        }
                    
                        /**
                         * @dev Destroys `amount` tokens from `account`, reducing the
                         * total supply.
                         *
                         * Emits a {Transfer} event with `to` set to the zero address.
                         *
                         * Requirements
                         *
                         * - `account` cannot be the zero address.
                         * - `account` must have at least `amount` tokens.
                         */
                        function _burn(address account, uint256 amount) internal {
                            require(account != address(0), "ERC20: burn from the zero address");
                    
                            _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
                            _totalSupply = _totalSupply.sub(amount);
                            emit Transfer(account, address(0), amount);
                        }
                    
                        /**
                         * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens.
                         *
                         * This is internal function is equivalent to `approve`, and can be used to
                         * e.g. set automatic allowances for certain subsystems, etc.
                         *
                         * Emits an {Approval} event.
                         *
                         * Requirements:
                         *
                         * - `owner` cannot be the zero address.
                         * - `spender` cannot be the zero address.
                         */
                        function _approve(address owner, address spender, uint256 amount) internal {
                            require(owner != address(0), "ERC20: approve from the zero address");
                            require(spender != address(0), "ERC20: approve to the zero address");
                    
                            _allowances[owner][spender] = amount;
                            emit Approval(owner, spender, amount);
                        }
                    
                        /**
                         * @dev Destroys `amount` tokens from `account`.`amount` is then deducted
                         * from the caller's allowance.
                         *
                         * See {_burn} and {_approve}.
                         */
                        function _burnFrom(address account, uint256 amount) internal {
                            _burn(account, amount);
                            _approve(account, _msgSender(), _allowances[account][_msgSender()].sub(amount, "ERC20: burn amount exceeds allowance"));
                        }
                    
                        uint256[50] private ______gap;
                    }
                    
                    
                    
                    
                    /** 
                     *  SourceUnit: /Users/prateekyammanuru/work/marlin/contracts_github/Contracts/contracts/Stake/ClusterRewards.sol
                    */
                                
                    pragma solidity ^0.5.0;
                    
                    
                    /**
                     * @dev Contract module which provides a basic access control mechanism, where
                     * there is an account (an owner) that can be granted exclusive access to
                     * specific functions.
                     *
                     * This module is used through inheritance. It will make available the modifier
                     * `onlyOwner`, which can be aplied to your functions to restrict their use to
                     * the owner.
                     */
                    contract Ownable is Initializable, Context {
                        address private _owner;
                    
                        event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                    
                        /**
                         * @dev Initializes the contract setting the deployer as the initial owner.
                         */
                        function initialize(address sender) public initializer {
                            _owner = sender;
                            emit OwnershipTransferred(address(0), _owner);
                        }
                    
                        /**
                         * @dev Returns the address of the current owner.
                         */
                        function owner() public view returns (address) {
                            return _owner;
                        }
                    
                        /**
                         * @dev Throws if called by any account other than the owner.
                         */
                        modifier onlyOwner() {
                            require(isOwner(), "Ownable: caller is not the owner");
                            _;
                        }
                    
                        /**
                         * @dev Returns true if the caller is the current owner.
                         */
                        function isOwner() public view returns (bool) {
                            return _msgSender() == _owner;
                        }
                    
                        /**
                         * @dev Leaves the contract without owner. It will not be possible to call
                         * `onlyOwner` functions anymore. Can only be called by the current owner.
                         *
                         * > Note: Renouncing ownership will leave the contract without an owner,
                         * thereby removing any functionality that is only available to the owner.
                         */
                        function renounceOwnership() public onlyOwner {
                            emit OwnershipTransferred(_owner, address(0));
                            _owner = address(0);
                        }
                    
                        /**
                         * @dev Transfers ownership of the contract to a new account (`newOwner`).
                         * Can only be called by the current owner.
                         */
                        function transferOwnership(address newOwner) public onlyOwner {
                            _transferOwnership(newOwner);
                        }
                    
                        /**
                         * @dev Transfers ownership of the contract to a new account (`newOwner`).
                         */
                        function _transferOwnership(address newOwner) internal {
                            require(newOwner != address(0), "Ownable: new owner is the zero address");
                            emit OwnershipTransferred(_owner, newOwner);
                            _owner = newOwner;
                        }
                    
                        uint256[50] private ______gap;
                    }
                    
                    
                    /** 
                     *  SourceUnit: /Users/prateekyammanuru/work/marlin/contracts_github/Contracts/contracts/Stake/ClusterRewards.sol
                    */
                    
                    pragma solidity >=0.4.21 <0.7.0;
                    
                    
                    contract ClusterRewards is Initializable, Ownable {
                    
                        using SafeMath for uint256;
                    
                        mapping(address => uint256) public clusterRewards;
                    
                        mapping(bytes32 => uint256) public rewardWeight;
                        uint256 totalWeight;
                        uint256 public totalRewardsPerEpoch;
                        uint256 payoutDenomination;
                    
                        address rewardDelegatorsAddress;
                        ERC20 POND;
                        address public feeder;
                        mapping(uint256 => uint256) rewardDistributedPerEpoch;
                        uint256 latestNewEpochRewardAt;
                        uint256 public rewardDistributionWaitTime;
                    
                        event NetworkAdded(bytes32 networkId, uint256 rewardPerEpoch);
                        event NetworkRemoved(bytes32 networkId);
                        event NetworkRewardUpdated(bytes32 networkId, uint256 updatedRewardPerEpoch);
                        event ClusterRewarded(bytes32 networkId);
                        event FeederChanged(address _newFeeder);
                        event RewardDelegatorAddressUpdated(address _updatedRewardDelegator);
                        event PONDAddressUpdated(address _updatedPOND);
                        event RewardPerEpochChanged(uint256 _updatedRewardPerEpoch);
                        event PayoutDenominationChanged(uint256 _updatedPayoutDenomination);
                        event RewardDistributionWaitTimeUpdated(uint256 _updatedRewardDistributionWaitTime);
                    
                        modifier onlyRewardDelegatorsContract() {
                            require(msg.sender == rewardDelegatorsAddress, "Sender not Reward Delegators contract");
                            _;
                        }
                    
                        modifier onlyFeeder() {
                            require(msg.sender == feeder, "Sender not feeder");
                            _;
                        }
                    
                        function initialize(
                            address _owner, 
                            address _rewardDelegatorsAddress, 
                            bytes32[] memory _networkIds,
                            uint256[] memory _rewardWeight,
                            uint256 _totalRewardsPerEpoch, 
                            address _PONDAddress,
                            uint256 _payoutDenomination,
                            address _feeder,
                            uint256 _rewardDistributionWaitTime) 
                            public
                            initializer
                        {
                            require(
                                _networkIds.length == _rewardWeight.length, 
                                "CRW:I-Each NetworkId need a corresponding RewardPerEpoch and vice versa"
                            );
                            super.initialize(_owner);
                            uint256 weight = 0;
                            rewardDelegatorsAddress = _rewardDelegatorsAddress;
                            for(uint256 i=0; i < _networkIds.length; i++) {
                                rewardWeight[_networkIds[i]] = _rewardWeight[i];
                                weight = weight.add(_rewardWeight[i]);
                                emit NetworkAdded(_networkIds[i], _rewardWeight[i]);
                            }
                            totalWeight = weight;
                            totalRewardsPerEpoch = _totalRewardsPerEpoch;
                            POND = ERC20(_PONDAddress);
                            payoutDenomination = _payoutDenomination;
                            feeder = _feeder;
                            rewardDistributionWaitTime = _rewardDistributionWaitTime;
                        }
                    
                        function changeFeeder(address _newFeeder) external onlyOwner {
                            feeder = _newFeeder;
                            emit FeederChanged(_newFeeder);
                        }
                    
                        function addNetwork(bytes32 _networkId, uint256 _rewardWeight) external onlyOwner {
                            require(rewardWeight[_networkId] == 0, "CRW:AN-Network already exists");
                            require(_rewardWeight != 0, "CRW:AN-Reward cant be 0");
                            rewardWeight[_networkId] = _rewardWeight;
                            totalWeight = totalWeight.add(_rewardWeight);
                            emit NetworkAdded(_networkId, _rewardWeight);
                        }
                    
                        function removeNetwork(bytes32 _networkId) external onlyOwner {
                            uint256 networkWeight = rewardWeight[_networkId];
                            require( networkWeight != 0, "CRW:RN-Network doesnt exist");
                            delete rewardWeight[_networkId];
                            totalWeight = totalWeight.sub(networkWeight);
                            emit NetworkRemoved(_networkId);
                        }
                    
                        function changeNetworkReward(bytes32 _networkId, uint256 _updatedRewardWeight) external onlyOwner {
                            uint256 networkWeight = rewardWeight[_networkId];
                            require( networkWeight != 0, "CRW:CNR-Network doesnt exist");
                            rewardWeight[_networkId] = _updatedRewardWeight;
                            totalWeight = totalWeight.sub(networkWeight).add(_updatedRewardWeight);
                            emit NetworkRewardUpdated(_networkId, _updatedRewardWeight);
                        }
                    
                    
                        function feed(
                            bytes32 _networkId, 
                            address[] calldata _clusters, 
                            uint256[] calldata _payouts, 
                            uint256 _epoch
                        ) external onlyFeeder {
                            uint256 rewardDistributed = rewardDistributedPerEpoch[_epoch];
                            if(rewardDistributed == 0) {
                                require(
                                    block.timestamp > latestNewEpochRewardAt.add(rewardDistributionWaitTime), 
                                    "CRW:F-Cant distribute reward for new epoch within such short interval"
                                );
                                latestNewEpochRewardAt = block.timestamp;
                            }
                            uint256 totalNetworkWeight = totalWeight;
                            uint256 currentTotalRewardsPerEpoch = totalRewardsPerEpoch;
                            uint256 currentPayoutDenomination = payoutDenomination;
                            uint256 networkRewardWeight = rewardWeight[_networkId];
                            for(uint256 i=0; i < _clusters.length; i++) {
                              uint256 clusterReward = currentTotalRewardsPerEpoch
                                                        .mul(networkRewardWeight)
                                                        .mul(_payouts[i])
                                                        .div(totalNetworkWeight)
                                                        .div(currentPayoutDenomination);
                                rewardDistributed = rewardDistributed.add(clusterReward);
                                clusterRewards[_clusters[i]] = clusterRewards[_clusters[i]].add(clusterReward);
                            }
                            require(
                                rewardDistributed <= totalRewardsPerEpoch, 
                                "CRW:F-Reward Distributed  cant  be more  than totalRewardPerEpoch"
                            );
                            rewardDistributedPerEpoch[_epoch] = rewardDistributed;
                            emit ClusterRewarded(_networkId);
                        }
                    
                        function getRewardPerEpoch(bytes32 _networkId) external view returns(uint256) {
                            return totalRewardsPerEpoch.mul(rewardWeight[_networkId]).div(totalWeight);
                        }
                    
                        // only cluster registry is necessary because the rewards 
                        // should be updated in the cluster registry against the cluster
                        function claimReward(address _cluster) external onlyRewardDelegatorsContract returns(uint256) {
                            uint256 pendingRewards = clusterRewards[_cluster];
                            if(pendingRewards > 1) {
                                uint256 rewardsToTransfer = pendingRewards.sub(1);
                                clusterRewards[_cluster] = 1;
                                return rewardsToTransfer;
                            }
                            return 0;
                        }
                    
                        function transferRewardsToRewardDelegators() external onlyOwner returns(uint256) {
                            POND.transfer(rewardDelegatorsAddress, POND.balanceOf(address(this)));
                        }
                    
                        function updateRewardDelegatorAddress(address _updatedRewardDelegator) external onlyOwner {
                            require(
                                _updatedRewardDelegator != address(0),
                                "CRW:URDA-Updated Reward delegator address cant be 0"
                            );
                            rewardDelegatorsAddress = _updatedRewardDelegator;
                            emit RewardDelegatorAddressUpdated(_updatedRewardDelegator);
                        }
                    
                        function updatePONDAddress(address _updatedPOND) external onlyOwner {
                            require(
                                _updatedPOND != address(0),
                                "CRW:UPA-POND token address cant be 0"
                            );
                            POND = ERC20(_updatedPOND);
                            emit PONDAddressUpdated(_updatedPOND);
                        }
                    
                        function changeRewardPerEpoch(uint256 _updatedRewardPerEpoch) external onlyOwner {
                            totalRewardsPerEpoch = _updatedRewardPerEpoch;
                            emit RewardPerEpochChanged(_updatedRewardPerEpoch);
                        }
                    
                        function changePayoutDenomination(uint256 _updatedPayoutDenomination) external onlyOwner {
                            payoutDenomination = _updatedPayoutDenomination;
                            emit PayoutDenominationChanged(_updatedPayoutDenomination);
                        }
                    
                        function updateRewardDistributionWaitTime(uint256 _updatedRewardDistributionWaitTime) external onlyOwner {
                            rewardDistributionWaitTime = _updatedRewardDistributionWaitTime;
                            emit RewardDistributionWaitTimeUpdated(_updatedRewardDistributionWaitTime);
                        }
                    }

                    File 8 of 10: ClusterRegistryProxy
                    pragma solidity >=0.4.21 <0.7.0;
                    
                    
                    /// @title Contract to reward overlapping stakes
                    /// @author Marlin
                    /// @notice Use this contract only for testing
                    /// @dev Contract may or may not change in future (depending upon the new slots in proxy-store)
                    contract ClusterRegistryProxy {
                        bytes32 internal constant IMPLEMENTATION_SLOT = bytes32(
                            uint256(keccak256("eip1967.proxy.implementation")) - 1
                        );
                        bytes32 internal constant PROXY_ADMIN_SLOT = bytes32(
                            uint256(keccak256("eip1967.proxy.admin")) - 1
                        );
                    
                        constructor(address contractLogic, address proxyAdmin) public {
                            // save the code address
                            bytes32 slot = IMPLEMENTATION_SLOT;
                            assembly {
                                sstore(slot, contractLogic)
                            }
                            // save the proxy admin
                            slot = PROXY_ADMIN_SLOT;
                            address sender = proxyAdmin;
                            assembly {
                                sstore(slot, sender)
                            }
                        }
                    
                        function updateAdmin(address _newAdmin) public {
                            require(
                                msg.sender == getAdmin(),
                                "Only the current admin should be able to new admin"
                            );
                            bytes32 slot = PROXY_ADMIN_SLOT;
                            assembly {
                                sstore(slot, _newAdmin)
                            }
                        }
                    
                        /// @author Marlin
                        /// @dev Only admin can update the contract
                        /// @param _newLogic address is the address of the contract that has to updated to
                        function updateLogic(address _newLogic) public {
                            require(
                                msg.sender == getAdmin(),
                                "Only Admin should be able to update the contracts"
                            );
                            bytes32 slot = IMPLEMENTATION_SLOT;
                            assembly {
                                sstore(slot, _newLogic)
                            }
                        }
                    
                        /// @author Marlin
                        /// @dev use assembly as contract store slot is manually decided
                        function getAdmin() internal view returns (address result) {
                            bytes32 slot = PROXY_ADMIN_SLOT;
                            assembly {
                                result := sload(slot)
                            }
                        }
                    
                        /// @author Marlin
                        /// @dev add functionality to forward the balance as well.
                        function() external payable {
                            bytes32 slot = IMPLEMENTATION_SLOT;
                            assembly {
                                let contractLogic := sload(slot)
                                calldatacopy(0x0, 0x0, calldatasize())
                                let success := delegatecall(
                                    sub(gas(), 10000),
                                    contractLogic,
                                    0x0,
                                    calldatasize(),
                                    0,
                                    0
                                )
                                let retSz := returndatasize()
                                returndatacopy(0, 0, retSz)
                    
                                switch success
                                    case 0 {
                                        revert(0, retSz)
                                    }
                                    default {
                                        return(0, retSz)
                                    }
                            }
                        }
                    }

                    File 9 of 10: ClusterRegistry
                    /** 
                     *  SourceUnit: /Users/prateekyammanuru/work/marlin/contracts_github/Contracts/contracts/Stake/ClusterRegistry.sol
                    */
                                
                    pragma solidity >=0.4.24 <0.7.0;
                    
                    
                    /**
                     * @title Initializable
                     *
                     * @dev Helper contract to support initializer functions. To use it, replace
                     * the constructor with a function that has the `initializer` modifier.
                     * WARNING: Unlike constructors, initializer functions must be manually
                     * invoked. This applies both to deploying an Initializable contract, as well
                     * as extending an Initializable contract via inheritance.
                     * WARNING: When used with inheritance, manual care must be taken to not invoke
                     * a parent initializer twice, or ensure that all initializers are idempotent,
                     * because this is not dealt with automatically as with constructors.
                     */
                    contract Initializable {
                    
                      /**
                       * @dev Indicates that the contract has been initialized.
                       */
                      bool private initialized;
                    
                      /**
                       * @dev Indicates that the contract is in the process of being initialized.
                       */
                      bool private initializing;
                    
                      /**
                       * @dev Modifier to use in the initializer function of a contract.
                       */
                      modifier initializer() {
                        require(initializing || isConstructor() || !initialized, "Contract instance has already been initialized");
                    
                        bool isTopLevelCall = !initializing;
                        if (isTopLevelCall) {
                          initializing = true;
                          initialized = true;
                        }
                    
                        _;
                    
                        if (isTopLevelCall) {
                          initializing = false;
                        }
                      }
                    
                      /// @dev Returns true if and only if the function is running in the constructor
                      function isConstructor() private view returns (bool) {
                        // extcodesize checks the size of the code stored in an address, and
                        // address returns the current address. Since the code is still not
                        // deployed when running a constructor, any checks on its code size will
                        // yield zero, making it an effective way to detect if a contract is
                        // under construction or not.
                        address self = address(this);
                        uint256 cs;
                        assembly { cs := extcodesize(self) }
                        return cs == 0;
                      }
                    
                      // Reserved storage space to allow for layout changes in the future.
                      uint256[50] private ______gap;
                    }
                    
                    
                    
                    
                    /** 
                     *  SourceUnit: /Users/prateekyammanuru/work/marlin/contracts_github/Contracts/contracts/Stake/ClusterRegistry.sol
                    */
                                
                    pragma solidity ^0.5.0;
                    
                    /**
                     * @dev Wrappers over Solidity's arithmetic operations with added overflow
                     * checks.
                     *
                     * Arithmetic operations in Solidity wrap on overflow. This can easily result
                     * in bugs, because programmers usually assume that an overflow raises an
                     * error, which is the standard behavior in high level programming languages.
                     * `SafeMath` restores this intuition by reverting the transaction when an
                     * operation overflows.
                     *
                     * Using this library instead of the unchecked operations eliminates an entire
                     * class of bugs, so it's recommended to use it always.
                     */
                    library SafeMath {
                        /**
                         * @dev Returns the addition of two unsigned integers, reverting on
                         * overflow.
                         *
                         * Counterpart to Solidity's `+` operator.
                         *
                         * Requirements:
                         * - Addition cannot overflow.
                         */
                        function add(uint256 a, uint256 b) internal pure returns (uint256) {
                            uint256 c = a + b;
                            require(c >= a, "SafeMath: addition overflow");
                    
                            return c;
                        }
                    
                        /**
                         * @dev Returns the subtraction of two unsigned integers, reverting on
                         * overflow (when the result is negative).
                         *
                         * Counterpart to Solidity's `-` operator.
                         *
                         * Requirements:
                         * - Subtraction cannot overflow.
                         */
                        function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                            return sub(a, b, "SafeMath: subtraction overflow");
                        }
                    
                        /**
                         * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
                         * overflow (when the result is negative).
                         *
                         * Counterpart to Solidity's `-` operator.
                         *
                         * Requirements:
                         * - Subtraction cannot overflow.
                         *
                         * _Available since v2.4.0._
                         */
                        function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                            require(b <= a, errorMessage);
                            uint256 c = a - b;
                    
                            return c;
                        }
                    
                        /**
                         * @dev Returns the multiplication of two unsigned integers, reverting on
                         * overflow.
                         *
                         * Counterpart to Solidity's `*` operator.
                         *
                         * Requirements:
                         * - Multiplication cannot overflow.
                         */
                        function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                            // benefit is lost if 'b' is also tested.
                            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                            if (a == 0) {
                                return 0;
                            }
                    
                            uint256 c = a * b;
                            require(c / a == b, "SafeMath: multiplication overflow");
                    
                            return c;
                        }
                    
                        /**
                         * @dev Returns the integer division of two unsigned integers. Reverts on
                         * division by zero. The result is rounded towards zero.
                         *
                         * Counterpart to Solidity's `/` operator. Note: this function uses a
                         * `revert` opcode (which leaves remaining gas untouched) while Solidity
                         * uses an invalid opcode to revert (consuming all remaining gas).
                         *
                         * Requirements:
                         * - The divisor cannot be zero.
                         */
                        function div(uint256 a, uint256 b) internal pure returns (uint256) {
                            return div(a, b, "SafeMath: division by zero");
                        }
                    
                        /**
                         * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
                         * division by zero. The result is rounded towards zero.
                         *
                         * Counterpart to Solidity's `/` operator. Note: this function uses a
                         * `revert` opcode (which leaves remaining gas untouched) while Solidity
                         * uses an invalid opcode to revert (consuming all remaining gas).
                         *
                         * Requirements:
                         * - The divisor cannot be zero.
                         *
                         * _Available since v2.4.0._
                         */
                        function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                            // Solidity only automatically asserts when dividing by 0
                            require(b > 0, errorMessage);
                            uint256 c = a / b;
                            // assert(a == b * c + a % b); // There is no case in which this doesn't hold
                    
                            return c;
                        }
                    
                        /**
                         * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                         * Reverts when dividing by zero.
                         *
                         * Counterpart to Solidity's `%` operator. This function uses a `revert`
                         * opcode (which leaves remaining gas untouched) while Solidity uses an
                         * invalid opcode to revert (consuming all remaining gas).
                         *
                         * Requirements:
                         * - The divisor cannot be zero.
                         */
                        function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                            return mod(a, b, "SafeMath: modulo by zero");
                        }
                    
                        /**
                         * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                         * Reverts with custom message when dividing by zero.
                         *
                         * Counterpart to Solidity's `%` operator. This function uses a `revert`
                         * opcode (which leaves remaining gas untouched) while Solidity uses an
                         * invalid opcode to revert (consuming all remaining gas).
                         *
                         * Requirements:
                         * - The divisor cannot be zero.
                         *
                         * _Available since v2.4.0._
                         */
                        function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                            require(b != 0, errorMessage);
                            return a % b;
                        }
                    }
                    
                    
                    
                    
                    /** 
                     *  SourceUnit: /Users/prateekyammanuru/work/marlin/contracts_github/Contracts/contracts/Stake/ClusterRegistry.sol
                    */
                                
                    pragma solidity ^0.5.0;
                    
                    /**
                     * @dev Interface of the ERC20 standard as defined in the EIP. Does not include
                     * the optional functions; to access them see {ERC20Detailed}.
                     */
                    interface IERC20 {
                        /**
                         * @dev Returns the amount of tokens in existence.
                         */
                        function totalSupply() external view returns (uint256);
                    
                        /**
                         * @dev Returns the amount of tokens owned by `account`.
                         */
                        function balanceOf(address account) external view returns (uint256);
                    
                        /**
                         * @dev Moves `amount` tokens from the caller's account to `recipient`.
                         *
                         * Returns a boolean value indicating whether the operation succeeded.
                         *
                         * Emits a {Transfer} event.
                         */
                        function transfer(address recipient, uint256 amount) external returns (bool);
                    
                        /**
                         * @dev Returns the remaining number of tokens that `spender` will be
                         * allowed to spend on behalf of `owner` through {transferFrom}. This is
                         * zero by default.
                         *
                         * This value changes when {approve} or {transferFrom} are called.
                         */
                        function allowance(address owner, address spender) external view returns (uint256);
                    
                        /**
                         * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
                         *
                         * Returns a boolean value indicating whether the operation succeeded.
                         *
                         * ////IMPORTANT: Beware that changing an allowance with this method brings the risk
                         * that someone may use both the old and the new allowance by unfortunate
                         * transaction ordering. One possible solution to mitigate this race
                         * condition is to first reduce the spender's allowance to 0 and set the
                         * desired value afterwards:
                         * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                         *
                         * Emits an {Approval} event.
                         */
                        function approve(address spender, uint256 amount) external returns (bool);
                    
                        /**
                         * @dev Moves `amount` tokens from `sender` to `recipient` using the
                         * allowance mechanism. `amount` is then deducted from the caller's
                         * allowance.
                         *
                         * Returns a boolean value indicating whether the operation succeeded.
                         *
                         * Emits a {Transfer} event.
                         */
                        function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
                    
                        /**
                         * @dev Emitted when `value` tokens are moved from one account (`from`) to
                         * another (`to`).
                         *
                         * Note that `value` may be zero.
                         */
                        event Transfer(address indexed from, address indexed to, uint256 value);
                    
                        /**
                         * @dev Emitted when the allowance of a `spender` for an `owner` is set by
                         * a call to {approve}. `value` is the new allowance.
                         */
                        event Approval(address indexed owner, address indexed spender, uint256 value);
                    }
                    
                    
                    
                    
                    /** 
                     *  SourceUnit: /Users/prateekyammanuru/work/marlin/contracts_github/Contracts/contracts/Stake/ClusterRegistry.sol
                    */
                                
                    pragma solidity ^0.5.0;
                    
                    
                    /*
                     * @dev Provides information about the current execution context, including the
                     * sender of the transaction and its data. While these are generally available
                     * via msg.sender and msg.data, they should not be accessed in such a direct
                     * manner, since when dealing with GSN meta-transactions the account sending and
                     * paying for execution may not be the actual sender (as far as an application
                     * is concerned).
                     *
                     * This contract is only required for intermediate, library-like contracts.
                     */
                    contract Context is Initializable {
                        // Empty internal constructor, to prevent people from mistakenly deploying
                        // an instance of this contract, which should be used via inheritance.
                        constructor () internal { }
                        // solhint-disable-previous-line no-empty-blocks
                    
                        function _msgSender() internal view returns (address payable) {
                            return msg.sender;
                        }
                    
                        function _msgData() internal view returns (bytes memory) {
                            this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                            return msg.data;
                        }
                    }
                    
                    
                    
                    
                    /** 
                     *  SourceUnit: /Users/prateekyammanuru/work/marlin/contracts_github/Contracts/contracts/Stake/ClusterRegistry.sol
                    */
                                
                    pragma solidity ^0.5.0;
                    
                    ////import "@openzeppelin/upgrades/contracts/Initializable.sol";
                    
                    ////import "../../GSN/Context.sol";
                    ////import "./IERC20.sol";
                    ////import "../../math/SafeMath.sol";
                    
                    /**
                     * @dev Implementation of the {IERC20} interface.
                     *
                     * This implementation is agnostic to the way tokens are created. This means
                     * that a supply mechanism has to be added in a derived contract using {_mint}.
                     * For a generic mechanism see {ERC20Mintable}.
                     *
                     * TIP: For a detailed writeup see our guide
                     * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
                     * to implement supply mechanisms].
                     *
                     * We have followed general OpenZeppelin guidelines: functions revert instead
                     * of returning `false` on failure. This behavior is nonetheless conventional
                     * and does not conflict with the expectations of ERC20 applications.
                     *
                     * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
                     * This allows applications to reconstruct the allowance for all accounts just
                     * by listening to said events. Other implementations of the EIP may not emit
                     * these events, as it isn't required by the specification.
                     *
                     * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
                     * functions have been added to mitigate the well-known issues around setting
                     * allowances. See {IERC20-approve}.
                     */
                    contract ERC20 is Initializable, Context, IERC20 {
                        using SafeMath for uint256;
                    
                        mapping (address => uint256) private _balances;
                    
                        mapping (address => mapping (address => uint256)) private _allowances;
                    
                        uint256 private _totalSupply;
                    
                        /**
                         * @dev See {IERC20-totalSupply}.
                         */
                        function totalSupply() public view returns (uint256) {
                            return _totalSupply;
                        }
                    
                        /**
                         * @dev See {IERC20-balanceOf}.
                         */
                        function balanceOf(address account) public view returns (uint256) {
                            return _balances[account];
                        }
                    
                        /**
                         * @dev See {IERC20-transfer}.
                         *
                         * Requirements:
                         *
                         * - `recipient` cannot be the zero address.
                         * - the caller must have a balance of at least `amount`.
                         */
                        function transfer(address recipient, uint256 amount) public returns (bool) {
                            _transfer(_msgSender(), recipient, amount);
                            return true;
                        }
                    
                        /**
                         * @dev See {IERC20-allowance}.
                         */
                        function allowance(address owner, address spender) public view returns (uint256) {
                            return _allowances[owner][spender];
                        }
                    
                        /**
                         * @dev See {IERC20-approve}.
                         *
                         * Requirements:
                         *
                         * - `spender` cannot be the zero address.
                         */
                        function approve(address spender, uint256 amount) public returns (bool) {
                            _approve(_msgSender(), spender, amount);
                            return true;
                        }
                    
                        /**
                         * @dev See {IERC20-transferFrom}.
                         *
                         * Emits an {Approval} event indicating the updated allowance. This is not
                         * required by the EIP. See the note at the beginning of {ERC20};
                         *
                         * Requirements:
                         * - `sender` and `recipient` cannot be the zero address.
                         * - `sender` must have a balance of at least `amount`.
                         * - the caller must have allowance for `sender`'s tokens of at least
                         * `amount`.
                         */
                        function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) {
                            _transfer(sender, recipient, amount);
                            _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
                            return true;
                        }
                    
                        /**
                         * @dev Atomically increases the allowance granted to `spender` by the caller.
                         *
                         * This is an alternative to {approve} that can be used as a mitigation for
                         * problems described in {IERC20-approve}.
                         *
                         * Emits an {Approval} event indicating the updated allowance.
                         *
                         * Requirements:
                         *
                         * - `spender` cannot be the zero address.
                         */
                        function increaseAllowance(address spender, uint256 addedValue) public returns (bool) {
                            _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
                            return true;
                        }
                    
                        /**
                         * @dev Atomically decreases the allowance granted to `spender` by the caller.
                         *
                         * This is an alternative to {approve} that can be used as a mitigation for
                         * problems described in {IERC20-approve}.
                         *
                         * Emits an {Approval} event indicating the updated allowance.
                         *
                         * Requirements:
                         *
                         * - `spender` cannot be the zero address.
                         * - `spender` must have allowance for the caller of at least
                         * `subtractedValue`.
                         */
                        function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) {
                            _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
                            return true;
                        }
                    
                        /**
                         * @dev Moves tokens `amount` from `sender` to `recipient`.
                         *
                         * This is internal function is equivalent to {transfer}, and can be used to
                         * e.g. implement automatic token fees, slashing mechanisms, etc.
                         *
                         * Emits a {Transfer} event.
                         *
                         * Requirements:
                         *
                         * - `sender` cannot be the zero address.
                         * - `recipient` cannot be the zero address.
                         * - `sender` must have a balance of at least `amount`.
                         */
                        function _transfer(address sender, address recipient, uint256 amount) internal {
                            require(sender != address(0), "ERC20: transfer from the zero address");
                            require(recipient != address(0), "ERC20: transfer to the zero address");
                    
                            _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
                            _balances[recipient] = _balances[recipient].add(amount);
                            emit Transfer(sender, recipient, amount);
                        }
                    
                        /** @dev Creates `amount` tokens and assigns them to `account`, increasing
                         * the total supply.
                         *
                         * Emits a {Transfer} event with `from` set to the zero address.
                         *
                         * Requirements
                         *
                         * - `to` cannot be the zero address.
                         */
                        function _mint(address account, uint256 amount) internal {
                            require(account != address(0), "ERC20: mint to the zero address");
                    
                            _totalSupply = _totalSupply.add(amount);
                            _balances[account] = _balances[account].add(amount);
                            emit Transfer(address(0), account, amount);
                        }
                    
                        /**
                         * @dev Destroys `amount` tokens from `account`, reducing the
                         * total supply.
                         *
                         * Emits a {Transfer} event with `to` set to the zero address.
                         *
                         * Requirements
                         *
                         * - `account` cannot be the zero address.
                         * - `account` must have at least `amount` tokens.
                         */
                        function _burn(address account, uint256 amount) internal {
                            require(account != address(0), "ERC20: burn from the zero address");
                    
                            _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
                            _totalSupply = _totalSupply.sub(amount);
                            emit Transfer(account, address(0), amount);
                        }
                    
                        /**
                         * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens.
                         *
                         * This is internal function is equivalent to `approve`, and can be used to
                         * e.g. set automatic allowances for certain subsystems, etc.
                         *
                         * Emits an {Approval} event.
                         *
                         * Requirements:
                         *
                         * - `owner` cannot be the zero address.
                         * - `spender` cannot be the zero address.
                         */
                        function _approve(address owner, address spender, uint256 amount) internal {
                            require(owner != address(0), "ERC20: approve from the zero address");
                            require(spender != address(0), "ERC20: approve to the zero address");
                    
                            _allowances[owner][spender] = amount;
                            emit Approval(owner, spender, amount);
                        }
                    
                        /**
                         * @dev Destroys `amount` tokens from `account`.`amount` is then deducted
                         * from the caller's allowance.
                         *
                         * See {_burn} and {_approve}.
                         */
                        function _burnFrom(address account, uint256 amount) internal {
                            _burn(account, amount);
                            _approve(account, _msgSender(), _allowances[account][_msgSender()].sub(amount, "ERC20: burn amount exceeds allowance"));
                        }
                    
                        uint256[50] private ______gap;
                    }
                    
                    
                    
                    
                    /** 
                     *  SourceUnit: /Users/prateekyammanuru/work/marlin/contracts_github/Contracts/contracts/Stake/ClusterRegistry.sol
                    */
                                
                    pragma solidity ^0.5.0;
                    
                    ////import "@openzeppelin/upgrades/contracts/Initializable.sol";
                    
                    ////import "../GSN/Context.sol";
                    
                    /**
                     * @dev Contract module which provides a basic access control mechanism, where
                     * there is an account (an owner) that can be granted exclusive access to
                     * specific functions.
                     *
                     * This module is used through inheritance. It will make available the modifier
                     * `onlyOwner`, which can be aplied to your functions to restrict their use to
                     * the owner.
                     */
                    contract Ownable is Initializable, Context {
                        address private _owner;
                    
                        event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                    
                        /**
                         * @dev Initializes the contract setting the deployer as the initial owner.
                         */
                        function initialize(address sender) public initializer {
                            _owner = sender;
                            emit OwnershipTransferred(address(0), _owner);
                        }
                    
                        /**
                         * @dev Returns the address of the current owner.
                         */
                        function owner() public view returns (address) {
                            return _owner;
                        }
                    
                        /**
                         * @dev Throws if called by any account other than the owner.
                         */
                        modifier onlyOwner() {
                            require(isOwner(), "Ownable: caller is not the owner");
                            _;
                        }
                    
                        /**
                         * @dev Returns true if the caller is the current owner.
                         */
                        function isOwner() public view returns (bool) {
                            return _msgSender() == _owner;
                        }
                    
                        /**
                         * @dev Leaves the contract without owner. It will not be possible to call
                         * `onlyOwner` functions anymore. Can only be called by the current owner.
                         *
                         * > Note: Renouncing ownership will leave the contract without an owner,
                         * thereby removing any functionality that is only available to the owner.
                         */
                        function renounceOwnership() public onlyOwner {
                            emit OwnershipTransferred(_owner, address(0));
                            _owner = address(0);
                        }
                    
                        /**
                         * @dev Transfers ownership of the contract to a new account (`newOwner`).
                         * Can only be called by the current owner.
                         */
                        function transferOwnership(address newOwner) public onlyOwner {
                            _transferOwnership(newOwner);
                        }
                    
                        /**
                         * @dev Transfers ownership of the contract to a new account (`newOwner`).
                         */
                        function _transferOwnership(address newOwner) internal {
                            require(newOwner != address(0), "Ownable: new owner is the zero address");
                            emit OwnershipTransferred(_owner, newOwner);
                            _owner = newOwner;
                        }
                    
                        uint256[50] private ______gap;
                    }
                    
                    
                    /** 
                     *  SourceUnit: /Users/prateekyammanuru/work/marlin/contracts_github/Contracts/contracts/Stake/ClusterRegistry.sol
                    */
                    
                    pragma solidity >=0.4.21 <0.7.0;
                    
                    
                    contract ClusterRegistry is Initializable, Ownable {
                    
                        using SafeMath for uint256;
                    
                        uint256 constant UINT256_MAX = ~uint256(0);
                    
                        struct Cluster {
                            uint256 commission;
                            address rewardAddress;
                            address clientKey;
                            bytes32 networkId; // keccak256("ETH") // token ticker for anyother chain in place of ETH
                            Status status;
                        }
                    
                        struct Lock {
                            uint256 unlockBlock;
                            uint256 iValue;
                        }
                    
                        mapping(address => Cluster) clusters;
                    
                        mapping(bytes32 => Lock) public locks;
                        mapping(bytes32 => uint256) public lockWaitTime;
                        bytes32 constant COMMISSION_LOCK_SELECTOR = keccak256("COMMISSION_LOCK");
                        bytes32 constant SWITCH_NETWORK_LOCK_SELECTOR = keccak256("SWITCH_NETWORK_LOCK");
                        bytes32 constant UNREGISTER_LOCK_SELECTOR = keccak256("UNREGISTER_LOCK");
                    
                        enum Status{NOT_REGISTERED, REGISTERED}
                    
                        mapping(address => address) public clientKeys;
                    
                        event ClusterRegistered(
                            address cluster, 
                            bytes32 networkId, 
                            uint256 commission, 
                            address rewardAddress, 
                            address clientKey
                        );
                        event CommissionUpdateRequested(address cluster, uint256 commissionAfterUpdate, uint256 effectiveBlock);
                        event CommissionUpdated(address cluster, uint256 updatedCommission, uint256 updatedAt);
                        event RewardAddressUpdated(address cluster, address updatedRewardAddress);
                        event NetworkSwitchRequested(address cluster, bytes32 networkId, uint256 effectiveBlock);
                        event NetworkSwitched(address cluster, bytes32 networkId, uint256 updatedAt);
                        event ClientKeyUpdated(address cluster, address clientKey);
                        event ClusterUnregisterRequested(address cluster, uint256 effectiveBlock);
                        event ClusterUnregistered(address cluster, uint256 updatedAt);
                        event LockTimeUpdated(bytes32 selector, uint256 prevLockTime, uint256 updatedLockTime);
                    
                        function initialize(bytes32[] memory _selectors, uint256[] memory _lockWaitTimes, address _owner) 
                            public 
                            initializer
                        {
                            require(
                                _selectors.length == _lockWaitTimes.length,
                                "CR:I-Invalid params"
                            );
                            for(uint256 i=0; i < _selectors.length; i++) {
                                lockWaitTime[_selectors[i]] = _lockWaitTimes[i];
                                emit LockTimeUpdated(_selectors[i], 0, _lockWaitTimes[i]);
                            }
                            super.initialize(_owner);
                        }
                    
                        function updateLockWaitTime(bytes32 _selector, uint256 _updatedWaitTime) external onlyOwner {
                            emit LockTimeUpdated(_selector, lockWaitTime[_selector], _updatedWaitTime);
                            lockWaitTime[_selector] = _updatedWaitTime; 
                        }
                    
                        function register(
                            bytes32 _networkId, 
                            uint256 _commission, 
                            address _rewardAddress, 
                            address _clientKey
                        ) external returns(bool) {
                            // This happens only when the data of the cluster is registered or it wasn't registered before
                            require(
                                !isClusterValid(msg.sender), 
                                "CR:R-Cluster is already registered"
                            );
                            require(_commission <= 100, "CR:R-Commission more than 100%");
                            require(clientKeys[_clientKey] ==  address(0), "CR:R - Client key is already used");
                            clusters[msg.sender].commission = _commission;
                            clusters[msg.sender].rewardAddress = _rewardAddress;
                            clusters[msg.sender].clientKey = _clientKey;
                            clusters[msg.sender].networkId = _networkId;
                            clusters[msg.sender].status = Status.REGISTERED;
                    
                            clientKeys[_clientKey] = msg.sender;
                            
                            emit ClusterRegistered(msg.sender, _networkId, _commission, _rewardAddress, _clientKey);
                        }
                    
                        function updateCluster(uint256 _commission, bytes32 _networkId, address _rewardAddress, address _clientKey) public {
                            if(_networkId != bytes32(0)) {
                                switchNetwork(_networkId);
                            }
                            if(_rewardAddress != address(0)) {
                                updateRewardAddress(_rewardAddress);
                            }
                            if(_clientKey != address(0)) {
                                updateClientKey(_clientKey);
                            }
                            if(_commission != UINT256_MAX) {
                                updateCommission(_commission);
                            }
                        }
                    
                        function updateCommission(uint256 _commission) public {
                            require(
                                isClusterValid(msg.sender),
                                "CR:UCM-Cluster not registered"
                            );
                            require(_commission <= 100, "CR:UCM-Commission more than 100%");
                            bytes32 lockId = keccak256(abi.encodePacked(COMMISSION_LOCK_SELECTOR, msg.sender));
                            uint256 unlockBlock = locks[lockId].unlockBlock;
                            require(
                                unlockBlock < block.number, 
                                "CR:UCM-Commission update in progress"
                            );
                            if(unlockBlock != 0) {
                                uint256 currentCommission = locks[lockId].iValue;
                                clusters[msg.sender].commission = currentCommission;
                                emit CommissionUpdated(msg.sender, currentCommission, unlockBlock);
                            }
                            uint256 updatedUnlockBlock = block.number.add(lockWaitTime[COMMISSION_LOCK_SELECTOR]);
                            locks[lockId] = Lock(updatedUnlockBlock, _commission);
                            emit CommissionUpdateRequested(msg.sender, _commission, updatedUnlockBlock);
                        }
                    
                        function switchNetwork(bytes32 _networkId) public {
                            require(
                                isClusterValid(msg.sender),
                                "CR:SN-Cluster not registered"
                            );
                            bytes32 lockId = keccak256(abi.encodePacked(SWITCH_NETWORK_LOCK_SELECTOR, msg.sender));
                            uint256 unlockBlock = locks[lockId].unlockBlock;
                            require(
                                unlockBlock < block.number,
                                "CR:SN-Network switch in progress"
                            );
                            if(unlockBlock != 0) {
                                bytes32 currentNetwork = bytes32(locks[lockId].iValue);
                                clusters[msg.sender].networkId = currentNetwork;
                                emit NetworkSwitched(msg.sender, currentNetwork, unlockBlock);
                            }
                            uint256 updatedUnlockBlock = block.number.add(lockWaitTime[SWITCH_NETWORK_LOCK_SELECTOR]);
                            locks[lockId] = Lock(updatedUnlockBlock, uint256(_networkId));
                            emit NetworkSwitchRequested(msg.sender, _networkId, updatedUnlockBlock);
                        }
                    
                        function updateRewardAddress(address _rewardAddress) public {
                            require(
                                isClusterValid(msg.sender),
                                "CR:URA-Cluster not registered"
                            );
                            clusters[msg.sender].rewardAddress = _rewardAddress;
                            emit RewardAddressUpdated(msg.sender, _rewardAddress);
                        }
                    
                        function updateClientKey(address _clientKey) public {
                            // TODO: Add delay to client key updates as well
                            require(
                                isClusterValid(msg.sender),
                                "CR:UCK-Cluster not registered"
                            );
                            require(clientKeys[_clientKey] ==  address(0), "CR:UCK - Client key is already used");
                            delete clientKeys[clusters[msg.sender].clientKey];
                            clusters[msg.sender].clientKey = _clientKey;
                            clientKeys[_clientKey] = msg.sender;
                            emit ClientKeyUpdated(msg.sender, _clientKey);
                        }
                    
                        function unregister() external {
                            require(
                                clusters[msg.sender].status != Status.NOT_REGISTERED,
                                "CR:UR-Cluster not registered"
                            );
                            bytes32 lockId = keccak256(abi.encodePacked(UNREGISTER_LOCK_SELECTOR, msg.sender));
                            uint256 unlockBlock = locks[lockId].unlockBlock;
                            require(
                                unlockBlock < block.number,
                                "CR:UR-Unregistration already in progress"
                            );
                            if(unlockBlock != 0) {
                                clusters[msg.sender].status = Status.NOT_REGISTERED;
                                emit ClusterUnregistered(msg.sender, unlockBlock);
                                delete clientKeys[clusters[msg.sender].clientKey];
                                delete locks[lockId];
                                delete locks[keccak256(abi.encodePacked(COMMISSION_LOCK_SELECTOR, msg.sender))];
                                delete locks[keccak256(abi.encodePacked(SWITCH_NETWORK_LOCK_SELECTOR, msg.sender))];
                                return;
                            }
                            uint256 updatedUnlockBlock = block.number.add(lockWaitTime[UNREGISTER_LOCK_SELECTOR]);
                            locks[lockId] = Lock(updatedUnlockBlock, 0);
                            emit ClusterUnregisterRequested(msg.sender, updatedUnlockBlock);
                        }
                    
                        function isClusterValid(address _cluster) public returns(bool) {
                            bytes32 lockId = keccak256(abi.encodePacked(UNREGISTER_LOCK_SELECTOR, _cluster));
                            uint256 unlockBlock = locks[lockId].unlockBlock;
                            if(unlockBlock != 0 && unlockBlock < block.number) {
                                clusters[_cluster].status = Status.NOT_REGISTERED;
                                delete clientKeys[clusters[_cluster].clientKey];
                                emit ClusterUnregistered(_cluster, unlockBlock);
                                delete locks[lockId];
                                delete locks[keccak256(abi.encodePacked(COMMISSION_LOCK_SELECTOR, msg.sender))];
                                delete locks[keccak256(abi.encodePacked(SWITCH_NETWORK_LOCK_SELECTOR, msg.sender))];
                                return false;
                            }
                            return (clusters[_cluster].status != Status.NOT_REGISTERED);    // returns true if the status is registered
                        }
                    
                        function getCommission(address _cluster) public returns(uint256) {
                            bytes32 lockId = keccak256(abi.encodePacked(COMMISSION_LOCK_SELECTOR, _cluster));
                            uint256 unlockBlock = locks[lockId].unlockBlock;
                            if(unlockBlock != 0 && unlockBlock < block.number) {
                                uint256 currentCommission = locks[lockId].iValue;
                                clusters[_cluster].commission = currentCommission;
                                emit CommissionUpdated(_cluster, currentCommission, unlockBlock);
                                delete locks[lockId];
                                return currentCommission;
                            }
                            return clusters[_cluster].commission;
                        }
                    
                        function getNetwork(address _cluster) public returns(bytes32) {
                            bytes32 lockId = keccak256(abi.encodePacked(SWITCH_NETWORK_LOCK_SELECTOR, _cluster));
                            uint256 unlockBlock = locks[lockId].unlockBlock;
                            if(unlockBlock != 0 && unlockBlock < block.number) {
                                bytes32 currentNetwork = bytes32(locks[lockId].iValue);
                                clusters[msg.sender].networkId = currentNetwork;
                                emit NetworkSwitched(msg.sender, currentNetwork, unlockBlock);
                                delete locks[lockId];
                                return currentNetwork;
                            }
                            return clusters[_cluster].networkId;
                        }
                    
                        function getRewardAddress(address _cluster) external view returns(address) {
                            return clusters[_cluster].rewardAddress;
                        }
                    
                        function getClientKey(address _cluster) external view returns(address) {
                            return clusters[_cluster].clientKey;
                        }
                    
                        function getCluster(address _cluster) external returns(
                            uint256 commission, 
                            address rewardAddress, 
                            address clientKey, 
                            bytes32 networkId, 
                            bool isValidCluster
                        ) {
                            return (
                                getCommission(_cluster), 
                                clusters[_cluster].rewardAddress, 
                                clusters[_cluster].clientKey, 
                                getNetwork(_cluster), 
                                isClusterValid(_cluster)
                            );
                        }
                    
                        function getRewardInfo(address _cluster) external returns(uint256, address) {
                            return (getCommission(_cluster), clusters[_cluster].rewardAddress);
                        }
                        
                        function addClientKeys(address[] calldata _clusters) external onlyOwner {
                            for(uint256 i=0; i < _clusters.length; i++) {
                                address _clientKey = clusters[_clusters[i]].clientKey;
                                require(_clientKey != address(0), "CR:ACK - Cluster has invalid client key");
                                clientKeys[_clientKey] = _clusters[i];
                            }
                        }
                    }

                    File 10 of 10: TokenLogic
                    // File: @openzeppelin/upgrades/contracts/Initializable.sol
                    
                    pragma solidity >=0.4.24 <0.7.0;
                    
                    
                    /**
                     * @title Initializable
                     *
                     * @dev Helper contract to support initializer functions. To use it, replace
                     * the constructor with a function that has the `initializer` modifier.
                     * WARNING: Unlike constructors, initializer functions must be manually
                     * invoked. This applies both to deploying an Initializable contract, as well
                     * as extending an Initializable contract via inheritance.
                     * WARNING: When used with inheritance, manual care must be taken to not invoke
                     * a parent initializer twice, or ensure that all initializers are idempotent,
                     * because this is not dealt with automatically as with constructors.
                     */
                    contract Initializable {
                    
                      /**
                       * @dev Indicates that the contract has been initialized.
                       */
                      bool private initialized;
                    
                      /**
                       * @dev Indicates that the contract is in the process of being initialized.
                       */
                      bool private initializing;
                    
                      /**
                       * @dev Modifier to use in the initializer function of a contract.
                       */
                      modifier initializer() {
                        require(initializing || isConstructor() || !initialized, "Contract instance has already been initialized");
                    
                        bool isTopLevelCall = !initializing;
                        if (isTopLevelCall) {
                          initializing = true;
                          initialized = true;
                        }
                    
                        _;
                    
                        if (isTopLevelCall) {
                          initializing = false;
                        }
                      }
                    
                      /// @dev Returns true if and only if the function is running in the constructor
                      function isConstructor() private view returns (bool) {
                        // extcodesize checks the size of the code stored in an address, and
                        // address returns the current address. Since the code is still not
                        // deployed when running a constructor, any checks on its code size will
                        // yield zero, making it an effective way to detect if a contract is
                        // under construction or not.
                        address self = address(this);
                        uint256 cs;
                        assembly { cs := extcodesize(self) }
                        return cs == 0;
                      }
                    
                      // Reserved storage space to allow for layout changes in the future.
                      uint256[50] private ______gap;
                    }
                    
                    // File: @openzeppelin/contracts-ethereum-package/contracts/GSN/Context.sol
                    
                    pragma solidity ^0.5.0;
                    
                    /*
                     * @dev Provides information about the current execution context, including the
                     * sender of the transaction and its data. While these are generally available
                     * via msg.sender and msg.data, they not should not be accessed in such a direct
                     * manner, since when dealing with GSN meta-transactions the account sending and
                     * paying for execution may not be the actual sender (as far as an application
                     * is concerned).
                     *
                     * This contract is only required for intermediate, library-like contracts.
                     */
                    contract Context {
                        // Empty internal constructor, to prevent people from mistakenly deploying
                        // an instance of this contract, with should be used via inheritance.
                        constructor () internal { }
                        // solhint-disable-previous-line no-empty-blocks
                    
                        function _msgSender() internal view returns (address) {
                            return msg.sender;
                        }
                    
                        function _msgData() internal view returns (bytes memory) {
                            this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                            return msg.data;
                        }
                    }
                    
                    // File: @openzeppelin/contracts-ethereum-package/contracts/token/ERC20/IERC20.sol
                    
                    pragma solidity ^0.5.0;
                    
                    /**
                     * @dev Interface of the ERC20 standard as defined in the EIP. Does not include
                     * the optional functions; to access them see `ERC20Detailed`.
                     */
                    interface IERC20 {
                        /**
                         * @dev Returns the amount of tokens in existence.
                         */
                        function totalSupply() external view returns (uint256);
                    
                        /**
                         * @dev Returns the amount of tokens owned by `account`.
                         */
                        function balanceOf(address account) external view returns (uint256);
                    
                        /**
                         * @dev Moves `amount` tokens from the caller's account to `recipient`.
                         *
                         * Returns a boolean value indicating whether the operation succeeded.
                         *
                         * Emits a `Transfer` event.
                         */
                        function transfer(address recipient, uint256 amount) external returns (bool);
                    
                        /**
                         * @dev Returns the remaining number of tokens that `spender` will be
                         * allowed to spend on behalf of `owner` through `transferFrom`. This is
                         * zero by default.
                         *
                         * This value changes when `approve` or `transferFrom` are called.
                         */
                        function allowance(address owner, address spender) external view returns (uint256);
                    
                        /**
                         * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
                         *
                         * Returns a boolean value indicating whether the operation succeeded.
                         *
                         * > Beware that changing an allowance with this method brings the risk
                         * that someone may use both the old and the new allowance by unfortunate
                         * transaction ordering. One possible solution to mitigate this race
                         * condition is to first reduce the spender's allowance to 0 and set the
                         * desired value afterwards:
                         * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                         *
                         * Emits an `Approval` event.
                         */
                        function approve(address spender, uint256 amount) external returns (bool);
                    
                        /**
                         * @dev Moves `amount` tokens from `sender` to `recipient` using the
                         * allowance mechanism. `amount` is then deducted from the caller's
                         * allowance.
                         *
                         * Returns a boolean value indicating whether the operation succeeded.
                         *
                         * Emits a `Transfer` event.
                         */
                        function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
                    
                        /**
                         * @dev Emitted when `value` tokens are moved from one account (`from`) to
                         * another (`to`).
                         *
                         * Note that `value` may be zero.
                         */
                        event Transfer(address indexed from, address indexed to, uint256 value);
                    
                        /**
                         * @dev Emitted when the allowance of a `spender` for an `owner` is set by
                         * a call to `approve`. `value` is the new allowance.
                         */
                        event Approval(address indexed owner, address indexed spender, uint256 value);
                    }
                    
                    // File: @openzeppelin/contracts-ethereum-package/contracts/math/SafeMath.sol
                    
                    pragma solidity ^0.5.0;
                    
                    /**
                     * @dev Wrappers over Solidity's arithmetic operations with added overflow
                     * checks.
                     *
                     * Arithmetic operations in Solidity wrap on overflow. This can easily result
                     * in bugs, because programmers usually assume that an overflow raises an
                     * error, which is the standard behavior in high level programming languages.
                     * `SafeMath` restores this intuition by reverting the transaction when an
                     * operation overflows.
                     *
                     * Using this library instead of the unchecked operations eliminates an entire
                     * class of bugs, so it's recommended to use it always.
                     */
                    library SafeMath {
                        /**
                         * @dev Returns the addition of two unsigned integers, reverting on
                         * overflow.
                         *
                         * Counterpart to Solidity's `+` operator.
                         *
                         * Requirements:
                         * - Addition cannot overflow.
                         */
                        function add(uint256 a, uint256 b) internal pure returns (uint256) {
                            uint256 c = a + b;
                            require(c >= a, "SafeMath: addition overflow");
                    
                            return c;
                        }
                    
                        /**
                         * @dev Returns the subtraction of two unsigned integers, reverting on
                         * overflow (when the result is negative).
                         *
                         * Counterpart to Solidity's `-` operator.
                         *
                         * Requirements:
                         * - Subtraction cannot overflow.
                         */
                        function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                            require(b <= a, "SafeMath: subtraction overflow");
                            uint256 c = a - b;
                    
                            return c;
                        }
                    
                        /**
                         * @dev Returns the multiplication of two unsigned integers, reverting on
                         * overflow.
                         *
                         * Counterpart to Solidity's `*` operator.
                         *
                         * Requirements:
                         * - Multiplication cannot overflow.
                         */
                        function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                            // benefit is lost if 'b' is also tested.
                            // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
                            if (a == 0) {
                                return 0;
                            }
                    
                            uint256 c = a * b;
                            require(c / a == b, "SafeMath: multiplication overflow");
                    
                            return c;
                        }
                    
                        /**
                         * @dev Returns the integer division of two unsigned integers. Reverts on
                         * division by zero. The result is rounded towards zero.
                         *
                         * Counterpart to Solidity's `/` operator. Note: this function uses a
                         * `revert` opcode (which leaves remaining gas untouched) while Solidity
                         * uses an invalid opcode to revert (consuming all remaining gas).
                         *
                         * Requirements:
                         * - The divisor cannot be zero.
                         */
                        function div(uint256 a, uint256 b) internal pure returns (uint256) {
                            // Solidity only automatically asserts when dividing by 0
                            require(b > 0, "SafeMath: division by zero");
                            uint256 c = a / b;
                            // assert(a == b * c + a % b); // There is no case in which this doesn't hold
                    
                            return c;
                        }
                    
                        /**
                         * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                         * Reverts when dividing by zero.
                         *
                         * Counterpart to Solidity's `%` operator. This function uses a `revert`
                         * opcode (which leaves remaining gas untouched) while Solidity uses an
                         * invalid opcode to revert (consuming all remaining gas).
                         *
                         * Requirements:
                         * - The divisor cannot be zero.
                         */
                        function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                            require(b != 0, "SafeMath: modulo by zero");
                            return a % b;
                        }
                    }
                    
                    // File: @openzeppelin/contracts-ethereum-package/contracts/token/ERC20/ERC20.sol
                    
                    pragma solidity ^0.5.0;
                    
                    
                    
                    
                    
                    /**
                     * @dev Implementation of the `IERC20` interface.
                     *
                     * This implementation is agnostic to the way tokens are created. This means
                     * that a supply mechanism has to be added in a derived contract using `_mint`.
                     * For a generic mechanism see `ERC20Mintable`.
                     *
                     * *For a detailed writeup see our guide [How to implement supply
                     * mechanisms](https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226).*
                     *
                     * We have followed general OpenZeppelin guidelines: functions revert instead
                     * of returning `false` on failure. This behavior is nonetheless conventional
                     * and does not conflict with the expectations of ERC20 applications.
                     *
                     * Additionally, an `Approval` event is emitted on calls to `transferFrom`.
                     * This allows applications to reconstruct the allowance for all accounts just
                     * by listening to said events. Other implementations of the EIP may not emit
                     * these events, as it isn't required by the specification.
                     *
                     * Finally, the non-standard `decreaseAllowance` and `increaseAllowance`
                     * functions have been added to mitigate the well-known issues around setting
                     * allowances. See `IERC20.approve`.
                     */
                    contract ERC20 is Initializable, Context, IERC20 {
                        using SafeMath for uint256;
                    
                        mapping (address => uint256) private _balances;
                    
                        mapping (address => mapping (address => uint256)) private _allowances;
                    
                        uint256 private _totalSupply;
                    
                        /**
                         * @dev See `IERC20.totalSupply`.
                         */
                        function totalSupply() public view returns (uint256) {
                            return _totalSupply;
                        }
                    
                        /**
                         * @dev See `IERC20.balanceOf`.
                         */
                        function balanceOf(address account) public view returns (uint256) {
                            return _balances[account];
                        }
                    
                        /**
                         * @dev See `IERC20.transfer`.
                         *
                         * Requirements:
                         *
                         * - `recipient` cannot be the zero address.
                         * - the caller must have a balance of at least `amount`.
                         */
                        function transfer(address to, uint256 value) public returns (bool) {
                            _transfer(_msgSender(), to, value);
                            return true;
                        }
                    
                        /**
                         * @dev See `IERC20.allowance`.
                         */
                        function allowance(address owner, address spender) public view returns (uint256) {
                            return _allowances[owner][spender];
                        }
                    
                        /**
                         * @dev See `IERC20.approve`.
                         *
                         * Requirements:
                         *
                         * - `spender` cannot be the zero address.
                         */
                        function approve(address spender, uint256 value) public returns (bool) {
                            _approve(_msgSender(), spender, value);
                            return true;
                        }
                    
                        /**
                         * @dev See `IERC20.transferFrom`.
                         *
                         * Emits an `Approval` event indicating the updated allowance. This is not
                         * required by the EIP. See the note at the beginning of `ERC20`;
                         *
                         * Requirements:
                         * - `sender` and `recipient` cannot be the zero address.
                         * - `sender` must have a balance of at least `value`.
                         * - the caller must have allowance for `sender`'s tokens of at least
                         * `amount`.
                         */
                        function transferFrom(address from, address to, uint256 value) public returns (bool) {
                            _transfer(from, to, value);
                            _approve(from, _msgSender(), _allowances[from][_msgSender()].sub(value));
                            return true;
                        }
                    
                        /**
                         * @dev Atomically increases the allowance granted to `spender` by the caller.
                         *
                         * This is an alternative to `approve` that can be used as a mitigation for
                         * problems described in `IERC20.approve`.
                         *
                         * Emits an `Approval` event indicating the updated allowance.
                         *
                         * Requirements:
                         *
                         * - `spender` cannot be the zero address.
                         */
                        function increaseAllowance(address spender, uint256 addedValue) public returns (bool) {
                            _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
                            return true;
                        }
                    
                        /**
                         * @dev Atomically decreases the allowance granted to `spender` by the caller.
                         *
                         * This is an alternative to `approve` that can be used as a mitigation for
                         * problems described in `IERC20.approve`.
                         *
                         * Emits an `Approval` event indicating the updated allowance.
                         *
                         * Requirements:
                         *
                         * - `spender` cannot be the zero address.
                         * - `spender` must have allowance for the caller of at least
                         * `subtractedValue`.
                         */
                        function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) {
                            _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue));
                            return true;
                        }
                    
                        /**
                         * @dev Moves tokens `amount` from `sender` to `recipient`.
                         *
                         * This is internal function is equivalent to `transfer`, and can be used to
                         * e.g. implement automatic token fees, slashing mechanisms, etc.
                         *
                         * Emits a `Transfer` event.
                         *
                         * Requirements:
                         *
                         * - `sender` cannot be the zero address.
                         * - `recipient` cannot be the zero address.
                         * - `sender` must have a balance of at least `amount`.
                         */
                        function _transfer(address sender, address recipient, uint256 amount) internal {
                            require(sender != address(0), "ERC20: transfer from the zero address");
                            require(recipient != address(0), "ERC20: transfer to the zero address");
                    
                            _balances[sender] = _balances[sender].sub(amount);
                            _balances[recipient] = _balances[recipient].add(amount);
                            emit Transfer(sender, recipient, amount);
                        }
                    
                        /** @dev Creates `amount` tokens and assigns them to `account`, increasing
                         * the total supply.
                         *
                         * Emits a `Transfer` event with `from` set to the zero address.
                         *
                         * Requirements
                         *
                         * - `to` cannot be the zero address.
                         */
                        function _mint(address account, uint256 amount) internal {
                            require(account != address(0), "ERC20: mint to the zero address");
                    
                            _totalSupply = _totalSupply.add(amount);
                            _balances[account] = _balances[account].add(amount);
                            emit Transfer(address(0), account, amount);
                        }
                    
                         /**
                         * @dev Destoys `amount` tokens from `account`, reducing the
                         * total supply.
                         *
                         * Emits a `Transfer` event with `to` set to the zero address.
                         *
                         * Requirements
                         *
                         * - `account` cannot be the zero address.
                         * - `account` must have at least `amount` tokens.
                         */
                        function _burn(address account, uint256 value) internal {
                            require(account != address(0), "ERC20: burn from the zero address");
                    
                            _totalSupply = _totalSupply.sub(value);
                            _balances[account] = _balances[account].sub(value);
                            emit Transfer(account, address(0), value);
                        }
                    
                        /**
                         * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens.
                         *
                         * This is internal function is equivalent to `approve`, and can be used to
                         * e.g. set automatic allowances for certain subsystems, etc.
                         *
                         * Emits an `Approval` event.
                         *
                         * Requirements:
                         *
                         * - `owner` cannot be the zero address.
                         * - `spender` cannot be the zero address.
                         */
                        function _approve(address owner, address spender, uint256 value) internal {
                            require(owner != address(0), "ERC20: approve from the zero address");
                            require(spender != address(0), "ERC20: approve to the zero address");
                    
                            _allowances[owner][spender] = value;
                            emit Approval(owner, spender, value);
                        }
                    
                        /**
                         * @dev Destoys `amount` tokens from `account`.`amount` is then deducted
                         * from the caller's allowance.
                         *
                         * See `_burn` and `_approve`.
                         */
                        function _burnFrom(address account, uint256 amount) internal {
                            _burn(account, amount);
                            _approve(account, _msgSender(), _allowances[account][_msgSender()].sub(amount));
                        }
                    
                        uint256[50] private ______gap;
                    }
                    
                    // File: @openzeppelin/contracts-ethereum-package/contracts/token/ERC20/ERC20Detailed.sol
                    
                    pragma solidity ^0.5.0;
                    
                    
                    
                    /**
                     * @dev Optional functions from the ERC20 standard.
                     */
                    contract ERC20Detailed is Initializable, IERC20 {
                        string private _name;
                        string private _symbol;
                        uint8 private _decimals;
                    
                        /**
                         * @dev Sets the values for `name`, `symbol`, and `decimals`. All three of
                         * these values are immutable: they can only be set once during
                         * construction.
                         */
                        function initialize(string memory name, string memory symbol, uint8 decimals) public initializer {
                            _name = name;
                            _symbol = symbol;
                            _decimals = decimals;
                        }
                    
                        /**
                         * @dev Returns the name of the token.
                         */
                        function name() public view returns (string memory) {
                            return _name;
                        }
                    
                        /**
                         * @dev Returns the symbol of the token, usually a shorter version of the
                         * name.
                         */
                        function symbol() public view returns (string memory) {
                            return _symbol;
                        }
                    
                        /**
                         * @dev Returns the number of decimals used to get its user representation.
                         * For example, if `decimals` equals `2`, a balance of `505` tokens should
                         * be displayed to a user as `5,05` (`505 / 10 ** 2`).
                         *
                         * Tokens usually opt for a value of 18, imitating the relationship between
                         * Ether and Wei.
                         *
                         * > Note that this information is only used for _display_ purposes: it in
                         * no way affects any of the arithmetic of the contract, including
                         * `IERC20.balanceOf` and `IERC20.transfer`.
                         */
                        function decimals() public view returns (uint8) {
                            return _decimals;
                        }
                    
                        uint256[50] private ______gap;
                    }
                    
                    // File: @openzeppelin/contracts-ethereum-package/contracts/access/Roles.sol
                    
                    pragma solidity ^0.5.0;
                    
                    /**
                     * @title Roles
                     * @dev Library for managing addresses assigned to a Role.
                     */
                    library Roles {
                        struct Role {
                            mapping (address => bool) bearer;
                        }
                    
                        /**
                         * @dev Give an account access to this role.
                         */
                        function add(Role storage role, address account) internal {
                            require(!has(role, account), "Roles: account already has role");
                            role.bearer[account] = true;
                        }
                    
                        /**
                         * @dev Remove an account's access to this role.
                         */
                        function remove(Role storage role, address account) internal {
                            require(has(role, account), "Roles: account does not have role");
                            role.bearer[account] = false;
                        }
                    
                        /**
                         * @dev Check if an account has this role.
                         * @return bool
                         */
                        function has(Role storage role, address account) internal view returns (bool) {
                            require(account != address(0), "Roles: account is the zero address");
                            return role.bearer[account];
                        }
                    }
                    
                    // File: @openzeppelin/contracts-ethereum-package/contracts/access/roles/MinterRole.sol
                    
                    pragma solidity ^0.5.0;
                    
                    
                    
                    
                    contract MinterRole is Initializable, Context {
                        using Roles for Roles.Role;
                    
                        event MinterAdded(address indexed account);
                        event MinterRemoved(address indexed account);
                    
                        Roles.Role private _minters;
                    
                        function initialize(address sender) public initializer {
                            if (!isMinter(sender)) {
                                _addMinter(sender);
                            }
                        }
                    
                        modifier onlyMinter() {
                            require(isMinter(_msgSender()), "MinterRole: caller does not have the Minter role");
                            _;
                        }
                    
                        function isMinter(address account) public view returns (bool) {
                            return _minters.has(account);
                        }
                    
                        function addMinter(address account) public onlyMinter {
                            _addMinter(account);
                        }
                    
                        function renounceMinter() public {
                            _removeMinter(_msgSender());
                        }
                    
                        function _addMinter(address account) internal {
                            _minters.add(account);
                            emit MinterAdded(account);
                        }
                    
                        function _removeMinter(address account) internal {
                            _minters.remove(account);
                            emit MinterRemoved(account);
                        }
                    
                        uint256[50] private ______gap;
                    }
                    
                    // File: @openzeppelin/contracts-ethereum-package/contracts/token/ERC20/ERC20Mintable.sol
                    
                    pragma solidity ^0.5.0;
                    
                    
                    
                    
                    /**
                     * @dev Extension of `ERC20` that adds a set of accounts with the `MinterRole`,
                     * which have permission to mint (create) new tokens as they see fit.
                     *
                     * At construction, the deployer of the contract is the only minter.
                     */
                    contract ERC20Mintable is Initializable, ERC20, MinterRole {
                        function initialize(address sender) public initializer {
                            MinterRole.initialize(sender);
                        }
                    
                        /**
                         * @dev See `ERC20._mint`.
                         *
                         * Requirements:
                         *
                         * - the caller must have the `MinterRole`.
                         */
                        function mint(address account, uint256 amount) public onlyMinter returns (bool) {
                            _mint(account, amount);
                            return true;
                        }
                    
                        uint256[50] private ______gap;
                    }
                    
                    // File: @openzeppelin/contracts-ethereum-package/contracts/token/ERC20/ERC20Capped.sol
                    
                    pragma solidity ^0.5.0;
                    
                    
                    
                    /**
                     * @dev Extension of `ERC20Mintable` that adds a cap to the supply of tokens.
                     */
                    contract ERC20Capped is Initializable, ERC20Mintable {
                        uint256 private _cap;
                    
                        /**
                         * @dev Sets the value of the `cap`. This value is immutable, it can only be
                         * set once during construction.
                         */
                        function initialize(uint256 cap, address sender) public initializer {
                            ERC20Mintable.initialize(sender);
                    
                            require(cap > 0, "ERC20Capped: cap is 0");
                            _cap = cap;
                        }
                    
                        /**
                         * @dev Returns the cap on the token's total supply.
                         */
                        function cap() public view returns (uint256) {
                            return _cap;
                        }
                    
                        /**
                         * @dev See `ERC20Mintable.mint`.
                         *
                         * Requirements:
                         *
                         * - `value` must not cause the total supply to go over the cap.
                         */
                        function _mint(address account, uint256 value) internal {
                            require(totalSupply().add(value) <= _cap, "ERC20Capped: cap exceeded");
                            super._mint(account, value);
                        }
                    
                        uint256[50] private ______gap;
                    }
                    
                    // File: @openzeppelin/contracts-ethereum-package/contracts/token/ERC20/ERC20Burnable.sol
                    
                    pragma solidity ^0.5.0;
                    
                    
                    
                    
                    /**
                     * @dev Extension of `ERC20` that allows token holders to destroy both their own
                     * tokens and those that they have an allowance for, in a way that can be
                     * recognized off-chain (via event analysis).
                     */
                    contract ERC20Burnable is Initializable, Context, ERC20 {
                        /**
                         * @dev Destoys `amount` tokens from the caller.
                         *
                         * See `ERC20._burn`.
                         */
                        function burn(uint256 amount) public {
                            _burn(_msgSender(), amount);
                        }
                    
                        /**
                         * @dev See `ERC20._burnFrom`.
                         */
                        function burnFrom(address account, uint256 amount) public {
                            _burnFrom(account, amount);
                        }
                    
                        uint256[50] private ______gap;
                    }
                    
                    // File: contracts/Token/TokenLogic.sol
                    
                    pragma solidity 0.5.17;
                    
                    
                    
                    // import "@openzeppelin/contracts-ethereum-package/contracts/token/ERC20/ERC20Mintable.sol";
                    
                    
                    
                    
                    contract TokenLogic is
                        Initializable,
                        ERC20,
                        ERC20Detailed,
                        // ERC20Mintable,
                        ERC20Capped,
                        ERC20Burnable
                    {
                        function initialize(
                            string memory _name,
                            string memory _symbol,
                            uint8 _decimal,
                            address _bridge
                        ) public initializer {
                            ERC20Detailed.initialize(_name, _symbol, _decimal);
                            // ERC20Mintable.initialize(msg.sender);
                            ERC20Capped.initialize(10000000000e18, msg.sender);
                            mint(_bridge, 1000000000e18);
                        }
                    }