Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 280 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Claim All Pendin... | 12314316 | 1384 days ago | IN | 0 ETH | 0.02382192 | ||||
Claim All Pendin... | 12313447 | 1384 days ago | IN | 0 ETH | 0.0075948 | ||||
Claim All Pendin... | 12311219 | 1385 days ago | IN | 0 ETH | 0.00775302 | ||||
Claim All Pendin... | 12310502 | 1385 days ago | IN | 0 ETH | 0.00743657 | ||||
Claim All Pendin... | 12309804 | 1385 days ago | IN | 0 ETH | 0.00617077 | ||||
Claim All Pendin... | 12308467 | 1385 days ago | IN | 0 ETH | 0.00701727 | ||||
Claim All Pendin... | 12307867 | 1385 days ago | IN | 0 ETH | 0.00585432 | ||||
Claim All Pendin... | 12307738 | 1385 days ago | IN | 0 ETH | 0.00648702 | ||||
Claim All Pendin... | 12307461 | 1385 days ago | IN | 0 ETH | 0.00621527 | ||||
Claim All Pendin... | 12305703 | 1385 days ago | IN | 0 ETH | 0.00791125 | ||||
Claim All Pendin... | 12305012 | 1386 days ago | IN | 0 ETH | 0.00791125 | ||||
Claim All Pendin... | 12304887 | 1386 days ago | IN | 0 ETH | 0.02359045 | ||||
Claim All Pendin... | 12303278 | 1386 days ago | IN | 0 ETH | 0.01407385 | ||||
Claim All Pendin... | 12301661 | 1386 days ago | IN | 0 ETH | 0.01256088 | ||||
Claim All Pendin... | 12301146 | 1386 days ago | IN | 0 ETH | 0.0139453 | ||||
Claim All Pendin... | 12300399 | 1386 days ago | IN | 0 ETH | 0.01028462 | ||||
Claim All Pendin... | 12299880 | 1386 days ago | IN | 0 ETH | 0.01107575 | ||||
Claim All Pendin... | 12298441 | 1387 days ago | IN | 0 ETH | 0.021039 | ||||
Claim All Pendin... | 12290024 | 1388 days ago | IN | 0 ETH | 0.0151896 | ||||
Claim All Pendin... | 12281183 | 1389 days ago | IN | 0 ETH | 0.0202528 | ||||
Claim All Pendin... | 12271929 | 1391 days ago | IN | 0 ETH | 0.0348095 | ||||
Claim All Pendin... | 12270961 | 1391 days ago | IN | 0 ETH | 0.0386069 | ||||
Claim All Pendin... | 12264999 | 1392 days ago | IN | 0 ETH | 0.03518463 | ||||
Claim All Pendin... | 12246486 | 1395 days ago | IN | 0 ETH | 0.01107575 | ||||
Claim All Pendin... | 12246177 | 1395 days ago | IN | 0 ETH | 0.00980995 |
Latest 1 internal transaction
Advanced mode:
Parent Transaction Hash | Block |
From
|
To
|
|||
---|---|---|---|---|---|---|
11733731 | 1473 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
ClaimsReward
Compiler Version
v0.5.17+commit.d19bba13
Contract Source Code (Solidity)
/** *Submitted for verification at Etherscan.io on 2021-01-27 */ // File: contracts/interfaces/IPooledStaking.sol pragma solidity ^0.5.0; interface IPooledStaking { function accumulateReward(address contractAddress, uint amount) external; function pushBurn(address contractAddress, uint amount) external; function hasPendingActions() external view returns (bool); function contractStake(address contractAddress) external view returns (uint); function stakerReward(address staker) external view returns (uint); function stakerDeposit(address staker) external view returns (uint); function stakerContractStake(address staker, address contractAddress) external view returns (uint); function withdraw(uint amount) external; function stakerMaxWithdrawable(address stakerAddress) external view returns (uint); function withdrawReward(address stakerAddress) external; } // File: @openzeppelin/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) { 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; } } // File: @openzeppelin/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. * * 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); } // File: @openzeppelin/contracts/utils/Address.sol pragma solidity ^0.5.5; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== */ function isContract(address account) internal view returns (bool) { // According to EIP-1052, 0x0 is the value returned for not-yet created accounts // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned // for accounts without code, i.e. `keccak256('')` bytes32 codehash; bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; // solhint-disable-next-line no-inline-assembly assembly { codehash := extcodehash(account) } return (codehash != accountHash && codehash != 0x0); } /** * @dev Converts an `address` into `address payable`. Note that this is * simply a type cast: the actual underlying value is not changed. * * _Available since v2.4.0._ */ function toPayable(address account) internal pure returns (address payable) { return address(uint160(account)); } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. * * _Available since v2.4.0._ */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); // solhint-disable-next-line avoid-call-value (bool success, ) = recipient.call.value(amount)(""); require(success, "Address: unable to send value, recipient may have reverted"); } } // File: @openzeppelin/contracts/token/ERC20/SafeERC20.sol pragma solidity ^0.5.0; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for ERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using SafeMath for uint256; using Address for address; function safeTransfer(IERC20 token, address to, uint256 value) internal { callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } function safeApprove(IERC20 token, address spender, uint256 value) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' // solhint-disable-next-line max-line-length require((value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).add(value); callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero"); callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. // A Solidity high level call has three parts: // 1. The target address is checked to verify it contains contract code // 2. The call itself is made, and success asserted // 3. The return value is decoded, which in turn checks the size of the returned data. // solhint-disable-next-line max-line-length require(address(token).isContract(), "SafeERC20: call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = address(token).call(data); require(success, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional // solhint-disable-next-line max-line-length require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } } // File: @openzeppelin/contracts/utils/ReentrancyGuard.sol pragma solidity ^0.5.0; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. * * _Since v2.5.0:_ this module is now much more gas efficient, given net gas * metering changes introduced in the Istanbul hardfork. */ contract ReentrancyGuard { bool private _notEntered; constructor () internal { // Storing an initial non-zero value makes deployment a bit more // expensive, but in exchange the refund on every call to nonReentrant // will be lower in amount. Since refunds are capped to a percetange of // the total transaction's gas, it is best to keep them low in cases // like this one, to increase the likelihood of the full refund coming // into effect. _notEntered = true; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and make it call a * `private` function that does the actual work. */ modifier nonReentrant() { // On the first call to nonReentrant, _notEntered will be true require(_notEntered, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _notEntered = false; _; // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _notEntered = true; } } // File: @uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol pragma solidity >=0.5.0; interface IUniswapV2Pair { event Approval(address indexed owner, address indexed spender, uint value); event Transfer(address indexed from, address indexed to, uint value); function name() external pure returns (string memory); function symbol() external pure returns (string memory); function decimals() external pure returns (uint8); function totalSupply() external view returns (uint); function balanceOf(address owner) external view returns (uint); function allowance(address owner, address spender) external view returns (uint); function approve(address spender, uint value) external returns (bool); function transfer(address to, uint value) external returns (bool); function transferFrom(address from, address to, uint value) external returns (bool); function DOMAIN_SEPARATOR() external view returns (bytes32); function PERMIT_TYPEHASH() external pure returns (bytes32); function nonces(address owner) external view returns (uint); function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external; event Mint(address indexed sender, uint amount0, uint amount1); event Burn(address indexed sender, uint amount0, uint amount1, address indexed to); event Swap( address indexed sender, uint amount0In, uint amount1In, uint amount0Out, uint amount1Out, address indexed to ); event Sync(uint112 reserve0, uint112 reserve1); function MINIMUM_LIQUIDITY() external pure returns (uint); function factory() external view returns (address); function token0() external view returns (address); function token1() external view returns (address); function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast); function price0CumulativeLast() external view returns (uint); function price1CumulativeLast() external view returns (uint); function kLast() external view returns (uint); function mint(address to) external returns (uint liquidity); function burn(address to) external returns (uint amount0, uint amount1); function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external; function skim(address to) external; function sync() external; function initialize(address, address) external; } // File: contracts/abstract/INXMMaster.sol /* Copyright (C) 2020 NexusMutual.io This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/ */ pragma solidity ^0.5.0; contract INXMMaster { address public tokenAddress; address public owner; uint public pauseTime; function delegateCallBack(bytes32 myid) external; function masterInitialized() public view returns (bool); function isInternal(address _add) public view returns (bool); function isPause() public view returns (bool check); function isOwner(address _add) public view returns (bool); function isMember(address _add) public view returns (bool); function checkIsAuthToGoverned(address _add) public view returns (bool); function updatePauseTime(uint _time) public; function dAppLocker() public view returns (address _add); function dAppToken() public view returns (address _add); function getLatestAddress(bytes2 _contractName) public view returns (address payable contractAddress); } // File: contracts/abstract/MasterAware.sol /* Copyright (C) 2020 NexusMutual.io This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/ */ pragma solidity ^0.5.0; contract MasterAware { INXMMaster public master; modifier onlyMember { require(master.isMember(msg.sender), "Caller is not a member"); _; } modifier onlyInternal { require(master.isInternal(msg.sender), "Caller is not an internal contract"); _; } modifier onlyMaster { if (address(master) != address(0)) { require(address(master) == msg.sender, "Not master"); } _; } modifier onlyGovernance { require( master.checkIsAuthToGoverned(msg.sender), "Caller is not authorized to govern" ); _; } modifier whenPaused { require(master.isPause(), "System is not paused"); _; } modifier whenNotPaused { require(!master.isPause(), "System is paused"); _; } function changeMasterAddress(address masterAddress) public onlyMaster { master = INXMMaster(masterAddress); } } // File: contracts/abstract/Iupgradable.sol pragma solidity ^0.5.0; contract Iupgradable { INXMMaster public ms; address public nxMasterAddress; modifier onlyInternal { require(ms.isInternal(msg.sender)); _; } modifier isMemberAndcheckPause { require(ms.isPause() == false && ms.isMember(msg.sender) == true); _; } modifier onlyOwner { require(ms.isOwner(msg.sender)); _; } modifier checkPause { require(ms.isPause() == false); _; } modifier isMember { require(ms.isMember(msg.sender), "Not member"); _; } /** * @dev Iupgradable Interface to update dependent contract address */ function changeDependentContractAddress() public; /** * @dev change master address * @param _masterAddress is the new address */ function changeMasterAddress(address _masterAddress) public { if (address(ms) != address(0)) { require(address(ms) == msg.sender, "Not master"); } ms = INXMMaster(_masterAddress); nxMasterAddress = _masterAddress; } } // File: contracts/modules/cover/QuotationData.sol /* Copyright (C) 2020 NexusMutual.io This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/ */ pragma solidity ^0.5.0; contract QuotationData is Iupgradable { using SafeMath for uint; enum HCIDStatus {NA, kycPending, kycPass, kycFailedOrRefunded, kycPassNoCover} enum CoverStatus {Active, ClaimAccepted, ClaimDenied, CoverExpired, ClaimSubmitted, Requested} struct Cover { address payable memberAddress; bytes4 currencyCode; uint sumAssured; uint16 coverPeriod; uint validUntil; address scAddress; uint premiumNXM; } struct HoldCover { uint holdCoverId; address payable userAddress; address scAddress; bytes4 coverCurr; uint[] coverDetails; uint16 coverPeriod; } address public authQuoteEngine; mapping(bytes4 => uint) internal currencyCSA; mapping(address => uint[]) internal userCover; mapping(address => uint[]) public userHoldedCover; mapping(address => bool) public refundEligible; mapping(address => mapping(bytes4 => uint)) internal currencyCSAOfSCAdd; mapping(uint => uint8) public coverStatus; mapping(uint => uint) public holdedCoverIDStatus; mapping(uint => bool) public timestampRepeated; Cover[] internal allCovers; HoldCover[] internal allCoverHolded; uint public stlp; uint public stl; uint public pm; uint public minDays; uint public tokensRetained; address public kycAuthAddress; event CoverDetailsEvent( uint indexed cid, address scAdd, uint sumAssured, uint expiry, uint premium, uint premiumNXM, bytes4 curr ); event CoverStatusEvent(uint indexed cid, uint8 statusNum); constructor(address _authQuoteAdd, address _kycAuthAdd) public { authQuoteEngine = _authQuoteAdd; kycAuthAddress = _kycAuthAdd; stlp = 90; stl = 100; pm = 30; minDays = 30; tokensRetained = 10; allCovers.push(Cover(address(0), "0x00", 0, 0, 0, address(0), 0)); uint[] memory arr = new uint[](1); allCoverHolded.push(HoldCover(0, address(0), address(0), 0x00, arr, 0)); } /// @dev Adds the amount in Total Sum Assured of a given currency of a given smart contract address. /// @param _add Smart Contract Address. /// @param _amount Amount to be added. function addInTotalSumAssuredSC(address _add, bytes4 _curr, uint _amount) external onlyInternal { currencyCSAOfSCAdd[_add][_curr] = currencyCSAOfSCAdd[_add][_curr].add(_amount); } /// @dev Subtracts the amount from Total Sum Assured of a given currency and smart contract address. /// @param _add Smart Contract Address. /// @param _amount Amount to be subtracted. function subFromTotalSumAssuredSC(address _add, bytes4 _curr, uint _amount) external onlyInternal { currencyCSAOfSCAdd[_add][_curr] = currencyCSAOfSCAdd[_add][_curr].sub(_amount); } /// @dev Subtracts the amount from Total Sum Assured of a given currency. /// @param _curr Currency Name. /// @param _amount Amount to be subtracted. function subFromTotalSumAssured(bytes4 _curr, uint _amount) external onlyInternal { currencyCSA[_curr] = currencyCSA[_curr].sub(_amount); } /// @dev Adds the amount in Total Sum Assured of a given currency. /// @param _curr Currency Name. /// @param _amount Amount to be added. function addInTotalSumAssured(bytes4 _curr, uint _amount) external onlyInternal { currencyCSA[_curr] = currencyCSA[_curr].add(_amount); } /// @dev sets bit for timestamp to avoid replay attacks. function setTimestampRepeated(uint _timestamp) external onlyInternal { timestampRepeated[_timestamp] = true; } /// @dev Creates a blank new cover. function addCover( uint16 _coverPeriod, uint _sumAssured, address payable _userAddress, bytes4 _currencyCode, address _scAddress, uint premium, uint premiumNXM ) external onlyInternal { uint expiryDate = now.add(uint(_coverPeriod).mul(1 days)); allCovers.push(Cover(_userAddress, _currencyCode, _sumAssured, _coverPeriod, expiryDate, _scAddress, premiumNXM)); uint cid = allCovers.length.sub(1); userCover[_userAddress].push(cid); emit CoverDetailsEvent(cid, _scAddress, _sumAssured, expiryDate, premium, premiumNXM, _currencyCode); } /// @dev create holded cover which will process after verdict of KYC. function addHoldCover( address payable from, address scAddress, bytes4 coverCurr, uint[] calldata coverDetails, uint16 coverPeriod ) external onlyInternal { uint holdedCoverLen = allCoverHolded.length; holdedCoverIDStatus[holdedCoverLen] = uint(HCIDStatus.kycPending); allCoverHolded.push(HoldCover(holdedCoverLen, from, scAddress, coverCurr, coverDetails, coverPeriod)); userHoldedCover[from].push(allCoverHolded.length.sub(1)); } ///@dev sets refund eligible bit. ///@param _add user address. ///@param status indicates if user have pending kyc. function setRefundEligible(address _add, bool status) external onlyInternal { refundEligible[_add] = status; } /// @dev to set current status of particular holded coverID (1 for not completed KYC, /// 2 for KYC passed, 3 for failed KYC or full refunded, /// 4 for KYC completed but cover not processed) function setHoldedCoverIDStatus(uint holdedCoverID, uint status) external onlyInternal { holdedCoverIDStatus[holdedCoverID] = status; } /** * @dev to set address of kyc authentication * @param _add is the new address */ function setKycAuthAddress(address _add) external onlyInternal { kycAuthAddress = _add; } /// @dev Changes authorised address for generating quote off chain. function changeAuthQuoteEngine(address _add) external onlyInternal { authQuoteEngine = _add; } /** * @dev Gets Uint Parameters of a code * @param code whose details we want * @return string value of the code * @return associated amount (time or perc or value) to the code */ function getUintParameters(bytes8 code) external view returns (bytes8 codeVal, uint val) { codeVal = code; if (code == "STLP") { val = stlp; } else if (code == "STL") { val = stl; } else if (code == "PM") { val = pm; } else if (code == "QUOMIND") { val = minDays; } else if (code == "QUOTOK") { val = tokensRetained; } } /// @dev Gets Product details. /// @return _minDays minimum cover period. /// @return _PM Profit margin. /// @return _STL short term Load. /// @return _STLP short term load period. function getProductDetails() external view returns ( uint _minDays, uint _pm, uint _stl, uint _stlp ) { _minDays = minDays; _pm = pm; _stl = stl; _stlp = stlp; } /// @dev Gets total number covers created till date. function getCoverLength() external view returns (uint len) { return (allCovers.length); } /// @dev Gets Authorised Engine address. function getAuthQuoteEngine() external view returns (address _add) { _add = authQuoteEngine; } /// @dev Gets the Total Sum Assured amount of a given currency. function getTotalSumAssured(bytes4 _curr) external view returns (uint amount) { amount = currencyCSA[_curr]; } /// @dev Gets all the Cover ids generated by a given address. /// @param _add User's address. /// @return allCover array of covers. function getAllCoversOfUser(address _add) external view returns (uint[] memory allCover) { return (userCover[_add]); } /// @dev Gets total number of covers generated by a given address function getUserCoverLength(address _add) external view returns (uint len) { len = userCover[_add].length; } /// @dev Gets the status of a given cover. function getCoverStatusNo(uint _cid) external view returns (uint8) { return coverStatus[_cid]; } /// @dev Gets the Cover Period (in days) of a given cover. function getCoverPeriod(uint _cid) external view returns (uint32 cp) { cp = allCovers[_cid].coverPeriod; } /// @dev Gets the Sum Assured Amount of a given cover. function getCoverSumAssured(uint _cid) external view returns (uint sa) { sa = allCovers[_cid].sumAssured; } /// @dev Gets the Currency Name in which a given cover is assured. function getCurrencyOfCover(uint _cid) external view returns (bytes4 curr) { curr = allCovers[_cid].currencyCode; } /// @dev Gets the validity date (timestamp) of a given cover. function getValidityOfCover(uint _cid) external view returns (uint date) { date = allCovers[_cid].validUntil; } /// @dev Gets Smart contract address of cover. function getscAddressOfCover(uint _cid) external view returns (uint, address) { return (_cid, allCovers[_cid].scAddress); } /// @dev Gets the owner address of a given cover. function getCoverMemberAddress(uint _cid) external view returns (address payable _add) { _add = allCovers[_cid].memberAddress; } /// @dev Gets the premium amount of a given cover in NXM. function getCoverPremiumNXM(uint _cid) external view returns (uint _premiumNXM) { _premiumNXM = allCovers[_cid].premiumNXM; } /// @dev Provides the details of a cover Id /// @param _cid cover Id /// @return memberAddress cover user address. /// @return scAddress smart contract Address /// @return currencyCode currency of cover /// @return sumAssured sum assured of cover /// @return premiumNXM premium in NXM function getCoverDetailsByCoverID1( uint _cid ) external view returns ( uint cid, address _memberAddress, address _scAddress, bytes4 _currencyCode, uint _sumAssured, uint premiumNXM ) { return ( _cid, allCovers[_cid].memberAddress, allCovers[_cid].scAddress, allCovers[_cid].currencyCode, allCovers[_cid].sumAssured, allCovers[_cid].premiumNXM ); } /// @dev Provides details of a cover Id /// @param _cid cover Id /// @return status status of cover. /// @return sumAssured Sum assurance of cover. /// @return coverPeriod Cover Period of cover (in days). /// @return validUntil is validity of cover. function getCoverDetailsByCoverID2( uint _cid ) external view returns ( uint cid, uint8 status, uint sumAssured, uint16 coverPeriod, uint validUntil ) { return ( _cid, coverStatus[_cid], allCovers[_cid].sumAssured, allCovers[_cid].coverPeriod, allCovers[_cid].validUntil ); } /// @dev Provides details of a holded cover Id /// @param _hcid holded cover Id /// @return scAddress SmartCover address of cover. /// @return coverCurr currency of cover. /// @return coverPeriod Cover Period of cover (in days). function getHoldedCoverDetailsByID1( uint _hcid ) external view returns ( uint hcid, address scAddress, bytes4 coverCurr, uint16 coverPeriod ) { return ( _hcid, allCoverHolded[_hcid].scAddress, allCoverHolded[_hcid].coverCurr, allCoverHolded[_hcid].coverPeriod ); } /// @dev Gets total number holded covers created till date. function getUserHoldedCoverLength(address _add) external view returns (uint) { return userHoldedCover[_add].length; } /// @dev Gets holded cover index by index of user holded covers. function getUserHoldedCoverByIndex(address _add, uint index) external view returns (uint) { return userHoldedCover[_add][index]; } /// @dev Provides the details of a holded cover Id /// @param _hcid holded cover Id /// @return memberAddress holded cover user address. /// @return coverDetails array contains SA, Cover Currency Price,Price in NXM, Expiration time of Qoute. function getHoldedCoverDetailsByID2( uint _hcid ) external view returns ( uint hcid, address payable memberAddress, uint[] memory coverDetails ) { return ( _hcid, allCoverHolded[_hcid].userAddress, allCoverHolded[_hcid].coverDetails ); } /// @dev Gets the Total Sum Assured amount of a given currency and smart contract address. function getTotalSumAssuredSC(address _add, bytes4 _curr) external view returns (uint amount) { amount = currencyCSAOfSCAdd[_add][_curr]; } //solhint-disable-next-line function changeDependentContractAddress() public {} /// @dev Changes the status of a given cover. /// @param _cid cover Id. /// @param _stat New status. function changeCoverStatusNo(uint _cid, uint8 _stat) public onlyInternal { coverStatus[_cid] = _stat; emit CoverStatusEvent(_cid, _stat); } /** * @dev Updates Uint Parameters of a code * @param code whose details we want to update * @param val value to set */ function updateUintParameters(bytes8 code, uint val) public { require(ms.checkIsAuthToGoverned(msg.sender)); if (code == "STLP") { _changeSTLP(val); } else if (code == "STL") { _changeSTL(val); } else if (code == "PM") { _changePM(val); } else if (code == "QUOMIND") { _changeMinDays(val); } else if (code == "QUOTOK") { _setTokensRetained(val); } else { revert("Invalid param code"); } } /// @dev Changes the existing Profit Margin value function _changePM(uint _pm) internal { pm = _pm; } /// @dev Changes the existing Short Term Load Period (STLP) value. function _changeSTLP(uint _stlp) internal { stlp = _stlp; } /// @dev Changes the existing Short Term Load (STL) value. function _changeSTL(uint _stl) internal { stl = _stl; } /// @dev Changes the existing Minimum cover period (in days) function _changeMinDays(uint _days) internal { minDays = _days; } /** * @dev to set the the amount of tokens retained * @param val is the amount retained */ function _setTokensRetained(uint val) internal { tokensRetained = val; } } // File: contracts/modules/token/external/OZIERC20.sol pragma solidity ^0.5.0; /** * @title ERC20 interface * @dev see https://github.com/ethereum/EIPs/issues/20 */ interface OZIERC20 { function transfer(address to, uint256 value) external returns (bool); function approve(address spender, uint256 value) external returns (bool); function transferFrom(address from, address to, uint256 value) external returns (bool); function totalSupply() external view returns (uint256); function balanceOf(address who) external view returns (uint256); function allowance(address owner, address spender) external view returns (uint256); event Transfer( address indexed from, address indexed to, uint256 value ); event Approval( address indexed owner, address indexed spender, uint256 value ); } // File: contracts/modules/token/external/OZSafeMath.sol pragma solidity ^0.5.0; /** * @title SafeMath * @dev Math operations with safety checks that revert on error */ library OZSafeMath { /** * @dev Multiplies two numbers, reverts on overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b); return c; } /** * @dev Integer division of two numbers truncating the quotient, reverts on division by zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { require(b > 0); // Solidity only automatically asserts when dividing by 0 uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } /** * @dev Subtracts two numbers, reverts on overflow (i.e. if subtrahend is greater than minuend). */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { require(b <= a); uint256 c = a - b; return c; } /** * @dev Adds two numbers, reverts on overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a); return c; } /** * @dev Divides two numbers and returns the remainder (unsigned integer modulo), * reverts when dividing by zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { require(b != 0); return a % b; } } // File: contracts/modules/token/NXMToken.sol /* Copyright (C) 2020 NexusMutual.io This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/ */ pragma solidity ^0.5.0; contract NXMToken is OZIERC20 { using OZSafeMath for uint256; event WhiteListed(address indexed member); event BlackListed(address indexed member); mapping(address => uint256) private _balances; mapping(address => mapping(address => uint256)) private _allowed; mapping(address => bool) public whiteListed; mapping(address => uint) public isLockedForMV; uint256 private _totalSupply; string public name = "NXM"; string public symbol = "NXM"; uint8 public decimals = 18; address public operator; modifier canTransfer(address _to) { require(whiteListed[_to]); _; } modifier onlyOperator() { if (operator != address(0)) require(msg.sender == operator); _; } constructor(address _founderAddress, uint _initialSupply) public { _mint(_founderAddress, _initialSupply); } /** * @dev Total number of tokens in existence */ function totalSupply() public view returns (uint256) { return _totalSupply; } /** * @dev Gets the balance of the specified address. * @param owner The address to query the balance of. * @return An uint256 representing the amount owned by the passed address. */ function balanceOf(address owner) public view returns (uint256) { return _balances[owner]; } /** * @dev Function to check the amount of tokens that an owner allowed to a spender. * @param owner address The address which owns the funds. * @param spender address The address which will spend the funds. * @return A uint256 specifying the amount of tokens still available for the spender. */ function allowance( address owner, address spender ) public view returns (uint256) { return _allowed[owner][spender]; } /** * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. * Beware that changing an allowance with this method brings the risk that someone may use both the old * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * @param spender The address which will spend the funds. * @param value The amount of tokens to be spent. */ function approve(address spender, uint256 value) public returns (bool) { require(spender != address(0)); _allowed[msg.sender][spender] = value; emit Approval(msg.sender, spender, value); return true; } /** * @dev Increase the amount of tokens that an owner allowed to a spender. * approve should be called when allowed_[_spender] == 0. To increment * allowed value is better to use this function to avoid 2 calls (and wait until * the first transaction is mined) * From MonolithDAO Token.sol * @param spender The address which will spend the funds. * @param addedValue The amount of tokens to increase the allowance by. */ function increaseAllowance( address spender, uint256 addedValue ) public returns (bool) { require(spender != address(0)); _allowed[msg.sender][spender] = ( _allowed[msg.sender][spender].add(addedValue)); emit Approval(msg.sender, spender, _allowed[msg.sender][spender]); return true; } /** * @dev Decrease the amount of tokens that an owner allowed to a spender. * approve should be called when allowed_[_spender] == 0. To decrement * allowed value is better to use this function to avoid 2 calls (and wait until * the first transaction is mined) * From MonolithDAO Token.sol * @param spender The address which will spend the funds. * @param subtractedValue The amount of tokens to decrease the allowance by. */ function decreaseAllowance( address spender, uint256 subtractedValue ) public returns (bool) { require(spender != address(0)); _allowed[msg.sender][spender] = ( _allowed[msg.sender][spender].sub(subtractedValue)); emit Approval(msg.sender, spender, _allowed[msg.sender][spender]); return true; } /** * @dev Adds a user to whitelist * @param _member address to add to whitelist */ function addToWhiteList(address _member) public onlyOperator returns (bool) { whiteListed[_member] = true; emit WhiteListed(_member); return true; } /** * @dev removes a user from whitelist * @param _member address to remove from whitelist */ function removeFromWhiteList(address _member) public onlyOperator returns (bool) { whiteListed[_member] = false; emit BlackListed(_member); return true; } /** * @dev change operator address * @param _newOperator address of new operator */ function changeOperator(address _newOperator) public onlyOperator returns (bool) { operator = _newOperator; return true; } /** * @dev burns an amount of the tokens of the message sender * account. * @param amount The amount that will be burnt. */ function burn(uint256 amount) public returns (bool) { _burn(msg.sender, amount); return true; } /** * @dev Burns a specific amount of tokens from the target address and decrements allowance * @param from address The address which you want to send tokens from * @param value uint256 The amount of token to be burned */ function burnFrom(address from, uint256 value) public returns (bool) { _burnFrom(from, value); return true; } /** * @dev function that mints an amount of the token and assigns it to * an account. * @param account The account that will receive the created tokens. * @param amount The amount that will be created. */ function mint(address account, uint256 amount) public onlyOperator { _mint(account, amount); } /** * @dev Transfer token for a specified address * @param to The address to transfer to. * @param value The amount to be transferred. */ function transfer(address to, uint256 value) public canTransfer(to) returns (bool) { require(isLockedForMV[msg.sender] < now); // if not voted under governance require(value <= _balances[msg.sender]); _transfer(to, value); return true; } /** * @dev Transfer tokens to the operator from the specified address * @param from The address to transfer from. * @param value The amount to be transferred. */ function operatorTransfer(address from, uint256 value) public onlyOperator returns (bool) { require(value <= _balances[from]); _transferFrom(from, operator, value); return true; } /** * @dev Transfer tokens from one address to another * @param from address The address which you want to send tokens from * @param to address The address which you want to transfer to * @param value uint256 the amount of tokens to be transferred */ function transferFrom( address from, address to, uint256 value ) public canTransfer(to) returns (bool) { require(isLockedForMV[from] < now); // if not voted under governance require(value <= _balances[from]); require(value <= _allowed[from][msg.sender]); _transferFrom(from, to, value); return true; } /** * @dev Lock the user's tokens * @param _of user's address. */ function lockForMemberVote(address _of, uint _days) public onlyOperator { if (_days.add(now) > isLockedForMV[_of]) isLockedForMV[_of] = _days.add(now); } /** * @dev Transfer token for a specified address * @param to The address to transfer to. * @param value The amount to be transferred. */ function _transfer(address to, uint256 value) internal { _balances[msg.sender] = _balances[msg.sender].sub(value); _balances[to] = _balances[to].add(value); emit Transfer(msg.sender, to, value); } /** * @dev Transfer tokens from one address to another * @param from address The address which you want to send tokens from * @param to address The address which you want to transfer to * @param value uint256 the amount of tokens to be transferred */ function _transferFrom( address from, address to, uint256 value ) internal { _balances[from] = _balances[from].sub(value); _balances[to] = _balances[to].add(value); _allowed[from][msg.sender] = _allowed[from][msg.sender].sub(value); emit Transfer(from, to, value); } /** * @dev Internal function that mints an amount of the token and assigns it to * an account. This encapsulates the modification of balances such that the * proper events are emitted. * @param account The account that will receive the created tokens. * @param amount The amount that will be created. */ function _mint(address account, uint256 amount) internal { require(account != address(0)); _totalSupply = _totalSupply.add(amount); _balances[account] = _balances[account].add(amount); emit Transfer(address(0), account, amount); } /** * @dev Internal function that burns an amount of the token of a given * account. * @param account The account whose tokens will be burnt. * @param amount The amount that will be burnt. */ function _burn(address account, uint256 amount) internal { require(amount <= _balances[account]); _totalSupply = _totalSupply.sub(amount); _balances[account] = _balances[account].sub(amount); emit Transfer(account, address(0), amount); } /** * @dev Internal function that burns an amount of the token of a given * account, deducting from the sender's allowance for said account. Uses the * internal burn function. * @param account The account whose tokens will be burnt. * @param value The amount that will be burnt. */ function _burnFrom(address account, uint256 value) internal { require(value <= _allowed[account][msg.sender]); // Should https://github.com/OpenZeppelin/zeppelin-solidity/issues/707 be accepted, // this function needs to emit an event with the updated approval. _allowed[account][msg.sender] = _allowed[account][msg.sender].sub( value); _burn(account, value); } } // File: contracts/modules/token/external/IERC1132.sol pragma solidity ^0.5.0; /** * @title ERC1132 interface * @dev see https://github.com/ethereum/EIPs/issues/1132 */ contract IERC1132 { /** * @dev Reasons why a user's tokens have been locked */ mapping(address => bytes32[]) public lockReason; /** * @dev locked token structure */ struct LockToken { uint256 amount; uint256 validity; bool claimed; } /** * @dev Holds number & validity of tokens locked for a given reason for * a specified address */ mapping(address => mapping(bytes32 => LockToken)) public locked; /** * @dev Records data of all the tokens Locked */ event Locked( address indexed _of, bytes32 indexed _reason, uint256 _amount, uint256 _validity ); /** * @dev Records data of all the tokens unlocked */ event Unlocked( address indexed _of, bytes32 indexed _reason, uint256 _amount ); /** * @dev Locks a specified amount of tokens against an address, * for a specified reason and time * @param _reason The reason to lock tokens * @param _amount Number of tokens to be locked * @param _time Lock time in seconds */ function lock(bytes32 _reason, uint256 _amount, uint256 _time) public returns (bool); /** * @dev Returns tokens locked for a specified address for a * specified reason * * @param _of The address whose tokens are locked * @param _reason The reason to query the lock tokens for */ function tokensLocked(address _of, bytes32 _reason) public view returns (uint256 amount); /** * @dev Returns tokens locked for a specified address for a * specified reason at a specific time * * @param _of The address whose tokens are locked * @param _reason The reason to query the lock tokens for * @param _time The timestamp to query the lock tokens for */ function tokensLockedAtTime(address _of, bytes32 _reason, uint256 _time) public view returns (uint256 amount); /** * @dev Returns total tokens held by an address (locked + transferable) * @param _of The address to query the total balance of */ function totalBalanceOf(address _of) public view returns (uint256 amount); /** * @dev Extends lock for a specified reason and time * @param _reason The reason to lock tokens * @param _time Lock extension time in seconds */ function extendLock(bytes32 _reason, uint256 _time) public returns (bool); /** * @dev Increase number of tokens locked for a specified reason * @param _reason The reason to lock tokens * @param _amount Number of tokens to be increased */ function increaseLockAmount(bytes32 _reason, uint256 _amount) public returns (bool); /** * @dev Returns unlockable tokens for a specified address for a specified reason * @param _of The address to query the the unlockable token count of * @param _reason The reason to query the unlockable tokens for */ function tokensUnlockable(address _of, bytes32 _reason) public view returns (uint256 amount); /** * @dev Unlocks the unlockable tokens of a specified address * @param _of Address of user, claiming back unlockable tokens */ function unlock(address _of) public returns (uint256 unlockableTokens); /** * @dev Gets the unlockable tokens of a specified address * @param _of The address to query the the unlockable token count of */ function getUnlockableTokens(address _of) public view returns (uint256 unlockableTokens); } // File: contracts/modules/token/TokenController.sol /* Copyright (C) 2020 NexusMutual.io This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/ */ pragma solidity ^0.5.0; contract TokenController is IERC1132, Iupgradable { using SafeMath for uint256; event Burned(address indexed member, bytes32 lockedUnder, uint256 amount); NXMToken public token; IPooledStaking public pooledStaking; uint public minCALockTime = uint(30).mul(1 days); bytes32 private constant CLA = bytes32("CLA"); /** * @dev Just for interface */ function changeDependentContractAddress() public { token = NXMToken(ms.tokenAddress()); pooledStaking = IPooledStaking(ms.getLatestAddress("PS")); } /** * @dev to change the operator address * @param _newOperator is the new address of operator */ function changeOperator(address _newOperator) public onlyInternal { token.changeOperator(_newOperator); } /** * @dev Proxies token transfer through this contract to allow staking when members are locked for voting * @param _from Source address * @param _to Destination address * @param _value Amount to transfer */ function operatorTransfer(address _from, address _to, uint _value) onlyInternal external returns (bool) { require(msg.sender == address(pooledStaking), "Call is only allowed from PooledStaking address"); require(token.operatorTransfer(_from, _value), "Operator transfer failed"); require(token.transfer(_to, _value), "Internal transfer failed"); return true; } /** * @dev Locks a specified amount of tokens, * for CLA reason and for a specified time * @param _reason The reason to lock tokens, currently restricted to CLA * @param _amount Number of tokens to be locked * @param _time Lock time in seconds */ function lock(bytes32 _reason, uint256 _amount, uint256 _time) public checkPause returns (bool) { require(_reason == CLA, "Restricted to reason CLA"); require(minCALockTime <= _time, "Should lock for minimum time"); // If tokens are already locked, then functions extendLock or // increaseLockAmount should be used to make any changes _lock(msg.sender, _reason, _amount, _time); return true; } /** * @dev Locks a specified amount of tokens against an address, * for a specified reason and time * @param _reason The reason to lock tokens * @param _amount Number of tokens to be locked * @param _time Lock time in seconds * @param _of address whose tokens are to be locked */ function lockOf(address _of, bytes32 _reason, uint256 _amount, uint256 _time) public onlyInternal returns (bool) { // If tokens are already locked, then functions extendLock or // increaseLockAmount should be used to make any changes _lock(_of, _reason, _amount, _time); return true; } /** * @dev Extends lock for reason CLA for a specified time * @param _reason The reason to lock tokens, currently restricted to CLA * @param _time Lock extension time in seconds */ function extendLock(bytes32 _reason, uint256 _time) public checkPause returns (bool) { require(_reason == CLA, "Restricted to reason CLA"); _extendLock(msg.sender, _reason, _time); return true; } /** * @dev Extends lock for a specified reason and time * @param _reason The reason to lock tokens * @param _time Lock extension time in seconds */ function extendLockOf(address _of, bytes32 _reason, uint256 _time) public onlyInternal returns (bool) { _extendLock(_of, _reason, _time); return true; } /** * @dev Increase number of tokens locked for a CLA reason * @param _reason The reason to lock tokens, currently restricted to CLA * @param _amount Number of tokens to be increased */ function increaseLockAmount(bytes32 _reason, uint256 _amount) public checkPause returns (bool) { require(_reason == CLA, "Restricted to reason CLA"); require(_tokensLocked(msg.sender, _reason) > 0); token.operatorTransfer(msg.sender, _amount); locked[msg.sender][_reason].amount = locked[msg.sender][_reason].amount.add(_amount); emit Locked(msg.sender, _reason, _amount, locked[msg.sender][_reason].validity); return true; } /** * @dev burns tokens of an address * @param _of is the address to burn tokens of * @param amount is the amount to burn * @return the boolean status of the burning process */ function burnFrom(address _of, uint amount) public onlyInternal returns (bool) { return token.burnFrom(_of, amount); } /** * @dev Burns locked tokens of a user * @param _of address whose tokens are to be burned * @param _reason lock reason for which tokens are to be burned * @param _amount amount of tokens to burn */ function burnLockedTokens(address _of, bytes32 _reason, uint256 _amount) public onlyInternal { _burnLockedTokens(_of, _reason, _amount); } /** * @dev reduce lock duration for a specified reason and time * @param _of The address whose tokens are locked * @param _reason The reason to lock tokens * @param _time Lock reduction time in seconds */ function reduceLock(address _of, bytes32 _reason, uint256 _time) public onlyInternal { _reduceLock(_of, _reason, _time); } /** * @dev Released locked tokens of an address locked for a specific reason * @param _of address whose tokens are to be released from lock * @param _reason reason of the lock * @param _amount amount of tokens to release */ function releaseLockedTokens(address _of, bytes32 _reason, uint256 _amount) public onlyInternal { _releaseLockedTokens(_of, _reason, _amount); } /** * @dev Adds an address to whitelist maintained in the contract * @param _member address to add to whitelist */ function addToWhitelist(address _member) public onlyInternal { token.addToWhiteList(_member); } /** * @dev Removes an address from the whitelist in the token * @param _member address to remove */ function removeFromWhitelist(address _member) public onlyInternal { token.removeFromWhiteList(_member); } /** * @dev Mints new token for an address * @param _member address to reward the minted tokens * @param _amount number of tokens to mint */ function mint(address _member, uint _amount) public onlyInternal { token.mint(_member, _amount); } /** * @dev Lock the user's tokens * @param _of user's address. */ function lockForMemberVote(address _of, uint _days) public onlyInternal { token.lockForMemberVote(_of, _days); } /** * @dev Unlocks the unlockable tokens against CLA of a specified address * @param _of Address of user, claiming back unlockable tokens against CLA */ function unlock(address _of) public checkPause returns (uint256 unlockableTokens) { unlockableTokens = _tokensUnlockable(_of, CLA); if (unlockableTokens > 0) { locked[_of][CLA].claimed = true; emit Unlocked(_of, CLA, unlockableTokens); require(token.transfer(_of, unlockableTokens)); } } /** * @dev Updates Uint Parameters of a code * @param code whose details we want to update * @param val value to set */ function updateUintParameters(bytes8 code, uint val) public { require(ms.checkIsAuthToGoverned(msg.sender)); if (code == "MNCLT") { minCALockTime = val.mul(1 days); } else { revert("Invalid param code"); } } /** * @dev Gets the validity of locked tokens of a specified address * @param _of The address to query the validity * @param reason reason for which tokens were locked */ function getLockedTokensValidity(address _of, bytes32 reason) public view returns (uint256 validity) { validity = locked[_of][reason].validity; } /** * @dev Gets the unlockable tokens of a specified address * @param _of The address to query the the unlockable token count of */ function getUnlockableTokens(address _of) public view returns (uint256 unlockableTokens) { for (uint256 i = 0; i < lockReason[_of].length; i++) { unlockableTokens = unlockableTokens.add(_tokensUnlockable(_of, lockReason[_of][i])); } } /** * @dev Returns tokens locked for a specified address for a * specified reason * * @param _of The address whose tokens are locked * @param _reason The reason to query the lock tokens for */ function tokensLocked(address _of, bytes32 _reason) public view returns (uint256 amount) { return _tokensLocked(_of, _reason); } /** * @dev Returns unlockable tokens for a specified address for a specified reason * @param _of The address to query the the unlockable token count of * @param _reason The reason to query the unlockable tokens for */ function tokensUnlockable(address _of, bytes32 _reason) public view returns (uint256 amount) { return _tokensUnlockable(_of, _reason); } function totalSupply() public view returns (uint256) { return token.totalSupply(); } /** * @dev Returns tokens locked for a specified address for a * specified reason at a specific time * * @param _of The address whose tokens are locked * @param _reason The reason to query the lock tokens for * @param _time The timestamp to query the lock tokens for */ function tokensLockedAtTime(address _of, bytes32 _reason, uint256 _time) public view returns (uint256 amount) { return _tokensLockedAtTime(_of, _reason, _time); } /** * @dev Returns the total amount of tokens held by an address: * transferable + locked + staked for pooled staking - pending burns. * Used by Claims and Governance in member voting to calculate the user's vote weight. * * @param _of The address to query the total balance of * @param _of The address to query the total balance of */ function totalBalanceOf(address _of) public view returns (uint256 amount) { amount = token.balanceOf(_of); for (uint256 i = 0; i < lockReason[_of].length; i++) { amount = amount.add(_tokensLocked(_of, lockReason[_of][i])); } uint stakerReward = pooledStaking.stakerReward(_of); uint stakerDeposit = pooledStaking.stakerDeposit(_of); amount = amount.add(stakerDeposit).add(stakerReward); } /** * @dev Returns the total locked tokens at time * Returns the total amount of locked and staked tokens at a given time. Used by MemberRoles to check eligibility * for withdraw / switch membership. Includes tokens locked for Claim Assessment and staked for Risk Assessment. * Does not take into account pending burns. * * @param _of member whose locked tokens are to be calculate * @param _time timestamp when the tokens should be locked */ function totalLockedBalance(address _of, uint256 _time) public view returns (uint256 amount) { for (uint256 i = 0; i < lockReason[_of].length; i++) { amount = amount.add(_tokensLockedAtTime(_of, lockReason[_of][i], _time)); } amount = amount.add(pooledStaking.stakerDeposit(_of)); } /** * @dev Locks a specified amount of tokens against an address, * for a specified reason and time * @param _of address whose tokens are to be locked * @param _reason The reason to lock tokens * @param _amount Number of tokens to be locked * @param _time Lock time in seconds */ function _lock(address _of, bytes32 _reason, uint256 _amount, uint256 _time) internal { require(_tokensLocked(_of, _reason) == 0); require(_amount != 0); if (locked[_of][_reason].amount == 0) { lockReason[_of].push(_reason); } require(token.operatorTransfer(_of, _amount)); uint256 validUntil = now.add(_time); // solhint-disable-line locked[_of][_reason] = LockToken(_amount, validUntil, false); emit Locked(_of, _reason, _amount, validUntil); } /** * @dev Returns tokens locked for a specified address for a * specified reason * * @param _of The address whose tokens are locked * @param _reason The reason to query the lock tokens for */ function _tokensLocked(address _of, bytes32 _reason) internal view returns (uint256 amount) { if (!locked[_of][_reason].claimed) { amount = locked[_of][_reason].amount; } } /** * @dev Returns tokens locked for a specified address for a * specified reason at a specific time * * @param _of The address whose tokens are locked * @param _reason The reason to query the lock tokens for * @param _time The timestamp to query the lock tokens for */ function _tokensLockedAtTime(address _of, bytes32 _reason, uint256 _time) internal view returns (uint256 amount) { if (locked[_of][_reason].validity > _time) { amount = locked[_of][_reason].amount; } } /** * @dev Extends lock for a specified reason and time * @param _of The address whose tokens are locked * @param _reason The reason to lock tokens * @param _time Lock extension time in seconds */ function _extendLock(address _of, bytes32 _reason, uint256 _time) internal { require(_tokensLocked(_of, _reason) > 0); emit Unlocked(_of, _reason, locked[_of][_reason].amount); locked[_of][_reason].validity = locked[_of][_reason].validity.add(_time); emit Locked(_of, _reason, locked[_of][_reason].amount, locked[_of][_reason].validity); } /** * @dev reduce lock duration for a specified reason and time * @param _of The address whose tokens are locked * @param _reason The reason to lock tokens * @param _time Lock reduction time in seconds */ function _reduceLock(address _of, bytes32 _reason, uint256 _time) internal { require(_tokensLocked(_of, _reason) > 0); emit Unlocked(_of, _reason, locked[_of][_reason].amount); locked[_of][_reason].validity = locked[_of][_reason].validity.sub(_time); emit Locked(_of, _reason, locked[_of][_reason].amount, locked[_of][_reason].validity); } /** * @dev Returns unlockable tokens for a specified address for a specified reason * @param _of The address to query the the unlockable token count of * @param _reason The reason to query the unlockable tokens for */ function _tokensUnlockable(address _of, bytes32 _reason) internal view returns (uint256 amount) { if (locked[_of][_reason].validity <= now && !locked[_of][_reason].claimed) { amount = locked[_of][_reason].amount; } } /** * @dev Burns locked tokens of a user * @param _of address whose tokens are to be burned * @param _reason lock reason for which tokens are to be burned * @param _amount amount of tokens to burn */ function _burnLockedTokens(address _of, bytes32 _reason, uint256 _amount) internal { uint256 amount = _tokensLocked(_of, _reason); require(amount >= _amount); if (amount == _amount) { locked[_of][_reason].claimed = true; } locked[_of][_reason].amount = locked[_of][_reason].amount.sub(_amount); if (locked[_of][_reason].amount == 0) { _removeReason(_of, _reason); } token.burn(_amount); emit Burned(_of, _reason, _amount); } /** * @dev Released locked tokens of an address locked for a specific reason * @param _of address whose tokens are to be released from lock * @param _reason reason of the lock * @param _amount amount of tokens to release */ function _releaseLockedTokens(address _of, bytes32 _reason, uint256 _amount) internal { uint256 amount = _tokensLocked(_of, _reason); require(amount >= _amount); if (amount == _amount) { locked[_of][_reason].claimed = true; } locked[_of][_reason].amount = locked[_of][_reason].amount.sub(_amount); if (locked[_of][_reason].amount == 0) { _removeReason(_of, _reason); } require(token.transfer(_of, _amount)); emit Unlocked(_of, _reason, _amount); } function _removeReason(address _of, bytes32 _reason) internal { uint len = lockReason[_of].length; for (uint i = 0; i < len; i++) { if (lockReason[_of][i] == _reason) { lockReason[_of][i] = lockReason[_of][len.sub(1)]; lockReason[_of].pop(); break; } } } } // File: contracts/modules/governance/external/Governed.sol /* Copyright (C) 2017 GovBlocks.io This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/ */ pragma solidity ^0.5.0; interface IMaster { function getLatestAddress(bytes2 _module) external view returns (address); } contract Governed { address public masterAddress; // Name of the dApp, needs to be set by contracts inheriting this contract /// @dev modifier that allows only the authorized addresses to execute the function modifier onlyAuthorizedToGovern() { IMaster ms = IMaster(masterAddress); require(ms.getLatestAddress("GV") == msg.sender, "Not authorized"); _; } /// @dev checks if an address is authorized to govern function isAuthorizedToGovern(address _toCheck) public view returns (bool) { IMaster ms = IMaster(masterAddress); return (ms.getLatestAddress("GV") == _toCheck); } } // File: contracts/modules/governance/external/IProposalCategory.sol /* Copyright (C) 2017 GovBlocks.io This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/ */ pragma solidity ^0.5.0; contract IProposalCategory { event Category( uint indexed categoryId, string categoryName, string actionHash ); /// @dev Adds new category /// @param _name Category name /// @param _memberRoleToVote Voting Layer sequence in which the voting has to be performed. /// @param _allowedToCreateProposal Member roles allowed to create the proposal /// @param _majorityVotePerc Majority Vote threshold for Each voting layer /// @param _quorumPerc minimum threshold percentage required in voting to calculate result /// @param _closingTime Vote closing time for Each voting layer /// @param _actionHash hash of details containing the action that has to be performed after proposal is accepted /// @param _contractAddress address of contract to call after proposal is accepted /// @param _contractName name of contract to be called after proposal is accepted /// @param _incentives rewards to distributed after proposal is accepted function addCategory( string calldata _name, uint _memberRoleToVote, uint _majorityVotePerc, uint _quorumPerc, uint[] calldata _allowedToCreateProposal, uint _closingTime, string calldata _actionHash, address _contractAddress, bytes2 _contractName, uint[] calldata _incentives ) external; /// @dev gets category details function category(uint _categoryId) external view returns ( uint categoryId, uint memberRoleToVote, uint majorityVotePerc, uint quorumPerc, uint[] memory allowedToCreateProposal, uint closingTime, uint minStake ); ///@dev gets category action details function categoryAction(uint _categoryId) external view returns ( uint categoryId, address contractAddress, bytes2 contractName, uint defaultIncentive ); /// @dev Gets Total number of categories added till now function totalCategories() external view returns (uint numberOfCategories); /// @dev Updates category details /// @param _categoryId Category id that needs to be updated /// @param _name Category name /// @param _memberRoleToVote Voting Layer sequence in which the voting has to be performed. /// @param _allowedToCreateProposal Member roles allowed to create the proposal /// @param _majorityVotePerc Majority Vote threshold for Each voting layer /// @param _quorumPerc minimum threshold percentage required in voting to calculate result /// @param _closingTime Vote closing time for Each voting layer /// @param _actionHash hash of details containing the action that has to be performed after proposal is accepted /// @param _contractAddress address of contract to call after proposal is accepted /// @param _contractName name of contract to be called after proposal is accepted /// @param _incentives rewards to distributed after proposal is accepted function updateCategory( uint _categoryId, string memory _name, uint _memberRoleToVote, uint _majorityVotePerc, uint _quorumPerc, uint[] memory _allowedToCreateProposal, uint _closingTime, string memory _actionHash, address _contractAddress, bytes2 _contractName, uint[] memory _incentives ) public; } // File: contracts/modules/governance/ProposalCategory.sol /* Copyright (C) 2017 GovBlocks.io This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/ */ pragma solidity ^0.5.0; contract ProposalCategory is Governed, IProposalCategory, Iupgradable { bool public constructorCheck; MemberRoles internal mr; struct CategoryStruct { uint memberRoleToVote; uint majorityVotePerc; uint quorumPerc; uint[] allowedToCreateProposal; uint closingTime; uint minStake; } struct CategoryAction { uint defaultIncentive; address contractAddress; bytes2 contractName; } CategoryStruct[] internal allCategory; mapping(uint => CategoryAction) internal categoryActionData; mapping(uint => uint) public categoryABReq; mapping(uint => uint) public isSpecialResolution; mapping(uint => bytes) public categoryActionHashes; bool public categoryActionHashUpdated; /** * @dev Adds new category (Discontinued, moved functionality to newCategory) * @param _name Category name * @param _memberRoleToVote Voting Layer sequence in which the voting has to be performed. * @param _majorityVotePerc Majority Vote threshold for Each voting layer * @param _quorumPerc minimum threshold percentage required in voting to calculate result * @param _allowedToCreateProposal Member roles allowed to create the proposal * @param _closingTime Vote closing time for Each voting layer * @param _actionHash hash of details containing the action that has to be performed after proposal is accepted * @param _contractAddress address of contract to call after proposal is accepted * @param _contractName name of contract to be called after proposal is accepted * @param _incentives rewards to distributed after proposal is accepted */ function addCategory( string calldata _name, uint _memberRoleToVote, uint _majorityVotePerc, uint _quorumPerc, uint[] calldata _allowedToCreateProposal, uint _closingTime, string calldata _actionHash, address _contractAddress, bytes2 _contractName, uint[] calldata _incentives ) external {} /** * @dev Initiates Default settings for Proposal Category contract (Adding default categories) */ function proposalCategoryInitiate() external {} /** * @dev Initiates Default action function hashes for existing categories * To be called after the contract has been upgraded by governance */ function updateCategoryActionHashes() external onlyOwner { require(!categoryActionHashUpdated, "Category action hashes already updated"); categoryActionHashUpdated = true; categoryActionHashes[1] = abi.encodeWithSignature("addRole(bytes32,string,address)"); categoryActionHashes[2] = abi.encodeWithSignature("updateRole(address,uint256,bool)"); categoryActionHashes[3] = abi.encodeWithSignature("newCategory(string,uint256,uint256,uint256,uint256[],uint256,string,address,bytes2,uint256[],string)"); // solhint-disable-line categoryActionHashes[4] = abi.encodeWithSignature("editCategory(uint256,string,uint256,uint256,uint256,uint256[],uint256,string,address,bytes2,uint256[],string)"); // solhint-disable-line categoryActionHashes[5] = abi.encodeWithSignature("upgradeContractImplementation(bytes2,address)"); categoryActionHashes[6] = abi.encodeWithSignature("startEmergencyPause()"); categoryActionHashes[7] = abi.encodeWithSignature("addEmergencyPause(bool,bytes4)"); categoryActionHashes[8] = abi.encodeWithSignature("burnCAToken(uint256,uint256,address)"); categoryActionHashes[9] = abi.encodeWithSignature("setUserClaimVotePausedOn(address)"); categoryActionHashes[12] = abi.encodeWithSignature("transferEther(uint256,address)"); categoryActionHashes[13] = abi.encodeWithSignature("addInvestmentAssetCurrency(bytes4,address,bool,uint64,uint64,uint8)"); // solhint-disable-line categoryActionHashes[14] = abi.encodeWithSignature("changeInvestmentAssetHoldingPerc(bytes4,uint64,uint64)"); categoryActionHashes[15] = abi.encodeWithSignature("changeInvestmentAssetStatus(bytes4,bool)"); categoryActionHashes[16] = abi.encodeWithSignature("swapABMember(address,address)"); categoryActionHashes[17] = abi.encodeWithSignature("addCurrencyAssetCurrency(bytes4,address,uint256)"); categoryActionHashes[20] = abi.encodeWithSignature("updateUintParameters(bytes8,uint256)"); categoryActionHashes[21] = abi.encodeWithSignature("updateUintParameters(bytes8,uint256)"); categoryActionHashes[22] = abi.encodeWithSignature("updateUintParameters(bytes8,uint256)"); categoryActionHashes[23] = abi.encodeWithSignature("updateUintParameters(bytes8,uint256)"); categoryActionHashes[24] = abi.encodeWithSignature("updateUintParameters(bytes8,uint256)"); categoryActionHashes[25] = abi.encodeWithSignature("updateUintParameters(bytes8,uint256)"); categoryActionHashes[26] = abi.encodeWithSignature("updateUintParameters(bytes8,uint256)"); categoryActionHashes[27] = abi.encodeWithSignature("updateAddressParameters(bytes8,address)"); categoryActionHashes[28] = abi.encodeWithSignature("updateOwnerParameters(bytes8,address)"); categoryActionHashes[29] = abi.encodeWithSignature("upgradeMultipleContracts(bytes2[],address[])"); categoryActionHashes[30] = abi.encodeWithSignature("changeCurrencyAssetAddress(bytes4,address)"); categoryActionHashes[31] = abi.encodeWithSignature("changeCurrencyAssetBaseMin(bytes4,uint256)"); categoryActionHashes[32] = abi.encodeWithSignature("changeInvestmentAssetAddressAndDecimal(bytes4,address,uint8)"); // solhint-disable-line categoryActionHashes[33] = abi.encodeWithSignature("externalLiquidityTrade()"); } /** * @dev Gets Total number of categories added till now */ function totalCategories() external view returns (uint) { return allCategory.length; } /** * @dev Gets category details */ function category(uint _categoryId) external view returns (uint, uint, uint, uint, uint[] memory, uint, uint) { return ( _categoryId, allCategory[_categoryId].memberRoleToVote, allCategory[_categoryId].majorityVotePerc, allCategory[_categoryId].quorumPerc, allCategory[_categoryId].allowedToCreateProposal, allCategory[_categoryId].closingTime, allCategory[_categoryId].minStake ); } /** * @dev Gets category ab required and isSpecialResolution * @return the category id * @return if AB voting is required * @return is category a special resolution */ function categoryExtendedData(uint _categoryId) external view returns (uint, uint, uint) { return ( _categoryId, categoryABReq[_categoryId], isSpecialResolution[_categoryId] ); } /** * @dev Gets the category acion details * @param _categoryId is the category id in concern * @return the category id * @return the contract address * @return the contract name * @return the default incentive */ function categoryAction(uint _categoryId) external view returns (uint, address, bytes2, uint) { return ( _categoryId, categoryActionData[_categoryId].contractAddress, categoryActionData[_categoryId].contractName, categoryActionData[_categoryId].defaultIncentive ); } /** * @dev Gets the category acion details of a category id * @param _categoryId is the category id in concern * @return the category id * @return the contract address * @return the contract name * @return the default incentive * @return action function hash */ function categoryActionDetails(uint _categoryId) external view returns (uint, address, bytes2, uint, bytes memory) { return ( _categoryId, categoryActionData[_categoryId].contractAddress, categoryActionData[_categoryId].contractName, categoryActionData[_categoryId].defaultIncentive, categoryActionHashes[_categoryId] ); } /** * @dev Updates dependant contract addresses */ function changeDependentContractAddress() public { mr = MemberRoles(ms.getLatestAddress("MR")); } /** * @dev Adds new category * @param _name Category name * @param _memberRoleToVote Voting Layer sequence in which the voting has to be performed. * @param _majorityVotePerc Majority Vote threshold for Each voting layer * @param _quorumPerc minimum threshold percentage required in voting to calculate result * @param _allowedToCreateProposal Member roles allowed to create the proposal * @param _closingTime Vote closing time for Each voting layer * @param _actionHash hash of details containing the action that has to be performed after proposal is accepted * @param _contractAddress address of contract to call after proposal is accepted * @param _contractName name of contract to be called after proposal is accepted * @param _incentives rewards to distributed after proposal is accepted * @param _functionHash function signature to be executed */ function newCategory( string memory _name, uint _memberRoleToVote, uint _majorityVotePerc, uint _quorumPerc, uint[] memory _allowedToCreateProposal, uint _closingTime, string memory _actionHash, address _contractAddress, bytes2 _contractName, uint[] memory _incentives, string memory _functionHash ) public onlyAuthorizedToGovern { require(_quorumPerc <= 100 && _majorityVotePerc <= 100, "Invalid percentage"); require((_contractName == "EX" && _contractAddress == address(0)) || bytes(_functionHash).length > 0); require(_incentives[3] <= 1, "Invalid special resolution flag"); //If category is special resolution role authorized should be member if (_incentives[3] == 1) { require(_memberRoleToVote == uint(MemberRoles.Role.Member)); _majorityVotePerc = 0; _quorumPerc = 0; } _addCategory( _name, _memberRoleToVote, _majorityVotePerc, _quorumPerc, _allowedToCreateProposal, _closingTime, _actionHash, _contractAddress, _contractName, _incentives ); if (bytes(_functionHash).length > 0 && abi.encodeWithSignature(_functionHash).length == 4) { categoryActionHashes[allCategory.length - 1] = abi.encodeWithSignature(_functionHash); } } /** * @dev Changes the master address and update it's instance * @param _masterAddress is the new master address */ function changeMasterAddress(address _masterAddress) public { if (masterAddress != address(0)) require(masterAddress == msg.sender); masterAddress = _masterAddress; ms = INXMMaster(_masterAddress); nxMasterAddress = _masterAddress; } /** * @dev Updates category details (Discontinued, moved functionality to editCategory) * @param _categoryId Category id that needs to be updated * @param _name Category name * @param _memberRoleToVote Voting Layer sequence in which the voting has to be performed. * @param _allowedToCreateProposal Member roles allowed to create the proposal * @param _majorityVotePerc Majority Vote threshold for Each voting layer * @param _quorumPerc minimum threshold percentage required in voting to calculate result * @param _closingTime Vote closing time for Each voting layer * @param _actionHash hash of details containing the action that has to be performed after proposal is accepted * @param _contractAddress address of contract to call after proposal is accepted * @param _contractName name of contract to be called after proposal is accepted * @param _incentives rewards to distributed after proposal is accepted */ function updateCategory( uint _categoryId, string memory _name, uint _memberRoleToVote, uint _majorityVotePerc, uint _quorumPerc, uint[] memory _allowedToCreateProposal, uint _closingTime, string memory _actionHash, address _contractAddress, bytes2 _contractName, uint[] memory _incentives ) public {} /** * @dev Updates category details * @param _categoryId Category id that needs to be updated * @param _name Category name * @param _memberRoleToVote Voting Layer sequence in which the voting has to be performed. * @param _allowedToCreateProposal Member roles allowed to create the proposal * @param _majorityVotePerc Majority Vote threshold for Each voting layer * @param _quorumPerc minimum threshold percentage required in voting to calculate result * @param _closingTime Vote closing time for Each voting layer * @param _actionHash hash of details containing the action that has to be performed after proposal is accepted * @param _contractAddress address of contract to call after proposal is accepted * @param _contractName name of contract to be called after proposal is accepted * @param _incentives rewards to distributed after proposal is accepted * @param _functionHash function signature to be executed */ function editCategory( uint _categoryId, string memory _name, uint _memberRoleToVote, uint _majorityVotePerc, uint _quorumPerc, uint[] memory _allowedToCreateProposal, uint _closingTime, string memory _actionHash, address _contractAddress, bytes2 _contractName, uint[] memory _incentives, string memory _functionHash ) public onlyAuthorizedToGovern { require(_verifyMemberRoles(_memberRoleToVote, _allowedToCreateProposal) == 1, "Invalid Role"); require(_quorumPerc <= 100 && _majorityVotePerc <= 100, "Invalid percentage"); require((_contractName == "EX" && _contractAddress == address(0)) || bytes(_functionHash).length > 0); require(_incentives[3] <= 1, "Invalid special resolution flag"); //If category is special resolution role authorized should be member if (_incentives[3] == 1) { require(_memberRoleToVote == uint(MemberRoles.Role.Member)); _majorityVotePerc = 0; _quorumPerc = 0; } delete categoryActionHashes[_categoryId]; if (bytes(_functionHash).length > 0 && abi.encodeWithSignature(_functionHash).length == 4) { categoryActionHashes[_categoryId] = abi.encodeWithSignature(_functionHash); } allCategory[_categoryId].memberRoleToVote = _memberRoleToVote; allCategory[_categoryId].majorityVotePerc = _majorityVotePerc; allCategory[_categoryId].closingTime = _closingTime; allCategory[_categoryId].allowedToCreateProposal = _allowedToCreateProposal; allCategory[_categoryId].minStake = _incentives[0]; allCategory[_categoryId].quorumPerc = _quorumPerc; categoryActionData[_categoryId].defaultIncentive = _incentives[1]; categoryActionData[_categoryId].contractName = _contractName; categoryActionData[_categoryId].contractAddress = _contractAddress; categoryABReq[_categoryId] = _incentives[2]; isSpecialResolution[_categoryId] = _incentives[3]; emit Category(_categoryId, _name, _actionHash); } /** * @dev Internal call to add new category * @param _name Category name * @param _memberRoleToVote Voting Layer sequence in which the voting has to be performed. * @param _majorityVotePerc Majority Vote threshold for Each voting layer * @param _quorumPerc minimum threshold percentage required in voting to calculate result * @param _allowedToCreateProposal Member roles allowed to create the proposal * @param _closingTime Vote closing time for Each voting layer * @param _actionHash hash of details containing the action that has to be performed after proposal is accepted * @param _contractAddress address of contract to call after proposal is accepted * @param _contractName name of contract to be called after proposal is accepted * @param _incentives rewards to distributed after proposal is accepted */ function _addCategory( string memory _name, uint _memberRoleToVote, uint _majorityVotePerc, uint _quorumPerc, uint[] memory _allowedToCreateProposal, uint _closingTime, string memory _actionHash, address _contractAddress, bytes2 _contractName, uint[] memory _incentives ) internal { require(_verifyMemberRoles(_memberRoleToVote, _allowedToCreateProposal) == 1, "Invalid Role"); allCategory.push( CategoryStruct( _memberRoleToVote, _majorityVotePerc, _quorumPerc, _allowedToCreateProposal, _closingTime, _incentives[0] ) ); uint categoryId = allCategory.length - 1; categoryActionData[categoryId] = CategoryAction(_incentives[1], _contractAddress, _contractName); categoryABReq[categoryId] = _incentives[2]; isSpecialResolution[categoryId] = _incentives[3]; emit Category(categoryId, _name, _actionHash); } /** * @dev Internal call to check if given roles are valid or not */ function _verifyMemberRoles(uint _memberRoleToVote, uint[] memory _allowedToCreateProposal) internal view returns (uint) { uint totalRoles = mr.totalRoles(); if (_memberRoleToVote >= totalRoles) { return 0; } for (uint i = 0; i < _allowedToCreateProposal.length; i++) { if (_allowedToCreateProposal[i] >= totalRoles) { return 0; } } return 1; } } // File: contracts/modules/governance/external/IGovernance.sol /* Copyright (C) 2017 GovBlocks.io This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/ */ pragma solidity ^0.5.0; contract IGovernance { event Proposal( address indexed proposalOwner, uint256 indexed proposalId, uint256 dateAdd, string proposalTitle, string proposalSD, string proposalDescHash ); event Solution( uint256 indexed proposalId, address indexed solutionOwner, uint256 indexed solutionId, string solutionDescHash, uint256 dateAdd ); event Vote( address indexed from, uint256 indexed proposalId, uint256 indexed voteId, uint256 dateAdd, uint256 solutionChosen ); event RewardClaimed( address indexed member, uint gbtReward ); /// @dev VoteCast event is called whenever a vote is cast that can potentially close the proposal. event VoteCast (uint256 proposalId); /// @dev ProposalAccepted event is called when a proposal is accepted so that a server can listen that can /// call any offchain actions event ProposalAccepted (uint256 proposalId); /// @dev CloseProposalOnTime event is called whenever a proposal is created or updated to close it on time. event CloseProposalOnTime ( uint256 indexed proposalId, uint256 time ); /// @dev ActionSuccess event is called whenever an onchain action is executed. event ActionSuccess ( uint256 proposalId ); /// @dev Creates a new proposal /// @param _proposalDescHash Proposal description hash through IPFS having Short and long description of proposal /// @param _categoryId This id tells under which the proposal is categorized i.e. Proposal's Objective function createProposal( string calldata _proposalTitle, string calldata _proposalSD, string calldata _proposalDescHash, uint _categoryId ) external; /// @dev Edits the details of an existing proposal and creates new version /// @param _proposalId Proposal id that details needs to be updated /// @param _proposalDescHash Proposal description hash having long and short description of proposal. function updateProposal( uint _proposalId, string calldata _proposalTitle, string calldata _proposalSD, string calldata _proposalDescHash ) external; /// @dev Categorizes proposal to proceed further. Categories shows the proposal objective. function categorizeProposal( uint _proposalId, uint _categoryId, uint _incentives ) external; /// @dev Submit proposal with solution /// @param _proposalId Proposal id /// @param _solutionHash Solution hash contains parameters, values and description needed according to proposal function submitProposalWithSolution( uint _proposalId, string calldata _solutionHash, bytes calldata _action ) external; /// @dev Creates a new proposal with solution and votes for the solution /// @param _proposalDescHash Proposal description hash through IPFS having Short and long description of proposal /// @param _categoryId This id tells under which the proposal is categorized i.e. Proposal's Objective /// @param _solutionHash Solution hash contains parameters, values and description needed according to proposal function createProposalwithSolution( string calldata _proposalTitle, string calldata _proposalSD, string calldata _proposalDescHash, uint _categoryId, string calldata _solutionHash, bytes calldata _action ) external; /// @dev Casts vote /// @param _proposalId Proposal id /// @param _solutionChosen solution chosen while voting. _solutionChosen[0] is the chosen solution function submitVote(uint _proposalId, uint _solutionChosen) external; function closeProposal(uint _proposalId) external; function claimReward(address _memberAddress, uint _maxRecords) external returns (uint pendingDAppReward); function proposal(uint _proposalId) external view returns ( uint proposalId, uint category, uint status, uint finalVerdict, uint totalReward ); function canCloseProposal(uint _proposalId) public view returns (uint closeValue); function allowedToCatgorize() public view returns (uint roleId); } // File: contracts/modules/governance/Governance.sol // /* Copyright (C) 2017 GovBlocks.io // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see http://www.gnu.org/licenses/ */ pragma solidity ^0.5.0; contract Governance is IGovernance, Iupgradable { using SafeMath for uint; enum ProposalStatus { Draft, AwaitingSolution, VotingStarted, Accepted, Rejected, Majority_Not_Reached_But_Accepted, Denied } struct ProposalData { uint propStatus; uint finalVerdict; uint category; uint commonIncentive; uint dateUpd; address owner; } struct ProposalVote { address voter; uint proposalId; uint dateAdd; } struct VoteTally { mapping(uint => uint) memberVoteValue; mapping(uint => uint) abVoteValue; uint voters; } struct DelegateVote { address follower; address leader; uint lastUpd; } ProposalVote[] internal allVotes; DelegateVote[] public allDelegation; mapping(uint => ProposalData) internal allProposalData; mapping(uint => bytes[]) internal allProposalSolutions; mapping(address => uint[]) internal allVotesByMember; mapping(uint => mapping(address => bool)) public rewardClaimed; mapping(address => mapping(uint => uint)) public memberProposalVote; mapping(address => uint) public followerDelegation; mapping(address => uint) internal followerCount; mapping(address => uint[]) internal leaderDelegation; mapping(uint => VoteTally) public proposalVoteTally; mapping(address => bool) public isOpenForDelegation; mapping(address => uint) public lastRewardClaimed; bool internal constructorCheck; uint public tokenHoldingTime; uint internal roleIdAllowedToCatgorize; uint internal maxVoteWeigthPer; uint internal specialResolutionMajPerc; uint internal maxFollowers; uint internal totalProposals; uint internal maxDraftTime; MemberRoles internal memberRole; ProposalCategory internal proposalCategory; TokenController internal tokenInstance; mapping(uint => uint) public proposalActionStatus; mapping(uint => uint) internal proposalExecutionTime; mapping(uint => mapping(address => bool)) public proposalRejectedByAB; mapping(uint => uint) internal actionRejectedCount; bool internal actionParamsInitialised; uint internal actionWaitingTime; uint constant internal AB_MAJ_TO_REJECT_ACTION = 3; enum ActionStatus { Pending, Accepted, Rejected, Executed, NoAction } /** * @dev Called whenever an action execution is failed. */ event ActionFailed ( uint256 proposalId ); /** * @dev Called whenever an AB member rejects the action execution. */ event ActionRejected ( uint256 indexed proposalId, address rejectedBy ); /** * @dev Checks if msg.sender is proposal owner */ modifier onlyProposalOwner(uint _proposalId) { require(msg.sender == allProposalData[_proposalId].owner, "Not allowed"); _; } /** * @dev Checks if proposal is opened for voting */ modifier voteNotStarted(uint _proposalId) { require(allProposalData[_proposalId].propStatus < uint(ProposalStatus.VotingStarted)); _; } /** * @dev Checks if msg.sender is allowed to create proposal under given category */ modifier isAllowed(uint _categoryId) { require(allowedToCreateProposal(_categoryId), "Not allowed"); _; } /** * @dev Checks if msg.sender is allowed categorize proposal under given category */ modifier isAllowedToCategorize() { require(memberRole.checkRole(msg.sender, roleIdAllowedToCatgorize), "Not allowed"); _; } /** * @dev Checks if msg.sender had any pending rewards to be claimed */ modifier checkPendingRewards { require(getPendingReward(msg.sender) == 0, "Claim reward"); _; } /** * @dev Event emitted whenever a proposal is categorized */ event ProposalCategorized( uint indexed proposalId, address indexed categorizedBy, uint categoryId ); /** * @dev Removes delegation of an address. * @param _add address to undelegate. */ function removeDelegation(address _add) external onlyInternal { _unDelegate(_add); } /** * @dev Creates a new proposal * @param _proposalDescHash Proposal description hash through IPFS having Short and long description of proposal * @param _categoryId This id tells under which the proposal is categorized i.e. Proposal's Objective */ function createProposal( string calldata _proposalTitle, string calldata _proposalSD, string calldata _proposalDescHash, uint _categoryId ) external isAllowed(_categoryId) { require(ms.isMember(msg.sender), "Not Member"); _createProposal(_proposalTitle, _proposalSD, _proposalDescHash, _categoryId); } /** * @dev Edits the details of an existing proposal * @param _proposalId Proposal id that details needs to be updated * @param _proposalDescHash Proposal description hash having long and short description of proposal. */ function updateProposal( uint _proposalId, string calldata _proposalTitle, string calldata _proposalSD, string calldata _proposalDescHash ) external onlyProposalOwner(_proposalId) { require( allProposalSolutions[_proposalId].length < 2, "Not allowed" ); allProposalData[_proposalId].propStatus = uint(ProposalStatus.Draft); allProposalData[_proposalId].category = 0; allProposalData[_proposalId].commonIncentive = 0; emit Proposal( allProposalData[_proposalId].owner, _proposalId, now, _proposalTitle, _proposalSD, _proposalDescHash ); } /** * @dev Categorizes proposal to proceed further. Categories shows the proposal objective. */ function categorizeProposal( uint _proposalId, uint _categoryId, uint _incentive ) external voteNotStarted(_proposalId) isAllowedToCategorize { _categorizeProposal(_proposalId, _categoryId, _incentive); } /** * @dev Submit proposal with solution * @param _proposalId Proposal id * @param _solutionHash Solution hash contains parameters, values and description needed according to proposal */ function submitProposalWithSolution( uint _proposalId, string calldata _solutionHash, bytes calldata _action ) external onlyProposalOwner(_proposalId) { require(allProposalData[_proposalId].propStatus == uint(ProposalStatus.AwaitingSolution)); _proposalSubmission(_proposalId, _solutionHash, _action); } /** * @dev Creates a new proposal with solution * @param _proposalDescHash Proposal description hash through IPFS having Short and long description of proposal * @param _categoryId This id tells under which the proposal is categorized i.e. Proposal's Objective * @param _solutionHash Solution hash contains parameters, values and description needed according to proposal */ function createProposalwithSolution( string calldata _proposalTitle, string calldata _proposalSD, string calldata _proposalDescHash, uint _categoryId, string calldata _solutionHash, bytes calldata _action ) external isAllowed(_categoryId) { uint proposalId = totalProposals; _createProposal(_proposalTitle, _proposalSD, _proposalDescHash, _categoryId); require(_categoryId > 0); _proposalSubmission( proposalId, _solutionHash, _action ); } /** * @dev Submit a vote on the proposal. * @param _proposalId to vote upon. * @param _solutionChosen is the chosen vote. */ function submitVote(uint _proposalId, uint _solutionChosen) external { require(allProposalData[_proposalId].propStatus == uint(Governance.ProposalStatus.VotingStarted), "Not allowed"); require(_solutionChosen < allProposalSolutions[_proposalId].length); _submitVote(_proposalId, _solutionChosen); } /** * @dev Closes the proposal. * @param _proposalId of proposal to be closed. */ function closeProposal(uint _proposalId) external { uint category = allProposalData[_proposalId].category; uint _memberRole; if (allProposalData[_proposalId].dateUpd.add(maxDraftTime) <= now && allProposalData[_proposalId].propStatus < uint(ProposalStatus.VotingStarted)) { _updateProposalStatus(_proposalId, uint(ProposalStatus.Denied)); } else { require(canCloseProposal(_proposalId) == 1); (, _memberRole,,,,,) = proposalCategory.category(allProposalData[_proposalId].category); if (_memberRole == uint(MemberRoles.Role.AdvisoryBoard)) { _closeAdvisoryBoardVote(_proposalId, category); } else { _closeMemberVote(_proposalId, category); } } } /** * @dev Claims reward for member. * @param _memberAddress to claim reward of. * @param _maxRecords maximum number of records to claim reward for. _proposals list of proposals of which reward will be claimed. * @return amount of pending reward. */ function claimReward(address _memberAddress, uint _maxRecords) external returns (uint pendingDAppReward) { uint voteId; address leader; uint lastUpd; require(msg.sender == ms.getLatestAddress("CR")); uint delegationId = followerDelegation[_memberAddress]; DelegateVote memory delegationData = allDelegation[delegationId]; if (delegationId > 0 && delegationData.leader != address(0)) { leader = delegationData.leader; lastUpd = delegationData.lastUpd; } else leader = _memberAddress; uint proposalId; uint totalVotes = allVotesByMember[leader].length; uint lastClaimed = totalVotes; uint j; uint i; for (i = lastRewardClaimed[_memberAddress]; i < totalVotes && j < _maxRecords; i++) { voteId = allVotesByMember[leader][i]; proposalId = allVotes[voteId].proposalId; if (proposalVoteTally[proposalId].voters > 0 && (allVotes[voteId].dateAdd > ( lastUpd.add(tokenHoldingTime)) || leader == _memberAddress)) { if (allProposalData[proposalId].propStatus > uint(ProposalStatus.VotingStarted)) { if (!rewardClaimed[voteId][_memberAddress]) { pendingDAppReward = pendingDAppReward.add( allProposalData[proposalId].commonIncentive.div( proposalVoteTally[proposalId].voters ) ); rewardClaimed[voteId][_memberAddress] = true; j++; } } else { if (lastClaimed == totalVotes) { lastClaimed = i; } } } } if (lastClaimed == totalVotes) { lastRewardClaimed[_memberAddress] = i; } else { lastRewardClaimed[_memberAddress] = lastClaimed; } if (j > 0) { emit RewardClaimed( _memberAddress, pendingDAppReward ); } } /** * @dev Sets delegation acceptance status of individual user * @param _status delegation acceptance status */ function setDelegationStatus(bool _status) external isMemberAndcheckPause checkPendingRewards { isOpenForDelegation[msg.sender] = _status; } /** * @dev Delegates vote to an address. * @param _add is the address to delegate vote to. */ function delegateVote(address _add) external isMemberAndcheckPause checkPendingRewards { require(ms.masterInitialized()); require(allDelegation[followerDelegation[_add]].leader == address(0)); if (followerDelegation[msg.sender] > 0) { require((allDelegation[followerDelegation[msg.sender]].lastUpd).add(tokenHoldingTime) < now); } require(!alreadyDelegated(msg.sender)); require(!memberRole.checkRole(msg.sender, uint(MemberRoles.Role.Owner))); require(!memberRole.checkRole(msg.sender, uint(MemberRoles.Role.AdvisoryBoard))); require(followerCount[_add] < maxFollowers); if (allVotesByMember[msg.sender].length > 0) { require((allVotes[allVotesByMember[msg.sender][allVotesByMember[msg.sender].length - 1]].dateAdd).add(tokenHoldingTime) < now); } require(ms.isMember(_add)); require(isOpenForDelegation[_add]); allDelegation.push(DelegateVote(msg.sender, _add, now)); followerDelegation[msg.sender] = allDelegation.length - 1; leaderDelegation[_add].push(allDelegation.length - 1); followerCount[_add]++; lastRewardClaimed[msg.sender] = allVotesByMember[_add].length; } /** * @dev Undelegates the sender */ function unDelegate() external isMemberAndcheckPause checkPendingRewards { _unDelegate(msg.sender); } /** * @dev Triggers action of accepted proposal after waiting time is finished */ function triggerAction(uint _proposalId) external { require(proposalActionStatus[_proposalId] == uint(ActionStatus.Accepted) && proposalExecutionTime[_proposalId] <= now, "Cannot trigger"); _triggerAction(_proposalId, allProposalData[_proposalId].category); } /** * @dev Provides option to Advisory board member to reject proposal action execution within actionWaitingTime, if found suspicious */ function rejectAction(uint _proposalId) external { require(memberRole.checkRole(msg.sender, uint(MemberRoles.Role.AdvisoryBoard)) && proposalExecutionTime[_proposalId] > now); require(proposalActionStatus[_proposalId] == uint(ActionStatus.Accepted)); require(!proposalRejectedByAB[_proposalId][msg.sender]); require( keccak256(proposalCategory.categoryActionHashes(allProposalData[_proposalId].category)) != keccak256(abi.encodeWithSignature("swapABMember(address,address)")) ); proposalRejectedByAB[_proposalId][msg.sender] = true; actionRejectedCount[_proposalId]++; emit ActionRejected(_proposalId, msg.sender); if (actionRejectedCount[_proposalId] == AB_MAJ_TO_REJECT_ACTION) { proposalActionStatus[_proposalId] = uint(ActionStatus.Rejected); } } /** * @dev Sets intial actionWaitingTime value * To be called after governance implementation has been updated */ function setInitialActionParameters() external onlyOwner { require(!actionParamsInitialised); actionParamsInitialised = true; actionWaitingTime = 24 * 1 hours; } /** * @dev Gets Uint Parameters of a code * @param code whose details we want * @return string value of the code * @return associated amount (time or perc or value) to the code */ function getUintParameters(bytes8 code) external view returns (bytes8 codeVal, uint val) { codeVal = code; if (code == "GOVHOLD") { val = tokenHoldingTime / (1 days); } else if (code == "MAXFOL") { val = maxFollowers; } else if (code == "MAXDRFT") { val = maxDraftTime / (1 days); } else if (code == "EPTIME") { val = ms.pauseTime() / (1 days); } else if (code == "ACWT") { val = actionWaitingTime / (1 hours); } } /** * @dev Gets all details of a propsal * @param _proposalId whose details we want * @return proposalId * @return category * @return status * @return finalVerdict * @return totalReward */ function proposal(uint _proposalId) external view returns ( uint proposalId, uint category, uint status, uint finalVerdict, uint totalRewar ) { return ( _proposalId, allProposalData[_proposalId].category, allProposalData[_proposalId].propStatus, allProposalData[_proposalId].finalVerdict, allProposalData[_proposalId].commonIncentive ); } /** * @dev Gets some details of a propsal * @param _proposalId whose details we want * @return proposalId * @return number of all proposal solutions * @return amount of votes */ function proposalDetails(uint _proposalId) external view returns (uint, uint, uint) { return ( _proposalId, allProposalSolutions[_proposalId].length, proposalVoteTally[_proposalId].voters ); } /** * @dev Gets solution action on a proposal * @param _proposalId whose details we want * @param _solution whose details we want * @return action of a solution on a proposal */ function getSolutionAction(uint _proposalId, uint _solution) external view returns (uint, bytes memory) { return ( _solution, allProposalSolutions[_proposalId][_solution] ); } /** * @dev Gets length of propsal * @return length of propsal */ function getProposalLength() external view returns (uint) { return totalProposals; } /** * @dev Get followers of an address * @return get followers of an address */ function getFollowers(address _add) external view returns (uint[] memory) { return leaderDelegation[_add]; } /** * @dev Gets pending rewards of a member * @param _memberAddress in concern * @return amount of pending reward */ function getPendingReward(address _memberAddress) public view returns (uint pendingDAppReward) { uint delegationId = followerDelegation[_memberAddress]; address leader; uint lastUpd; DelegateVote memory delegationData = allDelegation[delegationId]; if (delegationId > 0 && delegationData.leader != address(0)) { leader = delegationData.leader; lastUpd = delegationData.lastUpd; } else leader = _memberAddress; uint proposalId; for (uint i = lastRewardClaimed[_memberAddress]; i < allVotesByMember[leader].length; i++) { if (allVotes[allVotesByMember[leader][i]].dateAdd > ( lastUpd.add(tokenHoldingTime)) || leader == _memberAddress) { if (!rewardClaimed[allVotesByMember[leader][i]][_memberAddress]) { proposalId = allVotes[allVotesByMember[leader][i]].proposalId; if (proposalVoteTally[proposalId].voters > 0 && allProposalData[proposalId].propStatus > uint(ProposalStatus.VotingStarted)) { pendingDAppReward = pendingDAppReward.add( allProposalData[proposalId].commonIncentive.div( proposalVoteTally[proposalId].voters ) ); } } } } } /** * @dev Updates Uint Parameters of a code * @param code whose details we want to update * @param val value to set */ function updateUintParameters(bytes8 code, uint val) public { require(ms.checkIsAuthToGoverned(msg.sender)); if (code == "GOVHOLD") { tokenHoldingTime = val * 1 days; } else if (code == "MAXFOL") { maxFollowers = val; } else if (code == "MAXDRFT") { maxDraftTime = val * 1 days; } else if (code == "EPTIME") { ms.updatePauseTime(val * 1 days); } else if (code == "ACWT") { actionWaitingTime = val * 1 hours; } else { revert("Invalid code"); } } /** * @dev Updates all dependency addresses to latest ones from Master */ function changeDependentContractAddress() public { tokenInstance = TokenController(ms.dAppLocker()); memberRole = MemberRoles(ms.getLatestAddress("MR")); proposalCategory = ProposalCategory(ms.getLatestAddress("PC")); } /** * @dev Checks if msg.sender is allowed to create a proposal under given category */ function allowedToCreateProposal(uint category) public view returns (bool check) { if (category == 0) return true; uint[] memory mrAllowed; (,,,, mrAllowed,,) = proposalCategory.category(category); for (uint i = 0; i < mrAllowed.length; i++) { if (mrAllowed[i] == 0 || memberRole.checkRole(msg.sender, mrAllowed[i])) return true; } } /** * @dev Checks if an address is already delegated * @param _add in concern * @return bool value if the address is delegated or not */ function alreadyDelegated(address _add) public view returns (bool delegated) { for (uint i = 0; i < leaderDelegation[_add].length; i++) { if (allDelegation[leaderDelegation[_add][i]].leader == _add) { return true; } } } /** * @dev Checks If the proposal voting time is up and it's ready to close * i.e. Closevalue is 1 if proposal is ready to be closed, 2 if already closed, 0 otherwise! * @param _proposalId Proposal id to which closing value is being checked */ function canCloseProposal(uint _proposalId) public view returns (uint) { uint dateUpdate; uint pStatus; uint _closingTime; uint _roleId; uint majority; pStatus = allProposalData[_proposalId].propStatus; dateUpdate = allProposalData[_proposalId].dateUpd; (, _roleId, majority, , , _closingTime,) = proposalCategory.category(allProposalData[_proposalId].category); if ( pStatus == uint(ProposalStatus.VotingStarted) ) { uint numberOfMembers = memberRole.numberOfMembers(_roleId); if (_roleId == uint(MemberRoles.Role.AdvisoryBoard)) { if (proposalVoteTally[_proposalId].abVoteValue[1].mul(100).div(numberOfMembers) >= majority || proposalVoteTally[_proposalId].abVoteValue[1].add(proposalVoteTally[_proposalId].abVoteValue[0]) == numberOfMembers || dateUpdate.add(_closingTime) <= now) { return 1; } } else { if (numberOfMembers == proposalVoteTally[_proposalId].voters || dateUpdate.add(_closingTime) <= now) return 1; } } else if (pStatus > uint(ProposalStatus.VotingStarted)) { return 2; } else { return 0; } } /** * @dev Gets Id of member role allowed to categorize the proposal * @return roleId allowed to categorize the proposal */ function allowedToCatgorize() public view returns (uint roleId) { return roleIdAllowedToCatgorize; } /** * @dev Gets vote tally data * @param _proposalId in concern * @param _solution of a proposal id * @return member vote value * @return advisory board vote value * @return amount of votes */ function voteTallyData(uint _proposalId, uint _solution) public view returns (uint, uint, uint) { return (proposalVoteTally[_proposalId].memberVoteValue[_solution], proposalVoteTally[_proposalId].abVoteValue[_solution], proposalVoteTally[_proposalId].voters); } /** * @dev Internal call to create proposal * @param _proposalTitle of proposal * @param _proposalSD is short description of proposal * @param _proposalDescHash IPFS hash value of propsal * @param _categoryId of proposal */ function _createProposal( string memory _proposalTitle, string memory _proposalSD, string memory _proposalDescHash, uint _categoryId ) internal { require(proposalCategory.categoryABReq(_categoryId) == 0 || _categoryId == 0); uint _proposalId = totalProposals; allProposalData[_proposalId].owner = msg.sender; allProposalData[_proposalId].dateUpd = now; allProposalSolutions[_proposalId].push(""); totalProposals++; emit Proposal( msg.sender, _proposalId, now, _proposalTitle, _proposalSD, _proposalDescHash ); if (_categoryId > 0) _categorizeProposal(_proposalId, _categoryId, 0); } /** * @dev Internal call to categorize a proposal * @param _proposalId of proposal * @param _categoryId of proposal * @param _incentive is commonIncentive */ function _categorizeProposal( uint _proposalId, uint _categoryId, uint _incentive ) internal { require( _categoryId > 0 && _categoryId < proposalCategory.totalCategories(), "Invalid category" ); allProposalData[_proposalId].category = _categoryId; allProposalData[_proposalId].commonIncentive = _incentive; allProposalData[_proposalId].propStatus = uint(ProposalStatus.AwaitingSolution); emit ProposalCategorized(_proposalId, msg.sender, _categoryId); } /** * @dev Internal call to add solution to a proposal * @param _proposalId in concern * @param _action on that solution * @param _solutionHash string value */ function _addSolution(uint _proposalId, bytes memory _action, string memory _solutionHash) internal { allProposalSolutions[_proposalId].push(_action); emit Solution(_proposalId, msg.sender, allProposalSolutions[_proposalId].length - 1, _solutionHash, now); } /** * @dev Internal call to add solution and open proposal for voting */ function _proposalSubmission( uint _proposalId, string memory _solutionHash, bytes memory _action ) internal { uint _categoryId = allProposalData[_proposalId].category; if (proposalCategory.categoryActionHashes(_categoryId).length == 0) { require(keccak256(_action) == keccak256("")); proposalActionStatus[_proposalId] = uint(ActionStatus.NoAction); } _addSolution( _proposalId, _action, _solutionHash ); _updateProposalStatus(_proposalId, uint(ProposalStatus.VotingStarted)); (, , , , , uint closingTime,) = proposalCategory.category(_categoryId); emit CloseProposalOnTime(_proposalId, closingTime.add(now)); } /** * @dev Internal call to submit vote * @param _proposalId of proposal in concern * @param _solution for that proposal */ function _submitVote(uint _proposalId, uint _solution) internal { uint delegationId = followerDelegation[msg.sender]; uint mrSequence; uint majority; uint closingTime; (, mrSequence, majority, , , closingTime,) = proposalCategory.category(allProposalData[_proposalId].category); require(allProposalData[_proposalId].dateUpd.add(closingTime) > now, "Closed"); require(memberProposalVote[msg.sender][_proposalId] == 0, "Not allowed"); require((delegationId == 0) || (delegationId > 0 && allDelegation[delegationId].leader == address(0) && _checkLastUpd(allDelegation[delegationId].lastUpd))); require(memberRole.checkRole(msg.sender, mrSequence), "Not Authorized"); uint totalVotes = allVotes.length; allVotesByMember[msg.sender].push(totalVotes); memberProposalVote[msg.sender][_proposalId] = totalVotes; allVotes.push(ProposalVote(msg.sender, _proposalId, now)); emit Vote(msg.sender, _proposalId, totalVotes, now, _solution); if (mrSequence == uint(MemberRoles.Role.Owner)) { if (_solution == 1) _callIfMajReached(_proposalId, uint(ProposalStatus.Accepted), allProposalData[_proposalId].category, 1, MemberRoles.Role.Owner); else _updateProposalStatus(_proposalId, uint(ProposalStatus.Rejected)); } else { uint numberOfMembers = memberRole.numberOfMembers(mrSequence); _setVoteTally(_proposalId, _solution, mrSequence); if (mrSequence == uint(MemberRoles.Role.AdvisoryBoard)) { if (proposalVoteTally[_proposalId].abVoteValue[1].mul(100).div(numberOfMembers) >= majority || (proposalVoteTally[_proposalId].abVoteValue[1].add(proposalVoteTally[_proposalId].abVoteValue[0])) == numberOfMembers) { emit VoteCast(_proposalId); } } else { if (numberOfMembers == proposalVoteTally[_proposalId].voters) emit VoteCast(_proposalId); } } } /** * @dev Internal call to set vote tally of a proposal * @param _proposalId of proposal in concern * @param _solution of proposal in concern * @param mrSequence number of members for a role */ function _setVoteTally(uint _proposalId, uint _solution, uint mrSequence) internal { uint categoryABReq; uint isSpecialResolution; (, categoryABReq, isSpecialResolution) = proposalCategory.categoryExtendedData(allProposalData[_proposalId].category); if (memberRole.checkRole(msg.sender, uint(MemberRoles.Role.AdvisoryBoard)) && (categoryABReq > 0) || mrSequence == uint(MemberRoles.Role.AdvisoryBoard)) { proposalVoteTally[_proposalId].abVoteValue[_solution]++; } tokenInstance.lockForMemberVote(msg.sender, tokenHoldingTime); if (mrSequence != uint(MemberRoles.Role.AdvisoryBoard)) { uint voteWeight; uint voters = 1; uint tokenBalance = tokenInstance.totalBalanceOf(msg.sender); uint totalSupply = tokenInstance.totalSupply(); if (isSpecialResolution == 1) { voteWeight = tokenBalance.add(10 ** 18); } else { voteWeight = (_minOf(tokenBalance, maxVoteWeigthPer.mul(totalSupply).div(100))).add(10 ** 18); } DelegateVote memory delegationData; for (uint i = 0; i < leaderDelegation[msg.sender].length; i++) { delegationData = allDelegation[leaderDelegation[msg.sender][i]]; if (delegationData.leader == msg.sender && _checkLastUpd(delegationData.lastUpd)) { if (memberRole.checkRole(delegationData.follower, mrSequence)) { tokenBalance = tokenInstance.totalBalanceOf(delegationData.follower); tokenInstance.lockForMemberVote(delegationData.follower, tokenHoldingTime); voters++; if (isSpecialResolution == 1) { voteWeight = voteWeight.add(tokenBalance.add(10 ** 18)); } else { voteWeight = voteWeight.add((_minOf(tokenBalance, maxVoteWeigthPer.mul(totalSupply).div(100))).add(10 ** 18)); } } } } proposalVoteTally[_proposalId].memberVoteValue[_solution] = proposalVoteTally[_proposalId].memberVoteValue[_solution].add(voteWeight); proposalVoteTally[_proposalId].voters = proposalVoteTally[_proposalId].voters + voters; } } /** * @dev Gets minimum of two numbers * @param a one of the two numbers * @param b one of the two numbers * @return minimum number out of the two */ function _minOf(uint a, uint b) internal pure returns (uint res) { res = a; if (res > b) res = b; } /** * @dev Check the time since last update has exceeded token holding time or not * @param _lastUpd is last update time * @return the bool which tells if the time since last update has exceeded token holding time or not */ function _checkLastUpd(uint _lastUpd) internal view returns (bool) { return (now - _lastUpd) > tokenHoldingTime; } /** * @dev Checks if the vote count against any solution passes the threshold value or not. */ function _checkForThreshold(uint _proposalId, uint _category) internal view returns (bool check) { uint categoryQuorumPerc; uint roleAuthorized; (, roleAuthorized, , categoryQuorumPerc, , ,) = proposalCategory.category(_category); check = ((proposalVoteTally[_proposalId].memberVoteValue[0] .add(proposalVoteTally[_proposalId].memberVoteValue[1])) .mul(100)) .div( tokenInstance.totalSupply().add( memberRole.numberOfMembers(roleAuthorized).mul(10 ** 18) ) ) >= categoryQuorumPerc; } /** * @dev Called when vote majority is reached * @param _proposalId of proposal in concern * @param _status of proposal in concern * @param category of proposal in concern * @param max vote value of proposal in concern */ function _callIfMajReached(uint _proposalId, uint _status, uint category, uint max, MemberRoles.Role role) internal { allProposalData[_proposalId].finalVerdict = max; _updateProposalStatus(_proposalId, _status); emit ProposalAccepted(_proposalId); if (proposalActionStatus[_proposalId] != uint(ActionStatus.NoAction)) { if (role == MemberRoles.Role.AdvisoryBoard) { _triggerAction(_proposalId, category); } else { proposalActionStatus[_proposalId] = uint(ActionStatus.Accepted); proposalExecutionTime[_proposalId] = actionWaitingTime.add(now); } } } /** * @dev Internal function to trigger action of accepted proposal */ function _triggerAction(uint _proposalId, uint _categoryId) internal { proposalActionStatus[_proposalId] = uint(ActionStatus.Executed); bytes2 contractName; address actionAddress; bytes memory _functionHash; (, actionAddress, contractName, , _functionHash) = proposalCategory.categoryActionDetails(_categoryId); if (contractName == "MS") { actionAddress = address(ms); } else if (contractName != "EX") { actionAddress = ms.getLatestAddress(contractName); } // solhint-disable-next-line avoid-low-level-calls (bool actionStatus,) = actionAddress.call(abi.encodePacked(_functionHash, allProposalSolutions[_proposalId][1])); if (actionStatus) { emit ActionSuccess(_proposalId); } else { proposalActionStatus[_proposalId] = uint(ActionStatus.Accepted); emit ActionFailed(_proposalId); } } /** * @dev Internal call to update proposal status * @param _proposalId of proposal in concern * @param _status of proposal to set */ function _updateProposalStatus(uint _proposalId, uint _status) internal { if (_status == uint(ProposalStatus.Rejected) || _status == uint(ProposalStatus.Denied)) { proposalActionStatus[_proposalId] = uint(ActionStatus.NoAction); } allProposalData[_proposalId].dateUpd = now; allProposalData[_proposalId].propStatus = _status; } /** * @dev Internal call to undelegate a follower * @param _follower is address of follower to undelegate */ function _unDelegate(address _follower) internal { uint followerId = followerDelegation[_follower]; if (followerId > 0) { followerCount[allDelegation[followerId].leader] = followerCount[allDelegation[followerId].leader].sub(1); allDelegation[followerId].leader = address(0); allDelegation[followerId].lastUpd = now; lastRewardClaimed[_follower] = allVotesByMember[_follower].length; } } /** * @dev Internal call to close member voting * @param _proposalId of proposal in concern * @param category of proposal in concern */ function _closeMemberVote(uint _proposalId, uint category) internal { uint isSpecialResolution; uint abMaj; (, abMaj, isSpecialResolution) = proposalCategory.categoryExtendedData(category); if (isSpecialResolution == 1) { uint acceptedVotePerc = proposalVoteTally[_proposalId].memberVoteValue[1].mul(100) .div( tokenInstance.totalSupply().add( memberRole.numberOfMembers(uint(MemberRoles.Role.Member)).mul(10 ** 18) )); if (acceptedVotePerc >= specialResolutionMajPerc) { _callIfMajReached(_proposalId, uint(ProposalStatus.Accepted), category, 1, MemberRoles.Role.Member); } else { _updateProposalStatus(_proposalId, uint(ProposalStatus.Denied)); } } else { if (_checkForThreshold(_proposalId, category)) { uint majorityVote; (,, majorityVote,,,,) = proposalCategory.category(category); if ( ((proposalVoteTally[_proposalId].memberVoteValue[1].mul(100)) .div(proposalVoteTally[_proposalId].memberVoteValue[0] .add(proposalVoteTally[_proposalId].memberVoteValue[1]) )) >= majorityVote ) { _callIfMajReached(_proposalId, uint(ProposalStatus.Accepted), category, 1, MemberRoles.Role.Member); } else { _updateProposalStatus(_proposalId, uint(ProposalStatus.Rejected)); } } else { if (abMaj > 0 && proposalVoteTally[_proposalId].abVoteValue[1].mul(100) .div(memberRole.numberOfMembers(uint(MemberRoles.Role.AdvisoryBoard))) >= abMaj) { _callIfMajReached(_proposalId, uint(ProposalStatus.Accepted), category, 1, MemberRoles.Role.Member); } else { _updateProposalStatus(_proposalId, uint(ProposalStatus.Denied)); } } } if (proposalVoteTally[_proposalId].voters > 0) { tokenInstance.mint(ms.getLatestAddress("CR"), allProposalData[_proposalId].commonIncentive); } } /** * @dev Internal call to close advisory board voting * @param _proposalId of proposal in concern * @param category of proposal in concern */ function _closeAdvisoryBoardVote(uint _proposalId, uint category) internal { uint _majorityVote; MemberRoles.Role _roleId = MemberRoles.Role.AdvisoryBoard; (,, _majorityVote,,,,) = proposalCategory.category(category); if (proposalVoteTally[_proposalId].abVoteValue[1].mul(100) .div(memberRole.numberOfMembers(uint(_roleId))) >= _majorityVote) { _callIfMajReached(_proposalId, uint(ProposalStatus.Accepted), category, 1, _roleId); } else { _updateProposalStatus(_proposalId, uint(ProposalStatus.Denied)); } } } // File: contracts/modules/token/TokenData.sol /* Copyright (C) 2020 NexusMutual.io This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/ */ pragma solidity ^0.5.0; contract TokenData is Iupgradable { using SafeMath for uint; address payable public walletAddress; uint public lockTokenTimeAfterCoverExp; uint public bookTime; uint public lockCADays; uint public lockMVDays; uint public scValidDays; uint public joiningFee; uint public stakerCommissionPer; uint public stakerMaxCommissionPer; uint public tokenExponent; uint public priceStep; struct StakeCommission { uint commissionEarned; uint commissionRedeemed; } struct Stake { address stakedContractAddress; uint stakedContractIndex; uint dateAdd; uint stakeAmount; uint unlockedAmount; uint burnedAmount; uint unLockableBeforeLastBurn; } struct Staker { address stakerAddress; uint stakerIndex; } struct CoverNote { uint amount; bool isDeposited; } /** * @dev mapping of uw address to array of sc address to fetch * all staked contract address of underwriter, pushing * data into this array of Stake returns stakerIndex */ mapping(address => Stake[]) public stakerStakedContracts; /** * @dev mapping of sc address to array of UW address to fetch * all underwritters of the staked smart contract * pushing data into this mapped array returns scIndex */ mapping(address => Staker[]) public stakedContractStakers; /** * @dev mapping of staked contract Address to the array of StakeCommission * here index of this array is stakedContractIndex */ mapping(address => mapping(uint => StakeCommission)) public stakedContractStakeCommission; mapping(address => uint) public lastCompletedStakeCommission; /** * @dev mapping of the staked contract address to the current * staker index who will receive commission. */ mapping(address => uint) public stakedContractCurrentCommissionIndex; /** * @dev mapping of the staked contract address to the * current staker index to burn token from. */ mapping(address => uint) public stakedContractCurrentBurnIndex; /** * @dev mapping to return true if Cover Note deposited against coverId */ mapping(uint => CoverNote) public depositedCN; mapping(address => uint) internal isBookedTokens; event Commission( address indexed stakedContractAddress, address indexed stakerAddress, uint indexed scIndex, uint commissionAmount ); constructor(address payable _walletAdd) public { walletAddress = _walletAdd; bookTime = 12 hours; joiningFee = 2000000000000000; // 0.002 Ether lockTokenTimeAfterCoverExp = 35 days; scValidDays = 250; lockCADays = 7 days; lockMVDays = 2 days; stakerCommissionPer = 20; stakerMaxCommissionPer = 50; tokenExponent = 4; priceStep = 1000; } /** * @dev Change the wallet address which receive Joining Fee */ function changeWalletAddress(address payable _address) external onlyInternal { walletAddress = _address; } /** * @dev Gets Uint Parameters of a code * @param code whose details we want * @return string value of the code * @return associated amount (time or perc or value) to the code */ function getUintParameters(bytes8 code) external view returns (bytes8 codeVal, uint val) { codeVal = code; if (code == "TOKEXP") { val = tokenExponent; } else if (code == "TOKSTEP") { val = priceStep; } else if (code == "RALOCKT") { val = scValidDays; } else if (code == "RACOMM") { val = stakerCommissionPer; } else if (code == "RAMAXC") { val = stakerMaxCommissionPer; } else if (code == "CABOOKT") { val = bookTime / (1 hours); } else if (code == "CALOCKT") { val = lockCADays / (1 days); } else if (code == "MVLOCKT") { val = lockMVDays / (1 days); } else if (code == "QUOLOCKT") { val = lockTokenTimeAfterCoverExp / (1 days); } else if (code == "JOINFEE") { val = joiningFee; } } /** * @dev Just for interface */ function changeDependentContractAddress() public {//solhint-disable-line } /** * @dev to get the contract staked by a staker * @param _stakerAddress is the address of the staker * @param _stakerIndex is the index of staker * @return the address of staked contract */ function getStakerStakedContractByIndex( address _stakerAddress, uint _stakerIndex ) public view returns (address stakedContractAddress) { stakedContractAddress = stakerStakedContracts[ _stakerAddress][_stakerIndex].stakedContractAddress; } /** * @dev to get the staker's staked burned * @param _stakerAddress is the address of the staker * @param _stakerIndex is the index of staker * @return amount burned */ function getStakerStakedBurnedByIndex( address _stakerAddress, uint _stakerIndex ) public view returns (uint burnedAmount) { burnedAmount = stakerStakedContracts[ _stakerAddress][_stakerIndex].burnedAmount; } /** * @dev to get the staker's staked unlockable before the last burn * @param _stakerAddress is the address of the staker * @param _stakerIndex is the index of staker * @return unlockable staked tokens */ function getStakerStakedUnlockableBeforeLastBurnByIndex( address _stakerAddress, uint _stakerIndex ) public view returns (uint unlockable) { unlockable = stakerStakedContracts[ _stakerAddress][_stakerIndex].unLockableBeforeLastBurn; } /** * @dev to get the staker's staked contract index * @param _stakerAddress is the address of the staker * @param _stakerIndex is the index of staker * @return is the index of the smart contract address */ function getStakerStakedContractIndex( address _stakerAddress, uint _stakerIndex ) public view returns (uint scIndex) { scIndex = stakerStakedContracts[ _stakerAddress][_stakerIndex].stakedContractIndex; } /** * @dev to get the staker index of the staked contract * @param _stakedContractAddress is the address of the staked contract * @param _stakedContractIndex is the index of staked contract * @return is the index of the staker */ function getStakedContractStakerIndex( address _stakedContractAddress, uint _stakedContractIndex ) public view returns (uint sIndex) { sIndex = stakedContractStakers[ _stakedContractAddress][_stakedContractIndex].stakerIndex; } /** * @dev to get the staker's initial staked amount on the contract * @param _stakerAddress is the address of the staker * @param _stakerIndex is the index of staker * @return staked amount */ function getStakerInitialStakedAmountOnContract( address _stakerAddress, uint _stakerIndex ) public view returns (uint amount) { amount = stakerStakedContracts[ _stakerAddress][_stakerIndex].stakeAmount; } /** * @dev to get the staker's staked contract length * @param _stakerAddress is the address of the staker * @return length of staked contract */ function getStakerStakedContractLength( address _stakerAddress ) public view returns (uint length) { length = stakerStakedContracts[_stakerAddress].length; } /** * @dev to get the staker's unlocked tokens which were staked * @param _stakerAddress is the address of the staker * @param _stakerIndex is the index of staker * @return amount */ function getStakerUnlockedStakedTokens( address _stakerAddress, uint _stakerIndex ) public view returns (uint amount) { amount = stakerStakedContracts[ _stakerAddress][_stakerIndex].unlockedAmount; } /** * @dev pushes the unlocked staked tokens by a staker. * @param _stakerAddress address of staker. * @param _stakerIndex index of the staker to distribute commission. * @param _amount amount to be given as commission. */ function pushUnlockedStakedTokens( address _stakerAddress, uint _stakerIndex, uint _amount ) public onlyInternal { stakerStakedContracts[_stakerAddress][ _stakerIndex].unlockedAmount = stakerStakedContracts[_stakerAddress][ _stakerIndex].unlockedAmount.add(_amount); } /** * @dev pushes the Burned tokens for a staker. * @param _stakerAddress address of staker. * @param _stakerIndex index of the staker. * @param _amount amount to be burned. */ function pushBurnedTokens( address _stakerAddress, uint _stakerIndex, uint _amount ) public onlyInternal { stakerStakedContracts[_stakerAddress][ _stakerIndex].burnedAmount = stakerStakedContracts[_stakerAddress][ _stakerIndex].burnedAmount.add(_amount); } /** * @dev pushes the unLockable tokens for a staker before last burn. * @param _stakerAddress address of staker. * @param _stakerIndex index of the staker. * @param _amount amount to be added to unlockable. */ function pushUnlockableBeforeLastBurnTokens( address _stakerAddress, uint _stakerIndex, uint _amount ) public onlyInternal { stakerStakedContracts[_stakerAddress][ _stakerIndex].unLockableBeforeLastBurn = stakerStakedContracts[_stakerAddress][ _stakerIndex].unLockableBeforeLastBurn.add(_amount); } /** * @dev sets the unLockable tokens for a staker before last burn. * @param _stakerAddress address of staker. * @param _stakerIndex index of the staker. * @param _amount amount to be added to unlockable. */ function setUnlockableBeforeLastBurnTokens( address _stakerAddress, uint _stakerIndex, uint _amount ) public onlyInternal { stakerStakedContracts[_stakerAddress][ _stakerIndex].unLockableBeforeLastBurn = _amount; } /** * @dev pushes the earned commission earned by a staker. * @param _stakerAddress address of staker. * @param _stakedContractAddress address of smart contract. * @param _stakedContractIndex index of the staker to distribute commission. * @param _commissionAmount amount to be given as commission. */ function pushEarnedStakeCommissions( address _stakerAddress, address _stakedContractAddress, uint _stakedContractIndex, uint _commissionAmount ) public onlyInternal { stakedContractStakeCommission[_stakedContractAddress][_stakedContractIndex]. commissionEarned = stakedContractStakeCommission[_stakedContractAddress][ _stakedContractIndex].commissionEarned.add(_commissionAmount); emit Commission( _stakerAddress, _stakedContractAddress, _stakedContractIndex, _commissionAmount ); } /** * @dev pushes the redeemed commission redeemed by a staker. * @param _stakerAddress address of staker. * @param _stakerIndex index of the staker to distribute commission. * @param _amount amount to be given as commission. */ function pushRedeemedStakeCommissions( address _stakerAddress, uint _stakerIndex, uint _amount ) public onlyInternal { uint stakedContractIndex = stakerStakedContracts[ _stakerAddress][_stakerIndex].stakedContractIndex; address stakedContractAddress = stakerStakedContracts[ _stakerAddress][_stakerIndex].stakedContractAddress; stakedContractStakeCommission[stakedContractAddress][stakedContractIndex]. commissionRedeemed = stakedContractStakeCommission[ stakedContractAddress][stakedContractIndex].commissionRedeemed.add(_amount); } /** * @dev Gets stake commission given to an underwriter * for particular stakedcontract on given index. * @param _stakerAddress address of staker. * @param _stakerIndex index of the staker commission. */ function getStakerEarnedStakeCommission( address _stakerAddress, uint _stakerIndex ) public view returns (uint) { return _getStakerEarnedStakeCommission(_stakerAddress, _stakerIndex); } /** * @dev Gets stake commission redeemed by an underwriter * for particular staked contract on given index. * @param _stakerAddress address of staker. * @param _stakerIndex index of the staker commission. * @return commissionEarned total amount given to staker. */ function getStakerRedeemedStakeCommission( address _stakerAddress, uint _stakerIndex ) public view returns (uint) { return _getStakerRedeemedStakeCommission(_stakerAddress, _stakerIndex); } /** * @dev Gets total stake commission given to an underwriter * @param _stakerAddress address of staker. * @return totalCommissionEarned total commission earned by staker. */ function getStakerTotalEarnedStakeCommission( address _stakerAddress ) public view returns (uint totalCommissionEarned) { totalCommissionEarned = 0; for (uint i = 0; i < stakerStakedContracts[_stakerAddress].length; i++) { totalCommissionEarned = totalCommissionEarned. add(_getStakerEarnedStakeCommission(_stakerAddress, i)); } } /** * @dev Gets total stake commission given to an underwriter * @param _stakerAddress address of staker. * @return totalCommissionEarned total commission earned by staker. */ function getStakerTotalReedmedStakeCommission( address _stakerAddress ) public view returns (uint totalCommissionRedeemed) { totalCommissionRedeemed = 0; for (uint i = 0; i < stakerStakedContracts[_stakerAddress].length; i++) { totalCommissionRedeemed = totalCommissionRedeemed.add( _getStakerRedeemedStakeCommission(_stakerAddress, i)); } } /** * @dev set flag to deposit/ undeposit cover note * against a cover Id * @param coverId coverId of Cover * @param flag true/false for deposit/undeposit */ function setDepositCN(uint coverId, bool flag) public onlyInternal { if (flag == true) { require(!depositedCN[coverId].isDeposited, "Cover note already deposited"); } depositedCN[coverId].isDeposited = flag; } /** * @dev set locked cover note amount * against a cover Id * @param coverId coverId of Cover * @param amount amount of nxm to be locked */ function setDepositCNAmount(uint coverId, uint amount) public onlyInternal { depositedCN[coverId].amount = amount; } /** * @dev to get the staker address on a staked contract * @param _stakedContractAddress is the address of the staked contract in concern * @param _stakedContractIndex is the index of staked contract's index * @return address of staker */ function getStakedContractStakerByIndex( address _stakedContractAddress, uint _stakedContractIndex ) public view returns (address stakerAddress) { stakerAddress = stakedContractStakers[ _stakedContractAddress][_stakedContractIndex].stakerAddress; } /** * @dev to get the length of stakers on a staked contract * @param _stakedContractAddress is the address of the staked contract in concern * @return length in concern */ function getStakedContractStakersLength( address _stakedContractAddress ) public view returns (uint length) { length = stakedContractStakers[_stakedContractAddress].length; } /** * @dev Adds a new stake record. * @param _stakerAddress staker address. * @param _stakedContractAddress smart contract address. * @param _amount amountof NXM to be staked. */ function addStake( address _stakerAddress, address _stakedContractAddress, uint _amount ) public onlyInternal returns (uint scIndex) { scIndex = (stakedContractStakers[_stakedContractAddress].push( Staker(_stakerAddress, stakerStakedContracts[_stakerAddress].length))).sub(1); stakerStakedContracts[_stakerAddress].push( Stake(_stakedContractAddress, scIndex, now, _amount, 0, 0, 0)); } /** * @dev books the user's tokens for maintaining Assessor Velocity, * i.e. once a token is used to cast a vote as a Claims assessor, * @param _of user's address. */ function bookCATokens(address _of) public onlyInternal { require(!isCATokensBooked(_of), "Tokens already booked"); isBookedTokens[_of] = now.add(bookTime); } /** * @dev to know if claim assessor's tokens are booked or not * @param _of is the claim assessor's address in concern * @return boolean representing the status of tokens booked */ function isCATokensBooked(address _of) public view returns (bool res) { if (now < isBookedTokens[_of]) res = true; } /** * @dev Sets the index which will receive commission. * @param _stakedContractAddress smart contract address. * @param _index current index. */ function setStakedContractCurrentCommissionIndex( address _stakedContractAddress, uint _index ) public onlyInternal { stakedContractCurrentCommissionIndex[_stakedContractAddress] = _index; } /** * @dev Sets the last complete commission index * @param _stakerAddress smart contract address. * @param _index current index. */ function setLastCompletedStakeCommissionIndex( address _stakerAddress, uint _index ) public onlyInternal { lastCompletedStakeCommission[_stakerAddress] = _index; } /** * @dev Sets the index till which commission is distrubuted. * @param _stakedContractAddress smart contract address. * @param _index current index. */ function setStakedContractCurrentBurnIndex( address _stakedContractAddress, uint _index ) public onlyInternal { stakedContractCurrentBurnIndex[_stakedContractAddress] = _index; } /** * @dev Updates Uint Parameters of a code * @param code whose details we want to update * @param val value to set */ function updateUintParameters(bytes8 code, uint val) public { require(ms.checkIsAuthToGoverned(msg.sender)); if (code == "TOKEXP") { _setTokenExponent(val); } else if (code == "TOKSTEP") { _setPriceStep(val); } else if (code == "RALOCKT") { _changeSCValidDays(val); } else if (code == "RACOMM") { _setStakerCommissionPer(val); } else if (code == "RAMAXC") { _setStakerMaxCommissionPer(val); } else if (code == "CABOOKT") { _changeBookTime(val * 1 hours); } else if (code == "CALOCKT") { _changelockCADays(val * 1 days); } else if (code == "MVLOCKT") { _changelockMVDays(val * 1 days); } else if (code == "QUOLOCKT") { _setLockTokenTimeAfterCoverExp(val * 1 days); } else if (code == "JOINFEE") { _setJoiningFee(val); } else { revert("Invalid param code"); } } /** * @dev Internal function to get stake commission given to an * underwriter for particular stakedcontract on given index. * @param _stakerAddress address of staker. * @param _stakerIndex index of the staker commission. */ function _getStakerEarnedStakeCommission( address _stakerAddress, uint _stakerIndex ) internal view returns (uint amount) { uint _stakedContractIndex; address _stakedContractAddress; _stakedContractAddress = stakerStakedContracts[ _stakerAddress][_stakerIndex].stakedContractAddress; _stakedContractIndex = stakerStakedContracts[ _stakerAddress][_stakerIndex].stakedContractIndex; amount = stakedContractStakeCommission[ _stakedContractAddress][_stakedContractIndex].commissionEarned; } /** * @dev Internal function to get stake commission redeemed by an * underwriter for particular stakedcontract on given index. * @param _stakerAddress address of staker. * @param _stakerIndex index of the staker commission. */ function _getStakerRedeemedStakeCommission( address _stakerAddress, uint _stakerIndex ) internal view returns (uint amount) { uint _stakedContractIndex; address _stakedContractAddress; _stakedContractAddress = stakerStakedContracts[ _stakerAddress][_stakerIndex].stakedContractAddress; _stakedContractIndex = stakerStakedContracts[ _stakerAddress][_stakerIndex].stakedContractIndex; amount = stakedContractStakeCommission[ _stakedContractAddress][_stakedContractIndex].commissionRedeemed; } /** * @dev to set the percentage of staker commission * @param _val is new percentage value */ function _setStakerCommissionPer(uint _val) internal { stakerCommissionPer = _val; } /** * @dev to set the max percentage of staker commission * @param _val is new percentage value */ function _setStakerMaxCommissionPer(uint _val) internal { stakerMaxCommissionPer = _val; } /** * @dev to set the token exponent value * @param _val is new value */ function _setTokenExponent(uint _val) internal { tokenExponent = _val; } /** * @dev to set the price step * @param _val is new value */ function _setPriceStep(uint _val) internal { priceStep = _val; } /** * @dev Changes number of days for which NXM needs to staked in case of underwriting */ function _changeSCValidDays(uint _days) internal { scValidDays = _days; } /** * @dev Changes the time period up to which tokens will be locked. * Used to generate the validity period of tokens booked by * a user for participating in claim's assessment/claim's voting. */ function _changeBookTime(uint _time) internal { bookTime = _time; } /** * @dev Changes lock CA days - number of days for which tokens * are locked while submitting a vote. */ function _changelockCADays(uint _val) internal { lockCADays = _val; } /** * @dev Changes lock MV days - number of days for which tokens are locked * while submitting a vote. */ function _changelockMVDays(uint _val) internal { lockMVDays = _val; } /** * @dev Changes extra lock period for a cover, post its expiry. */ function _setLockTokenTimeAfterCoverExp(uint time) internal { lockTokenTimeAfterCoverExp = time; } /** * @dev Set the joining fee for membership */ function _setJoiningFee(uint _amount) internal { joiningFee = _amount; } } // File: contracts/modules/token/TokenFunctions.sol /* Copyright (C) 2020 NexusMutual.io This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/ */ pragma solidity ^0.5.0; contract TokenFunctions is Iupgradable { using SafeMath for uint; NXMToken public tk; TokenController public tc; TokenData public td; QuotationData public qd; IPooledStaking public pooledStaking; event BurnCATokens(uint claimId, address addr, uint amount); /** * @dev Rewards stakers on purchase of cover on smart contract. * @param _contractAddress smart contract address. * @param _coverPriceNXM cover price in NXM. */ function pushStakerRewards(address _contractAddress, uint _coverPriceNXM) external onlyInternal { uint rewardValue = _coverPriceNXM.mul(td.stakerCommissionPer()).div(100); pooledStaking.accumulateReward(_contractAddress, rewardValue); } /** * @dev Returns amount of NXM Tokens locked as Cover Note for given coverId. * @param _of address of the coverHolder. * @param _coverId coverId of the cover. */ function getUserLockedCNTokens(address _of, uint _coverId) external view returns (uint) { return _getUserLockedCNTokens(_of, _coverId); } /** * @dev to get the all the cover locked tokens of a user * @param _of is the user address in concern * @return amount locked */ function getUserAllLockedCNTokens(address _of) external view returns (uint amount) { for (uint i = 0; i < qd.getUserCoverLength(_of); i++) { amount = amount.add(_getUserLockedCNTokens(_of, qd.getAllCoversOfUser(_of)[i])); } } /** * @dev Returns amount of NXM Tokens locked as Cover Note against given coverId. * @param _coverId coverId of the cover. */ function getLockedCNAgainstCover(uint _coverId) external view returns (uint) { return _getLockedCNAgainstCover(_coverId); } /** * @dev Change Dependent Contract Address */ function changeDependentContractAddress() public { tk = NXMToken(ms.tokenAddress()); td = TokenData(ms.getLatestAddress("TD")); tc = TokenController(ms.getLatestAddress("TC")); qd = QuotationData(ms.getLatestAddress("QD")); pooledStaking = IPooledStaking(ms.getLatestAddress("PS")); } /** * @dev Set the flag to check if cover note is deposited against the cover id * @param coverId Cover Id. */ function depositCN(uint coverId) public onlyInternal returns (bool success) { require(_getLockedCNAgainstCover(coverId) > 0, "No cover note available"); td.setDepositCN(coverId, true); success = true; } /** * @param _of address of Member * @param _coverId Cover Id * @param _lockTime Pending Time + Cover Period 7*1 days */ function extendCNEPOff(address _of, uint _coverId, uint _lockTime) public onlyInternal { uint timeStamp = now.add(_lockTime); uint coverValidUntil = qd.getValidityOfCover(_coverId); if (timeStamp >= coverValidUntil) { bytes32 reason = keccak256(abi.encodePacked("CN", _of, _coverId)); tc.extendLockOf(_of, reason, timeStamp); } } /** * @dev to burn the deposited cover tokens * @param coverId is id of cover whose tokens have to be burned * @return the status of the successful burning */ function burnDepositCN(uint coverId) public onlyInternal returns (bool success) { address _of = qd.getCoverMemberAddress(coverId); uint amount; (amount,) = td.depositedCN(coverId); amount = (amount.mul(50)).div(100); bytes32 reason = keccak256(abi.encodePacked("CN", _of, coverId)); tc.burnLockedTokens(_of, reason, amount); success = true; } /** * @dev Unlocks covernote locked against a given cover * @param coverId id of cover */ function unlockCN(uint coverId) public onlyInternal { (, bool isDeposited) = td.depositedCN(coverId); require(!isDeposited, "Cover note is deposited and can not be released"); uint lockedCN = _getLockedCNAgainstCover(coverId); if (lockedCN != 0) { address coverHolder = qd.getCoverMemberAddress(coverId); bytes32 reason = keccak256(abi.encodePacked("CN", coverHolder, coverId)); tc.releaseLockedTokens(coverHolder, reason, lockedCN); } } /** * @dev Burns tokens used for fraudulent voting against a claim * @param claimid Claim Id. * @param _value number of tokens to be burned * @param _of Claim Assessor's address. */ function burnCAToken(uint claimid, uint _value, address _of) public { require(ms.checkIsAuthToGoverned(msg.sender)); tc.burnLockedTokens(_of, "CLA", _value); emit BurnCATokens(claimid, _of, _value); } /** * @dev to lock cover note tokens * @param coverNoteAmount is number of tokens to be locked * @param coverPeriod is cover period in concern * @param coverId is the cover id of cover in concern * @param _of address whose tokens are to be locked */ function lockCN( uint coverNoteAmount, uint coverPeriod, uint coverId, address _of ) public onlyInternal { uint validity = (coverPeriod * 1 days).add(td.lockTokenTimeAfterCoverExp()); bytes32 reason = keccak256(abi.encodePacked("CN", _of, coverId)); td.setDepositCNAmount(coverId, coverNoteAmount); tc.lockOf(_of, reason, coverNoteAmount, validity); } /** * @dev to check if a member is locked for member vote * @param _of is the member address in concern * @return the boolean status */ function isLockedForMemberVote(address _of) public view returns (bool) { return now < tk.isLockedForMV(_of); } /** * @dev Returns amount of NXM Tokens locked as Cover Note for given coverId. * @param _coverId coverId of the cover. */ function _getLockedCNAgainstCover(uint _coverId) internal view returns (uint) { address coverHolder = qd.getCoverMemberAddress(_coverId); bytes32 reason = keccak256(abi.encodePacked("CN", coverHolder, _coverId)); return tc.tokensLockedAtTime(coverHolder, reason, now); } /** * @dev Returns amount of NXM Tokens locked as Cover Note for given coverId. * @param _of address of the coverHolder. * @param _coverId coverId of the cover. */ function _getUserLockedCNTokens(address _of, uint _coverId) internal view returns (uint) { bytes32 reason = keccak256(abi.encodePacked("CN", _of, _coverId)); return tc.tokensLockedAtTime(_of, reason, now); } } // File: contracts/modules/claims/ClaimsData.sol /* Copyright (C) 2020 NexusMutual.io This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/ */ pragma solidity ^0.5.0; contract ClaimsData is Iupgradable { using SafeMath for uint; struct Claim { uint coverId; uint dateUpd; } struct Vote { address voter; uint tokens; uint claimId; int8 verdict; bool rewardClaimed; } struct ClaimsPause { uint coverid; uint dateUpd; bool submit; } struct ClaimPauseVoting { uint claimid; uint pendingTime; bool voting; } struct RewardDistributed { uint lastCAvoteIndex; uint lastMVvoteIndex; } struct ClaimRewardDetails { uint percCA; uint percMV; uint tokenToBeDist; } struct ClaimTotalTokens { uint accept; uint deny; } struct ClaimRewardStatus { uint percCA; uint percMV; } ClaimRewardStatus[] internal rewardStatus; Claim[] internal allClaims; Vote[] internal allvotes; ClaimsPause[] internal claimPause; ClaimPauseVoting[] internal claimPauseVotingEP; mapping(address => RewardDistributed) internal voterVoteRewardReceived; mapping(uint => ClaimRewardDetails) internal claimRewardDetail; mapping(uint => ClaimTotalTokens) internal claimTokensCA; mapping(uint => ClaimTotalTokens) internal claimTokensMV; mapping(uint => int8) internal claimVote; mapping(uint => uint) internal claimsStatus; mapping(uint => uint) internal claimState12Count; mapping(uint => uint[]) internal claimVoteCA; mapping(uint => uint[]) internal claimVoteMember; mapping(address => uint[]) internal voteAddressCA; mapping(address => uint[]) internal voteAddressMember; mapping(address => uint[]) internal allClaimsByAddress; mapping(address => mapping(uint => uint)) internal userClaimVoteCA; mapping(address => mapping(uint => uint)) internal userClaimVoteMember; mapping(address => uint) public userClaimVotePausedOn; uint internal claimPauseLastsubmit; uint internal claimStartVotingFirstIndex; uint public pendingClaimStart; uint public claimDepositTime; uint public maxVotingTime; uint public minVotingTime; uint public payoutRetryTime; uint public claimRewardPerc; uint public minVoteThreshold; uint public maxVoteThreshold; uint public majorityConsensus; uint public pauseDaysCA; event ClaimRaise( uint indexed coverId, address indexed userAddress, uint claimId, uint dateSubmit ); event VoteCast( address indexed userAddress, uint indexed claimId, bytes4 indexed typeOf, uint tokens, uint submitDate, int8 verdict ); constructor() public { pendingClaimStart = 1; maxVotingTime = 48 * 1 hours; minVotingTime = 12 * 1 hours; payoutRetryTime = 24 * 1 hours; allvotes.push(Vote(address(0), 0, 0, 0, false)); allClaims.push(Claim(0, 0)); claimDepositTime = 7 days; claimRewardPerc = 20; minVoteThreshold = 5; maxVoteThreshold = 10; majorityConsensus = 70; pauseDaysCA = 3 days; _addRewardIncentive(); } /** * @dev Updates the pending claim start variable, * the lowest claim id with a pending decision/payout. */ function setpendingClaimStart(uint _start) external onlyInternal { require(pendingClaimStart <= _start); pendingClaimStart = _start; } /** * @dev Updates the max vote index for which claim assessor has received reward * @param _voter address of the voter. * @param caIndex last index till which reward was distributed for CA */ function setRewardDistributedIndexCA(address _voter, uint caIndex) external onlyInternal { voterVoteRewardReceived[_voter].lastCAvoteIndex = caIndex; } /** * @dev Used to pause claim assessor activity for 3 days * @param user Member address whose claim voting ability needs to be paused */ function setUserClaimVotePausedOn(address user) external { require(ms.checkIsAuthToGoverned(msg.sender)); userClaimVotePausedOn[user] = now; } /** * @dev Updates the max vote index for which member has received reward * @param _voter address of the voter. * @param mvIndex last index till which reward was distributed for member */ function setRewardDistributedIndexMV(address _voter, uint mvIndex) external onlyInternal { voterVoteRewardReceived[_voter].lastMVvoteIndex = mvIndex; } /** * @param claimid claim id. * @param percCA reward Percentage reward for claim assessor * @param percMV reward Percentage reward for members * @param tokens total tokens to be rewarded */ function setClaimRewardDetail( uint claimid, uint percCA, uint percMV, uint tokens ) external onlyInternal { claimRewardDetail[claimid].percCA = percCA; claimRewardDetail[claimid].percMV = percMV; claimRewardDetail[claimid].tokenToBeDist = tokens; } /** * @dev Sets the reward claim status against a vote id. * @param _voteid vote Id. * @param claimed true if reward for vote is claimed, else false. */ function setRewardClaimed(uint _voteid, bool claimed) external onlyInternal { allvotes[_voteid].rewardClaimed = claimed; } /** * @dev Sets the final vote's result(either accepted or declined)of a claim. * @param _claimId Claim Id. * @param _verdict 1 if claim is accepted,-1 if declined. */ function changeFinalVerdict(uint _claimId, int8 _verdict) external onlyInternal { claimVote[_claimId] = _verdict; } /** * @dev Creates a new claim. */ function addClaim( uint _claimId, uint _coverId, address _from, uint _nowtime ) external onlyInternal { allClaims.push(Claim(_coverId, _nowtime)); allClaimsByAddress[_from].push(_claimId); } /** * @dev Add Vote's details of a given claim. */ function addVote( address _voter, uint _tokens, uint claimId, int8 _verdict ) external onlyInternal { allvotes.push(Vote(_voter, _tokens, claimId, _verdict, false)); } /** * @dev Stores the id of the claim assessor vote given to a claim. * Maintains record of all votes given by all the CA to a claim. * @param _claimId Claim Id to which vote has given by the CA. * @param _voteid Vote Id. */ function addClaimVoteCA(uint _claimId, uint _voteid) external onlyInternal { claimVoteCA[_claimId].push(_voteid); } /** * @dev Sets the id of the vote. * @param _from Claim assessor's address who has given the vote. * @param _claimId Claim Id for which vote has been given by the CA. * @param _voteid Vote Id which will be stored against the given _from and claimid. */ function setUserClaimVoteCA( address _from, uint _claimId, uint _voteid ) external onlyInternal { userClaimVoteCA[_from][_claimId] = _voteid; voteAddressCA[_from].push(_voteid); } /** * @dev Stores the tokens locked by the Claim Assessors during voting of a given claim. * @param _claimId Claim Id. * @param _vote 1 for accept and increases the tokens of claim as accept, * -1 for deny and increases the tokens of claim as deny. * @param _tokens Number of tokens. */ function setClaimTokensCA(uint _claimId, int8 _vote, uint _tokens) external onlyInternal { if (_vote == 1) claimTokensCA[_claimId].accept = claimTokensCA[_claimId].accept.add(_tokens); if (_vote == - 1) claimTokensCA[_claimId].deny = claimTokensCA[_claimId].deny.add(_tokens); } /** * @dev Stores the tokens locked by the Members during voting of a given claim. * @param _claimId Claim Id. * @param _vote 1 for accept and increases the tokens of claim as accept, * -1 for deny and increases the tokens of claim as deny. * @param _tokens Number of tokens. */ function setClaimTokensMV(uint _claimId, int8 _vote, uint _tokens) external onlyInternal { if (_vote == 1) claimTokensMV[_claimId].accept = claimTokensMV[_claimId].accept.add(_tokens); if (_vote == - 1) claimTokensMV[_claimId].deny = claimTokensMV[_claimId].deny.add(_tokens); } /** * @dev Stores the id of the member vote given to a claim. * Maintains record of all votes given by all the Members to a claim. * @param _claimId Claim Id to which vote has been given by the Member. * @param _voteid Vote Id. */ function addClaimVotemember(uint _claimId, uint _voteid) external onlyInternal { claimVoteMember[_claimId].push(_voteid); } /** * @dev Sets the id of the vote. * @param _from Member's address who has given the vote. * @param _claimId Claim Id for which vote has been given by the Member. * @param _voteid Vote Id which will be stored against the given _from and claimid. */ function setUserClaimVoteMember( address _from, uint _claimId, uint _voteid ) external onlyInternal { userClaimVoteMember[_from][_claimId] = _voteid; voteAddressMember[_from].push(_voteid); } /** * @dev Increases the count of failure until payout of a claim is successful. */ function updateState12Count(uint _claimId, uint _cnt) external onlyInternal { claimState12Count[_claimId] = claimState12Count[_claimId].add(_cnt); } /** * @dev Sets status of a claim. * @param _claimId Claim Id. * @param _stat Status number. */ function setClaimStatus(uint _claimId, uint _stat) external onlyInternal { claimsStatus[_claimId] = _stat; } /** * @dev Sets the timestamp of a given claim at which the Claim's details has been updated. * @param _claimId Claim Id of claim which has been changed. * @param _dateUpd timestamp at which claim is updated. */ function setClaimdateUpd(uint _claimId, uint _dateUpd) external onlyInternal { allClaims[_claimId].dateUpd = _dateUpd; } /** @dev Queues Claims during Emergency Pause. */ function setClaimAtEmergencyPause( uint _coverId, uint _dateUpd, bool _submit ) external onlyInternal { claimPause.push(ClaimsPause(_coverId, _dateUpd, _submit)); } /** * @dev Set submission flag for Claims queued during emergency pause. * Set to true after EP is turned off and the claim is submitted . */ function setClaimSubmittedAtEPTrue(uint _index, bool _submit) external onlyInternal { claimPause[_index].submit = _submit; } /** * @dev Sets the index from which claim needs to be * submitted when emergency pause is swithched off. */ function setFirstClaimIndexToSubmitAfterEP( uint _firstClaimIndexToSubmit ) external onlyInternal { claimPauseLastsubmit = _firstClaimIndexToSubmit; } /** * @dev Sets the pending vote duration for a claim in case of emergency pause. */ function setPendingClaimDetails( uint _claimId, uint _pendingTime, bool _voting ) external onlyInternal { claimPauseVotingEP.push(ClaimPauseVoting(_claimId, _pendingTime, _voting)); } /** * @dev Sets voting flag true after claim is reopened for voting after emergency pause. */ function setPendingClaimVoteStatus(uint _claimId, bool _vote) external onlyInternal { claimPauseVotingEP[_claimId].voting = _vote; } /** * @dev Sets the index from which claim needs to be * reopened when emergency pause is swithched off. */ function setFirstClaimIndexToStartVotingAfterEP( uint _claimStartVotingFirstIndex ) external onlyInternal { claimStartVotingFirstIndex = _claimStartVotingFirstIndex; } /** * @dev Calls Vote Event. */ function callVoteEvent( address _userAddress, uint _claimId, bytes4 _typeOf, uint _tokens, uint _submitDate, int8 _verdict ) external onlyInternal { emit VoteCast( _userAddress, _claimId, _typeOf, _tokens, _submitDate, _verdict ); } /** * @dev Calls Claim Event. */ function callClaimEvent( uint _coverId, address _userAddress, uint _claimId, uint _datesubmit ) external onlyInternal { emit ClaimRaise(_coverId, _userAddress, _claimId, _datesubmit); } /** * @dev Gets Uint Parameters by parameter code * @param code whose details we want * @return string value of the parameter * @return associated amount (time or perc or value) to the code */ function getUintParameters(bytes8 code) external view returns (bytes8 codeVal, uint val) { codeVal = code; if (code == "CAMAXVT") { val = maxVotingTime / (1 hours); } else if (code == "CAMINVT") { val = minVotingTime / (1 hours); } else if (code == "CAPRETRY") { val = payoutRetryTime / (1 hours); } else if (code == "CADEPT") { val = claimDepositTime / (1 days); } else if (code == "CAREWPER") { val = claimRewardPerc; } else if (code == "CAMINTH") { val = minVoteThreshold; } else if (code == "CAMAXTH") { val = maxVoteThreshold; } else if (code == "CACONPER") { val = majorityConsensus; } else if (code == "CAPAUSET") { val = pauseDaysCA / (1 days); } } /** * @dev Get claim queued during emergency pause by index. */ function getClaimOfEmergencyPauseByIndex( uint _index ) external view returns ( uint coverId, uint dateUpd, bool submit ) { coverId = claimPause[_index].coverid; dateUpd = claimPause[_index].dateUpd; submit = claimPause[_index].submit; } /** * @dev Gets the Claim's details of given claimid. */ function getAllClaimsByIndex( uint _claimId ) external view returns ( uint coverId, int8 vote, uint status, uint dateUpd, uint state12Count ) { return ( allClaims[_claimId].coverId, claimVote[_claimId], claimsStatus[_claimId], allClaims[_claimId].dateUpd, claimState12Count[_claimId] ); } /** * @dev Gets the vote id of a given claim of a given Claim Assessor. */ function getUserClaimVoteCA( address _add, uint _claimId ) external view returns (uint idVote) { return userClaimVoteCA[_add][_claimId]; } /** * @dev Gets the vote id of a given claim of a given member. */ function getUserClaimVoteMember( address _add, uint _claimId ) external view returns (uint idVote) { return userClaimVoteMember[_add][_claimId]; } /** * @dev Gets the count of all votes. */ function getAllVoteLength() external view returns (uint voteCount) { return allvotes.length.sub(1); // Start Index always from 1. } /** * @dev Gets the status number of a given claim. * @param _claimId Claim id. * @return statno Status Number. */ function getClaimStatusNumber(uint _claimId) external view returns (uint claimId, uint statno) { return (_claimId, claimsStatus[_claimId]); } /** * @dev Gets the reward percentage to be distributed for a given status id * @param statusNumber the number of type of status * @return percCA reward Percentage for claim assessor * @return percMV reward Percentage for members */ function getRewardStatus(uint statusNumber) external view returns (uint percCA, uint percMV) { return (rewardStatus[statusNumber].percCA, rewardStatus[statusNumber].percMV); } /** * @dev Gets the number of tries that have been made for a successful payout of a Claim. */ function getClaimState12Count(uint _claimId) external view returns (uint num) { num = claimState12Count[_claimId]; } /** * @dev Gets the last update date of a claim. */ function getClaimDateUpd(uint _claimId) external view returns (uint dateupd) { dateupd = allClaims[_claimId].dateUpd; } /** * @dev Gets all Claims created by a user till date. * @param _member user's address. * @return claimarr List of Claims id. */ function getAllClaimsByAddress(address _member) external view returns (uint[] memory claimarr) { return allClaimsByAddress[_member]; } /** * @dev Gets the number of tokens that has been locked * while giving vote to a claim by Claim Assessors. * @param _claimId Claim Id. * @return accept Total number of tokens when CA accepts the claim. * @return deny Total number of tokens when CA declines the claim. */ function getClaimsTokenCA( uint _claimId ) external view returns ( uint claimId, uint accept, uint deny ) { return ( _claimId, claimTokensCA[_claimId].accept, claimTokensCA[_claimId].deny ); } /** * @dev Gets the number of tokens that have been * locked while assessing a claim as a member. * @param _claimId Claim Id. * @return accept Total number of tokens in acceptance of the claim. * @return deny Total number of tokens against the claim. */ function getClaimsTokenMV( uint _claimId ) external view returns ( uint claimId, uint accept, uint deny ) { return ( _claimId, claimTokensMV[_claimId].accept, claimTokensMV[_claimId].deny ); } /** * @dev Gets the total number of votes cast as Claims assessor for/against a given claim */ function getCaClaimVotesToken(uint _claimId) external view returns (uint claimId, uint cnt) { claimId = _claimId; cnt = 0; for (uint i = 0; i < claimVoteCA[_claimId].length; i++) { cnt = cnt.add(allvotes[claimVoteCA[_claimId][i]].tokens); } } /** * @dev Gets the total number of tokens cast as a member for/against a given claim */ function getMemberClaimVotesToken( uint _claimId ) external view returns (uint claimId, uint cnt) { claimId = _claimId; cnt = 0; for (uint i = 0; i < claimVoteMember[_claimId].length; i++) { cnt = cnt.add(allvotes[claimVoteMember[_claimId][i]].tokens); } } /** * @dev Provides information of a vote when given its vote id. * @param _voteid Vote Id. */ function getVoteDetails(uint _voteid) external view returns ( uint tokens, uint claimId, int8 verdict, bool rewardClaimed ) { return ( allvotes[_voteid].tokens, allvotes[_voteid].claimId, allvotes[_voteid].verdict, allvotes[_voteid].rewardClaimed ); } /** * @dev Gets the voter's address of a given vote id. */ function getVoterVote(uint _voteid) external view returns (address voter) { return allvotes[_voteid].voter; } /** * @dev Provides information of a Claim when given its claim id. * @param _claimId Claim Id. */ function getClaim( uint _claimId ) external view returns ( uint claimId, uint coverId, int8 vote, uint status, uint dateUpd, uint state12Count ) { return ( _claimId, allClaims[_claimId].coverId, claimVote[_claimId], claimsStatus[_claimId], allClaims[_claimId].dateUpd, claimState12Count[_claimId] ); } /** * @dev Gets the total number of votes of a given claim. * @param _claimId Claim Id. * @param _ca if 1: votes given by Claim Assessors to a claim, * else returns the number of votes of given by Members to a claim. * @return len total number of votes for/against a given claim. */ function getClaimVoteLength( uint _claimId, uint8 _ca ) external view returns (uint claimId, uint len) { claimId = _claimId; if (_ca == 1) len = claimVoteCA[_claimId].length; else len = claimVoteMember[_claimId].length; } /** * @dev Gets the verdict of a vote using claim id and index. * @param _ca 1 for vote given as a CA, else for vote given as a member. * @return ver 1 if vote was given in favour,-1 if given in against. */ function getVoteVerdict( uint _claimId, uint _index, uint8 _ca ) external view returns (int8 ver) { if (_ca == 1) ver = allvotes[claimVoteCA[_claimId][_index]].verdict; else ver = allvotes[claimVoteMember[_claimId][_index]].verdict; } /** * @dev Gets the Number of tokens of a vote using claim id and index. * @param _ca 1 for vote given as a CA, else for vote given as a member. * @return tok Number of tokens. */ function getVoteToken( uint _claimId, uint _index, uint8 _ca ) external view returns (uint tok) { if (_ca == 1) tok = allvotes[claimVoteCA[_claimId][_index]].tokens; else tok = allvotes[claimVoteMember[_claimId][_index]].tokens; } /** * @dev Gets the Voter's address of a vote using claim id and index. * @param _ca 1 for vote given as a CA, else for vote given as a member. * @return voter Voter's address. */ function getVoteVoter( uint _claimId, uint _index, uint8 _ca ) external view returns (address voter) { if (_ca == 1) voter = allvotes[claimVoteCA[_claimId][_index]].voter; else voter = allvotes[claimVoteMember[_claimId][_index]].voter; } /** * @dev Gets total number of Claims created by a user till date. * @param _add User's address. */ function getUserClaimCount(address _add) external view returns (uint len) { len = allClaimsByAddress[_add].length; } /** * @dev Calculates number of Claims that are in pending state. */ function getClaimLength() external view returns (uint len) { len = allClaims.length.sub(pendingClaimStart); } /** * @dev Gets the Number of all the Claims created till date. */ function actualClaimLength() external view returns (uint len) { len = allClaims.length; } /** * @dev Gets details of a claim. * @param _index claim id = pending claim start + given index * @param _add User's address. * @return coverid cover against which claim has been submitted. * @return claimId Claim Id. * @return voteCA verdict of vote given as a Claim Assessor. * @return voteMV verdict of vote given as a Member. * @return statusnumber Status of claim. */ function getClaimFromNewStart( uint _index, address _add ) external view returns ( uint coverid, uint claimId, int8 voteCA, int8 voteMV, uint statusnumber ) { uint i = pendingClaimStart.add(_index); coverid = allClaims[i].coverId; claimId = i; if (userClaimVoteCA[_add][i] > 0) voteCA = allvotes[userClaimVoteCA[_add][i]].verdict; else voteCA = 0; if (userClaimVoteMember[_add][i] > 0) voteMV = allvotes[userClaimVoteMember[_add][i]].verdict; else voteMV = 0; statusnumber = claimsStatus[i]; } /** * @dev Gets details of a claim of a user at a given index. */ function getUserClaimByIndex( uint _index, address _add ) external view returns ( uint status, uint coverid, uint claimId ) { claimId = allClaimsByAddress[_add][_index]; status = claimsStatus[claimId]; coverid = allClaims[claimId].coverId; } /** * @dev Gets Id of all the votes given to a claim. * @param _claimId Claim Id. * @return ca id of all the votes given by Claim assessors to a claim. * @return mv id of all the votes given by members to a claim. */ function getAllVotesForClaim( uint _claimId ) external view returns ( uint claimId, uint[] memory ca, uint[] memory mv ) { return (_claimId, claimVoteCA[_claimId], claimVoteMember[_claimId]); } /** * @dev Gets Number of tokens deposit in a vote using * Claim assessor's address and claim id. * @return tokens Number of deposited tokens. */ function getTokensClaim( address _of, uint _claimId ) external view returns ( uint claimId, uint tokens ) { return (_claimId, allvotes[userClaimVoteCA[_of][_claimId]].tokens); } /** * @param _voter address of the voter. * @return lastCAvoteIndex last index till which reward was distributed for CA * @return lastMVvoteIndex last index till which reward was distributed for member */ function getRewardDistributedIndex( address _voter ) external view returns ( uint lastCAvoteIndex, uint lastMVvoteIndex ) { return ( voterVoteRewardReceived[_voter].lastCAvoteIndex, voterVoteRewardReceived[_voter].lastMVvoteIndex ); } /** * @param claimid claim id. * @return perc_CA reward Percentage for claim assessor * @return perc_MV reward Percentage for members * @return tokens total tokens to be rewarded */ function getClaimRewardDetail( uint claimid ) external view returns ( uint percCA, uint percMV, uint tokens ) { return ( claimRewardDetail[claimid].percCA, claimRewardDetail[claimid].percMV, claimRewardDetail[claimid].tokenToBeDist ); } /** * @dev Gets cover id of a claim. */ function getClaimCoverId(uint _claimId) external view returns (uint claimId, uint coverid) { return (_claimId, allClaims[_claimId].coverId); } /** * @dev Gets total number of tokens staked during voting by Claim Assessors. * @param _claimId Claim Id. * @param _verdict 1 to get total number of accept tokens, -1 to get total number of deny tokens. * @return token token Number of tokens(either accept or deny on the basis of verdict given as parameter). */ function getClaimVote(uint _claimId, int8 _verdict) external view returns (uint claimId, uint token) { claimId = _claimId; token = 0; for (uint i = 0; i < claimVoteCA[_claimId].length; i++) { if (allvotes[claimVoteCA[_claimId][i]].verdict == _verdict) token = token.add(allvotes[claimVoteCA[_claimId][i]].tokens); } } /** * @dev Gets total number of tokens staked during voting by Members. * @param _claimId Claim Id. * @param _verdict 1 to get total number of accept tokens, * -1 to get total number of deny tokens. * @return token token Number of tokens(either accept or * deny on the basis of verdict given as parameter). */ function getClaimMVote(uint _claimId, int8 _verdict) external view returns (uint claimId, uint token) { claimId = _claimId; token = 0; for (uint i = 0; i < claimVoteMember[_claimId].length; i++) { if (allvotes[claimVoteMember[_claimId][i]].verdict == _verdict) token = token.add(allvotes[claimVoteMember[_claimId][i]].tokens); } } /** * @param _voter address of voteid * @param index index to get voteid in CA */ function getVoteAddressCA(address _voter, uint index) external view returns (uint) { return voteAddressCA[_voter][index]; } /** * @param _voter address of voter * @param index index to get voteid in member vote */ function getVoteAddressMember(address _voter, uint index) external view returns (uint) { return voteAddressMember[_voter][index]; } /** * @param _voter address of voter */ function getVoteAddressCALength(address _voter) external view returns (uint) { return voteAddressCA[_voter].length; } /** * @param _voter address of voter */ function getVoteAddressMemberLength(address _voter) external view returns (uint) { return voteAddressMember[_voter].length; } /** * @dev Gets the Final result of voting of a claim. * @param _claimId Claim id. * @return verdict 1 if claim is accepted, -1 if declined. */ function getFinalVerdict(uint _claimId) external view returns (int8 verdict) { return claimVote[_claimId]; } /** * @dev Get number of Claims queued for submission during emergency pause. */ function getLengthOfClaimSubmittedAtEP() external view returns (uint len) { len = claimPause.length; } /** * @dev Gets the index from which claim needs to be * submitted when emergency pause is swithched off. */ function getFirstClaimIndexToSubmitAfterEP() external view returns (uint indexToSubmit) { indexToSubmit = claimPauseLastsubmit; } /** * @dev Gets number of Claims to be reopened for voting post emergency pause period. */ function getLengthOfClaimVotingPause() external view returns (uint len) { len = claimPauseVotingEP.length; } /** * @dev Gets claim details to be reopened for voting after emergency pause. */ function getPendingClaimDetailsByIndex( uint _index ) external view returns ( uint claimId, uint pendingTime, bool voting ) { claimId = claimPauseVotingEP[_index].claimid; pendingTime = claimPauseVotingEP[_index].pendingTime; voting = claimPauseVotingEP[_index].voting; } /** * @dev Gets the index from which claim needs to be reopened when emergency pause is swithched off. */ function getFirstClaimIndexToStartVotingAfterEP() external view returns (uint firstindex) { firstindex = claimStartVotingFirstIndex; } /** * @dev Updates Uint Parameters of a code * @param code whose details we want to update * @param val value to set */ function updateUintParameters(bytes8 code, uint val) public { require(ms.checkIsAuthToGoverned(msg.sender)); if (code == "CAMAXVT") { _setMaxVotingTime(val * 1 hours); } else if (code == "CAMINVT") { _setMinVotingTime(val * 1 hours); } else if (code == "CAPRETRY") { _setPayoutRetryTime(val * 1 hours); } else if (code == "CADEPT") { _setClaimDepositTime(val * 1 days); } else if (code == "CAREWPER") { _setClaimRewardPerc(val); } else if (code == "CAMINTH") { _setMinVoteThreshold(val); } else if (code == "CAMAXTH") { _setMaxVoteThreshold(val); } else if (code == "CACONPER") { _setMajorityConsensus(val); } else if (code == "CAPAUSET") { _setPauseDaysCA(val * 1 days); } else { revert("Invalid param code"); } } /** * @dev Iupgradable Interface to update dependent contract address */ function changeDependentContractAddress() public onlyInternal {} /** * @dev Adds status under which a claim can lie. * @param percCA reward percentage for claim assessor * @param percMV reward percentage for members */ function _pushStatus(uint percCA, uint percMV) internal { rewardStatus.push(ClaimRewardStatus(percCA, percMV)); } /** * @dev adds reward incentive for all possible claim status for Claim assessors and members */ function _addRewardIncentive() internal { _pushStatus(0, 0); // 0 Pending-Claim Assessor Vote _pushStatus(0, 0); // 1 Pending-Claim Assessor Vote Denied, Pending Member Vote _pushStatus(0, 0); // 2 Pending-CA Vote Threshold not Reached Accept, Pending Member Vote _pushStatus(0, 0); // 3 Pending-CA Vote Threshold not Reached Deny, Pending Member Vote _pushStatus(0, 0); // 4 Pending-CA Consensus not reached Accept, Pending Member Vote _pushStatus(0, 0); // 5 Pending-CA Consensus not reached Deny, Pending Member Vote _pushStatus(100, 0); // 6 Final-Claim Assessor Vote Denied _pushStatus(100, 0); // 7 Final-Claim Assessor Vote Accepted _pushStatus(0, 100); // 8 Final-Claim Assessor Vote Denied, MV Accepted _pushStatus(0, 100); // 9 Final-Claim Assessor Vote Denied, MV Denied _pushStatus(0, 0); // 10 Final-Claim Assessor Vote Accept, MV Nodecision _pushStatus(0, 0); // 11 Final-Claim Assessor Vote Denied, MV Nodecision _pushStatus(0, 0); // 12 Claim Accepted Payout Pending _pushStatus(0, 0); // 13 Claim Accepted No Payout _pushStatus(0, 0); // 14 Claim Accepted Payout Done } /** * @dev Sets Maximum time(in seconds) for which claim assessment voting is open */ function _setMaxVotingTime(uint _time) internal { maxVotingTime = _time; } /** * @dev Sets Minimum time(in seconds) for which claim assessment voting is open */ function _setMinVotingTime(uint _time) internal { minVotingTime = _time; } /** * @dev Sets Minimum vote threshold required */ function _setMinVoteThreshold(uint val) internal { minVoteThreshold = val; } /** * @dev Sets Maximum vote threshold required */ function _setMaxVoteThreshold(uint val) internal { maxVoteThreshold = val; } /** * @dev Sets the value considered as Majority Consenus in voting */ function _setMajorityConsensus(uint val) internal { majorityConsensus = val; } /** * @dev Sets the payout retry time */ function _setPayoutRetryTime(uint _time) internal { payoutRetryTime = _time; } /** * @dev Sets percentage of reward given for claim assessment */ function _setClaimRewardPerc(uint _val) internal { claimRewardPerc = _val; } /** * @dev Sets the time for which claim is deposited. */ function _setClaimDepositTime(uint _time) internal { claimDepositTime = _time; } /** * @dev Sets number of days claim assessment will be paused */ function _setPauseDaysCA(uint val) internal { pauseDaysCA = val; } } // File: contracts/modules/claims/Claims.sol /* Copyright (C) 2020 NexusMutual.io This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/ */ pragma solidity ^0.5.0; contract Claims is Iupgradable { using SafeMath for uint; TokenFunctions internal tf; NXMToken internal tk; TokenController internal tc; ClaimsReward internal cr; Pool internal p1; ClaimsData internal cd; TokenData internal td; QuotationData internal qd; uint private constant DECIMAL1E18 = uint(10) ** 18; /** * @dev Sets the status of claim using claim id. * @param claimId claim id. * @param stat status to be set. */ function setClaimStatus(uint claimId, uint stat) external onlyInternal { _setClaimStatus(claimId, stat); } /** * @dev Gets claim details of claim id = pending claim start + given index */ function getClaimFromNewStart( uint index ) external view returns ( uint coverId, uint claimId, int8 voteCA, int8 voteMV, uint statusnumber ) { (coverId, claimId, voteCA, voteMV, statusnumber) = cd.getClaimFromNewStart(index, msg.sender); } /** * @dev Gets details of a claim submitted by the calling user, at a given index */ function getUserClaimByIndex( uint index ) external view returns ( uint status, uint coverId, uint claimId ) { uint statusno; (statusno, coverId, claimId) = cd.getUserClaimByIndex(index, msg.sender); status = statusno; } /** * @dev Gets details of a given claim id. * @param _claimId Claim Id. * @return status Current status of claim id * @return finalVerdict Decision made on the claim, 1 -> acceptance, -1 -> denial * @return claimOwner Address through which claim is submitted * @return coverId Coverid associated with the claim id */ function getClaimbyIndex(uint _claimId) external view returns ( uint claimId, uint status, int8 finalVerdict, address claimOwner, uint coverId ) { uint stat; claimId = _claimId; (, coverId, finalVerdict, stat,,) = cd.getClaim(_claimId); claimOwner = qd.getCoverMemberAddress(coverId); status = stat; } /** * @dev Calculates total amount that has been used to assess a claim. * Computaion:Adds acceptCA(tokens used for voting in favor of a claim) * denyCA(tokens used for voting against a claim) * current token price. * @param claimId Claim Id. * @param member Member type 0 -> Claim Assessors, else members. * @return tokens Total Amount used in Claims assessment. */ function getCATokens(uint claimId, uint member) external view returns (uint tokens) { uint coverId; (, coverId) = cd.getClaimCoverId(claimId); bytes4 currency = qd.getCurrencyOfCover(coverId); address asset = cr.getCurrencyAssetAddress(currency); uint tokenx1e18 = p1.getTokenPrice(asset); uint accept; uint deny; if (member == 0) { (, accept, deny) = cd.getClaimsTokenCA(claimId); } else { (, accept, deny) = cd.getClaimsTokenMV(claimId); } tokens = ((accept.add(deny)).mul(tokenx1e18)).div(DECIMAL1E18); // amount (not in tokens) } /** * Iupgradable Interface to update dependent contract address */ function changeDependentContractAddress() public onlyInternal { tk = NXMToken(ms.tokenAddress()); td = TokenData(ms.getLatestAddress("TD")); tf = TokenFunctions(ms.getLatestAddress("TF")); tc = TokenController(ms.getLatestAddress("TC")); p1 = Pool(ms.getLatestAddress("P1")); cr = ClaimsReward(ms.getLatestAddress("CR")); cd = ClaimsData(ms.getLatestAddress("CD")); qd = QuotationData(ms.getLatestAddress("QD")); } /** * @dev Updates the pending claim start variable, * the lowest claim id with a pending decision/payout. */ function changePendingClaimStart() public onlyInternal { uint origstat; uint state12Count; uint pendingClaimStart = cd.pendingClaimStart(); uint actualClaimLength = cd.actualClaimLength(); for (uint i = pendingClaimStart; i < actualClaimLength; i++) { (, , , origstat, , state12Count) = cd.getClaim(i); if (origstat > 5 && ((origstat != 12) || (origstat == 12 && state12Count >= 60))) { cd.setpendingClaimStart(i); } else { break; } } } /** * @dev Submits a claim for a given cover note. * Adds claim to queue incase of emergency pause else directly submits the claim. * @param coverId Cover Id. */ function submitClaim(uint coverId) public { address qadd = qd.getCoverMemberAddress(coverId); require(qadd == msg.sender); uint8 cStatus; (, cStatus,,,) = qd.getCoverDetailsByCoverID2(coverId); require(cStatus != uint8(QuotationData.CoverStatus.ClaimSubmitted), "Claim already submitted"); require(cStatus != uint8(QuotationData.CoverStatus.CoverExpired), "Cover already expired"); if (ms.isPause() == false) { _addClaim(coverId, now, qadd); } else { cd.setClaimAtEmergencyPause(coverId, now, false); qd.changeCoverStatusNo(coverId, uint8(QuotationData.CoverStatus.Requested)); } } /** * @dev Submits the Claims queued once the emergency pause is switched off. */ function submitClaimAfterEPOff() public onlyInternal { uint lengthOfClaimSubmittedAtEP = cd.getLengthOfClaimSubmittedAtEP(); uint firstClaimIndexToSubmitAfterEP = cd.getFirstClaimIndexToSubmitAfterEP(); uint coverId; uint dateUpd; bool submit; address qadd; for (uint i = firstClaimIndexToSubmitAfterEP; i < lengthOfClaimSubmittedAtEP; i++) { (coverId, dateUpd, submit) = cd.getClaimOfEmergencyPauseByIndex(i); require(submit == false); qadd = qd.getCoverMemberAddress(coverId); _addClaim(coverId, dateUpd, qadd); cd.setClaimSubmittedAtEPTrue(i, true); } cd.setFirstClaimIndexToSubmitAfterEP(lengthOfClaimSubmittedAtEP); } /** * @dev Castes vote for members who have tokens locked under Claims Assessment * @param claimId claim id. * @param verdict 1 for Accept,-1 for Deny. */ function submitCAVote(uint claimId, int8 verdict) public isMemberAndcheckPause { require(checkVoteClosing(claimId) != 1); require(cd.userClaimVotePausedOn(msg.sender).add(cd.pauseDaysCA()) < now); uint tokens = tc.tokensLockedAtTime(msg.sender, "CLA", now.add(cd.claimDepositTime())); require(tokens > 0); uint stat; (, stat) = cd.getClaimStatusNumber(claimId); require(stat == 0); require(cd.getUserClaimVoteCA(msg.sender, claimId) == 0); td.bookCATokens(msg.sender); cd.addVote(msg.sender, tokens, claimId, verdict); cd.callVoteEvent(msg.sender, claimId, "CAV", tokens, now, verdict); uint voteLength = cd.getAllVoteLength(); cd.addClaimVoteCA(claimId, voteLength); cd.setUserClaimVoteCA(msg.sender, claimId, voteLength); cd.setClaimTokensCA(claimId, verdict, tokens); tc.extendLockOf(msg.sender, "CLA", td.lockCADays()); int close = checkVoteClosing(claimId); if (close == 1) { cr.changeClaimStatus(claimId); } } /** * @dev Submits a member vote for assessing a claim. * Tokens other than those locked under Claims * Assessment can be used to cast a vote for a given claim id. * @param claimId Selected claim id. * @param verdict 1 for Accept,-1 for Deny. */ function submitMemberVote(uint claimId, int8 verdict) public isMemberAndcheckPause { require(checkVoteClosing(claimId) != 1); uint stat; uint tokens = tc.totalBalanceOf(msg.sender); (, stat) = cd.getClaimStatusNumber(claimId); require(stat >= 1 && stat <= 5); require(cd.getUserClaimVoteMember(msg.sender, claimId) == 0); cd.addVote(msg.sender, tokens, claimId, verdict); cd.callVoteEvent(msg.sender, claimId, "MV", tokens, now, verdict); tc.lockForMemberVote(msg.sender, td.lockMVDays()); uint voteLength = cd.getAllVoteLength(); cd.addClaimVotemember(claimId, voteLength); cd.setUserClaimVoteMember(msg.sender, claimId, voteLength); cd.setClaimTokensMV(claimId, verdict, tokens); int close = checkVoteClosing(claimId); if (close == 1) { cr.changeClaimStatus(claimId); } } /** * @dev Pause Voting of All Pending Claims when Emergency Pause Start. */ function pauseAllPendingClaimsVoting() public onlyInternal { uint firstIndex = cd.pendingClaimStart(); uint actualClaimLength = cd.actualClaimLength(); for (uint i = firstIndex; i < actualClaimLength; i++) { if (checkVoteClosing(i) == 0) { uint dateUpd = cd.getClaimDateUpd(i); cd.setPendingClaimDetails(i, (dateUpd.add(cd.maxVotingTime())).sub(now), false); } } } /** * @dev Resume the voting phase of all Claims paused due to an emergency pause. */ function startAllPendingClaimsVoting() public onlyInternal { uint firstIndx = cd.getFirstClaimIndexToStartVotingAfterEP(); uint i; uint lengthOfClaimVotingPause = cd.getLengthOfClaimVotingPause(); for (i = firstIndx; i < lengthOfClaimVotingPause; i++) { uint pendingTime; uint claimID; (claimID, pendingTime,) = cd.getPendingClaimDetailsByIndex(i); uint pTime = (now.sub(cd.maxVotingTime())).add(pendingTime); cd.setClaimdateUpd(claimID, pTime); cd.setPendingClaimVoteStatus(i, true); uint coverid; (, coverid) = cd.getClaimCoverId(claimID); address qadd = qd.getCoverMemberAddress(coverid); tf.extendCNEPOff(qadd, coverid, pendingTime.add(cd.claimDepositTime())); } cd.setFirstClaimIndexToStartVotingAfterEP(i); } /** * @dev Checks if voting of a claim should be closed or not. * @param claimId Claim Id. * @return close 1 -> voting should be closed, 0 -> if voting should not be closed, * -1 -> voting has already been closed. */ function checkVoteClosing(uint claimId) public view returns (int8 close) { close = 0; uint status; (, status) = cd.getClaimStatusNumber(claimId); uint dateUpd = cd.getClaimDateUpd(claimId); if (status == 12 && dateUpd.add(cd.payoutRetryTime()) < now) { if (cd.getClaimState12Count(claimId) < 60) close = 1; } if (status > 5 && status != 12) { close = - 1; } else if (status != 12 && dateUpd.add(cd.maxVotingTime()) <= now) { close = 1; } else if (status != 12 && dateUpd.add(cd.minVotingTime()) >= now) { close = 0; } else if (status == 0 || (status >= 1 && status <= 5)) { close = _checkVoteClosingFinal(claimId, status); } } /** * @dev Checks if voting of a claim should be closed or not. * Internally called by checkVoteClosing method * for Claims whose status number is 0 or status number lie between 2 and 6. * @param claimId Claim Id. * @param status Current status of claim. * @return close 1 if voting should be closed,0 in case voting should not be closed, * -1 if voting has already been closed. */ function _checkVoteClosingFinal(uint claimId, uint status) internal view returns (int8 close) { close = 0; uint coverId; (, coverId) = cd.getClaimCoverId(claimId); bytes4 currency = qd.getCurrencyOfCover(coverId); address asset = cr.getCurrencyAssetAddress(currency); uint tokenx1e18 = p1.getTokenPrice(asset); uint accept; uint deny; (, accept, deny) = cd.getClaimsTokenCA(claimId); uint caTokens = ((accept.add(deny)).mul(tokenx1e18)).div(DECIMAL1E18); (, accept, deny) = cd.getClaimsTokenMV(claimId); uint mvTokens = ((accept.add(deny)).mul(tokenx1e18)).div(DECIMAL1E18); uint sumassured = qd.getCoverSumAssured(coverId).mul(DECIMAL1E18); if (status == 0 && caTokens >= sumassured.mul(10)) { close = 1; } else if (status >= 1 && status <= 5 && mvTokens >= sumassured.mul(10)) { close = 1; } } /** * @dev Changes the status of an existing claim id, based on current * status and current conditions of the system * @param claimId Claim Id. * @param stat status number. */ function _setClaimStatus(uint claimId, uint stat) internal { uint origstat; uint state12Count; uint dateUpd; uint coverId; (, coverId, , origstat, dateUpd, state12Count) = cd.getClaim(claimId); (, origstat) = cd.getClaimStatusNumber(claimId); if (stat == 12 && origstat == 12) { cd.updateState12Count(claimId, 1); } cd.setClaimStatus(claimId, stat); if (state12Count >= 60 && stat == 12) { cd.setClaimStatus(claimId, 13); qd.changeCoverStatusNo(coverId, uint8(QuotationData.CoverStatus.ClaimDenied)); } cd.setClaimdateUpd(claimId, now); } /** * @dev Submits a claim for a given cover note. * Set deposits flag against cover. */ function _addClaim(uint coverId, uint time, address add) internal { tf.depositCN(coverId); uint len = cd.actualClaimLength(); cd.addClaim(len, coverId, add, now); cd.callClaimEvent(coverId, add, len, time); qd.changeCoverStatusNo(coverId, uint8(QuotationData.CoverStatus.ClaimSubmitted)); } } // File: contracts/modules/claims/ClaimsReward.sol /* Copyright (C) 2020 NexusMutual.io This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/ */ //Claims Reward Contract contains the functions for calculating number of tokens // that will get rewarded, unlocked or burned depending upon the status of claim. pragma solidity ^0.5.0; contract ClaimsReward is Iupgradable { using SafeMath for uint; NXMToken internal tk; TokenController internal tc; TokenFunctions internal tf; TokenData internal td; QuotationData internal qd; Claims internal c1; ClaimsData internal cd; Pool internal pool; Governance internal gv; IPooledStaking internal pooledStaking; MemberRoles internal memberRoles; // assigned in constructor address public DAI; // constants address public constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; uint private constant DECIMAL1E18 = uint(10) ** 18; constructor (address masterAddress, address _daiAddress) public { changeMasterAddress(masterAddress); DAI = _daiAddress; } function changeDependentContractAddress() public onlyInternal { c1 = Claims(ms.getLatestAddress("CL")); cd = ClaimsData(ms.getLatestAddress("CD")); tk = NXMToken(ms.tokenAddress()); tc = TokenController(ms.getLatestAddress("TC")); td = TokenData(ms.getLatestAddress("TD")); tf = TokenFunctions(ms.getLatestAddress("TF")); qd = QuotationData(ms.getLatestAddress("QD")); gv = Governance(ms.getLatestAddress("GV")); pooledStaking = IPooledStaking(ms.getLatestAddress("PS")); memberRoles = MemberRoles(ms.getLatestAddress("MR")); pool = Pool(ms.getLatestAddress("P1")); } /// @dev Decides the next course of action for a given claim. function changeClaimStatus(uint claimid) public checkPause onlyInternal { (, uint coverid) = cd.getClaimCoverId(claimid); (, uint status) = cd.getClaimStatusNumber(claimid); // when current status is "Pending-Claim Assessor Vote" if (status == 0) { _changeClaimStatusCA(claimid, coverid, status); } else if (status >= 1 && status <= 5) { _changeClaimStatusMV(claimid, coverid, status); } else if (status == 12) {// when current status is "Claim Accepted Payout Pending" bool payoutSucceeded = attemptClaimPayout(coverid); if (payoutSucceeded) { c1.setClaimStatus(claimid, 14); } else { c1.setClaimStatus(claimid, 12); } } c1.changePendingClaimStart(); } function getCurrencyAssetAddress(bytes4 currency) public view returns (address) { if (currency == "ETH") { return ETH; } if (currency == "DAI") { return DAI; } revert("ClaimsReward: unknown asset"); } function attemptClaimPayout(uint coverId) internal returns (bool success) { uint sumAssured = qd.getCoverSumAssured(coverId); // TODO: when adding new cover currencies, fetch the correct decimals for this multiplication uint sumAssuredWei = sumAssured.mul(1e18); // get asset address bytes4 coverCurrency = qd.getCurrencyOfCover(coverId); address asset = getCurrencyAssetAddress(coverCurrency); // get payout address address payable coverHolder = qd.getCoverMemberAddress(coverId); address payable payoutAddress = memberRoles.getClaimPayoutAddress(coverHolder); // execute the payout bool payoutSucceeded = pool.sendClaimPayout(asset, payoutAddress, sumAssuredWei); if (payoutSucceeded) { // burn staked tokens (, address scAddress) = qd.getscAddressOfCover(coverId); uint tokenPrice = pool.getTokenPrice(asset); // note: for new assets "18" needs to be replaced with target asset decimals uint burnNXMAmount = sumAssuredWei.mul(1e18).div(tokenPrice); pooledStaking.pushBurn(scAddress, burnNXMAmount); // adjust total sum assured (, address coverContract) = qd.getscAddressOfCover(coverId); qd.subFromTotalSumAssured(coverCurrency, sumAssured); qd.subFromTotalSumAssuredSC(coverContract, coverCurrency, sumAssured); return true; } return false; } /// @dev Amount of tokens to be rewarded to a user for a particular vote id. /// @param check 1 -> CA vote, else member vote /// @param voteid vote id for which reward has to be Calculated /// @param flag if 1 calculate even if claimed,else don't calculate if already claimed /// @return tokenCalculated reward to be given for vote id /// @return lastClaimedCheck true if final verdict is still pending for that voteid /// @return tokens number of tokens locked under that voteid /// @return perc percentage of reward to be given. function getRewardToBeGiven( uint check, uint voteid, uint flag ) public view returns ( uint tokenCalculated, bool lastClaimedCheck, uint tokens, uint perc ) { uint claimId; int8 verdict; bool claimed; uint tokensToBeDist; uint totalTokens; (tokens, claimId, verdict, claimed) = cd.getVoteDetails(voteid); lastClaimedCheck = false; int8 claimVerdict = cd.getFinalVerdict(claimId); if (claimVerdict == 0) { lastClaimedCheck = true; } if (claimVerdict == verdict && (claimed == false || flag == 1)) { if (check == 1) { (perc, , tokensToBeDist) = cd.getClaimRewardDetail(claimId); } else { (, perc, tokensToBeDist) = cd.getClaimRewardDetail(claimId); } if (perc > 0) { if (check == 1) { if (verdict == 1) { (, totalTokens,) = cd.getClaimsTokenCA(claimId); } else { (,, totalTokens) = cd.getClaimsTokenCA(claimId); } } else { if (verdict == 1) { (, totalTokens,) = cd.getClaimsTokenMV(claimId); } else { (,, totalTokens) = cd.getClaimsTokenMV(claimId); } } tokenCalculated = (perc.mul(tokens).mul(tokensToBeDist)).div(totalTokens.mul(100)); } } } /// @dev Transfers all tokens held by contract to a new contract in case of upgrade. function upgrade(address _newAdd) public onlyInternal { uint amount = tk.balanceOf(address(this)); if (amount > 0) { require(tk.transfer(_newAdd, amount)); } } /// @dev Total reward in token due for claim by a user. /// @return total total number of tokens function getRewardToBeDistributedByUser(address _add) public view returns (uint total) { uint lengthVote = cd.getVoteAddressCALength(_add); uint lastIndexCA; uint lastIndexMV; uint tokenForVoteId; uint voteId; (lastIndexCA, lastIndexMV) = cd.getRewardDistributedIndex(_add); for (uint i = lastIndexCA; i < lengthVote; i++) { voteId = cd.getVoteAddressCA(_add, i); (tokenForVoteId,,,) = getRewardToBeGiven(1, voteId, 0); total = total.add(tokenForVoteId); } lengthVote = cd.getVoteAddressMemberLength(_add); for (uint j = lastIndexMV; j < lengthVote; j++) { voteId = cd.getVoteAddressMember(_add, j); (tokenForVoteId,,,) = getRewardToBeGiven(0, voteId, 0); total = total.add(tokenForVoteId); } return (total); } /// @dev Gets reward amount and claiming status for a given claim id. /// @return reward amount of tokens to user. /// @return claimed true if already claimed false if yet to be claimed. function getRewardAndClaimedStatus(uint check, uint claimId) public view returns (uint reward, bool claimed) { uint voteId; uint claimid; uint lengthVote; if (check == 1) { lengthVote = cd.getVoteAddressCALength(msg.sender); for (uint i = 0; i < lengthVote; i++) { voteId = cd.getVoteAddressCA(msg.sender, i); (, claimid, , claimed) = cd.getVoteDetails(voteId); if (claimid == claimId) {break;} } } else { lengthVote = cd.getVoteAddressMemberLength(msg.sender); for (uint j = 0; j < lengthVote; j++) { voteId = cd.getVoteAddressMember(msg.sender, j); (, claimid, , claimed) = cd.getVoteDetails(voteId); if (claimid == claimId) {break;} } } (reward,,,) = getRewardToBeGiven(check, voteId, 1); } /** * @dev Function used to claim all pending rewards : Claims Assessment + Risk Assessment + Governance * Claim assesment, Risk assesment, Governance rewards */ function claimAllPendingReward(uint records) public isMemberAndcheckPause { _claimRewardToBeDistributed(records); pooledStaking.withdrawReward(msg.sender); uint governanceRewards = gv.claimReward(msg.sender, records); if (governanceRewards > 0) { require(tk.transfer(msg.sender, governanceRewards)); } } /** * @dev Function used to get pending rewards of a particular user address. * @param _add user address. * @return total reward amount of the user */ function getAllPendingRewardOfUser(address _add) public view returns (uint) { uint caReward = getRewardToBeDistributedByUser(_add); uint pooledStakingReward = pooledStaking.stakerReward(_add); uint governanceReward = gv.getPendingReward(_add); return caReward.add(pooledStakingReward).add(governanceReward); } /// @dev Rewards/Punishes users who participated in Claims assessment. // Unlocking and burning of the tokens will also depend upon the status of claim. /// @param claimid Claim Id. function _rewardAgainstClaim(uint claimid, uint coverid, uint status) internal { uint premiumNXM = qd.getCoverPremiumNXM(coverid); uint distributableTokens = premiumNXM.mul(cd.claimRewardPerc()).div(100); // 20% of premium uint percCA; uint percMV; (percCA, percMV) = cd.getRewardStatus(status); cd.setClaimRewardDetail(claimid, percCA, percMV, distributableTokens); if (percCA > 0 || percMV > 0) { tc.mint(address(this), distributableTokens); } // denied if (status == 6 || status == 9 || status == 11) { cd.changeFinalVerdict(claimid, - 1); td.setDepositCN(coverid, false); // Unset flag tf.burnDepositCN(coverid); // burn Deposited CN // accepted } else if (status == 7 || status == 8 || status == 10) { cd.changeFinalVerdict(claimid, 1); td.setDepositCN(coverid, false); // Unset flag tf.unlockCN(coverid); bool payoutSucceeded = attemptClaimPayout(coverid); // 12 = payout pending, 14 = payout succeeded uint nextStatus = payoutSucceeded ? 14 : 12; c1.setClaimStatus(claimid, nextStatus); } } /// @dev Computes the result of Claim Assessors Voting for a given claim id. function _changeClaimStatusCA(uint claimid, uint coverid, uint status) internal { // Check if voting should be closed or not if (c1.checkVoteClosing(claimid) == 1) { uint caTokens = c1.getCATokens(claimid, 0); // converted in cover currency. uint accept; uint deny; uint acceptAndDeny; bool rewardOrPunish; uint sumAssured; (, accept) = cd.getClaimVote(claimid, 1); (, deny) = cd.getClaimVote(claimid, - 1); acceptAndDeny = accept.add(deny); accept = accept.mul(100); deny = deny.mul(100); if (caTokens == 0) { status = 3; } else { sumAssured = qd.getCoverSumAssured(coverid).mul(DECIMAL1E18); // Min threshold reached tokens used for voting > 5* sum assured if (caTokens > sumAssured.mul(5)) { if (accept.div(acceptAndDeny) > 70) { status = 7; qd.changeCoverStatusNo(coverid, uint8(QuotationData.CoverStatus.ClaimAccepted)); rewardOrPunish = true; } else if (deny.div(acceptAndDeny) > 70) { status = 6; qd.changeCoverStatusNo(coverid, uint8(QuotationData.CoverStatus.ClaimDenied)); rewardOrPunish = true; } else if (accept.div(acceptAndDeny) > deny.div(acceptAndDeny)) { status = 4; } else { status = 5; } } else { if (accept.div(acceptAndDeny) > deny.div(acceptAndDeny)) { status = 2; } else { status = 3; } } } c1.setClaimStatus(claimid, status); if (rewardOrPunish) { _rewardAgainstClaim(claimid, coverid, status); } } } /// @dev Computes the result of Member Voting for a given claim id. function _changeClaimStatusMV(uint claimid, uint coverid, uint status) internal { // Check if voting should be closed or not if (c1.checkVoteClosing(claimid) == 1) { uint8 coverStatus; uint statusOrig = status; uint mvTokens = c1.getCATokens(claimid, 1); // converted in cover currency. // If tokens used for acceptance >50%, claim is accepted uint sumAssured = qd.getCoverSumAssured(coverid).mul(DECIMAL1E18); uint thresholdUnreached = 0; // Minimum threshold for member voting is reached only when // value of tokens used for voting > 5* sum assured of claim id if (mvTokens < sumAssured.mul(5)) { thresholdUnreached = 1; } uint accept; (, accept) = cd.getClaimMVote(claimid, 1); uint deny; (, deny) = cd.getClaimMVote(claimid, - 1); if (accept.add(deny) > 0) { if (accept.mul(100).div(accept.add(deny)) >= 50 && statusOrig > 1 && statusOrig <= 5 && thresholdUnreached == 0) { status = 8; coverStatus = uint8(QuotationData.CoverStatus.ClaimAccepted); } else if (deny.mul(100).div(accept.add(deny)) >= 50 && statusOrig > 1 && statusOrig <= 5 && thresholdUnreached == 0) { status = 9; coverStatus = uint8(QuotationData.CoverStatus.ClaimDenied); } } if (thresholdUnreached == 1 && (statusOrig == 2 || statusOrig == 4)) { status = 10; coverStatus = uint8(QuotationData.CoverStatus.ClaimAccepted); } else if (thresholdUnreached == 1 && (statusOrig == 5 || statusOrig == 3 || statusOrig == 1)) { status = 11; coverStatus = uint8(QuotationData.CoverStatus.ClaimDenied); } c1.setClaimStatus(claimid, status); qd.changeCoverStatusNo(coverid, uint8(coverStatus)); // Reward/Punish Claim Assessors and Members who participated in Claims assessment _rewardAgainstClaim(claimid, coverid, status); } } /// @dev Allows a user to claim all pending Claims assessment rewards. function _claimRewardToBeDistributed(uint _records) internal { uint lengthVote = cd.getVoteAddressCALength(msg.sender); uint voteid; uint lastIndex; (lastIndex,) = cd.getRewardDistributedIndex(msg.sender); uint total = 0; uint tokenForVoteId = 0; bool lastClaimedCheck; uint _days = td.lockCADays(); bool claimed; uint counter = 0; uint claimId; uint perc; uint i; uint lastClaimed = lengthVote; for (i = lastIndex; i < lengthVote && counter < _records; i++) { voteid = cd.getVoteAddressCA(msg.sender, i); (tokenForVoteId, lastClaimedCheck, , perc) = getRewardToBeGiven(1, voteid, 0); if (lastClaimed == lengthVote && lastClaimedCheck == true) { lastClaimed = i; } (, claimId, , claimed) = cd.getVoteDetails(voteid); if (perc > 0 && !claimed) { counter++; cd.setRewardClaimed(voteid, true); } else if (perc == 0 && cd.getFinalVerdict(claimId) != 0 && !claimed) { (perc,,) = cd.getClaimRewardDetail(claimId); if (perc == 0) { counter++; } cd.setRewardClaimed(voteid, true); } if (tokenForVoteId > 0) { total = tokenForVoteId.add(total); } } if (lastClaimed == lengthVote) { cd.setRewardDistributedIndexCA(msg.sender, i); } else { cd.setRewardDistributedIndexCA(msg.sender, lastClaimed); } lengthVote = cd.getVoteAddressMemberLength(msg.sender); lastClaimed = lengthVote; _days = _days.mul(counter); if (tc.tokensLockedAtTime(msg.sender, "CLA", now) > 0) { tc.reduceLock(msg.sender, "CLA", _days); } (, lastIndex) = cd.getRewardDistributedIndex(msg.sender); lastClaimed = lengthVote; counter = 0; for (i = lastIndex; i < lengthVote && counter < _records; i++) { voteid = cd.getVoteAddressMember(msg.sender, i); (tokenForVoteId, lastClaimedCheck,,) = getRewardToBeGiven(0, voteid, 0); if (lastClaimed == lengthVote && lastClaimedCheck == true) { lastClaimed = i; } (, claimId, , claimed) = cd.getVoteDetails(voteid); if (claimed == false && cd.getFinalVerdict(claimId) != 0) { cd.setRewardClaimed(voteid, true); counter++; } if (tokenForVoteId > 0) { total = tokenForVoteId.add(total); } } if (total > 0) { require(tk.transfer(msg.sender, total)); } if (lastClaimed == lengthVote) { cd.setRewardDistributedIndexMV(msg.sender, i); } else { cd.setRewardDistributedIndexMV(msg.sender, lastClaimed); } } /** * @dev Function used to claim the commission earned by the staker. */ function _claimStakeCommission(uint _records, address _user) external onlyInternal { uint total = 0; uint len = td.getStakerStakedContractLength(_user); uint lastCompletedStakeCommission = td.lastCompletedStakeCommission(_user); uint commissionEarned; uint commissionRedeemed; uint maxCommission; uint lastCommisionRedeemed = len; uint counter; uint i; for (i = lastCompletedStakeCommission; i < len && counter < _records; i++) { commissionRedeemed = td.getStakerRedeemedStakeCommission(_user, i); commissionEarned = td.getStakerEarnedStakeCommission(_user, i); maxCommission = td.getStakerInitialStakedAmountOnContract( _user, i).mul(td.stakerMaxCommissionPer()).div(100); if (lastCommisionRedeemed == len && maxCommission != commissionEarned) lastCommisionRedeemed = i; td.pushRedeemedStakeCommissions(_user, i, commissionEarned.sub(commissionRedeemed)); total = total.add(commissionEarned.sub(commissionRedeemed)); counter++; } if (lastCommisionRedeemed == len) { td.setLastCompletedStakeCommissionIndex(_user, i); } else { td.setLastCompletedStakeCommissionIndex(_user, lastCommisionRedeemed); } if (total > 0) require(tk.transfer(_user, total)); // solhint-disable-line } } // File: contracts/modules/governance/MemberRoles.sol /* Copyright (C) 2017 GovBlocks.io This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/ */ pragma solidity ^0.5.0; contract MemberRoles is Governed, Iupgradable { TokenController public dAppToken; TokenData internal td; QuotationData internal qd; ClaimsReward internal cr; Governance internal gv; TokenFunctions internal tf; NXMToken public tk; struct MemberRoleDetails { uint memberCounter; mapping(address => bool) memberActive; address[] memberAddress; address authorized; } enum Role {UnAssigned, AdvisoryBoard, Member, Owner} event MemberRole(uint256 indexed roleId, bytes32 roleName, string roleDescription); event switchedMembership(address indexed previousMember, address indexed newMember, uint timeStamp); event ClaimPayoutAddressSet(address indexed member, address indexed payoutAddress); MemberRoleDetails[] internal memberRoleData; bool internal constructorCheck; uint public maxABCount; bool public launched; uint public launchedOn; mapping (address => address payable) internal claimPayoutAddress; modifier checkRoleAuthority(uint _memberRoleId) { if (memberRoleData[_memberRoleId].authorized != address(0)) require(msg.sender == memberRoleData[_memberRoleId].authorized); else require(isAuthorizedToGovern(msg.sender), "Not Authorized"); _; } /** * @dev to swap advisory board member * @param _newABAddress is address of new AB member * @param _removeAB is advisory board member to be removed */ function swapABMember( address _newABAddress, address _removeAB ) external checkRoleAuthority(uint(Role.AdvisoryBoard)) { _updateRole(_newABAddress, uint(Role.AdvisoryBoard), true); _updateRole(_removeAB, uint(Role.AdvisoryBoard), false); } /** * @dev to swap the owner address * @param _newOwnerAddress is the new owner address */ function swapOwner( address _newOwnerAddress ) external { require(msg.sender == address(ms)); _updateRole(ms.owner(), uint(Role.Owner), false); _updateRole(_newOwnerAddress, uint(Role.Owner), true); } /** * @dev is used to add initital advisory board members * @param abArray is the list of initial advisory board members */ function addInitialABMembers(address[] calldata abArray) external onlyOwner { //Ensure that NXMaster has initialized. require(ms.masterInitialized()); require(maxABCount >= SafeMath.add(numberOfMembers(uint(Role.AdvisoryBoard)), abArray.length) ); //AB count can't exceed maxABCount for (uint i = 0; i < abArray.length; i++) { require(checkRole(abArray[i], uint(MemberRoles.Role.Member))); _updateRole(abArray[i], uint(Role.AdvisoryBoard), true); } } /** * @dev to change max number of AB members allowed * @param _val is the new value to be set */ function changeMaxABCount(uint _val) external onlyInternal { maxABCount = _val; } /** * @dev Iupgradable Interface to update dependent contract address */ function changeDependentContractAddress() public { td = TokenData(ms.getLatestAddress("TD")); cr = ClaimsReward(ms.getLatestAddress("CR")); qd = QuotationData(ms.getLatestAddress("QD")); gv = Governance(ms.getLatestAddress("GV")); tf = TokenFunctions(ms.getLatestAddress("TF")); tk = NXMToken(ms.tokenAddress()); dAppToken = TokenController(ms.getLatestAddress("TC")); } /** * @dev to change the master address * @param _masterAddress is the new master address */ function changeMasterAddress(address _masterAddress) public { if (masterAddress != address(0)) require(masterAddress == msg.sender); masterAddress = _masterAddress; ms = INXMMaster(_masterAddress); nxMasterAddress = _masterAddress; } /** * @dev to initiate the member roles * @param _firstAB is the address of the first AB member * @param memberAuthority is the authority (role) of the member */ function memberRolesInitiate(address _firstAB, address memberAuthority) public { require(!constructorCheck); _addInitialMemberRoles(_firstAB, memberAuthority); constructorCheck = true; } /// @dev Adds new member role /// @param _roleName New role name /// @param _roleDescription New description hash /// @param _authorized Authorized member against every role id function addRole(//solhint-disable-line bytes32 _roleName, string memory _roleDescription, address _authorized ) public onlyAuthorizedToGovern { _addRole(_roleName, _roleDescription, _authorized); } /// @dev Assign or Delete a member from specific role. /// @param _memberAddress Address of Member /// @param _roleId RoleId to update /// @param _active active is set to be True if we want to assign this role to member, False otherwise! function updateRole(//solhint-disable-line address _memberAddress, uint _roleId, bool _active ) public checkRoleAuthority(_roleId) { _updateRole(_memberAddress, _roleId, _active); } /** * @dev to add members before launch * @param userArray is list of addresses of members * @param tokens is list of tokens minted for each array element */ function addMembersBeforeLaunch(address[] memory userArray, uint[] memory tokens) public onlyOwner { require(!launched); for (uint i = 0; i < userArray.length; i++) { require(!ms.isMember(userArray[i])); dAppToken.addToWhitelist(userArray[i]); _updateRole(userArray[i], uint(Role.Member), true); dAppToken.mint(userArray[i], tokens[i]); } launched = true; launchedOn = now; } /** * @dev Called by user to pay joining membership fee */ function payJoiningFee(address _userAddress) public payable { require(_userAddress != address(0)); require(!ms.isPause(), "Emergency Pause Applied"); if (msg.sender == address(ms.getLatestAddress("QT"))) { require(td.walletAddress() != address(0), "No walletAddress present"); dAppToken.addToWhitelist(_userAddress); _updateRole(_userAddress, uint(Role.Member), true); td.walletAddress().transfer(msg.value); } else { require(!qd.refundEligible(_userAddress)); require(!ms.isMember(_userAddress)); require(msg.value == td.joiningFee()); qd.setRefundEligible(_userAddress, true); } } /** * @dev to perform kyc verdict * @param _userAddress whose kyc is being performed * @param verdict of kyc process */ function kycVerdict(address payable _userAddress, bool verdict) public { require(msg.sender == qd.kycAuthAddress()); require(!ms.isPause()); require(_userAddress != address(0)); require(!ms.isMember(_userAddress)); require(qd.refundEligible(_userAddress)); if (verdict) { qd.setRefundEligible(_userAddress, false); uint fee = td.joiningFee(); dAppToken.addToWhitelist(_userAddress); _updateRole(_userAddress, uint(Role.Member), true); td.walletAddress().transfer(fee); // solhint-disable-line } else { qd.setRefundEligible(_userAddress, false); _userAddress.transfer(td.joiningFee()); // solhint-disable-line } } /** * @dev Called by existed member if wish to Withdraw membership. */ function withdrawMembership() public { require(!ms.isPause() && ms.isMember(msg.sender)); require(dAppToken.totalLockedBalance(msg.sender, now) == 0); // solhint-disable-line require(!tf.isLockedForMemberVote(msg.sender)); // No locked tokens for Member/Governance voting require(cr.getAllPendingRewardOfUser(msg.sender) == 0); // No pending reward to be claimed(claim assesment). require(dAppToken.tokensUnlockable(msg.sender, "CLA") == 0, "Member should have no CLA unlockable tokens"); gv.removeDelegation(msg.sender); dAppToken.burnFrom(msg.sender, tk.balanceOf(msg.sender)); _updateRole(msg.sender, uint(Role.Member), false); dAppToken.removeFromWhitelist(msg.sender); // need clarification on whitelist if (claimPayoutAddress[msg.sender] != address(0)) { claimPayoutAddress[msg.sender] = address(0); emit ClaimPayoutAddressSet(msg.sender, address(0)); } } /** * @dev Called by existed member if wish to switch membership to other address. * @param _add address of user to forward membership. */ function switchMembership(address _add) external { require(!ms.isPause() && ms.isMember(msg.sender) && !ms.isMember(_add)); require(dAppToken.totalLockedBalance(msg.sender, now) == 0); // solhint-disable-line require(!tf.isLockedForMemberVote(msg.sender)); // No locked tokens for Member/Governance voting require(cr.getAllPendingRewardOfUser(msg.sender) == 0); // No pending reward to be claimed(claim assesment). require(dAppToken.tokensUnlockable(msg.sender, "CLA") == 0, "Member should have no CLA unlockable tokens"); gv.removeDelegation(msg.sender); dAppToken.addToWhitelist(_add); _updateRole(_add, uint(Role.Member), true); tk.transferFrom(msg.sender, _add, tk.balanceOf(msg.sender)); _updateRole(msg.sender, uint(Role.Member), false); dAppToken.removeFromWhitelist(msg.sender); address payable previousPayoutAddress = claimPayoutAddress[msg.sender]; if (previousPayoutAddress != address(0)) { address payable storedAddress = previousPayoutAddress == _add ? address(0) : previousPayoutAddress; claimPayoutAddress[msg.sender] = address(0); claimPayoutAddress[_add] = storedAddress; // emit event for old address reset emit ClaimPayoutAddressSet(msg.sender, address(0)); if (storedAddress != address(0)) { // emit event for setting the payout address on the new member address if it's non zero emit ClaimPayoutAddressSet(_add, storedAddress); } } emit switchedMembership(msg.sender, _add, now); } function getClaimPayoutAddress(address payable _member) external view returns (address payable) { address payable payoutAddress = claimPayoutAddress[_member]; return payoutAddress != address(0) ? payoutAddress : _member; } function setClaimPayoutAddress(address payable _address) external { require(!ms.isPause(), "system is paused"); require(ms.isMember(msg.sender), "sender is not a member"); require(_address != msg.sender, "should be different than the member address"); claimPayoutAddress[msg.sender] = _address; emit ClaimPayoutAddressSet(msg.sender, _address); } /// @dev Return number of member roles function totalRoles() public view returns (uint256) {//solhint-disable-line return memberRoleData.length; } /// @dev Change Member Address who holds the authority to Add/Delete any member from specific role. /// @param _roleId roleId to update its Authorized Address /// @param _newAuthorized New authorized address against role id function changeAuthorized(uint _roleId, address _newAuthorized) public checkRoleAuthority(_roleId) {//solhint-disable-line memberRoleData[_roleId].authorized = _newAuthorized; } /// @dev Gets the member addresses assigned by a specific role /// @param _memberRoleId Member role id /// @return roleId Role id /// @return allMemberAddress Member addresses of specified role id function members(uint _memberRoleId) public view returns (uint, address[] memory memberArray) {//solhint-disable-line uint length = memberRoleData[_memberRoleId].memberAddress.length; uint i; uint j = 0; memberArray = new address[](memberRoleData[_memberRoleId].memberCounter); for (i = 0; i < length; i++) { address member = memberRoleData[_memberRoleId].memberAddress[i]; if (memberRoleData[_memberRoleId].memberActive[member] && !_checkMemberInArray(member, memberArray)) {//solhint-disable-line memberArray[j] = member; j++; } } return (_memberRoleId, memberArray); } /// @dev Gets all members' length /// @param _memberRoleId Member role id /// @return memberRoleData[_memberRoleId].memberCounter Member length function numberOfMembers(uint _memberRoleId) public view returns (uint) {//solhint-disable-line return memberRoleData[_memberRoleId].memberCounter; } /// @dev Return member address who holds the right to add/remove any member from specific role. function authorized(uint _memberRoleId) public view returns (address) {//solhint-disable-line return memberRoleData[_memberRoleId].authorized; } /// @dev Get All role ids array that has been assigned to a member so far. function roles(address _memberAddress) public view returns (uint[] memory) {//solhint-disable-line uint length = memberRoleData.length; uint[] memory assignedRoles = new uint[](length); uint counter = 0; for (uint i = 1; i < length; i++) { if (memberRoleData[i].memberActive[_memberAddress]) { assignedRoles[counter] = i; counter++; } } return assignedRoles; } /// @dev Returns true if the given role id is assigned to a member. /// @param _memberAddress Address of member /// @param _roleId Checks member's authenticity with the roleId. /// i.e. Returns true if this roleId is assigned to member function checkRole(address _memberAddress, uint _roleId) public view returns (bool) {//solhint-disable-line if (_roleId == uint(Role.UnAssigned)) return true; else if (memberRoleData[_roleId].memberActive[_memberAddress]) //solhint-disable-line return true; else return false; } /// @dev Return total number of members assigned against each role id. /// @return totalMembers Total members in particular role id function getMemberLengthForAllRoles() public view returns (uint[] memory totalMembers) {//solhint-disable-line totalMembers = new uint[](memberRoleData.length); for (uint i = 0; i < memberRoleData.length; i++) { totalMembers[i] = numberOfMembers(i); } } /** * @dev to update the member roles * @param _memberAddress in concern * @param _roleId the id of role * @param _active if active is true, add the member, else remove it */ function _updateRole(address _memberAddress, uint _roleId, bool _active) internal { // require(_roleId != uint(Role.TokenHolder), "Membership to Token holder is detected automatically"); if (_active) { require(!memberRoleData[_roleId].memberActive[_memberAddress]); memberRoleData[_roleId].memberCounter = SafeMath.add(memberRoleData[_roleId].memberCounter, 1); memberRoleData[_roleId].memberActive[_memberAddress] = true; memberRoleData[_roleId].memberAddress.push(_memberAddress); } else { require(memberRoleData[_roleId].memberActive[_memberAddress]); memberRoleData[_roleId].memberCounter = SafeMath.sub(memberRoleData[_roleId].memberCounter, 1); delete memberRoleData[_roleId].memberActive[_memberAddress]; } } /// @dev Adds new member role /// @param _roleName New role name /// @param _roleDescription New description hash /// @param _authorized Authorized member against every role id function _addRole( bytes32 _roleName, string memory _roleDescription, address _authorized ) internal { emit MemberRole(memberRoleData.length, _roleName, _roleDescription); memberRoleData.push(MemberRoleDetails(0, new address[](0), _authorized)); } /** * @dev to check if member is in the given member array * @param _memberAddress in concern * @param memberArray in concern * @return boolean to represent the presence */ function _checkMemberInArray( address _memberAddress, address[] memory memberArray ) internal pure returns (bool memberExists) { uint i; for (i = 0; i < memberArray.length; i++) { if (memberArray[i] == _memberAddress) { memberExists = true; break; } } } /** * @dev to add initial member roles * @param _firstAB is the member address to be added * @param memberAuthority is the member authority(role) to be added for */ function _addInitialMemberRoles(address _firstAB, address memberAuthority) internal { maxABCount = 5; _addRole("Unassigned", "Unassigned", address(0)); _addRole( "Advisory Board", "Selected few members that are deeply entrusted by the dApp. An ideal advisory board should be a mix of skills of domain, governance, research, technology, consulting etc to improve the performance of the dApp.", //solhint-disable-line address(0) ); _addRole( "Member", "Represents all users of Mutual.", //solhint-disable-line memberAuthority ); _addRole( "Owner", "Represents Owner of Mutual.", //solhint-disable-line address(0) ); // _updateRole(_firstAB, uint(Role.AdvisoryBoard), true); _updateRole(_firstAB, uint(Role.Owner), true); // _updateRole(_firstAB, uint(Role.Member), true); launchedOn = 0; } function memberAtIndex(uint _memberRoleId, uint index) external view returns (address, bool) { address memberAddress = memberRoleData[_memberRoleId].memberAddress[index]; return (memberAddress, memberRoleData[_memberRoleId].memberActive[memberAddress]); } function membersLength(uint _memberRoleId) external view returns (uint) { return memberRoleData[_memberRoleId].memberAddress.length; } } // File: contracts/modules/oracles/PriceFeedOracle.sol /* Copyright (C) 2020 NexusMutual.io This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/ */ pragma solidity ^0.5.0; contract Aggregator { function latestAnswer() public view returns (int); } contract PriceFeedOracle { using SafeMath for uint; mapping (address => address) public aggregators; address public daiAddress; address constant public ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; constructor (address[] memory assets, address[] memory _aggregators, address _daiAddress) public { require(assets.length == _aggregators.length, "PriceFeedOracle: assets and _aggregators need to have same length"); for (uint i = 0; i < assets.length; i++) { aggregators[assets[i]] = _aggregators[i]; } daiAddress = _daiAddress; } /** * @dev Returns the amount of ether in wei that are equivalent to 1 unit (10 ** decimals) of asset * @param asset quoted currency * @return price in ether */ function getAssetToEthRate(address asset) public view returns (uint) { if (asset == ETH) { return 1 ether; } address aggregatorAddress = aggregators[asset]; if (aggregatorAddress == address(0)) { revert("PriceFeedOracle: Oracle asset not found"); } int rate = Aggregator(aggregatorAddress).latestAnswer(); require(rate > 0, "PriceFeedOracle: Rate must be > 0"); return uint(rate); } /** * @dev Returns the amount of currency that is equivalent to ethIn amount of ether. * @param asset quoted Supported values: ["DAI", "ETH"] * @param ethIn amount of ether to be converted to the currency * @return price in ether */ function getAssetForEth(address asset, uint ethIn) external view returns (uint) { if (asset == daiAddress) { return ethIn.mul(1e18).div(getAssetToEthRate(daiAddress)); } if (asset == ETH) { return ethIn; } revert("PriceFeedOracle: Unknown asset"); } } // File: contracts/modules/capital/PoolData.sol /* Copyright (C) 2020 NexusMutual.io This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/ */ pragma solidity ^0.5.0; contract DSValue { function peek() public view returns (bytes32, bool); function read() public view returns (bytes32); } contract PoolData is Iupgradable { using SafeMath for uint; struct ApiId { bytes4 typeOf; bytes4 currency; uint id; uint64 dateAdd; uint64 dateUpd; } struct CurrencyAssets { address currAddress; uint baseMin; uint varMin; } struct InvestmentAssets { address currAddress; bool status; uint64 minHoldingPercX100; uint64 maxHoldingPercX100; uint8 decimals; } struct IARankDetails { bytes4 maxIACurr; uint64 maxRate; bytes4 minIACurr; uint64 minRate; } struct McrData { uint mcrPercx100; uint mcrEther; uint vFull; //Pool funds uint64 date; } IARankDetails[] internal allIARankDetails; McrData[] public allMCRData; bytes4[] internal allInvestmentCurrencies; bytes4[] internal allCurrencies; bytes32[] public allAPIcall; mapping(bytes32 => ApiId) public allAPIid; mapping(uint64 => uint) internal datewiseId; mapping(bytes16 => uint) internal currencyLastIndex; mapping(bytes4 => CurrencyAssets) internal allCurrencyAssets; mapping(bytes4 => InvestmentAssets) internal allInvestmentAssets; mapping(bytes4 => uint) internal caAvgRate; mapping(bytes4 => uint) internal iaAvgRate; address public notariseMCR; address public daiFeedAddress; uint private constant DECIMAL1E18 = uint(10) ** 18; uint public uniswapDeadline; uint public liquidityTradeCallbackTime; uint public lastLiquidityTradeTrigger; uint64 internal lastDate; uint public variationPercX100; uint public iaRatesTime; uint public minCap; uint public mcrTime; uint public a; uint public shockParameter; uint public c; uint public mcrFailTime; uint public ethVolumeLimit; uint public capReached; uint public capacityLimit; constructor(address _notariseAdd, address _daiFeedAdd, address _daiAdd) public { notariseMCR = _notariseAdd; daiFeedAddress = _daiFeedAdd; c = 5800000; a = 1028; mcrTime = 24 hours; mcrFailTime = 6 hours; allMCRData.push(McrData(0, 0, 0, 0)); minCap = 12000 * DECIMAL1E18; shockParameter = 50; variationPercX100 = 100; // 1% iaRatesTime = 24 hours; // 24 hours in seconds uniswapDeadline = 20 minutes; liquidityTradeCallbackTime = 4 hours; ethVolumeLimit = 4; capacityLimit = 10; allCurrencies.push("ETH"); allCurrencyAssets["ETH"] = CurrencyAssets(address(0), 1000 * DECIMAL1E18, 0); allCurrencies.push("DAI"); allCurrencyAssets["DAI"] = CurrencyAssets(_daiAdd, 50000 * DECIMAL1E18, 0); allInvestmentCurrencies.push("ETH"); allInvestmentAssets["ETH"] = InvestmentAssets(address(0), true, 2500, 10000, 18); allInvestmentCurrencies.push("DAI"); allInvestmentAssets["DAI"] = InvestmentAssets(_daiAdd, true, 250, 1500, 18); } /** * @dev to set the maximum cap allowed * @param val is the new value */ function setCapReached(uint val) external onlyInternal { capReached = val; } /// @dev Updates the 3 day average rate of a IA currency. /// To be replaced by MakerDao's on chain rates /// @param curr IA Currency Name. /// @param rate Average exchange rate X 100 (of last 3 days). function updateIAAvgRate(bytes4 curr, uint rate) external onlyInternal { iaAvgRate[curr] = rate; } /// @dev Updates the 3 day average rate of a CA currency. /// To be replaced by MakerDao's on chain rates /// @param curr Currency Name. /// @param rate Average exchange rate X 100 (of last 3 days). function updateCAAvgRate(bytes4 curr, uint rate) external onlyInternal { caAvgRate[curr] = rate; } /// @dev Adds details of (Minimum Capital Requirement)MCR. /// @param mcrp Minimum Capital Requirement percentage (MCR% * 100 ,Ex:for 54.56% ,given 5456) /// @param vf Pool fund value in Ether used in the last full daily calculation from the Capital model. function pushMCRData(uint mcrp, uint mcre, uint vf, uint64 time) external onlyInternal { allMCRData.push(McrData(mcrp, mcre, vf, time)); } /** * @dev Updates the Timestamp at which result of oracalize call is received. */ function updateDateUpdOfAPI(bytes32 myid) external onlyInternal { allAPIid[myid].dateUpd = uint64(now); } /** * @dev Saves the details of the Oraclize API. * @param myid Id return by the oraclize query. * @param _typeof type of the query for which oraclize call is made. * @param id ID of the proposal,quote,cover etc. for which oraclize call is made */ function saveApiDetails(bytes32 myid, bytes4 _typeof, uint id) external onlyInternal { allAPIid[myid] = ApiId(_typeof, "", id, uint64(now), uint64(now)); } /** * @dev Stores the id return by the oraclize query. * Maintains record of all the Ids return by oraclize query. * @param myid Id return by the oraclize query. */ function addInAllApiCall(bytes32 myid) external onlyInternal { allAPIcall.push(myid); } /** * @dev Saves investment asset rank details. * @param maxIACurr Maximum ranked investment asset currency. * @param maxRate Maximum ranked investment asset rate. * @param minIACurr Minimum ranked investment asset currency. * @param minRate Minimum ranked investment asset rate. * @param date in yyyymmdd. */ function saveIARankDetails( bytes4 maxIACurr, uint64 maxRate, bytes4 minIACurr, uint64 minRate, uint64 date ) external onlyInternal { allIARankDetails.push(IARankDetails(maxIACurr, maxRate, minIACurr, minRate)); datewiseId[date] = allIARankDetails.length.sub(1); } /** * @dev to get the time for the laste liquidity trade trigger */ function setLastLiquidityTradeTrigger() external onlyInternal { lastLiquidityTradeTrigger = now; } /** * @dev Updates Last Date. */ function updatelastDate(uint64 newDate) external onlyInternal { lastDate = newDate; } /** * @dev Adds currency asset currency. * @param curr currency of the asset * @param currAddress address of the currency * @param baseMin base minimum in 10^18. */ function addCurrencyAssetCurrency( bytes4 curr, address currAddress, uint baseMin ) external { require(ms.checkIsAuthToGoverned(msg.sender)); allCurrencies.push(curr); allCurrencyAssets[curr] = CurrencyAssets(currAddress, baseMin, 0); } /** * @dev Adds investment asset. */ function addInvestmentAssetCurrency( bytes4 curr, address currAddress, bool status, uint64 minHoldingPercX100, uint64 maxHoldingPercX100, uint8 decimals ) external { require(ms.checkIsAuthToGoverned(msg.sender)); allInvestmentCurrencies.push(curr); allInvestmentAssets[curr] = InvestmentAssets(currAddress, status, minHoldingPercX100, maxHoldingPercX100, decimals); } /** * @dev Changes base minimum of a given currency asset. */ function changeCurrencyAssetBaseMin(bytes4 curr, uint baseMin) external { require(ms.checkIsAuthToGoverned(msg.sender)); allCurrencyAssets[curr].baseMin = baseMin; } /** * @dev changes variable minimum of a given currency asset. */ function changeCurrencyAssetVarMin(bytes4 curr, uint varMin) external onlyInternal { allCurrencyAssets[curr].varMin = varMin; } /** * @dev Changes the investment asset status. */ function changeInvestmentAssetStatus(bytes4 curr, bool status) external { require(ms.checkIsAuthToGoverned(msg.sender)); allInvestmentAssets[curr].status = status; } /** * @dev Changes the investment asset Holding percentage of a given currency. */ function changeInvestmentAssetHoldingPerc( bytes4 curr, uint64 minPercX100, uint64 maxPercX100 ) external { require(ms.checkIsAuthToGoverned(msg.sender)); allInvestmentAssets[curr].minHoldingPercX100 = minPercX100; allInvestmentAssets[curr].maxHoldingPercX100 = maxPercX100; } /** * @dev Gets Currency asset token address. */ function changeCurrencyAssetAddress(bytes4 curr, address currAdd) external { require(ms.checkIsAuthToGoverned(msg.sender)); allCurrencyAssets[curr].currAddress = currAdd; } /** * @dev Changes Investment asset token address. */ function changeInvestmentAssetAddressAndDecimal( bytes4 curr, address currAdd, uint8 newDecimal ) external { require(ms.checkIsAuthToGoverned(msg.sender)); allInvestmentAssets[curr].currAddress = currAdd; allInvestmentAssets[curr].decimals = newDecimal; } /// @dev Changes address allowed to post MCR. function changeNotariseAddress(address _add) external onlyInternal { notariseMCR = _add; } /// @dev updates daiFeedAddress address. /// @param _add address of DAI feed. function changeDAIfeedAddress(address _add) external onlyInternal { daiFeedAddress = _add; } /** * @dev Gets Uint Parameters of a code * @param code whose details we want * @return string value of the code * @return associated amount (time or perc or value) to the code */ function getUintParameters(bytes8 code) external view returns (bytes8 codeVal, uint val) { codeVal = code; if (code == "MCRTIM") { val = mcrTime / (1 hours); } else if (code == "MCRFTIM") { val = mcrFailTime / (1 hours); } else if (code == "MCRMIN") { val = minCap; } else if (code == "MCRSHOCK") { val = shockParameter; } else if (code == "MCRCAPL") { val = capacityLimit; } else if (code == "IMZ") { val = variationPercX100; } else if (code == "IMRATET") { val = iaRatesTime / (1 hours); } else if (code == "IMUNIDL") { val = uniswapDeadline / (1 minutes); } else if (code == "IMLIQT") { val = liquidityTradeCallbackTime / (1 hours); } else if (code == "IMETHVL") { val = ethVolumeLimit; } else if (code == "C") { val = c; } else if (code == "A") { val = a; } } /// @dev Checks whether a given address can notaise MCR data or not. /// @param _add Address. /// @return res Returns 0 if address is not authorized, else 1. function isnotarise(address _add) external view returns (bool res) { res = false; if (_add == notariseMCR) res = true; } /// @dev Gets the details of last added MCR. /// @return mcrPercx100 Total Minimum Capital Requirement percentage of that month of year(multiplied by 100). /// @return vFull Total Pool fund value in Ether used in the last full daily calculation. function getLastMCR() external view returns (uint mcrPercx100, uint mcrEtherx1E18, uint vFull, uint64 date) { uint index = allMCRData.length.sub(1); return ( allMCRData[index].mcrPercx100, allMCRData[index].mcrEther, allMCRData[index].vFull, allMCRData[index].date ); } /// @dev Gets last Minimum Capital Requirement percentage of Capital Model /// @return val MCR% value,multiplied by 100. function getLastMCRPerc() external view returns (uint) { return allMCRData[allMCRData.length.sub(1)].mcrPercx100; } /// @dev Gets last Ether price of Capital Model /// @return val ether value,multiplied by 100. function getLastMCREther() external view returns (uint) { return allMCRData[allMCRData.length.sub(1)].mcrEther; } /// @dev Gets Pool fund value in Ether used in the last full daily calculation from the Capital model. function getLastVfull() external view returns (uint) { return allMCRData[allMCRData.length.sub(1)].vFull; } /// @dev Gets last Minimum Capital Requirement in Ether. /// @return date of MCR. function getLastMCRDate() external view returns (uint64 date) { date = allMCRData[allMCRData.length.sub(1)].date; } /// @dev Gets details for token price calculation. function getTokenPriceDetails(bytes4 curr) external view returns (uint _a, uint _c, uint rate) { _a = a; _c = c; rate = _getAvgRate(curr, false); } /// @dev Gets the total number of times MCR calculation has been made. function getMCRDataLength() external view returns (uint len) { len = allMCRData.length; } /** * @dev Gets investment asset rank details by given date. */ function getIARankDetailsByDate( uint64 date ) external view returns ( bytes4 maxIACurr, uint64 maxRate, bytes4 minIACurr, uint64 minRate ) { uint index = datewiseId[date]; return ( allIARankDetails[index].maxIACurr, allIARankDetails[index].maxRate, allIARankDetails[index].minIACurr, allIARankDetails[index].minRate ); } /** * @dev Gets Last Date. */ function getLastDate() external view returns (uint64 date) { return lastDate; } /** * @dev Gets investment currency for a given index. */ function getInvestmentCurrencyByIndex(uint index) external view returns (bytes4 currName) { return allInvestmentCurrencies[index]; } /** * @dev Gets count of investment currency. */ function getInvestmentCurrencyLen() external view returns (uint len) { return allInvestmentCurrencies.length; } /** * @dev Gets all the investment currencies. */ function getAllInvestmentCurrencies() external view returns (bytes4[] memory currencies) { return allInvestmentCurrencies; } /** * @dev Gets All currency for a given index. */ function getCurrenciesByIndex(uint index) external view returns (bytes4 currName) { return allCurrencies[index]; } /** * @dev Gets count of All currency. */ function getAllCurrenciesLen() external view returns (uint len) { return allCurrencies.length; } /** * @dev Gets all currencies */ function getAllCurrencies() external view returns (bytes4[] memory currencies) { return allCurrencies; } /** * @dev Gets currency asset details for a given currency. */ function getCurrencyAssetVarBase( bytes4 curr ) external view returns ( bytes4 currency, uint baseMin, uint varMin ) { return ( curr, allCurrencyAssets[curr].baseMin, allCurrencyAssets[curr].varMin ); } /** * @dev Gets minimum variable value for currency asset. */ function getCurrencyAssetVarMin(bytes4 curr) external view returns (uint varMin) { return allCurrencyAssets[curr].varMin; } /** * @dev Gets base minimum of a given currency asset. */ function getCurrencyAssetBaseMin(bytes4 curr) external view returns (uint baseMin) { return allCurrencyAssets[curr].baseMin; } /** * @dev Gets investment asset maximum and minimum holding percentage of a given currency. */ function getInvestmentAssetHoldingPerc( bytes4 curr ) external view returns ( uint64 minHoldingPercX100, uint64 maxHoldingPercX100 ) { return ( allInvestmentAssets[curr].minHoldingPercX100, allInvestmentAssets[curr].maxHoldingPercX100 ); } /** * @dev Gets investment asset decimals. */ function getInvestmentAssetDecimals(bytes4 curr) external view returns (uint8 decimal) { return allInvestmentAssets[curr].decimals; } /** * @dev Gets investment asset maximum holding percentage of a given currency. */ function getInvestmentAssetMaxHoldingPerc(bytes4 curr) external view returns (uint64 maxHoldingPercX100) { return allInvestmentAssets[curr].maxHoldingPercX100; } /** * @dev Gets investment asset minimum holding percentage of a given currency. */ function getInvestmentAssetMinHoldingPerc(bytes4 curr) external view returns (uint64 minHoldingPercX100) { return allInvestmentAssets[curr].minHoldingPercX100; } /** * @dev Gets investment asset details of a given currency */ function getInvestmentAssetDetails( bytes4 curr ) external view returns ( bytes4 currency, address currAddress, bool status, uint64 minHoldingPerc, uint64 maxHoldingPerc, uint8 decimals ) { return ( curr, allInvestmentAssets[curr].currAddress, allInvestmentAssets[curr].status, allInvestmentAssets[curr].minHoldingPercX100, allInvestmentAssets[curr].maxHoldingPercX100, allInvestmentAssets[curr].decimals ); } /** * @dev Gets Currency asset token address. */ function getCurrencyAssetAddress(bytes4 curr) external view returns (address) { return allCurrencyAssets[curr].currAddress; } /** * @dev Gets investment asset token address. */ function getInvestmentAssetAddress(bytes4 curr) external view returns (address) { return allInvestmentAssets[curr].currAddress; } /** * @dev Gets investment asset active Status of a given currency. */ function getInvestmentAssetStatus(bytes4 curr) external view returns (bool status) { return allInvestmentAssets[curr].status; } /** * @dev Gets type of oraclize query for a given Oraclize Query ID. * @param myid Oraclize Query ID identifying the query for which the result is being received. * @return _typeof It could be of type "quote","quotation","cover","claim" etc. */ function getApiIdTypeOf(bytes32 myid) external view returns (bytes4) { return allAPIid[myid].typeOf; } /** * @dev Gets ID associated to oraclize query for a given Oraclize Query ID. * @param myid Oraclize Query ID identifying the query for which the result is being received. * @return id1 It could be the ID of "proposal","quotation","cover","claim" etc. */ function getIdOfApiId(bytes32 myid) external view returns (uint) { return allAPIid[myid].id; } /** * @dev Gets the Timestamp of a oracalize call. */ function getDateAddOfAPI(bytes32 myid) external view returns (uint64) { return allAPIid[myid].dateAdd; } /** * @dev Gets the Timestamp at which result of oracalize call is received. */ function getDateUpdOfAPI(bytes32 myid) external view returns (uint64) { return allAPIid[myid].dateUpd; } /** * @dev Gets currency by oracalize id. */ function getCurrOfApiId(bytes32 myid) external view returns (bytes4) { return allAPIid[myid].currency; } /** * @dev Gets ID return by the oraclize query of a given index. * @param index Index. * @return myid ID return by the oraclize query. */ function getApiCallIndex(uint index) external view returns (bytes32 myid) { myid = allAPIcall[index]; } /** * @dev Gets Length of API call. */ function getApilCallLength() external view returns (uint) { return allAPIcall.length; } /** * @dev Get Details of Oraclize API when given Oraclize Id. * @param myid ID return by the oraclize query. * @return _typeof ype of the query for which oraclize * call is made.("proposal","quote","quotation" etc.) */ function getApiCallDetails( bytes32 myid ) external view returns ( bytes4 _typeof, bytes4 curr, uint id, uint64 dateAdd, uint64 dateUpd ) { return ( allAPIid[myid].typeOf, allAPIid[myid].currency, allAPIid[myid].id, allAPIid[myid].dateAdd, allAPIid[myid].dateUpd ); } /** * @dev Updates Uint Parameters of a code * @param code whose details we want to update * @param val value to set */ function updateUintParameters(bytes8 code, uint val) public { require(ms.checkIsAuthToGoverned(msg.sender)); if (code == "MCRTIM") { _changeMCRTime(val * 1 hours); } else if (code == "MCRFTIM") { _changeMCRFailTime(val * 1 hours); } else if (code == "MCRMIN") { _changeMinCap(val); } else if (code == "MCRSHOCK") { _changeShockParameter(val); } else if (code == "MCRCAPL") { _changeCapacityLimit(val); } else if (code == "IMZ") { _changeVariationPercX100(val); } else if (code == "IMRATET") { _changeIARatesTime(val * 1 hours); } else if (code == "IMUNIDL") { _changeUniswapDeadlineTime(val * 1 minutes); } else if (code == "IMLIQT") { _changeliquidityTradeCallbackTime(val * 1 hours); } else if (code == "IMETHVL") { _setEthVolumeLimit(val); } else if (code == "C") { _changeC(val); } else if (code == "A") { _changeA(val); } else { revert("Invalid param code"); } } /** * @dev to get the average rate of currency rate * @param curr is the currency in concern * @return required rate */ function getCAAvgRate(bytes4 curr) public view returns (uint rate) { return _getAvgRate(curr, false); } /** * @dev to get the average rate of investment rate * @param curr is the investment in concern * @return required rate */ function getIAAvgRate(bytes4 curr) public view returns (uint rate) { return _getAvgRate(curr, true); } function changeDependentContractAddress() public onlyInternal {} /// @dev Gets the average rate of a CA currency. /// @param curr Currency Name. /// @return rate Average rate X 100(of last 3 days). function _getAvgRate(bytes4 curr, bool isIA) internal view returns (uint rate) { if (curr == "DAI") { DSValue ds = DSValue(daiFeedAddress); rate = uint(ds.read()).div(uint(10) ** 16); } else if (isIA) { rate = iaAvgRate[curr]; } else { rate = caAvgRate[curr]; } } /** * @dev to set the ethereum volume limit * @param val is the new limit value */ function _setEthVolumeLimit(uint val) internal { ethVolumeLimit = val; } /// @dev Sets minimum Cap. function _changeMinCap(uint newCap) internal { minCap = newCap; } /// @dev Sets Shock Parameter. function _changeShockParameter(uint newParam) internal { shockParameter = newParam; } /// @dev Changes time period for obtaining new MCR data from external oracle query. function _changeMCRTime(uint _time) internal { mcrTime = _time; } /// @dev Sets MCR Fail time. function _changeMCRFailTime(uint _time) internal { mcrFailTime = _time; } /** * @dev to change the uniswap deadline time * @param newDeadline is the value */ function _changeUniswapDeadlineTime(uint newDeadline) internal { uniswapDeadline = newDeadline; } /** * @dev to change the liquidity trade call back time * @param newTime is the new value to be set */ function _changeliquidityTradeCallbackTime(uint newTime) internal { liquidityTradeCallbackTime = newTime; } /** * @dev Changes time after which investment asset rates need to be fed. */ function _changeIARatesTime(uint _newTime) internal { iaRatesTime = _newTime; } /** * @dev Changes the variation range percentage. */ function _changeVariationPercX100(uint newPercX100) internal { variationPercX100 = newPercX100; } /// @dev Changes Growth Step function _changeC(uint newC) internal { c = newC; } /// @dev Changes scaling factor. function _changeA(uint val) internal { a = val; } /** * @dev to change the capacity limit * @param val is the new value */ function _changeCapacityLimit(uint val) internal { capacityLimit = val; } } // File: contracts/modules/capital/MCR.sol /* Copyright (C) 2020 NexusMutual.io This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/ */ pragma solidity ^0.5.0; contract MCR is Iupgradable { using SafeMath for uint; Pool public pool; PoolData public pd; NXMToken public tk; QuotationData public qd; MemberRoles public mr; TokenData public td; ProposalCategory public proposalCategory; uint private constant minCapFactor = uint(10) ** 21; uint public variableMincap; uint public dynamicMincapThresholdx100 = 13000; uint public dynamicMincapIncrementx100 = 100; event MCREvent( uint indexed date, uint blockNumber, bytes4[] allCurr, uint[] allCurrRates, uint mcrEtherx100, uint mcrPercx100, uint vFull ); constructor (address masterAddress) public { changeMasterAddress(masterAddress); // we'll pass the zero address on the first deploy // due to missing previous MCR contract if (masterAddress == address(0)) { return; } address mcrAddress = ms.getLatestAddress("MC"); MCR previousMCR = MCR(mcrAddress); // fetch MCR parameters from previous contract variableMincap = previousMCR.variableMincap(); dynamicMincapThresholdx100 = previousMCR.dynamicMincapThresholdx100(); dynamicMincapIncrementx100 = previousMCR.dynamicMincapIncrementx100(); } /** * @dev Adds new MCR data. * @param mcrP Minimum Capital Requirement in percentage. * @param vF Pool fund value in Ether used in the last full daily calculation of the Capital model. * @param onlyDate Date(yyyymmdd) at which MCR details are getting added. */ function addMCRData( uint mcrP, uint mcrE, uint vF, bytes4[] calldata curr, uint[] calldata _threeDayAvg, uint64 onlyDate ) external checkPause { require(proposalCategory.constructorCheck()); require(pd.isnotarise(msg.sender)); if (mr.launched() && pd.capReached() != 1) { if (mcrP >= 10000) pd.setCapReached(1); } uint len = pd.getMCRDataLength(); _addMCRData(len, onlyDate, curr, mcrE, mcrP, vF, _threeDayAvg); } // proxying this call through mcr contract to get rid of pd from pool function getLastMCREther() external view returns (uint) { return pd.getLastMCREther(); } /** * @dev Iupgradable Interface to update dependent contract address */ function changeDependentContractAddress() public { qd = QuotationData(ms.getLatestAddress("QD")); pool = Pool(ms.getLatestAddress("P1")); pd = PoolData(ms.getLatestAddress("PD")); tk = NXMToken(ms.tokenAddress()); mr = MemberRoles(ms.getLatestAddress("MR")); td = TokenData(ms.getLatestAddress("TD")); proposalCategory = ProposalCategory(ms.getLatestAddress("PC")); } /** * @dev Gets total sum assured (in ETH). * @return amount of sum assured */ function getAllSumAssurance() public view returns (uint) { PriceFeedOracle priceFeed = pool.priceFeedOracle(); address daiAddress = priceFeed.daiAddress(); uint ethAmount = qd.getTotalSumAssured("ETH").mul(1e18); uint daiAmount = qd.getTotalSumAssured("DAI").mul(1e18); uint daiRate = priceFeed.getAssetToEthRate(daiAddress); uint daiAmountInEth = daiAmount.mul(daiRate).div(1e18); return ethAmount.add(daiAmountInEth); } function getThresholdValues(uint vtp, uint vF, uint totalSA, uint minCap) public view returns (uint lowerThreshold, uint upperThreshold) { minCap = (minCap.mul(minCapFactor)).add(variableMincap); uint lower = 0; if (vtp >= vF) { // Max Threshold = [MAX(Vtp, Vfull) x 120] / mcrMinCap upperThreshold = vtp.mul(120).mul(100).div((minCap)); } else { upperThreshold = vF.mul(120).mul(100).div((minCap)); } if (vtp > 0) { lower = totalSA.mul(pd.shockParameter()).div(100); if (lower < minCap.mul(11).div(10)) lower = minCap.mul(11).div(10); } if (lower > 0) { // Min Threshold = [Vtp / MAX(TotalActiveSA x ShockParameter, mcrMinCap x 1.1)] x 100 lowerThreshold = vtp.mul(100).mul(100).div(lower); } } /** * @dev Gets Uint Parameters of a code * @param code whose details we want * @return string value of the code * @return associated amount (time or perc or value) to the code */ function getUintParameters(bytes8 code) external view returns (bytes8 codeVal, uint val) { codeVal = code; if (code == "DMCT") { val = dynamicMincapThresholdx100; } else if (code == "DMCI") { val = dynamicMincapIncrementx100; } } /** * @dev Updates Uint Parameters of a code * @param code whose details we want to update * @param val value to set */ function updateUintParameters(bytes8 code, uint val) public { require(ms.checkIsAuthToGoverned(msg.sender)); if (code == "DMCT") { dynamicMincapThresholdx100 = val; } else if (code == "DMCI") { dynamicMincapIncrementx100 = val; } else { revert("Invalid param code"); } } /** * @dev Adds MCR Data. Checks if MCR is within valid * thresholds in order to rule out any incorrect calculations */ function _addMCRData( uint len, uint64 newMCRDate, bytes4[] memory curr, uint mcrE, uint mcrP, uint vF, uint[] memory _threeDayAvg ) internal { uint lowerThreshold = 0; uint upperThreshold = 0; if (len > 1) { uint vtp = pool.getPoolValueInEth(); (lowerThreshold, upperThreshold) = getThresholdValues(vtp, vF, getAllSumAssurance(), pd.minCap()); } if (mcrP > dynamicMincapThresholdx100) { variableMincap = (variableMincap.mul(dynamicMincapIncrementx100.add(10000)).add(minCapFactor.mul(pd.minCap().mul(dynamicMincapIncrementx100)))).div(10000); } // Explanation for above formula :- // actual formula -> variableMinCap = variableMinCap + (variableMinCap+minCap)*dynamicMincapIncrement/100 // Implemented formula is simplified form of actual formula. // Let consider above formula as b = b + (a+b)*c/100 // here, dynamicMincapIncrement is in x100 format. // so b+(a+b)*cx100/10000 can be written as => (10000.b + b.cx100 + a.cx100)/10000. // It can further simplify to (b.(10000+cx100) + a.cx100)/10000. if (len == 1 || (mcrP) >= lowerThreshold && (mcrP) <= upperThreshold) { pd.pushMCRData(mcrP, mcrE, vF, newMCRDate); for (uint i = 0; i < curr.length; i++) { pd.updateCAAvgRate(curr[i], _threeDayAvg[i]); } emit MCREvent(newMCRDate, block.number, curr, _threeDayAvg, mcrE, mcrP, vF); return; } revert("MCR: Failed"); } } // File: contracts/modules/cover/Quotation.sol /* Copyright (C) 2020 NexusMutual.io This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/ */ pragma solidity ^0.5.0; contract Quotation is Iupgradable { using SafeMath for uint; TokenFunctions public tf; TokenController public tc; TokenData public td; Pool public pool; PoolData public pd; QuotationData public qd; MCR public m1; MemberRoles public mr; ClaimsReward public cr; bool internal locked; event RefundEvent(address indexed user, bool indexed status, uint holdedCoverID, bytes32 reason); modifier noReentrancy() { require(!locked, "Reentrant call."); locked = true; _; locked = false; } /** * @dev Iupgradable Interface to update dependent contract address */ function changeDependentContractAddress() public onlyInternal { m1 = MCR(ms.getLatestAddress("MC")); tf = TokenFunctions(ms.getLatestAddress("TF")); tc = TokenController(ms.getLatestAddress("TC")); td = TokenData(ms.getLatestAddress("TD")); qd = QuotationData(ms.getLatestAddress("QD")); pd = PoolData(ms.getLatestAddress("PD")); mr = MemberRoles(ms.getLatestAddress("MR")); cr = ClaimsReward(ms.getLatestAddress("CR")); pool = Pool(ms.getLatestAddress("P1")); } function sendEther() public payable { } /** * @dev Expires a cover after a set period of time. * Changes the status of the Cover and reduces the current * sum assured of all areas in which the quotation lies * Unlocks the CN tokens of the cover. Updates the Total Sum Assured value. * @param _cid Cover Id. */ function expireCover(uint _cid) public { require(checkCoverExpired(_cid) && qd.getCoverStatusNo(_cid) != uint(QuotationData.CoverStatus.CoverExpired)); tf.unlockCN(_cid); bytes4 curr; address scAddress; uint sumAssured; (,, scAddress, curr, sumAssured,) = qd.getCoverDetailsByCoverID1(_cid); if (qd.getCoverStatusNo(_cid) != uint(QuotationData.CoverStatus.ClaimAccepted)) _removeSAFromCSA(_cid, sumAssured); qd.changeCoverStatusNo(_cid, uint8(QuotationData.CoverStatus.CoverExpired)); } /** * @dev Checks if a cover should get expired/closed or not. * @param _cid Cover Index. * @return expire true if the Cover's time has expired, false otherwise. */ function checkCoverExpired(uint _cid) public view returns (bool expire) { expire = qd.getValidityOfCover(_cid) < uint64(now); } /** * @dev Updates the Sum Assured Amount of all the quotation. * @param _cid Cover id * @param _amount that will get subtracted Current Sum Assured * amount that comes under a quotation. */ function removeSAFromCSA(uint _cid, uint _amount) public onlyInternal { _removeSAFromCSA(_cid, _amount); } /** * @dev Makes Cover funded via NXM tokens. * @param smartCAdd Smart Contract Address */ function makeCoverUsingNXMTokens( uint[] memory coverDetails, uint16 coverPeriod, bytes4 coverCurr, address smartCAdd, uint8 _v, bytes32 _r, bytes32 _s ) public isMemberAndcheckPause { tc.burnFrom(msg.sender, coverDetails[2]); // need burn allowance _verifyCoverDetails(msg.sender, smartCAdd, coverCurr, coverDetails, coverPeriod, _v, _r, _s, true); } /** * @dev Verifies cover details signed off chain. * @param from address of funder. * @param scAddress Smart Contract Address */ function verifyCoverDetails( address payable from, address scAddress, bytes4 coverCurr, uint[] memory coverDetails, uint16 coverPeriod, uint8 _v, bytes32 _r, bytes32 _s ) public onlyInternal { _verifyCoverDetails( from, scAddress, coverCurr, coverDetails, coverPeriod, _v, _r, _s, false ); } /** * @dev Verifies signature. * @param coverDetails details related to cover. * @param coverPeriod validity of cover. * @param smaratCA smarat contract address. * @param _v argument from vrs hash. * @param _r argument from vrs hash. * @param _s argument from vrs hash. */ function verifySign( uint[] memory coverDetails, uint16 coverPeriod, bytes4 curr, address smaratCA, uint8 _v, bytes32 _r, bytes32 _s ) public view returns (bool) { require(smaratCA != address(0)); require(pd.capReached() == 1, "Can not buy cover until cap reached for 1st time"); bytes32 hash = getOrderHash(coverDetails, coverPeriod, curr, smaratCA); return isValidSignature(hash, _v, _r, _s); } /** * @dev Gets order hash for given cover details. * @param coverDetails details realted to cover. * @param coverPeriod validity of cover. * @param smaratCA smarat contract address. */ function getOrderHash( uint[] memory coverDetails, uint16 coverPeriod, bytes4 curr, address smaratCA ) public view returns (bytes32) { return keccak256( abi.encodePacked( coverDetails[0], curr, coverPeriod, smaratCA, coverDetails[1], coverDetails[2], coverDetails[3], coverDetails[4], address(this) ) ); } /** * @dev Verifies signature. * @param hash order hash * @param v argument from vrs hash. * @param r argument from vrs hash. * @param s argument from vrs hash. */ function isValidSignature(bytes32 hash, uint8 v, bytes32 r, bytes32 s) public view returns (bool) { bytes memory prefix = "\x19Ethereum Signed Message:\n32"; bytes32 prefixedHash = keccak256(abi.encodePacked(prefix, hash)); address a = ecrecover(prefixedHash, v, r, s); return (a == qd.getAuthQuoteEngine()); } /** * @dev to get the status of recently holded coverID * @param userAdd is the user address in concern * @return the status of the concerned coverId */ function getRecentHoldedCoverIdStatus(address userAdd) public view returns (int) { uint holdedCoverLen = qd.getUserHoldedCoverLength(userAdd); if (holdedCoverLen == 0) { return - 1; } else { uint holdedCoverID = qd.getUserHoldedCoverByIndex(userAdd, holdedCoverLen.sub(1)); return int(qd.holdedCoverIDStatus(holdedCoverID)); } } /** * @dev to get the verdict of kyc process * @param status is the kyc status * @param _add is the address of member */ function kycVerdict(address _add, bool status) public checkPause noReentrancy { require(msg.sender == qd.kycAuthAddress()); _kycTrigger(status, _add); } /** * @dev transfering Ethers to newly created quotation contract. */ function transferAssetsToNewContract(address newAdd) public onlyInternal noReentrancy { uint amount = address(this).balance; IERC20 erc20; if (amount > 0) { // newAdd.transfer(amount); Quotation newQT = Quotation(newAdd); newQT.sendEther.value(amount)(); } uint currAssetLen = pd.getAllCurrenciesLen(); for (uint64 i = 1; i < currAssetLen; i++) { bytes4 currName = pd.getCurrenciesByIndex(i); address currAddr = pd.getCurrencyAssetAddress(currName); erc20 = IERC20(currAddr); // solhint-disable-line if (erc20.balanceOf(address(this)) > 0) { require(erc20.transfer(newAdd, erc20.balanceOf(address(this)))); } } } /** * @dev Creates cover of the quotation, changes the status of the quotation , * updates the total sum assured and locks the tokens of the cover against a quote. * @param from Quote member Ethereum address. */ function _makeCover(//solhint-disable-line address payable from, address scAddress, bytes4 coverCurr, uint[] memory coverDetails, uint16 coverPeriod ) internal { uint cid = qd.getCoverLength(); qd.addCover( coverPeriod, coverDetails[0], from, coverCurr, scAddress, coverDetails[1], coverDetails[2] ); uint coverNoteAmount = (coverDetails[2].mul(qd.tokensRetained())).div(100); tc.mint(from, coverNoteAmount); tf.lockCN(coverNoteAmount, coverPeriod, cid, from); qd.addInTotalSumAssured(coverCurr, coverDetails[0]); qd.addInTotalSumAssuredSC(scAddress, coverCurr, coverDetails[0]); tf.pushStakerRewards(scAddress, coverDetails[2]); } /** * @dev Makes a cover. * @param from address of funder. * @param scAddress Smart Contract Address */ function _verifyCoverDetails( address payable from, address scAddress, bytes4 coverCurr, uint[] memory coverDetails, uint16 coverPeriod, uint8 _v, bytes32 _r, bytes32 _s, bool isNXM ) internal { require(coverDetails[3] > now); require(!qd.timestampRepeated(coverDetails[4])); qd.setTimestampRepeated(coverDetails[4]); require(coverPeriod >= 30 && coverPeriod <= 365, "Quotation: Cover period out of bounds"); address asset = cr.getCurrencyAssetAddress(coverCurr); if (coverCurr != "ETH" && !isNXM) { pool.transferAssetFrom(asset, from, coverDetails[1]); } require(verifySign(coverDetails, coverPeriod, coverCurr, scAddress, _v, _r, _s)); _makeCover(from, scAddress, coverCurr, coverDetails, coverPeriod); } /** * @dev Updates the Sum Assured Amount of all the quotation. * @param _cid Cover id * @param _amount that will get subtracted Current Sum Assured * amount that comes under a quotation. */ function _removeSAFromCSA(uint _cid, uint _amount) internal checkPause { address _add; bytes4 coverCurr; (,, _add, coverCurr,,) = qd.getCoverDetailsByCoverID1(_cid); qd.subFromTotalSumAssured(coverCurr, _amount); qd.subFromTotalSumAssuredSC(_add, coverCurr, _amount); } /** * @dev to trigger the kyc process * @param status is the kyc status * @param _add is the address of member */ function _kycTrigger(bool status, address _add) internal { uint holdedCoverLen = qd.getUserHoldedCoverLength(_add).sub(1); uint holdedCoverID = qd.getUserHoldedCoverByIndex(_add, holdedCoverLen); address payable userAdd; address scAddress; bytes4 coverCurr; uint16 coverPeriod; uint[] memory coverDetails = new uint[](4); IERC20 erc20; (, userAdd, coverDetails) = qd.getHoldedCoverDetailsByID2(holdedCoverID); (, scAddress, coverCurr, coverPeriod) = qd.getHoldedCoverDetailsByID1(holdedCoverID); require(qd.refundEligible(userAdd)); qd.setRefundEligible(userAdd, false); require(qd.holdedCoverIDStatus(holdedCoverID) == uint(QuotationData.HCIDStatus.kycPending)); uint joinFee = td.joiningFee(); if (status) { mr.payJoiningFee.value(joinFee)(userAdd); if (coverDetails[3] > now) { qd.setHoldedCoverIDStatus(holdedCoverID, uint(QuotationData.HCIDStatus.kycPass)); if (coverCurr == "ETH") { (bool ok,) = address(pool).call.value(coverDetails[1])(""); require(ok, "Quotation: ether transfer to pool failed"); } else { erc20 = IERC20(pd.getCurrencyAssetAddress(coverCurr)); // solhint-disable-line require(erc20.transfer(address(pool), coverDetails[1])); } emit RefundEvent(userAdd, status, holdedCoverID, "KYC Passed"); _makeCover(userAdd, scAddress, coverCurr, coverDetails, coverPeriod); } else { qd.setHoldedCoverIDStatus(holdedCoverID, uint(QuotationData.HCIDStatus.kycPassNoCover)); if (coverCurr == "ETH") { userAdd.transfer(coverDetails[1]); } else { erc20 = IERC20(pd.getCurrencyAssetAddress(coverCurr)); // solhint-disable-line require(erc20.transfer(userAdd, coverDetails[1])); } emit RefundEvent(userAdd, status, holdedCoverID, "Cover Failed"); } } else { qd.setHoldedCoverIDStatus(holdedCoverID, uint(QuotationData.HCIDStatus.kycFailedOrRefunded)); uint totalRefund = joinFee; if (coverCurr == "ETH") { totalRefund = coverDetails[1].add(joinFee); } else { erc20 = IERC20(pd.getCurrencyAssetAddress(coverCurr)); // solhint-disable-line require(erc20.transfer(userAdd, coverDetails[1])); } userAdd.transfer(totalRefund); emit RefundEvent(userAdd, status, holdedCoverID, "KYC Failed"); } } } // File: contracts/external/uniswap/IUniswapV2Router01.sol pragma solidity ^0.5.0; interface IUniswapV2Router01 { function factory() external pure returns (address); function WETH() external pure returns (address); function addLiquidity( address tokenA, address tokenB, uint amountADesired, uint amountBDesired, uint amountAMin, uint amountBMin, address to, uint deadline ) external returns (uint amountA, uint amountB, uint liquidity); function addLiquidityETH( address token, uint amountTokenDesired, uint amountTokenMin, uint amountETHMin, address to, uint deadline ) external payable returns (uint amountToken, uint amountETH, uint liquidity); function removeLiquidity( address tokenA, address tokenB, uint liquidity, uint amountAMin, uint amountBMin, address to, uint deadline ) external returns (uint amountA, uint amountB); function removeLiquidityETH( address token, uint liquidity, uint amountTokenMin, uint amountETHMin, address to, uint deadline ) external returns (uint amountToken, uint amountETH); function removeLiquidityWithPermit( address tokenA, address tokenB, uint liquidity, uint amountAMin, uint amountBMin, address to, uint deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external returns (uint amountA, uint amountB); function removeLiquidityETHWithPermit( address token, uint liquidity, uint amountTokenMin, uint amountETHMin, address to, uint deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external returns (uint amountToken, uint amountETH); function swapExactTokensForTokens( uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline ) external returns (uint[] memory amounts); function swapTokensForExactTokens( uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline ) external returns (uint[] memory amounts); function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline) external payable returns (uint[] memory amounts); function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline) external returns (uint[] memory amounts); function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) external returns (uint[] memory amounts); function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline) external payable returns (uint[] memory amounts); function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB); function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut); function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn); function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts); function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts); } // File: contracts/external/uniswap/IUniswapV2Router02.sol pragma solidity ^0.5.0; contract IUniswapV2Router02 is IUniswapV2Router01 { function removeLiquidityETHSupportingFeeOnTransferTokens( address token, uint liquidity, uint amountTokenMin, uint amountETHMin, address to, uint deadline ) external returns (uint amountETH); function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens( address token, uint liquidity, uint amountTokenMin, uint amountETHMin, address to, uint deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external returns (uint amountETH); function swapExactTokensForTokensSupportingFeeOnTransferTokens( uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline ) external; function swapExactETHForTokensSupportingFeeOnTransferTokens( uint amountOutMin, address[] calldata path, address to, uint deadline ) external payable; function swapExactTokensForETHSupportingFeeOnTransferTokens( uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline ) external; } // File: @uniswap/lib/contracts/libraries/FixedPoint.sol pragma solidity >=0.4.0; // a library for handling binary fixed point numbers (https://en.wikipedia.org/wiki/Q_(number_format)) library FixedPoint { // range: [0, 2**112 - 1] // resolution: 1 / 2**112 struct uq112x112 { uint224 _x; } // range: [0, 2**144 - 1] // resolution: 1 / 2**112 struct uq144x112 { uint _x; } uint8 private constant RESOLUTION = 112; // encode a uint112 as a UQ112x112 function encode(uint112 x) internal pure returns (uq112x112 memory) { return uq112x112(uint224(x) << RESOLUTION); } // encodes a uint144 as a UQ144x112 function encode144(uint144 x) internal pure returns (uq144x112 memory) { return uq144x112(uint256(x) << RESOLUTION); } // divide a UQ112x112 by a uint112, returning a UQ112x112 function div(uq112x112 memory self, uint112 x) internal pure returns (uq112x112 memory) { require(x != 0, 'FixedPoint: DIV_BY_ZERO'); return uq112x112(self._x / uint224(x)); } // multiply a UQ112x112 by a uint, returning a UQ144x112 // reverts on overflow function mul(uq112x112 memory self, uint y) internal pure returns (uq144x112 memory) { uint z; require(y == 0 || (z = uint(self._x) * y) / y == uint(self._x), "FixedPoint: MULTIPLICATION_OVERFLOW"); return uq144x112(z); } // returns a UQ112x112 which represents the ratio of the numerator to the denominator // equivalent to encode(numerator).div(denominator) function fraction(uint112 numerator, uint112 denominator) internal pure returns (uq112x112 memory) { require(denominator > 0, "FixedPoint: DIV_BY_ZERO"); return uq112x112((uint224(numerator) << RESOLUTION) / denominator); } // decode a UQ112x112 into a uint112 by truncating after the radix point function decode(uq112x112 memory self) internal pure returns (uint112) { return uint112(self._x >> RESOLUTION); } // decode a UQ144x112 into a uint144 by truncating after the radix point function decode144(uq144x112 memory self) internal pure returns (uint144) { return uint144(self._x >> RESOLUTION); } } // File: @uniswap/v2-periphery/contracts/libraries/UniswapV2OracleLibrary.sol pragma solidity >=0.5.0; // library with helper methods for oracles that are concerned with computing average prices library UniswapV2OracleLibrary { using FixedPoint for *; // helper function that returns the current block timestamp within the range of uint32, i.e. [0, 2**32 - 1] function currentBlockTimestamp() internal view returns (uint32) { return uint32(block.timestamp % 2 ** 32); } // produces the cumulative price using counterfactuals to save gas and avoid a call to sync. function currentCumulativePrices( address pair ) internal view returns (uint price0Cumulative, uint price1Cumulative, uint32 blockTimestamp) { blockTimestamp = currentBlockTimestamp(); price0Cumulative = IUniswapV2Pair(pair).price0CumulativeLast(); price1Cumulative = IUniswapV2Pair(pair).price1CumulativeLast(); // if time has elapsed since the last update on the pair, mock the accumulated price values (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast) = IUniswapV2Pair(pair).getReserves(); if (blockTimestampLast != blockTimestamp) { // subtraction overflow is desired uint32 timeElapsed = blockTimestamp - blockTimestampLast; // addition overflow is desired // counterfactual price0Cumulative += uint(FixedPoint.fraction(reserve1, reserve0)._x) * timeElapsed; // counterfactual price1Cumulative += uint(FixedPoint.fraction(reserve0, reserve1)._x) * timeElapsed; } } } // File: contracts/modules/oracles/TwapOracle.sol /* Copyright (C) 2020 NexusMutual.io This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/ */ pragma solidity ^0.5.0; contract TwapOracle { using FixedPoint for *; struct Bucket { uint timestamp; uint price0Cumulative; uint price1Cumulative; } event Updated(address indexed pair, uint timestamp, uint price0Cumulative, uint price1Cumulative); uint constant public periodSize = 1800; uint constant public periodsPerWindow = 8; uint constant public windowSize = periodSize * periodsPerWindow; address public factory; // token pair => Bucket[8] mapping(address => Bucket[8]) public buckets; constructor (address _factory) public { factory = _factory; } /* utils */ // https://uniswap.org/docs/v2/smart-contract-integration/getting-pair-addresses/ function _pairFor(address _factory, address tokenA, address tokenB) internal pure returns (address pair) { // sort tokens (address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA); require(token0 != token1, "TWAP: identical addresses"); require(token0 != address(0), "TWAP: zero address"); pair = address(uint(keccak256(abi.encodePacked( hex'ff', _factory, keccak256(abi.encodePacked(token0, token1)), hex'96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f' )))); } function timestampToIndex(uint timestamp) internal pure returns (uint index) { uint epochPeriod = timestamp / periodSize; return epochPeriod % periodsPerWindow; } function pairFor(address tokenA, address tokenB) external view returns (address pair) { return _pairFor(factory, tokenA, tokenB); } function currentBucketIndex() external view returns (uint index) { return timestampToIndex(block.timestamp); } /* update */ function update(address[] calldata pairs) external { for (uint i = 0; i < pairs.length; i++) { // note: not reusing canUpdate() because we need the bucket variable address pair = pairs[i]; uint index = timestampToIndex(block.timestamp); Bucket storage bucket = buckets[pair][index]; if (block.timestamp - bucket.timestamp < periodSize) { continue; } (uint price0Cumulative, uint price1Cumulative,) = UniswapV2OracleLibrary.currentCumulativePrices(pair); bucket.timestamp = block.timestamp; bucket.price0Cumulative = price0Cumulative; bucket.price1Cumulative = price1Cumulative; emit Updated(pair, block.timestamp, price0Cumulative, price1Cumulative); } } function canUpdate(address pair) external view returns (bool) { uint index = timestampToIndex(block.timestamp); Bucket storage bucket = buckets[pair][index]; uint timeElapsed = block.timestamp - bucket.timestamp; return timeElapsed > periodSize; } /* consult */ function _getCumulativePrices( address tokenIn, address tokenOut ) internal view returns (uint priceCumulativeStart, uint priceCumulativeEnd, uint timeElapsed) { uint currentIndex = timestampToIndex(block.timestamp); uint firstBucketIndex = (currentIndex + 1) % periodsPerWindow; address pair = _pairFor(factory, tokenIn, tokenOut); Bucket storage firstBucket = buckets[pair][firstBucketIndex]; timeElapsed = block.timestamp - firstBucket.timestamp; require(timeElapsed <= windowSize, "TWAP: missing historical reading"); require(timeElapsed >= windowSize - periodSize * 2, "TWAP: unexpected time elapsed"); (uint price0Cumulative, uint price1Cumulative,) = UniswapV2OracleLibrary.currentCumulativePrices(pair); if (tokenIn < tokenOut) { return (firstBucket.price0Cumulative, price0Cumulative, timeElapsed); } return (firstBucket.price1Cumulative, price1Cumulative, timeElapsed); } function _computeAmountOut( uint priceCumulativeStart, uint priceCumulativeEnd, uint timeElapsed, uint amountIn ) internal pure returns (uint amountOut) { // overflow is desired. FixedPoint.uq112x112 memory priceAverage = FixedPoint.uq112x112( uint224((priceCumulativeEnd - priceCumulativeStart) / timeElapsed) ); return priceAverage.mul(amountIn).decode144(); } /** * @dev Returns the amount out corresponding to the amount in for a given token using the * @dev moving average over the time range [now - [windowSize, windowSize - periodSize * 2], now] * @dev update must have been called for the bucket corresponding to timestamp `now - windowSize` */ function consult(address tokenIn, uint amountIn, address tokenOut) external view returns (uint amountOut) { uint pastPriceCumulative; uint currentPriceCumulative; uint timeElapsed; (pastPriceCumulative, currentPriceCumulative, timeElapsed) = _getCumulativePrices(tokenIn, tokenOut); return _computeAmountOut( pastPriceCumulative, currentPriceCumulative, timeElapsed, amountIn ); } } // File: contracts/modules/capital/SwapAgent.sol /* Copyright (C) 2020 NexusMutual.io This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/ */ pragma solidity ^0.5.0; library SwapAgent { using SafeMath for uint; struct AssetData { uint112 minAmount; uint112 maxAmount; uint32 lastSwapTime; // 18 decimals of precision. 0.01% -> 0.0001 -> 1e14 uint maxSlippageRatio; } IUniswapV2Router02 constant public router = IUniswapV2Router02(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D); uint constant public MAX_LIQUIDITY_RATIO = 3 * 1e15; function getSwapQuote( uint tokenAmountIn, IERC20 fromToken, IERC20 toToken ) public view returns (uint tokenAmountOut) { address[] memory path = new address[](2); path[0] = address(fromToken); path[1] = address(toToken); uint[] memory amountsOut = router.getAmountsOut(tokenAmountIn, path); return amountsOut[1]; } function swapETHForAsset( address _oracle, AssetData storage assetData, address toTokenAddress, uint amountIn, uint amountOutMin, uint minLeftETH ) external returns (uint) { uint balanceBefore = IERC20(toTokenAddress).balanceOf(address(this)); address WETH = router.WETH(); { // scope for swap frequency check uint timeSinceLastTrade = block.timestamp.sub(uint(assetData.lastSwapTime)); require(timeSinceLastTrade > TwapOracle(_oracle).periodSize(), "SwapAgent: too fast"); } { // scope for liquidity check address pairAddress = TwapOracle(_oracle).pairFor(WETH, toTokenAddress); IUniswapV2Pair pair = IUniswapV2Pair(pairAddress); (uint112 reserve0, uint112 reserve1, /* time */) = pair.getReserves(); uint ethReserve = WETH < toTokenAddress ? reserve0 : reserve1; uint maxTradable = ethReserve.mul(MAX_LIQUIDITY_RATIO).div(1e18); require(amountIn <= maxTradable, "SwapAgent: exceeds max tradable amount"); } { // scope for ether checks uint ethBalanceBefore = address(this).balance; uint ethBalanceAfter = ethBalanceBefore.sub(amountIn); require(ethBalanceAfter >= minLeftETH, "SwapAgent: insufficient ether left"); } { // scope for token checks uint avgAmountOut = TwapOracle(_oracle).consult(WETH, amountIn, toTokenAddress); uint maxSlippageAmount = avgAmountOut.mul(assetData.maxSlippageRatio).div(1e18); uint minOutOnMaxSlippage = avgAmountOut.sub(maxSlippageAmount); // gas optimisation: reads both values using a single SLOAD (uint minAssetAmount, uint maxAssetAmount) = (assetData.minAmount, assetData.maxAmount); require(amountOutMin >= minOutOnMaxSlippage, "SwapAgent: amountOutMin < minOutOnMaxSlippage"); require(balanceBefore < minAssetAmount, "SwapAgent: balanceBefore >= min"); require(balanceBefore.add(amountOutMin) <= maxAssetAmount, "SwapAgent: balanceAfter > max"); } address[] memory path = new address[](2); path[0] = WETH; path[1] = toTokenAddress; router.swapExactETHForTokens.value(amountIn)(amountOutMin, path, address(this), block.timestamp); assetData.lastSwapTime = uint32(block.timestamp); uint balanceAfter = IERC20(toTokenAddress).balanceOf(address(this)); uint amountOut = balanceAfter.sub(balanceBefore); return amountOut; } function swapAssetForETH( address _oracle, AssetData storage assetData, address fromTokenAddress, uint amountIn, uint amountOutMin ) external returns (uint) { uint tokenBalanceBefore = IERC20(fromTokenAddress).balanceOf(address(this)); uint balanceBefore = address(this).balance; address WETH = router.WETH(); { // scope for swap frequency check uint timeSinceLastTrade = block.timestamp.sub(uint(assetData.lastSwapTime)); require(timeSinceLastTrade > TwapOracle(_oracle).periodSize(), "SwapAgent: too fast"); } { // scope for liquidity check address pairAddress = TwapOracle(_oracle).pairFor(fromTokenAddress, WETH); IUniswapV2Pair pair = IUniswapV2Pair(pairAddress); (uint112 reserve0, uint112 reserve1, /* time */) = pair.getReserves(); uint tokenReserve = fromTokenAddress < WETH ? reserve0 : reserve1; uint maxTradable = tokenReserve.mul(MAX_LIQUIDITY_RATIO).div(1e18); require(amountIn <= maxTradable, "SwapAgent: exceeds max tradable amount"); } { // scope for token checks uint avgAmountOut = TwapOracle(_oracle).consult(fromTokenAddress, amountIn, WETH); uint maxSlippageAmount = avgAmountOut.mul(assetData.maxSlippageRatio).div(1e18); uint minOutOnMaxSlippage = avgAmountOut.sub(maxSlippageAmount); // gas optimisation: reads both values using a single SLOAD (uint minAssetAmount, uint maxAssetAmount) = (assetData.minAmount, assetData.maxAmount); require(amountOutMin >= minOutOnMaxSlippage, "SwapAgent: amountOutMin < minOutOnMaxSlippage"); require(tokenBalanceBefore > maxAssetAmount, "SwapAgent: tokenBalanceBefore <= max"); require(tokenBalanceBefore.sub(amountIn) >= minAssetAmount, "SwapAgent: tokenBalanceAfter < min"); } address[] memory path = new address[](2); path[0] = fromTokenAddress; path[1] = router.WETH(); IERC20(fromTokenAddress).approve(address(router), amountIn); router.swapExactTokensForETH(amountIn, amountOutMin, path, address(this), block.timestamp); assetData.lastSwapTime = uint32(block.timestamp); uint balanceAfter = address(this).balance; uint amountOut = balanceAfter.sub(balanceBefore); return amountOut; } } // File: contracts/modules/capital/Pool.sol /* Copyright (C) 2020 NexusMutual.io This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/ */ pragma solidity ^0.5.0; contract Pool is MasterAware, ReentrancyGuard { using Address for address; using SafeMath for uint; using SafeERC20 for IERC20; /* storage */ address[] public assets; mapping(address => SwapAgent.AssetData) public assetData; // contracts Quotation public quotation; NXMToken public nxmToken; TokenController public tokenController; MCR public mcr; // parameters address public twapOracle; address public swapController; uint public minPoolEth; PriceFeedOracle public priceFeedOracle; /* constants */ address constant public ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; uint public constant MCR_RATIO_DECIMALS = 4; uint public constant MAX_MCR_RATIO = 40000; // 400% uint public constant MAX_BUY_SELL_MCR_ETH_FRACTION = 500; // 5%. 4 decimal points uint internal constant CONSTANT_C = 5800000; uint internal constant CONSTANT_A = 1028 * 1e13; uint internal constant TOKEN_EXPONENT = 4; /* events */ event Payout(address indexed to, address indexed asset, uint amount); event NXMSold (address indexed member, uint nxmIn, uint ethOut); event NXMBought (address indexed member, uint ethIn, uint nxmOut); event Swapped(address indexed fromAsset, address indexed toAsset, uint amountIn, uint amountOut); /* logic */ modifier onlySwapController { require(msg.sender == swapController, "Pool: not swapController"); _; } constructor ( address[] memory _assets, uint112[] memory _minAmounts, uint112[] memory _maxAmounts, uint[] memory _maxSlippageRatios, address _master, address _priceOracle, address _twapOracle, address _swapController ) public { require(_assets.length == _minAmounts.length, "Pool: length mismatch"); require(_assets.length == _maxAmounts.length, "Pool: length mismatch"); require(_assets.length == _maxSlippageRatios.length, "Pool: length mismatch"); for (uint i = 0; i < _assets.length; i++) { address asset = _assets[i]; require(asset != address(0), "Pool: asset is zero address"); require(_maxAmounts[i] >= _minAmounts[i], "Pool: max < min"); require(_maxSlippageRatios[i] <= 1 ether, "Pool: max < min"); assets.push(asset); assetData[asset].minAmount = _minAmounts[i]; assetData[asset].maxAmount = _maxAmounts[i]; assetData[asset].maxSlippageRatio = _maxSlippageRatios[i]; } master = INXMMaster(_master); priceFeedOracle = PriceFeedOracle(_priceOracle); twapOracle = _twapOracle; swapController = _swapController; } // fallback function function() external payable {} // for legacy Pool1 upgrade compatibility function sendEther() external payable {} /** * @dev Calculates total value of all pool assets in ether */ function getPoolValueInEth() public view returns (uint) { uint total = address(this).balance; for (uint i = 0; i < assets.length; i++) { address assetAddress = assets[i]; IERC20 token = IERC20(assetAddress); uint rate = priceFeedOracle.getAssetToEthRate(assetAddress); require(rate > 0, "Pool: zero rate"); uint assetBalance = token.balanceOf(address(this)); uint assetValue = assetBalance.mul(rate).div(1e18); total = total.add(assetValue); } return total; } /* asset related functions */ function getAssets() external view returns (address[] memory) { return assets; } function getAssetDetails(address _asset) external view returns ( uint balance, uint112 min, uint112 max, uint32 lastAssetSwapTime, uint maxSlippageRatio ) { IERC20 token = IERC20(_asset); balance = token.balanceOf(address(this)); SwapAgent.AssetData memory data = assetData[_asset]; return (balance, data.minAmount, data.maxAmount, data.lastSwapTime, data.maxSlippageRatio); } function addAsset( address _asset, uint112 _min, uint112 _max, uint _maxSlippageRatio ) external onlyGovernance { require(_asset != address(0), "Pool: asset is zero address"); require(_max >= _min, "Pool: max < min"); require(_maxSlippageRatio <= 1 ether, "Pool: max slippage ratio > 1"); for (uint i = 0; i < assets.length; i++) { require(_asset != assets[i], "Pool: asset exists"); } assets.push(_asset); assetData[_asset] = SwapAgent.AssetData(_min, _max, 0, _maxSlippageRatio); } function removeAsset(address _asset) external onlyGovernance { for (uint i = 0; i < assets.length; i++) { if (_asset != assets[i]) { continue; } delete assetData[_asset]; assets[i] = assets[assets.length - 1]; assets.pop(); return; } revert("Pool: asset not found"); } function setAssetDetails( address _asset, uint112 _min, uint112 _max, uint _maxSlippageRatio ) external onlyGovernance { require(_min <= _max, "Pool: min > max"); require(_maxSlippageRatio <= 1 ether, "Pool: max slippage ratio > 1"); for (uint i = 0; i < assets.length; i++) { if (_asset != assets[i]) { continue; } assetData[_asset].minAmount = _min; assetData[_asset].maxAmount = _max; assetData[_asset].maxSlippageRatio = _maxSlippageRatio; return; } revert("Pool: asset not found"); } /* swap functions */ function getSwapQuote( uint tokenAmountIn, IERC20 fromToken, IERC20 toToken ) public view returns (uint tokenAmountOut) { return SwapAgent.getSwapQuote( tokenAmountIn, fromToken, toToken ); } function swapETHForAsset( address toTokenAddress, uint amountIn, uint amountOutMin ) external whenNotPaused onlySwapController nonReentrant { SwapAgent.AssetData storage assetDetails = assetData[toTokenAddress]; uint amountOut = SwapAgent.swapETHForAsset( twapOracle, assetDetails, toTokenAddress, amountIn, amountOutMin, minPoolEth ); emit Swapped(ETH, toTokenAddress, amountIn, amountOut); } function swapAssetForETH( address fromTokenAddress, uint amountIn, uint amountOutMin ) external whenNotPaused onlySwapController nonReentrant { uint amountOut = SwapAgent.swapAssetForETH( twapOracle, assetData[fromTokenAddress], fromTokenAddress, amountIn, amountOutMin ); emit Swapped(fromTokenAddress, ETH, amountIn, amountOut); } /* claim related functions */ /** * @dev Execute the payout in case a claim is accepted * @param asset token address or 0xEee...EEeE for ether * @param payoutAddress send funds to this address * @param amount amount to send */ function sendClaimPayout ( address asset, address payable payoutAddress, uint amount ) external onlyInternal nonReentrant returns (bool success) { bool ok; if (asset == ETH) { // solhint-disable-next-line avoid-low-level-calls (ok, /* data */) = payoutAddress.call.value(amount)(""); } else { ok = _safeTokenTransfer(asset, payoutAddress, amount); } if (ok) { emit Payout(payoutAddress, asset, amount); } return ok; } /** * @dev safeTransfer implementation that does not revert * @param tokenAddress ERC20 address * @param to destination * @param value amount to send * @return success true if the transfer was successfull */ function _safeTokenTransfer ( address tokenAddress, address to, uint256 value ) internal returns (bool) { // token address is not a contract if (!tokenAddress.isContract()) { return false; } IERC20 token = IERC20(tokenAddress); bytes memory data = abi.encodeWithSelector(token.transfer.selector, to, value); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = tokenAddress.call(data); // low-level call failed/reverted if (!success) { return false; } // tokens that don't have return data if (returndata.length == 0) { return true; } // tokens that have return data will return a bool return abi.decode(returndata, (bool)); } /* pool lifecycle functions */ function transferAsset( address asset, address payable destination, uint amount ) external onlyGovernance nonReentrant { require(assetData[asset].maxAmount == 0, "Pool: max not zero"); require(destination != address(0), "Pool: dest zero"); IERC20 token = IERC20(asset); uint balance = token.balanceOf(address(this)); uint transferableAmount = amount > balance ? balance : amount; token.safeTransfer(destination, transferableAmount); } function upgradeCapitalPool(address payable newPoolAddress) external onlyMaster nonReentrant { // transfer ether uint ethBalance = address(this).balance; (bool ok, /* data */) = newPoolAddress.call.value(ethBalance)(""); require(ok, "Pool: transfer failed"); // transfer assets for (uint i = 0; i < assets.length; i++) { IERC20 token = IERC20(assets[i]); uint tokenBalance = token.balanceOf(address(this)); token.safeTransfer(newPoolAddress, tokenBalance); } } /** * @dev Update dependent contract address * @dev Implements MasterAware interface function */ function changeDependentContractAddress() public { nxmToken = NXMToken(master.tokenAddress()); tokenController = TokenController(master.getLatestAddress("TC")); quotation = Quotation(master.getLatestAddress("QT")); mcr = MCR(master.getLatestAddress("MC")); } /* cover purchase functions */ /// @dev Enables user to purchase cover with funding in ETH. /// @param smartCAdd Smart Contract Address function makeCoverBegin( address smartCAdd, bytes4 coverCurr, uint[] memory coverDetails, uint16 coverPeriod, uint8 _v, bytes32 _r, bytes32 _s ) public payable onlyMember whenNotPaused { require(coverCurr == "ETH", "Pool: Unexpected asset type"); require(msg.value == coverDetails[1], "Pool: ETH amount does not match premium"); quotation.verifyCoverDetails(msg.sender, smartCAdd, coverCurr, coverDetails, coverPeriod, _v, _r, _s); } /** * @dev Enables user to purchase cover via currency asset eg DAI */ function makeCoverUsingCA( address smartCAdd, bytes4 coverCurr, uint[] memory coverDetails, uint16 coverPeriod, uint8 _v, bytes32 _r, bytes32 _s ) public onlyMember whenNotPaused { require(coverCurr != "ETH", "Pool: Unexpected asset type"); quotation.verifyCoverDetails(msg.sender, smartCAdd, coverCurr, coverDetails, coverPeriod, _v, _r, _s); } function transferAssetFrom (address asset, address from, uint amount) public onlyInternal whenNotPaused { IERC20 token = IERC20(asset); token.safeTransferFrom(from, address(this), amount); } /* token sale functions */ /** * @dev (DEPRECATED, use sellTokens function instead) Allows selling of NXM for ether. * Seller first needs to give this contract allowance to * transfer/burn tokens in the NXMToken contract * @param _amount Amount of NXM to sell * @return success returns true on successfull sale */ function sellNXMTokens(uint _amount) public onlyMember whenNotPaused returns (bool success) { sellNXM(_amount, 0); return true; } /** * @dev (DEPRECATED, use calculateNXMForEth function instead) Returns the amount of wei a seller will get for selling NXM * @param amount Amount of NXM to sell * @return weiToPay Amount of wei the seller will get */ function getWei(uint amount) external view returns (uint weiToPay) { return getEthForNXM(amount); } /** * @dev Buys NXM tokens with ETH. * @param minTokensOut Minimum amount of tokens to be bought. Revert if boughtTokens falls below this number. * @return boughtTokens number of bought tokens. */ function buyNXM(uint minTokensOut) public payable onlyMember whenNotPaused { uint ethIn = msg.value; require(ethIn > 0, "Pool: ethIn > 0"); uint totalAssetValue = getPoolValueInEth().sub(ethIn); uint mcrEth = mcr.getLastMCREther(); uint mcrRatio = calculateMCRRatio(totalAssetValue, mcrEth); require(mcrRatio <= MAX_MCR_RATIO, "Pool: Cannot purchase if MCR% > 400%"); uint tokensOut = calculateNXMForEth(ethIn, totalAssetValue, mcrEth); require(tokensOut >= minTokensOut, "Pool: tokensOut is less than minTokensOut"); tokenController.mint(msg.sender, tokensOut); emit NXMBought(msg.sender, ethIn, tokensOut); } /** * @dev Sell NXM tokens and receive ETH. * @param tokenAmount Amount of tokens to sell. * @param minEthOut Minimum amount of ETH to be received. Revert if ethOut falls below this number. * @return ethOut amount of ETH received in exchange for the tokens. */ function sellNXM(uint tokenAmount, uint minEthOut) public onlyMember nonReentrant whenNotPaused { require(nxmToken.balanceOf(msg.sender) >= tokenAmount, "Pool: Not enough balance"); require(nxmToken.isLockedForMV(msg.sender) <= now, "Pool: NXM tokens are locked for voting"); uint currentTotalAssetValue = getPoolValueInEth(); uint mcrEth = mcr.getLastMCREther(); uint ethOut = calculateEthForNXM(tokenAmount, currentTotalAssetValue, mcrEth); require(currentTotalAssetValue.sub(ethOut) >= mcrEth, "Pool: MCR% cannot fall below 100%"); require(ethOut >= minEthOut, "Pool: ethOut < minEthOut"); tokenController.burnFrom(msg.sender, tokenAmount); (bool ok, /* data */) = msg.sender.call.value(ethOut)(""); require(ok, "Pool: Sell transfer failed"); emit NXMSold(msg.sender, tokenAmount, ethOut); } /** * @dev Get value in tokens for an ethAmount purchase. * @param ethAmount amount of ETH used for buying. * @return tokenValue tokens obtained by buying worth of ethAmount */ function getNXMForEth( uint ethAmount ) public view returns (uint) { uint totalAssetValue = getPoolValueInEth(); uint mcrEth = mcr.getLastMCREther(); return calculateNXMForEth(ethAmount, totalAssetValue, mcrEth); } function calculateNXMForEth( uint ethAmount, uint currentTotalAssetValue, uint mcrEth ) public pure returns (uint) { require( ethAmount <= mcrEth.mul(MAX_BUY_SELL_MCR_ETH_FRACTION).div(10 ** MCR_RATIO_DECIMALS), "Pool: Purchases worth higher than 5% of MCReth are not allowed" ); /* The price formula is: P(V) = A + MCReth / C * MCR% ^ 4 where MCR% = V / MCReth P(V) = A + 1 / (C * MCReth ^ 3) * V ^ 4 To compute the number of tokens issued we can integrate with respect to V the following: ΔT = ΔV / P(V) which assumes that for an infinitesimally small change in locked value V price is constant and we get an infinitesimally change in token supply ΔT. This is not computable on-chain, below we use an approximation that works well assuming * MCR% stays within [100%, 400%] * ethAmount <= 5% * MCReth Use a simplified formula excluding the constant A price offset to compute the amount of tokens to be minted. AdjustedP(V) = 1 / (C * MCReth ^ 3) * V ^ 4 AdjustedP(V) = 1 / (C * MCReth ^ 3) * V ^ 4 For a very small variation in tokens ΔT, we have, ΔT = ΔV / P(V), to get total T we integrate with respect to V. adjustedTokenAmount = ∫ (dV / AdjustedP(V)) from V0 (currentTotalAssetValue) to V1 (nextTotalAssetValue) adjustedTokenAmount = ∫ ((C * MCReth ^ 3) / V ^ 4 * dV) from V0 to V1 Evaluating the above using the antiderivative of the function we get: adjustedTokenAmount = - MCReth ^ 3 * C / (3 * V1 ^3) + MCReth * C /(3 * V0 ^ 3) */ if (currentTotalAssetValue == 0 || mcrEth.div(currentTotalAssetValue) > 1e12) { /* If the currentTotalAssetValue = 0, adjustedTokenPrice approaches 0. Therefore we can assume the price is A. If currentTotalAssetValue is far smaller than mcrEth, MCR% approaches 0, let the price be A (baseline price). This avoids overflow in the calculateIntegralAtPoint computation. This approximation is safe from arbitrage since at MCR% < 100% no sells are possible. */ uint tokenPrice = CONSTANT_A; return ethAmount.mul(1e18).div(tokenPrice); } // MCReth * C /(3 * V0 ^ 3) uint point0 = calculateIntegralAtPoint(currentTotalAssetValue, mcrEth); // MCReth * C / (3 * V1 ^3) uint nextTotalAssetValue = currentTotalAssetValue.add(ethAmount); uint point1 = calculateIntegralAtPoint(nextTotalAssetValue, mcrEth); uint adjustedTokenAmount = point0.sub(point1); /* Compute a preliminary adjustedTokenPrice for the minted tokens based on the adjustedTokenAmount above, and to that add the A constant (the price offset previously removed in the adjusted Price formula) to obtain the finalPrice and ultimately the tokenValue based on the finalPrice. adjustedPrice = ethAmount / adjustedTokenAmount finalPrice = adjustedPrice + A tokenValue = ethAmount / finalPrice */ // ethAmount is multiplied by 1e18 to cancel out the multiplication factor of 1e18 of the adjustedTokenAmount uint adjustedTokenPrice = ethAmount.mul(1e18).div(adjustedTokenAmount); uint tokenPrice = adjustedTokenPrice.add(CONSTANT_A); return ethAmount.mul(1e18).div(tokenPrice); } /** * @dev integral(V) = MCReth ^ 3 * C / (3 * V ^ 3) * 1e18 * computation result is multiplied by 1e18 to allow for a precision of 18 decimals. * NOTE: omits the minus sign of the correct integral to use a uint result type for simplicity * WARNING: this low-level function should be called from a contract which checks that * mcrEth / assetValue < 1e17 (no overflow) and assetValue != 0 */ function calculateIntegralAtPoint( uint assetValue, uint mcrEth ) internal pure returns (uint) { return CONSTANT_C .mul(1e18) .div(3) .mul(mcrEth).div(assetValue) .mul(mcrEth).div(assetValue) .mul(mcrEth).div(assetValue); } function getEthForNXM(uint nxmAmount) public view returns (uint ethAmount) { uint currentTotalAssetValue = getPoolValueInEth(); uint mcrEth = mcr.getLastMCREther(); return calculateEthForNXM(nxmAmount, currentTotalAssetValue, mcrEth); } /** * @dev Computes token sell value for a tokenAmount in ETH with a sell spread of 2.5%. * for values in ETH of the sale <= 1% * MCReth the sell spread is very close to the exact value of 2.5%. * for values higher than that sell spread may exceed 2.5% * (The higher amount being sold at any given time the higher the spread) */ function calculateEthForNXM( uint nxmAmount, uint currentTotalAssetValue, uint mcrEth ) public pure returns (uint) { // Step 1. Calculate spot price at current values and amount of ETH if tokens are sold at that price uint spotPrice0 = calculateTokenSpotPrice(currentTotalAssetValue, mcrEth); uint spotEthAmount = nxmAmount.mul(spotPrice0).div(1e18); // Step 2. Calculate spot price using V = currentTotalAssetValue - spotEthAmount from step 1 uint totalValuePostSpotPriceSell = currentTotalAssetValue.sub(spotEthAmount); uint spotPrice1 = calculateTokenSpotPrice(totalValuePostSpotPriceSell, mcrEth); // Step 3. Min [average[Price(0), Price(1)] x ( 1 - Sell Spread), Price(1) ] // Sell Spread = 2.5% uint averagePriceWithSpread = spotPrice0.add(spotPrice1).div(2).mul(975).div(1000); uint finalPrice = averagePriceWithSpread < spotPrice1 ? averagePriceWithSpread : spotPrice1; uint ethAmount = finalPrice.mul(nxmAmount).div(1e18); require( ethAmount <= mcrEth.mul(MAX_BUY_SELL_MCR_ETH_FRACTION).div(10 ** MCR_RATIO_DECIMALS), "Pool: Sales worth more than 5% of MCReth are not allowed" ); return ethAmount; } function calculateMCRRatio(uint totalAssetValue, uint mcrEth) public pure returns (uint) { return totalAssetValue.mul(10 ** MCR_RATIO_DECIMALS).div(mcrEth); } /** * @dev Calculates token price in ETH 1 NXM token. TokenPrice = A + (MCReth / C) * MCR%^4 */ function calculateTokenSpotPrice(uint totalAssetValue, uint mcrEth) public pure returns (uint tokenPrice) { uint mcrRatio = calculateMCRRatio(totalAssetValue, mcrEth); uint precisionDecimals = 10 ** TOKEN_EXPONENT.mul(MCR_RATIO_DECIMALS); return mcrEth .mul(mcrRatio ** TOKEN_EXPONENT) .div(CONSTANT_C) .div(precisionDecimals) .add(CONSTANT_A); } /** * @dev Returns the NXM price in a given asset * @param asset Asset name. */ function getTokenPrice(address asset) public view returns (uint tokenPrice) { uint totalAssetValue = getPoolValueInEth(); uint mcrEth = mcr.getLastMCREther(); uint tokenSpotPriceEth = calculateTokenSpotPrice(totalAssetValue, mcrEth); return priceFeedOracle.getAssetForEth(asset, tokenSpotPriceEth); } function getMCRRatio() public view returns (uint) { uint totalAssetValue = getPoolValueInEth(); uint mcrEth = mcr.getLastMCREther(); return calculateMCRRatio(totalAssetValue, mcrEth); } function updateUintParameters(bytes8 code, uint value) external onlyGovernance { if (code == "MIN_ETH") { minPoolEth = value; return; } revert("Pool: unknown parameter"); } function updateAddressParameters(bytes8 code, address value) external onlyGovernance { if (code == "TWAP") { twapOracle = value; return; } if (code == "SWAP") { swapController = value; return; } if (code == "PRC_FEED") { priceFeedOracle = PriceFeedOracle(value); return; } revert("Pool: unknown parameter"); } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"masterAddress","type":"address"},{"internalType":"address","name":"_daiAddress","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"constant":true,"inputs":[],"name":"DAI","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"ETH","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"_records","type":"uint256"},{"internalType":"address","name":"_user","type":"address"}],"name":"_claimStakeCommission","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"claimid","type":"uint256"}],"name":"changeClaimStatus","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"changeDependentContractAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_masterAddress","type":"address"}],"name":"changeMasterAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"records","type":"uint256"}],"name":"claimAllPendingReward","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"_add","type":"address"}],"name":"getAllPendingRewardOfUser","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes4","name":"currency","type":"bytes4"}],"name":"getCurrencyAssetAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"check","type":"uint256"},{"internalType":"uint256","name":"claimId","type":"uint256"}],"name":"getRewardAndClaimedStatus","outputs":[{"internalType":"uint256","name":"reward","type":"uint256"},{"internalType":"bool","name":"claimed","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"_add","type":"address"}],"name":"getRewardToBeDistributedByUser","outputs":[{"internalType":"uint256","name":"total","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"check","type":"uint256"},{"internalType":"uint256","name":"voteid","type":"uint256"},{"internalType":"uint256","name":"flag","type":"uint256"}],"name":"getRewardToBeGiven","outputs":[{"internalType":"uint256","name":"tokenCalculated","type":"uint256"},{"internalType":"bool","name":"lastClaimedCheck","type":"bool"},{"internalType":"uint256","name":"tokens","type":"uint256"},{"internalType":"uint256","name":"perc","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"ms","outputs":[{"internalType":"contract INXMMaster","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"nxMasterAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_newAdd","type":"address"}],"name":"upgrade","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
60806040523480156200001157600080fd5b5060405162004a3038038062004a30833981810160405260408110156200003757600080fd5b50805160209091015162000054826001600160e01b036200007b16565b600d80546001600160a01b0319166001600160a01b03929092169190911790555062000106565b6000546001600160a01b031615620000da576000546001600160a01b03163314620000da576040805162461bcd60e51b815260206004820152600a6024820152692737ba1036b0b9ba32b960b11b604482015290519081900360640190fd5b600080546001600160a01b039092166001600160a01b0319928316811790915560018054909216179055565b61491a80620001166000396000f3fe608060405234801561001057600080fd5b50600436106100f55760003560e01c80637f6715c911610097578063df31f20811610066578063df31f2081461027c578063e0bab4c4146102cb578063e3dee239146102d3578063f17a3bec146102fa576100f5565b80637f6715c91461020d5780638322fff21461022a578063a0b2d57f1461024e578063d46655f414610256576100f5565b806335afb14e116100d357806335afb14e1461015657806341becc071461018e578063452efce9146101ca57806363bb23be146101f0576100f5565b806304ee27c6146100fa5780630900f010146101285780630ea9c9841461014e575b600080fd5b6101266004803603604081101561011057600080fd5b50803590602001356001600160a01b0316610302565b005b6101266004803603602081101561013e57600080fd5b50356001600160a01b0316610942565b610126610ad0565b61017c6004803603602081101561016c57600080fd5b50356001600160a01b0316611178565b60408051918252519081900360200190f35b6101b1600480360360408110156101a457600080fd5b50803590602001356112af565b6040805192835290151560208301528051918290030190f35b61017c600480360360208110156101e057600080fd5b50356001600160a01b0316611625565b6101266004803603602081101561020657600080fd5b5035611929565b6101266004803603602081101561022357600080fd5b5035611b7a565b610232611f14565b604080516001600160a01b039092168252519081900360200190f35b610232611f2c565b6101266004803603602081101561026c57600080fd5b50356001600160a01b0316611f3b565b6102a56004803603606081101561029257600080fd5b5080359060208101359060400135611fc4565b604080519485529215156020850152838301919091526060830152519081900360800190f35b6102326124ad565b610232600480360360208110156102e957600080fd5b50356001600160e01b0319166124bc565b610232612568565b600054604080516323c5b10760e21b815233600482015290516001600160a01b0390921691638f16c41c91602480820192602092909190829003018186803b15801561034d57600080fd5b505afa158015610361573d6000803e3d6000fd5b505050506040513d602081101561037757600080fd5b505161038257600080fd5b600554604080516338c97bdd60e21b81526001600160a01b03848116600483015291516000938493169163e325ef74916024808301926020929190829003018186803b1580156103d157600080fd5b505afa1580156103e5573d6000803e3d6000fd5b505050506040513d60208110156103fb57600080fd5b505160055460408051631bb5ffcb60e31b81526001600160a01b0387811660048301529151939450600093919092169163ddaffe58916024808301926020929190829003018186803b15801561045057600080fd5b505afa158015610464573d6000803e3d6000fd5b505050506040513d602081101561047a57600080fd5b50519050600080808481855b878110801561049457508a82105b156107bc5760055460408051637b89c55760e11b81526001600160a01b038d81166004830152602482018590529151919092169163f7138aae916044808301926020929190829003018186803b1580156104ed57600080fd5b505afa158015610501573d6000803e3d6000fd5b505050506040513d602081101561051757600080fd5b50516005546040805163057844c560e31b81526001600160a01b038e81166004830152602482018690529151939850911691632bc2262891604480820192602092909190829003018186803b15801561056f57600080fd5b505afa158015610583573d6000803e3d6000fd5b505050506040513d602081101561059957600080fd5b505160055460408051636a96445160e01b815290519298506106e0926064926106d4926001600160a01b0390911691636a96445191600480820192602092909190829003018186803b1580156105ee57600080fd5b505afa158015610602573d6000803e3d6000fd5b505050506040513d602081101561061857600080fd5b8101908080519060200190929190505050600560009054906101000a90046001600160a01b03166001600160a01b03166343148d238f876040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b031681526020018281526020019250505060206040518083038186803b15801561069c57600080fd5b505afa1580156106b0573d6000803e3d6000fd5b505050506040513d60208110156106c657600080fd5b50519063ffffffff61257716565b9063ffffffff6125d916565b935087831480156106f15750858414155b156106fa578092505b6005546001600160a01b031663d9249bff8b8361071d8a8a63ffffffff61261b16565b6040518463ffffffff1660e01b815260040180846001600160a01b03166001600160a01b031681526020018381526020018281526020019350505050600060405180830381600087803b15801561077357600080fd5b505af1158015610787573d6000803e3d6000fd5b505050506107ae6107a1868861261b90919063ffffffff16565b8a9063ffffffff61265d16565b985060019182019101610486565b878314156108365760055460408051631819081760e01b81526001600160a01b038d811660048301526024820185905291519190921691631819081791604480830192600092919082900301818387803b15801561081957600080fd5b505af115801561082d573d6000803e3d6000fd5b505050506108a4565b60055460408051631819081760e01b81526001600160a01b038d811660048301526024820187905291519190921691631819081791604480830192600092919082900301818387803b15801561088b57600080fd5b505af115801561089f573d6000803e3d6000fd5b505050505b8815610935576002546040805163a9059cbb60e01b81526001600160a01b038d81166004830152602482018d90529151919092169163a9059cbb9160448083019260209291908290030181600087803b15801561090057600080fd5b505af1158015610914573d6000803e3d6000fd5b505050506040513d602081101561092a57600080fd5b505161093557600080fd5b5050505050505050505050565b600054604080516323c5b10760e21b815233600482015290516001600160a01b0390921691638f16c41c91602480820192602092909190829003018186803b15801561098d57600080fd5b505afa1580156109a1573d6000803e3d6000fd5b505050506040513d60208110156109b757600080fd5b50516109c257600080fd5b600254604080516370a0823160e01b815230600482015290516000926001600160a01b0316916370a08231916024808301926020929190829003018186803b158015610a0d57600080fd5b505afa158015610a21573d6000803e3d6000fd5b505050506040513d6020811015610a3757600080fd5b505190508015610acc576002546040805163a9059cbb60e01b81526001600160a01b038581166004830152602482018590529151919092169163a9059cbb9160448083019260209291908290030181600087803b158015610a9757600080fd5b505af1158015610aab573d6000803e3d6000fd5b505050506040513d6020811015610ac157600080fd5b5051610acc57600080fd5b5050565b600054604080516323c5b10760e21b815233600482015290516001600160a01b0390921691638f16c41c91602480820192602092909190829003018186803b158015610b1b57600080fd5b505afa158015610b2f573d6000803e3d6000fd5b505050506040513d6020811015610b4557600080fd5b5051610b5057600080fd5b600054604080516227050b60e31b81526110d360f21b600482015290516001600160a01b0390921691630138285891602480820192602092909190829003018186803b158015610b9f57600080fd5b505afa158015610bb3573d6000803e3d6000fd5b505050506040513d6020811015610bc957600080fd5b5051600780546001600160a01b0319166001600160a01b03928316179055600054604080516227050b60e31b81526110d160f21b6004820152905191909216916301382858916024808301926020929190829003018186803b158015610c2e57600080fd5b505afa158015610c42573d6000803e3d6000fd5b505050506040513d6020811015610c5857600080fd5b5051600880546001600160a01b0319166001600160a01b03928316179055600054604080516313aedd4b60e31b815290519190921691639d76ea58916004808301926020929190829003018186803b158015610cb357600080fd5b505afa158015610cc7573d6000803e3d6000fd5b505050506040513d6020811015610cdd57600080fd5b5051600280546001600160a01b0319166001600160a01b03928316179055600054604080516227050b60e31b815261544360f01b6004820152905191909216916301382858916024808301926020929190829003018186803b158015610d4257600080fd5b505afa158015610d56573d6000803e3d6000fd5b505050506040513d6020811015610d6c57600080fd5b5051600380546001600160a01b0319166001600160a01b03928316179055600054604080516227050b60e31b815261151160f21b6004820152905191909216916301382858916024808301926020929190829003018186803b158015610dd157600080fd5b505afa158015610de5573d6000803e3d6000fd5b505050506040513d6020811015610dfb57600080fd5b5051600580546001600160a01b0319166001600160a01b03928316179055600054604080516227050b60e31b8152612a2360f11b6004820152905191909216916301382858916024808301926020929190829003018186803b158015610e6057600080fd5b505afa158015610e74573d6000803e3d6000fd5b505050506040513d6020811015610e8a57600080fd5b5051600480546001600160a01b0319166001600160a01b03928316178155600054604080516227050b60e31b815261145160f21b9381019390935251921691630138285891602480820192602092909190829003018186803b158015610eef57600080fd5b505afa158015610f03573d6000803e3d6000fd5b505050506040513d6020811015610f1957600080fd5b5051600680546001600160a01b0319166001600160a01b03928316179055600054604080516227050b60e31b81526123ab60f11b6004820152905191909216916301382858916024808301926020929190829003018186803b158015610f7e57600080fd5b505afa158015610f92573d6000803e3d6000fd5b505050506040513d6020811015610fa857600080fd5b5051600a80546001600160a01b0319166001600160a01b03928316179055600054604080516227050b60e31b815261505360f01b6004820152905191909216916301382858916024808301926020929190829003018186803b15801561100d57600080fd5b505afa158015611021573d6000803e3d6000fd5b505050506040513d602081101561103757600080fd5b5051600b80546001600160a01b0319166001600160a01b03928316179055600054604080516227050b60e31b81526126a960f11b6004820152905191909216916301382858916024808301926020929190829003018186803b15801561109c57600080fd5b505afa1580156110b0573d6000803e3d6000fd5b505050506040513d60208110156110c657600080fd5b5051600c80546001600160a01b0319166001600160a01b03928316179055600054604080516227050b60e31b815261503160f01b6004820152905191909216916301382858916024808301926020929190829003018186803b15801561112b57600080fd5b505afa15801561113f573d6000803e3d6000fd5b505050506040513d602081101561115557600080fd5b5051600980546001600160a01b0319166001600160a01b03909216919091179055565b60008061118483611625565b600b5460408051630ea474c760e01b81526001600160a01b03878116600483015291519394506000939190921691630ea474c7916024808301926020929190829003018186803b1580156111d757600080fd5b505afa1580156111eb573d6000803e3d6000fd5b505050506040513d602081101561120157600080fd5b5051600a54604080516326fceb5d60e11b81526001600160a01b03888116600483015291519394506000939190921691634df9d6ba916024808301926020929190829003018186803b15801561125657600080fd5b505afa15801561126a573d6000803e3d6000fd5b505050506040513d602081101561128057600080fd5b505190506112a481611298858563ffffffff61265d16565b9063ffffffff61265d16565b93505050505b919050565b600080600080600086600114156114665760085460408051630d3c678b60e21b815233600482015290516001600160a01b03909216916334f19e2c91602480820192602092909190829003018186803b15801561130b57600080fd5b505afa15801561131f573d6000803e3d6000fd5b505050506040513d602081101561133557600080fd5b5051905060005b81811015611460576008546040805163fc57c9bb60e01b81523360048201526024810184905290516001600160a01b039092169163fc57c9bb91604480820192602092909190829003018186803b15801561139657600080fd5b505afa1580156113aa573d6000803e3d6000fd5b505050506040513d60208110156113c057600080fd5b5051600854604080516354a1b43160e01b81526004810184905290519296506001600160a01b03909116916354a1b43191602480820192608092909190829003018186803b15801561141157600080fd5b505afa158015611425573d6000803e3d6000fd5b505050506040513d608081101561143b57600080fd5b506020810151606090910151955092508683141561145857611460565b60010161133c565b50611608565b6008546040805163274d865f60e01b815233600482015290516001600160a01b039092169163274d865f91602480820192602092909190829003018186803b1580156114b157600080fd5b505afa1580156114c5573d6000803e3d6000fd5b505050506040513d60208110156114db57600080fd5b5051905060005b818110156116065760085460408051637968b71560e11b81523360048201526024810184905290516001600160a01b039092169163f2d16e2a91604480820192602092909190829003018186803b15801561153c57600080fd5b505afa158015611550573d6000803e3d6000fd5b505050506040513d602081101561156657600080fd5b5051600854604080516354a1b43160e01b81526004810184905290519296506001600160a01b03909116916354a1b43191602480820192608092909190829003018186803b1580156115b757600080fd5b505afa1580156115cb573d6000803e3d6000fd5b505050506040513d60808110156115e157600080fd5b50602081015160609091015195509250868314156115fe57611606565b6001016114e2565b505b61161487846001611fc4565b509199959850949650505050505050565b60085460408051630d3c678b60e21b81526001600160a01b0384811660048301529151600093849316916334f19e2c916024808301926020929190829003018186803b15801561167457600080fd5b505afa158015611688573d6000803e3d6000fd5b505050506040513d602081101561169e57600080fd5b5051600854604080516304ecffeb60e01b81526001600160a01b038781166004830152825194955060009485948594859493909116926304ecffeb9260248083019392829003018186803b1580156116f557600080fd5b505afa158015611709573d6000803e3d6000fd5b505050506040513d604081101561171f57600080fd5b5080516020909101519094509250835b858110156117e8576008546040805163fc57c9bb60e01b81526001600160a01b038b81166004830152602482018590529151919092169163fc57c9bb916044808301926020929190829003018186803b15801561178b57600080fd5b505afa15801561179f573d6000803e3d6000fd5b505050506040513d60208110156117b557600080fd5b505191506117c66001836000611fc4565b509194506117de91508890508463ffffffff61265d16565b965060010161172f565b506008546040805163274d865f60e01b81526001600160a01b038a811660048301529151919092169163274d865f916024808301926020929190829003018186803b15801561183657600080fd5b505afa15801561184a573d6000803e3d6000fd5b505050506040513d602081101561186057600080fd5b50519450825b8581101561191e5760085460408051637968b71560e11b81526001600160a01b038b81166004830152602482018590529151919092169163f2d16e2a916044808301926020929190829003018186803b1580156118c257600080fd5b505afa1580156118d6573d6000803e3d6000fd5b505050506040513d60208110156118ec57600080fd5b505191506118fc60008381611fc4565b5091945061191491508890508463ffffffff61265d16565b9650600101611866565b505050505050919050565b6000809054906101000a90046001600160a01b03166001600160a01b031663ff0938a76040518163ffffffff1660e01b815260040160206040518083038186803b15801561197657600080fd5b505afa15801561198a573d6000803e3d6000fd5b505050506040513d60208110156119a057600080fd5b5051158015611a2757506000546040805163288c314960e21b815233600482015290516001600160a01b039092169163a230c52491602480820192602092909190829003018186803b1580156119f557600080fd5b505afa158015611a09573d6000803e3d6000fd5b505050506040513d6020811015611a1f57600080fd5b505115156001145b611a3057600080fd5b611a39816126b7565b600b5460408051632e1b8c8760e21b815233600482015290516001600160a01b039092169163b86e321c9160248082019260009290919082900301818387803b158015611a8557600080fd5b505af1158015611a99573d6000803e3d6000fd5b5050600a54604080516305d38c7160e21b8152336004820152602481018690529051600094506001600160a01b03909216925063174e31c491604480830192602092919082900301818787803b158015611af257600080fd5b505af1158015611b06573d6000803e3d6000fd5b505050506040513d6020811015611b1c57600080fd5b505190508015610acc576002546040805163a9059cbb60e01b81523360048201526024810184905290516001600160a01b039092169163a9059cbb916044808201926020929091908290030181600087803b158015610a9757600080fd5b6000809054906101000a90046001600160a01b03166001600160a01b031663ff0938a76040518163ffffffff1660e01b815260040160206040518083038186803b158015611bc757600080fd5b505afa158015611bdb573d6000803e3d6000fd5b505050506040513d6020811015611bf157600080fd5b505115611bfd57600080fd5b600054604080516323c5b10760e21b815233600482015290516001600160a01b0390921691638f16c41c91602480820192602092909190829003018186803b158015611c4857600080fd5b505afa158015611c5c573d6000803e3d6000fd5b505050506040513d6020811015611c7257600080fd5b5051611c7d57600080fd5b600854604080516396ff592f60e01b81526004810184905281516000936001600160a01b0316926396ff592f9260248082019391829003018186803b158015611cc557600080fd5b505afa158015611cd9573d6000803e3d6000fd5b505050506040513d6040811015611cef57600080fd5b506020015160085460408051630325ea3760e01b81526004810186905281519394506000936001600160a01b0390931692630325ea3792602480840193919291829003018186803b158015611d4357600080fd5b505afa158015611d57573d6000803e3d6000fd5b505050506040513d6040811015611d6d57600080fd5b5060200151905080611d8957611d84838383613298565b611ea7565b60018110158015611d9b575060058111155b15611dab57611d84838383613727565b80600c1415611ea7576000611dbf83613bd4565b90508015611e385760075460408051637a88deff60e11b815260048101879052600e602482015290516001600160a01b039092169163f511bdfe9160448082019260009290919082900301818387803b158015611e1b57600080fd5b505af1158015611e2f573d6000803e3d6000fd5b50505050611ea5565b60075460408051637a88deff60e11b815260048101879052600c602482015290516001600160a01b039092169163f511bdfe9160448082019260009290919082900301818387803b158015611e8c57600080fd5b505af1158015611ea0573d6000803e3d6000fd5b505050505b505b600760009054906101000a90046001600160a01b03166001600160a01b031663b23d76346040518163ffffffff1660e01b8152600401600060405180830381600087803b158015611ef757600080fd5b505af1158015611f0b573d6000803e3d6000fd5b50505050505050565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b6000546001600160a01b031681565b6000546001600160a01b031615611f98576000546001600160a01b03163314611f98576040805162461bcd60e51b815260206004820152600a6024820152692737ba1036b0b9ba32b960b11b604482015290519081900360640190fd5b600080546001600160a01b039092166001600160a01b0319928316811790915560018054909216179055565b6000806000806000806000806000600860009054906101000a90046001600160a01b03166001600160a01b03166354a1b4318c6040518263ffffffff1660e01b81526004018082815260200191505060806040518083038186803b15801561202b57600080fd5b505afa15801561203f573d6000803e3d6000fd5b505050506040513d608081101561205557600080fd5b5080516020808301516040808501516060909501516008548251631381b00560e21b815260048101859052925160009f50959d50929a509498509396508b936001600160a01b0390911692634e06c01492602480840193919291829003018186803b1580156120c357600080fd5b505afa1580156120d7573d6000803e3d6000fd5b505050506040513d60208110156120ed57600080fd5b50519050600081900b6120ff57600198505b8460000b8160000b14801561211c575083158061211c57508a6001145b1561249e578c600114156121b3576008546040805163c49a8b1960e01b81526004810189905290516001600160a01b039092169163c49a8b1991602480820192606092909190829003018186803b15801561217657600080fd5b505afa15801561218a573d6000803e3d6000fd5b505050506040513d60608110156121a057600080fd5b508051604090910151909750925061223b565b6008546040805163c49a8b1960e01b81526004810189905290516001600160a01b039092169163c49a8b1991602480820192606092909190829003018186803b1580156121ff57600080fd5b505afa158015612213573d6000803e3d6000fd5b505050506040513d606081101561222957600080fd5b50602081015160409091015190975092505b861561249e578c6001141561235b578460000b600114156122d857600854604080516336f4019360e21b81526004810189905290516001600160a01b039092169163dbd0064c91602480820192606092909190829003018186803b1580156122a257600080fd5b505afa1580156122b6573d6000803e3d6000fd5b505050506040513d60608110156122cc57600080fd5b50602001519150612356565b600854604080516336f4019360e21b81526004810189905290516001600160a01b039092169163dbd0064c91602480820192606092909190829003018186803b15801561232457600080fd5b505afa158015612338573d6000803e3d6000fd5b505050506040513d606081101561234e57600080fd5b506040015191505b612467565b8460000b600114156123e95760085460408051634a4ecebb60e11b81526004810189905290516001600160a01b039092169163949d9d7691602480820192606092909190829003018186803b1580156123b357600080fd5b505afa1580156123c7573d6000803e3d6000fd5b505050506040513d60608110156123dd57600080fd5b50602001519150612467565b60085460408051634a4ecebb60e11b81526004810189905290516001600160a01b039092169163949d9d7691602480820192606092909190829003018186803b15801561243557600080fd5b505afa158015612449573d6000803e3d6000fd5b505050506040513d606081101561245f57600080fd5b506040015191505b61249b61247b83606463ffffffff61257716565b6106d48561248f8b8d63ffffffff61257716565b9063ffffffff61257716565b99505b50505050505093509350935093565b600d546001600160a01b031681565b60006208aa8960eb1b6001600160e01b0319831614156124f1575073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee6112aa565b6244414960e81b6001600160e01b03198316141561251b5750600d546001600160a01b03166112aa565b6040805162461bcd60e51b815260206004820152601b60248201527f436c61696d735265776172643a20756e6b6e6f776e2061737365740000000000604482015290519081900360640190fd5b6001546001600160a01b031681565b600082612586575060006125d3565b8282028284828161259357fe5b04146125d05760405162461bcd60e51b81526004018080602001828103825260218152602001806148c56021913960400191505060405180910390fd5b90505b92915050565b60006125d083836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f0000000000008152506141dc565b60006125d083836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f77000081525061427e565b6000828201838110156125d0576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b60085460408051630d3c678b60e21b815233600482015290516000926001600160a01b0316916334f19e2c916024808301926020929190829003018186803b15801561270257600080fd5b505afa158015612716573d6000803e3d6000fd5b505050506040513d602081101561272c57600080fd5b5051600854604080516304ecffeb60e01b8152336004820152815193945060009384936001600160a01b0316926304ecffeb9260248082019391829003018186803b15801561277a57600080fd5b505afa15801561278e573d6000803e3d6000fd5b505050506040513d60408110156127a457600080fd5b50516005546040805163034b044160e21b815290519293506000928392839283926001600160a01b0390921691630d2c110491600480820192602092909190829003018186803b1580156127f757600080fd5b505afa15801561280b573d6000803e3d6000fd5b505050506040513d602081101561282157600080fd5b505190506000808080888b5b8c8210801561283b57508d85105b15612bac576008546040805163fc57c9bb60e01b81523360048201526024810185905290516001600160a01b039092169163fc57c9bb91604480820192602092909190829003018186803b15801561289257600080fd5b505afa1580156128a6573d6000803e3d6000fd5b505050506040513d60208110156128bc57600080fd5b50519b506128cd60018d6000611fc4565b929b5090995090935050808d1480156128e857506001881515145b156128f05750805b600854604080516354a1b43160e01b8152600481018f905290516001600160a01b03909216916354a1b43191602480820192608092909190829003018186803b15801561293c57600080fd5b505afa158015612950573d6000803e3d6000fd5b505050506040513d608081101561296657600080fd5b506020810151606090910151965093508215801590612983575085155b156129ff576008546040805163f27a104760e01b8152600481018f9052600160248201819052915191909701966001600160a01b039092169163f27a104791604480830192600092919082900301818387803b1580156129e257600080fd5b505af11580156129f6573d6000803e3d6000fd5b50505050612b88565b82158015612a86575060085460408051631381b00560e21b81526004810187905290516001600160a01b0390921691634e06c01491602480820192602092909190829003018186803b158015612a5457600080fd5b505afa158015612a68573d6000803e3d6000fd5b505050506040513d6020811015612a7e57600080fd5b505160000b15155b8015612a90575085155b15612b88576008546040805163c49a8b1960e01b81526004810187905290516001600160a01b039092169163c49a8b1991602480820192606092909190829003018186803b158015612ae157600080fd5b505afa158015612af5573d6000803e3d6000fd5b505050506040513d6060811015612b0b57600080fd5b5051925082612b1b576001909401935b6008546040805163f27a104760e01b8152600481018f90526001602482015290516001600160a01b039092169163f27a10479160448082019260009290919082900301818387803b158015612b6f57600080fd5b505af1158015612b83573d6000803e3d6000fd5b505050505b8815612ba157612b9e898b63ffffffff61265d16565b99505b60019091019061282d565b8c811415612c245760085460408051632f9f2f1760e21b81523360048201526024810185905290516001600160a01b039092169163be7cbc5c9160448082019260009290919082900301818387803b158015612c0757600080fd5b505af1158015612c1b573d6000803e3d6000fd5b50505050612c90565b60085460408051632f9f2f1760e21b81523360048201526024810184905290516001600160a01b039092169163be7cbc5c9160448082019260009290919082900301818387803b158015612c7757600080fd5b505af1158015612c8b573d6000803e3d6000fd5b505050505b6008546040805163274d865f60e01b815233600482015290516001600160a01b039092169163274d865f91602480820192602092909190829003018186803b158015612cdb57600080fd5b505afa158015612cef573d6000803e3d6000fd5b505050506040513d6020811015612d0557600080fd5b50519c508c9050612d1c878663ffffffff61257716565b6003546040805163179e91f160e01b815233600482015262434c4160e81b602482015242604482015290519299506000926001600160a01b039092169163179e91f191606480820192602092909190829003018186803b158015612d7f57600080fd5b505afa158015612d93573d6000803e3d6000fd5b505050506040513d6020811015612da957600080fd5b50511115612e295760035460408051631fea558f60e01b815233600482015262434c4160e81b6024820152604481018a905290516001600160a01b0390921691631fea558f9160648082019260009290919082900301818387803b158015612e1057600080fd5b505af1158015612e24573d6000803e3d6000fd5b505050505b600854604080516304ecffeb60e01b815233600482015281516001600160a01b03909316926304ecffeb92602480840193919291829003018186803b158015612e7157600080fd5b505afa158015612e85573d6000803e3d6000fd5b505050506040513d6040811015612e9b57600080fd5b50602001519a50600094508a91508c90505b8c82108015612ebb57508d85105b156131155760085460408051637968b71560e11b81523360048201526024810185905290516001600160a01b039092169163f2d16e2a91604480820192602092909190829003018186803b158015612f1257600080fd5b505afa158015612f26573d6000803e3d6000fd5b505050506040513d6020811015612f3c57600080fd5b50519b50612f4c60008d81611fc4565b50919a50985050808d148015612f6457506001881515145b15612f6c5750805b600854604080516354a1b43160e01b8152600481018f905290516001600160a01b03909216916354a1b43191602480820192608092909190829003018186803b158015612fb857600080fd5b505afa158015612fcc573d6000803e3d6000fd5b505050506040513d6080811015612fe257600080fd5b5060208101516060909101519650935085158015613079575060085460408051631381b00560e21b81526004810187905290516001600160a01b0390921691634e06c01491602480820192602092909190829003018186803b15801561304757600080fd5b505afa15801561305b573d6000803e3d6000fd5b505050506040513d602081101561307157600080fd5b505160000b15155b156130f1576008546040805163f27a104760e01b8152600481018f90526001602482015290516001600160a01b039092169163f27a10479160448082019260009290919082900301818387803b1580156130d257600080fd5b505af11580156130e6573d6000803e3d6000fd5b505060019096019550505b881561310a57613107898b63ffffffff61265d16565b99505b600190910190612ead565b89156131a4576002546040805163a9059cbb60e01b8152336004820152602481018d905290516001600160a01b039092169163a9059cbb916044808201926020929091908290030181600087803b15801561316f57600080fd5b505af1158015613183573d6000803e3d6000fd5b505050506040513d602081101561319957600080fd5b50516131a457600080fd5b8c81141561321c5760085460408051630f305e9560e21b81523360048201526024810185905290516001600160a01b0390921691633cc17a549160448082019260009290919082900301818387803b1580156131ff57600080fd5b505af1158015613213573d6000803e3d6000fd5b50505050613288565b60085460408051630f305e9560e21b81523360048201526024810184905290516001600160a01b0390921691633cc17a549160448082019260009290919082900301818387803b15801561326f57600080fd5b505af1158015613283573d6000803e3d6000fd5b505050505b5050505050505050505050505050565b6007546040805163778aa67960e11b81526004810186905290516001600160a01b039092169163ef154cf291602480820192602092909190829003018186803b1580156132e457600080fd5b505afa1580156132f8573d6000803e3d6000fd5b505050506040513d602081101561330e57600080fd5b505160000b6001141561372257600754604080516357af051560e01b815260048101869052600060248201819052915191926001600160a01b0316916357af051591604480820192602092909190829003018186803b15801561337057600080fd5b505afa158015613384573d6000803e3d6000fd5b505050506040513d602081101561339a57600080fd5b505160085460408051631509d4c960e21b81526004810188905260016024820152815193945060009384938493849384936001600160a01b0390931692635427532492604480840193919291829003018186803b1580156133fa57600080fd5b505afa15801561340e573d6000803e3d6000fd5b505050506040513d604081101561342457600080fd5b506020015160085460408051631509d4c960e21b8152600481018d9052600019602482015281519398506001600160a01b03909216926354275324926044808201939291829003018186803b15801561347c57600080fd5b505afa158015613490573d6000803e3d6000fd5b505050506040513d60408110156134a657600080fd5b506020015193506134bd858563ffffffff61265d16565b92506134d085606463ffffffff61257716565b94506134e384606463ffffffff61257716565b9350856134f3576003965061369e565b60065460408051631ce48f3d60e01b8152600481018b9052905161354d92670de0b6b3a7640000926001600160a01b0390911691631ce48f3d91602480820192602092909190829003018186803b15801561069c57600080fd5b905061356081600563ffffffff61257716565b86111561366a576046613579868563ffffffff6125d916565b11156135fc57600654600797506001600160a01b031663ae1c5eca8960015b6040518363ffffffff1660e01b8152600401808381526020018260ff1660ff16815260200192505050600060405180830381600087803b1580156135db57600080fd5b505af11580156135ef573d6000803e3d6000fd5b5050505060019150613665565b604661360e858563ffffffff6125d916565b111561363157600680549097506001600160a01b031663ae1c5eca896002613598565b613641848463ffffffff6125d916565b613651868563ffffffff6125d916565b11156136605760049650613665565b600596505b61369e565b61367a848463ffffffff6125d916565b61368a868563ffffffff6125d916565b1115613699576002965061369e565b600396505b60075460408051637a88deff60e11b8152600481018c9052602481018a905290516001600160a01b039092169163f511bdfe9160448082019260009290919082900301818387803b1580156136f257600080fd5b505af1158015613706573d6000803e3d6000fd5b50505050811561371b5761371b8989896142d8565b5050505050505b505050565b6007546040805163778aa67960e11b81526004810186905290516001600160a01b039092169163ef154cf291602480820192602092909190829003018186803b15801561377357600080fd5b505afa158015613787573d6000803e3d6000fd5b505050506040513d602081101561379d57600080fd5b505160000b6001141561372257600754604080516357af051560e01b815260048101869052600160248201529051600092849284926001600160a01b03909216916357af051591604480820192602092909190829003018186803b15801561380457600080fd5b505afa158015613818573d6000803e3d6000fd5b505050506040513d602081101561382e57600080fd5b505160065460408051631ce48f3d60e01b815260048101899052905192935060009261389092670de0b6b3a7640000926001600160a01b0390911691631ce48f3d91602480820192602092909190829003018186803b15801561069c57600080fd5b905060006138a582600563ffffffff61257716565b8310156138b0575060015b60085460408051635ca1ce8b60e01b8152600481018b90526001602482015281516000936001600160a01b031692635ca1ce8b9260448082019391829003018186803b1580156138ff57600080fd5b505afa158015613913573d6000803e3d6000fd5b505050506040513d604081101561392957600080fd5b506020015160085460408051635ca1ce8b60e01b8152600481018d9052600019602482015281519394506000936001600160a01b0390931692635ca1ce8b92604480840193919291829003018186803b15801561398557600080fd5b505afa158015613999573d6000803e3d6000fd5b505050506040513d60408110156139af57600080fd5b5060200151905060006139c8838363ffffffff61265d16565b1115613a845760326139f46139e3848463ffffffff61265d16565b6106d485606463ffffffff61257716565b10158015613a025750600186115b8015613a0f575060058611155b8015613a19575082155b15613a2b576008975060019650613a84565b6032613a51613a40848463ffffffff61265d16565b6106d484606463ffffffff61257716565b10158015613a5f5750600186115b8015613a6c575060058611155b8015613a76575082155b15613a845760099750600296505b826001148015613a9e57508560021480613a9e5750856004145b15613ab057600a975060019650613ae3565b826001148015613ad557508560051480613aca5750856003145b80613ad55750856001145b15613ae357600b9750600296505b60075460408051637a88deff60e11b8152600481018d9052602481018b905290516001600160a01b039092169163f511bdfe9160448082019260009290919082900301818387803b158015613b3757600080fd5b505af1158015613b4b573d6000803e3d6000fd5b50506006546040805163570e2f6560e11b8152600481018e905260ff8c16602482015290516001600160a01b03909216935063ae1c5eca925060448082019260009290919082900301818387803b158015613ba557600080fd5b505af1158015613bb9573d6000803e3d6000fd5b50505050613bc88a8a8a6142d8565b50505050505050505050565b60065460408051631ce48f3d60e01b815260048101849052905160009283926001600160a01b0390911691631ce48f3d91602480820192602092909190829003018186803b158015613c2557600080fd5b505afa158015613c39573d6000803e3d6000fd5b505050506040513d6020811015613c4f57600080fd5b505190506000613c6d82670de0b6b3a764000063ffffffff61257716565b600654604080516322d5c8bd60e21b81526004810188905290519293506000926001600160a01b0390921691638b5722f491602480820192602092909190829003018186803b158015613cbf57600080fd5b505afa158015613cd3573d6000803e3d6000fd5b505050506040513d6020811015613ce957600080fd5b505190506000613cf8826124bc565b60065460408051633c2d4daf60e01b8152600481018a905290519293506000926001600160a01b0390921691633c2d4daf91602480820192602092909190829003018186803b158015613d4a57600080fd5b505afa158015613d5e573d6000803e3d6000fd5b505050506040513d6020811015613d7457600080fd5b5051600c5460408051621d624360e71b81526001600160a01b03808516600483015291519394506000939190921691630eb12180916024808301926020929190829003018186803b158015613dc857600080fd5b505afa158015613ddc573d6000803e3d6000fd5b505050506040513d6020811015613df257600080fd5b5051600954604080516375466b9b60e01b81526001600160a01b0387811660048301528085166024830152604482018a9052915193945060009391909216916375466b9b91606480830192602092919082900301818787803b158015613e5757600080fd5b505af1158015613e6b573d6000803e3d6000fd5b505050506040513d6020811015613e8157600080fd5b5051905080156141cd5760065460408051638d16a10560e01b8152600481018c905281516000936001600160a01b031692638d16a1059260248082019391829003018186803b158015613ed357600080fd5b505afa158015613ee7573d6000803e3d6000fd5b505050506040513d6040811015613efd57600080fd5b5060209081015160095460408051630681320d60e51b81526001600160a01b038a811660048301529151939550600094919092169263d02641a0926024808201939291829003018186803b158015613f5457600080fd5b505afa158015613f68573d6000803e3d6000fd5b505050506040513d6020811015613f7e57600080fd5b505190506000613fa0826106d48b670de0b6b3a764000063ffffffff61257716565b600b54604080516368989a5960e01b81526001600160a01b0387811660048301526024820185905291519394509116916368989a599160448082019260009290919082900301818387803b158015613ff757600080fd5b505af115801561400b573d6000803e3d6000fd5b505050506000600660009054906101000a90046001600160a01b03166001600160a01b0316638d16a1058e6040518263ffffffff1660e01b815260040180828152602001915050604080518083038186803b15801561406957600080fd5b505afa15801561407d573d6000803e3d6000fd5b505050506040513d604081101561409357600080fd5b5060200151600654604080516360a230e760e01b81526001600160e01b03198d166004820152602481018f905290519293506001600160a01b03909116916360a230e79160448082019260009290919082900301818387803b1580156140f857600080fd5b505af115801561410c573d6000803e3d6000fd5b50505050600660009054906101000a90046001600160a01b03166001600160a01b03166381d760ae828b8e6040518463ffffffff1660e01b815260040180846001600160a01b03166001600160a01b03168152602001836001600160e01b0319166001600160e01b03191681526020018281526020019350505050600060405180830381600087803b1580156141a157600080fd5b505af11580156141b5573d6000803e3d6000fd5b5050505060019b5050505050505050505050506112aa565b50600098975050505050505050565b600081836142685760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561422d578181015183820152602001614215565b50505050905090810190601f16801561425a5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b50600083858161427457fe5b0495945050505050565b600081848411156142d05760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561422d578181015183820152602001614215565b505050900390565b6006546040805163ba38de3560e01b81526004810185905290516000926001600160a01b03169163ba38de35916024808301926020929190829003018186803b15801561432457600080fd5b505afa158015614338573d6000803e3d6000fd5b505050506040513d602081101561434e57600080fd5b505160085460408051635459cad560e11b815290519293506000926143df926064926106d4926001600160a01b039092169163a8b395aa91600480820192602092909190829003018186803b1580156143a657600080fd5b505afa1580156143ba573d6000803e3d6000fd5b505050506040513d60208110156143d057600080fd5b5051859063ffffffff61257716565b60085460408051635f39579d60e01b815260048101879052815193945060009384936001600160a01b031692635f39579d9260248082019391829003018186803b15801561442c57600080fd5b505afa158015614440573d6000803e3d6000fd5b505050506040513d604081101561445657600080fd5b50805160209091015160085460408051636339392560e11b8152600481018c905260248101859052604481018490526064810188905290519395509193506001600160a01b03169163c672724a9160848082019260009290919082900301818387803b1580156144c557600080fd5b505af11580156144d9573d6000803e3d6000fd5b5050505060008211806144ec5750600081115b1561455d57600354604080516340c10f1960e01b81523060048201526024810186905290516001600160a01b03909216916340c10f199160448082019260009290919082900301818387803b15801561454457600080fd5b505af1158015614558573d6000803e3d6000fd5b505050505b846006148061456c5750846009145b80614577575084600b145b156146d057600854604080516346f4da5760e01b8152600481018a9052600019602482015290516001600160a01b03909216916346f4da579160448082019260009290919082900301818387803b1580156145d157600080fd5b505af11580156145e5573d6000803e3d6000fd5b5050600554604080516308831d5960e41b8152600481018b905260006024820181905291516001600160a01b039093169450638831d59093506044808201939182900301818387803b15801561463a57600080fd5b505af115801561464e573d6000803e3d6000fd5b50506004805460408051637dded30d60e01b81529283018b9052516001600160a01b039091169350637dded30d925060248083019260209291908290030181600087803b15801561469e57600080fd5b505af11580156146b2573d6000803e3d6000fd5b505050506040513d60208110156146c857600080fd5b50611f0b9050565b84600714806146df5750846008145b806146ea575084600a145b15611f0b57600854604080516346f4da5760e01b8152600481018a90526001602482015290516001600160a01b03909216916346f4da579160448082019260009290919082900301818387803b15801561474357600080fd5b505af1158015614757573d6000803e3d6000fd5b5050600554604080516308831d5960e41b8152600481018b905260006024820181905291516001600160a01b039093169450638831d59093506044808201939182900301818387803b1580156147ac57600080fd5b505af11580156147c0573d6000803e3d6000fd5b5050600480546040805163217d488160e21b81529283018b9052516001600160a01b0390911693506385f522049250602480830192600092919082900301818387803b15801561480f57600080fd5b505af1158015614823573d6000803e3d6000fd5b50505050600061483287613bd4565b905060008161484257600c614845565b600e5b60075460408051637a88deff60e11b8152600481018d905260ff939093166024840181905290519093506001600160a01b039091169163f511bdfe91604480830192600092919082900301818387803b1580156148a157600080fd5b505af11580156148b5573d6000803e3d6000fd5b5050505050505050505050505056fe536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77a265627a7a72315820f41a8a8ea947710cff64207aa72b5df28a6495481a3c4ee214437fd5eb52634c64736f6c6343000511003200000000000000000000000001bfd82675dbcc7762c84019ca518e701c0cd07e0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106100f55760003560e01c80637f6715c911610097578063df31f20811610066578063df31f2081461027c578063e0bab4c4146102cb578063e3dee239146102d3578063f17a3bec146102fa576100f5565b80637f6715c91461020d5780638322fff21461022a578063a0b2d57f1461024e578063d46655f414610256576100f5565b806335afb14e116100d357806335afb14e1461015657806341becc071461018e578063452efce9146101ca57806363bb23be146101f0576100f5565b806304ee27c6146100fa5780630900f010146101285780630ea9c9841461014e575b600080fd5b6101266004803603604081101561011057600080fd5b50803590602001356001600160a01b0316610302565b005b6101266004803603602081101561013e57600080fd5b50356001600160a01b0316610942565b610126610ad0565b61017c6004803603602081101561016c57600080fd5b50356001600160a01b0316611178565b60408051918252519081900360200190f35b6101b1600480360360408110156101a457600080fd5b50803590602001356112af565b6040805192835290151560208301528051918290030190f35b61017c600480360360208110156101e057600080fd5b50356001600160a01b0316611625565b6101266004803603602081101561020657600080fd5b5035611929565b6101266004803603602081101561022357600080fd5b5035611b7a565b610232611f14565b604080516001600160a01b039092168252519081900360200190f35b610232611f2c565b6101266004803603602081101561026c57600080fd5b50356001600160a01b0316611f3b565b6102a56004803603606081101561029257600080fd5b5080359060208101359060400135611fc4565b604080519485529215156020850152838301919091526060830152519081900360800190f35b6102326124ad565b610232600480360360208110156102e957600080fd5b50356001600160e01b0319166124bc565b610232612568565b600054604080516323c5b10760e21b815233600482015290516001600160a01b0390921691638f16c41c91602480820192602092909190829003018186803b15801561034d57600080fd5b505afa158015610361573d6000803e3d6000fd5b505050506040513d602081101561037757600080fd5b505161038257600080fd5b600554604080516338c97bdd60e21b81526001600160a01b03848116600483015291516000938493169163e325ef74916024808301926020929190829003018186803b1580156103d157600080fd5b505afa1580156103e5573d6000803e3d6000fd5b505050506040513d60208110156103fb57600080fd5b505160055460408051631bb5ffcb60e31b81526001600160a01b0387811660048301529151939450600093919092169163ddaffe58916024808301926020929190829003018186803b15801561045057600080fd5b505afa158015610464573d6000803e3d6000fd5b505050506040513d602081101561047a57600080fd5b50519050600080808481855b878110801561049457508a82105b156107bc5760055460408051637b89c55760e11b81526001600160a01b038d81166004830152602482018590529151919092169163f7138aae916044808301926020929190829003018186803b1580156104ed57600080fd5b505afa158015610501573d6000803e3d6000fd5b505050506040513d602081101561051757600080fd5b50516005546040805163057844c560e31b81526001600160a01b038e81166004830152602482018690529151939850911691632bc2262891604480820192602092909190829003018186803b15801561056f57600080fd5b505afa158015610583573d6000803e3d6000fd5b505050506040513d602081101561059957600080fd5b505160055460408051636a96445160e01b815290519298506106e0926064926106d4926001600160a01b0390911691636a96445191600480820192602092909190829003018186803b1580156105ee57600080fd5b505afa158015610602573d6000803e3d6000fd5b505050506040513d602081101561061857600080fd5b8101908080519060200190929190505050600560009054906101000a90046001600160a01b03166001600160a01b03166343148d238f876040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b031681526020018281526020019250505060206040518083038186803b15801561069c57600080fd5b505afa1580156106b0573d6000803e3d6000fd5b505050506040513d60208110156106c657600080fd5b50519063ffffffff61257716565b9063ffffffff6125d916565b935087831480156106f15750858414155b156106fa578092505b6005546001600160a01b031663d9249bff8b8361071d8a8a63ffffffff61261b16565b6040518463ffffffff1660e01b815260040180846001600160a01b03166001600160a01b031681526020018381526020018281526020019350505050600060405180830381600087803b15801561077357600080fd5b505af1158015610787573d6000803e3d6000fd5b505050506107ae6107a1868861261b90919063ffffffff16565b8a9063ffffffff61265d16565b985060019182019101610486565b878314156108365760055460408051631819081760e01b81526001600160a01b038d811660048301526024820185905291519190921691631819081791604480830192600092919082900301818387803b15801561081957600080fd5b505af115801561082d573d6000803e3d6000fd5b505050506108a4565b60055460408051631819081760e01b81526001600160a01b038d811660048301526024820187905291519190921691631819081791604480830192600092919082900301818387803b15801561088b57600080fd5b505af115801561089f573d6000803e3d6000fd5b505050505b8815610935576002546040805163a9059cbb60e01b81526001600160a01b038d81166004830152602482018d90529151919092169163a9059cbb9160448083019260209291908290030181600087803b15801561090057600080fd5b505af1158015610914573d6000803e3d6000fd5b505050506040513d602081101561092a57600080fd5b505161093557600080fd5b5050505050505050505050565b600054604080516323c5b10760e21b815233600482015290516001600160a01b0390921691638f16c41c91602480820192602092909190829003018186803b15801561098d57600080fd5b505afa1580156109a1573d6000803e3d6000fd5b505050506040513d60208110156109b757600080fd5b50516109c257600080fd5b600254604080516370a0823160e01b815230600482015290516000926001600160a01b0316916370a08231916024808301926020929190829003018186803b158015610a0d57600080fd5b505afa158015610a21573d6000803e3d6000fd5b505050506040513d6020811015610a3757600080fd5b505190508015610acc576002546040805163a9059cbb60e01b81526001600160a01b038581166004830152602482018590529151919092169163a9059cbb9160448083019260209291908290030181600087803b158015610a9757600080fd5b505af1158015610aab573d6000803e3d6000fd5b505050506040513d6020811015610ac157600080fd5b5051610acc57600080fd5b5050565b600054604080516323c5b10760e21b815233600482015290516001600160a01b0390921691638f16c41c91602480820192602092909190829003018186803b158015610b1b57600080fd5b505afa158015610b2f573d6000803e3d6000fd5b505050506040513d6020811015610b4557600080fd5b5051610b5057600080fd5b600054604080516227050b60e31b81526110d360f21b600482015290516001600160a01b0390921691630138285891602480820192602092909190829003018186803b158015610b9f57600080fd5b505afa158015610bb3573d6000803e3d6000fd5b505050506040513d6020811015610bc957600080fd5b5051600780546001600160a01b0319166001600160a01b03928316179055600054604080516227050b60e31b81526110d160f21b6004820152905191909216916301382858916024808301926020929190829003018186803b158015610c2e57600080fd5b505afa158015610c42573d6000803e3d6000fd5b505050506040513d6020811015610c5857600080fd5b5051600880546001600160a01b0319166001600160a01b03928316179055600054604080516313aedd4b60e31b815290519190921691639d76ea58916004808301926020929190829003018186803b158015610cb357600080fd5b505afa158015610cc7573d6000803e3d6000fd5b505050506040513d6020811015610cdd57600080fd5b5051600280546001600160a01b0319166001600160a01b03928316179055600054604080516227050b60e31b815261544360f01b6004820152905191909216916301382858916024808301926020929190829003018186803b158015610d4257600080fd5b505afa158015610d56573d6000803e3d6000fd5b505050506040513d6020811015610d6c57600080fd5b5051600380546001600160a01b0319166001600160a01b03928316179055600054604080516227050b60e31b815261151160f21b6004820152905191909216916301382858916024808301926020929190829003018186803b158015610dd157600080fd5b505afa158015610de5573d6000803e3d6000fd5b505050506040513d6020811015610dfb57600080fd5b5051600580546001600160a01b0319166001600160a01b03928316179055600054604080516227050b60e31b8152612a2360f11b6004820152905191909216916301382858916024808301926020929190829003018186803b158015610e6057600080fd5b505afa158015610e74573d6000803e3d6000fd5b505050506040513d6020811015610e8a57600080fd5b5051600480546001600160a01b0319166001600160a01b03928316178155600054604080516227050b60e31b815261145160f21b9381019390935251921691630138285891602480820192602092909190829003018186803b158015610eef57600080fd5b505afa158015610f03573d6000803e3d6000fd5b505050506040513d6020811015610f1957600080fd5b5051600680546001600160a01b0319166001600160a01b03928316179055600054604080516227050b60e31b81526123ab60f11b6004820152905191909216916301382858916024808301926020929190829003018186803b158015610f7e57600080fd5b505afa158015610f92573d6000803e3d6000fd5b505050506040513d6020811015610fa857600080fd5b5051600a80546001600160a01b0319166001600160a01b03928316179055600054604080516227050b60e31b815261505360f01b6004820152905191909216916301382858916024808301926020929190829003018186803b15801561100d57600080fd5b505afa158015611021573d6000803e3d6000fd5b505050506040513d602081101561103757600080fd5b5051600b80546001600160a01b0319166001600160a01b03928316179055600054604080516227050b60e31b81526126a960f11b6004820152905191909216916301382858916024808301926020929190829003018186803b15801561109c57600080fd5b505afa1580156110b0573d6000803e3d6000fd5b505050506040513d60208110156110c657600080fd5b5051600c80546001600160a01b0319166001600160a01b03928316179055600054604080516227050b60e31b815261503160f01b6004820152905191909216916301382858916024808301926020929190829003018186803b15801561112b57600080fd5b505afa15801561113f573d6000803e3d6000fd5b505050506040513d602081101561115557600080fd5b5051600980546001600160a01b0319166001600160a01b03909216919091179055565b60008061118483611625565b600b5460408051630ea474c760e01b81526001600160a01b03878116600483015291519394506000939190921691630ea474c7916024808301926020929190829003018186803b1580156111d757600080fd5b505afa1580156111eb573d6000803e3d6000fd5b505050506040513d602081101561120157600080fd5b5051600a54604080516326fceb5d60e11b81526001600160a01b03888116600483015291519394506000939190921691634df9d6ba916024808301926020929190829003018186803b15801561125657600080fd5b505afa15801561126a573d6000803e3d6000fd5b505050506040513d602081101561128057600080fd5b505190506112a481611298858563ffffffff61265d16565b9063ffffffff61265d16565b93505050505b919050565b600080600080600086600114156114665760085460408051630d3c678b60e21b815233600482015290516001600160a01b03909216916334f19e2c91602480820192602092909190829003018186803b15801561130b57600080fd5b505afa15801561131f573d6000803e3d6000fd5b505050506040513d602081101561133557600080fd5b5051905060005b81811015611460576008546040805163fc57c9bb60e01b81523360048201526024810184905290516001600160a01b039092169163fc57c9bb91604480820192602092909190829003018186803b15801561139657600080fd5b505afa1580156113aa573d6000803e3d6000fd5b505050506040513d60208110156113c057600080fd5b5051600854604080516354a1b43160e01b81526004810184905290519296506001600160a01b03909116916354a1b43191602480820192608092909190829003018186803b15801561141157600080fd5b505afa158015611425573d6000803e3d6000fd5b505050506040513d608081101561143b57600080fd5b506020810151606090910151955092508683141561145857611460565b60010161133c565b50611608565b6008546040805163274d865f60e01b815233600482015290516001600160a01b039092169163274d865f91602480820192602092909190829003018186803b1580156114b157600080fd5b505afa1580156114c5573d6000803e3d6000fd5b505050506040513d60208110156114db57600080fd5b5051905060005b818110156116065760085460408051637968b71560e11b81523360048201526024810184905290516001600160a01b039092169163f2d16e2a91604480820192602092909190829003018186803b15801561153c57600080fd5b505afa158015611550573d6000803e3d6000fd5b505050506040513d602081101561156657600080fd5b5051600854604080516354a1b43160e01b81526004810184905290519296506001600160a01b03909116916354a1b43191602480820192608092909190829003018186803b1580156115b757600080fd5b505afa1580156115cb573d6000803e3d6000fd5b505050506040513d60808110156115e157600080fd5b50602081015160609091015195509250868314156115fe57611606565b6001016114e2565b505b61161487846001611fc4565b509199959850949650505050505050565b60085460408051630d3c678b60e21b81526001600160a01b0384811660048301529151600093849316916334f19e2c916024808301926020929190829003018186803b15801561167457600080fd5b505afa158015611688573d6000803e3d6000fd5b505050506040513d602081101561169e57600080fd5b5051600854604080516304ecffeb60e01b81526001600160a01b038781166004830152825194955060009485948594859493909116926304ecffeb9260248083019392829003018186803b1580156116f557600080fd5b505afa158015611709573d6000803e3d6000fd5b505050506040513d604081101561171f57600080fd5b5080516020909101519094509250835b858110156117e8576008546040805163fc57c9bb60e01b81526001600160a01b038b81166004830152602482018590529151919092169163fc57c9bb916044808301926020929190829003018186803b15801561178b57600080fd5b505afa15801561179f573d6000803e3d6000fd5b505050506040513d60208110156117b557600080fd5b505191506117c66001836000611fc4565b509194506117de91508890508463ffffffff61265d16565b965060010161172f565b506008546040805163274d865f60e01b81526001600160a01b038a811660048301529151919092169163274d865f916024808301926020929190829003018186803b15801561183657600080fd5b505afa15801561184a573d6000803e3d6000fd5b505050506040513d602081101561186057600080fd5b50519450825b8581101561191e5760085460408051637968b71560e11b81526001600160a01b038b81166004830152602482018590529151919092169163f2d16e2a916044808301926020929190829003018186803b1580156118c257600080fd5b505afa1580156118d6573d6000803e3d6000fd5b505050506040513d60208110156118ec57600080fd5b505191506118fc60008381611fc4565b5091945061191491508890508463ffffffff61265d16565b9650600101611866565b505050505050919050565b6000809054906101000a90046001600160a01b03166001600160a01b031663ff0938a76040518163ffffffff1660e01b815260040160206040518083038186803b15801561197657600080fd5b505afa15801561198a573d6000803e3d6000fd5b505050506040513d60208110156119a057600080fd5b5051158015611a2757506000546040805163288c314960e21b815233600482015290516001600160a01b039092169163a230c52491602480820192602092909190829003018186803b1580156119f557600080fd5b505afa158015611a09573d6000803e3d6000fd5b505050506040513d6020811015611a1f57600080fd5b505115156001145b611a3057600080fd5b611a39816126b7565b600b5460408051632e1b8c8760e21b815233600482015290516001600160a01b039092169163b86e321c9160248082019260009290919082900301818387803b158015611a8557600080fd5b505af1158015611a99573d6000803e3d6000fd5b5050600a54604080516305d38c7160e21b8152336004820152602481018690529051600094506001600160a01b03909216925063174e31c491604480830192602092919082900301818787803b158015611af257600080fd5b505af1158015611b06573d6000803e3d6000fd5b505050506040513d6020811015611b1c57600080fd5b505190508015610acc576002546040805163a9059cbb60e01b81523360048201526024810184905290516001600160a01b039092169163a9059cbb916044808201926020929091908290030181600087803b158015610a9757600080fd5b6000809054906101000a90046001600160a01b03166001600160a01b031663ff0938a76040518163ffffffff1660e01b815260040160206040518083038186803b158015611bc757600080fd5b505afa158015611bdb573d6000803e3d6000fd5b505050506040513d6020811015611bf157600080fd5b505115611bfd57600080fd5b600054604080516323c5b10760e21b815233600482015290516001600160a01b0390921691638f16c41c91602480820192602092909190829003018186803b158015611c4857600080fd5b505afa158015611c5c573d6000803e3d6000fd5b505050506040513d6020811015611c7257600080fd5b5051611c7d57600080fd5b600854604080516396ff592f60e01b81526004810184905281516000936001600160a01b0316926396ff592f9260248082019391829003018186803b158015611cc557600080fd5b505afa158015611cd9573d6000803e3d6000fd5b505050506040513d6040811015611cef57600080fd5b506020015160085460408051630325ea3760e01b81526004810186905281519394506000936001600160a01b0390931692630325ea3792602480840193919291829003018186803b158015611d4357600080fd5b505afa158015611d57573d6000803e3d6000fd5b505050506040513d6040811015611d6d57600080fd5b5060200151905080611d8957611d84838383613298565b611ea7565b60018110158015611d9b575060058111155b15611dab57611d84838383613727565b80600c1415611ea7576000611dbf83613bd4565b90508015611e385760075460408051637a88deff60e11b815260048101879052600e602482015290516001600160a01b039092169163f511bdfe9160448082019260009290919082900301818387803b158015611e1b57600080fd5b505af1158015611e2f573d6000803e3d6000fd5b50505050611ea5565b60075460408051637a88deff60e11b815260048101879052600c602482015290516001600160a01b039092169163f511bdfe9160448082019260009290919082900301818387803b158015611e8c57600080fd5b505af1158015611ea0573d6000803e3d6000fd5b505050505b505b600760009054906101000a90046001600160a01b03166001600160a01b031663b23d76346040518163ffffffff1660e01b8152600401600060405180830381600087803b158015611ef757600080fd5b505af1158015611f0b573d6000803e3d6000fd5b50505050505050565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b6000546001600160a01b031681565b6000546001600160a01b031615611f98576000546001600160a01b03163314611f98576040805162461bcd60e51b815260206004820152600a6024820152692737ba1036b0b9ba32b960b11b604482015290519081900360640190fd5b600080546001600160a01b039092166001600160a01b0319928316811790915560018054909216179055565b6000806000806000806000806000600860009054906101000a90046001600160a01b03166001600160a01b03166354a1b4318c6040518263ffffffff1660e01b81526004018082815260200191505060806040518083038186803b15801561202b57600080fd5b505afa15801561203f573d6000803e3d6000fd5b505050506040513d608081101561205557600080fd5b5080516020808301516040808501516060909501516008548251631381b00560e21b815260048101859052925160009f50959d50929a509498509396508b936001600160a01b0390911692634e06c01492602480840193919291829003018186803b1580156120c357600080fd5b505afa1580156120d7573d6000803e3d6000fd5b505050506040513d60208110156120ed57600080fd5b50519050600081900b6120ff57600198505b8460000b8160000b14801561211c575083158061211c57508a6001145b1561249e578c600114156121b3576008546040805163c49a8b1960e01b81526004810189905290516001600160a01b039092169163c49a8b1991602480820192606092909190829003018186803b15801561217657600080fd5b505afa15801561218a573d6000803e3d6000fd5b505050506040513d60608110156121a057600080fd5b508051604090910151909750925061223b565b6008546040805163c49a8b1960e01b81526004810189905290516001600160a01b039092169163c49a8b1991602480820192606092909190829003018186803b1580156121ff57600080fd5b505afa158015612213573d6000803e3d6000fd5b505050506040513d606081101561222957600080fd5b50602081015160409091015190975092505b861561249e578c6001141561235b578460000b600114156122d857600854604080516336f4019360e21b81526004810189905290516001600160a01b039092169163dbd0064c91602480820192606092909190829003018186803b1580156122a257600080fd5b505afa1580156122b6573d6000803e3d6000fd5b505050506040513d60608110156122cc57600080fd5b50602001519150612356565b600854604080516336f4019360e21b81526004810189905290516001600160a01b039092169163dbd0064c91602480820192606092909190829003018186803b15801561232457600080fd5b505afa158015612338573d6000803e3d6000fd5b505050506040513d606081101561234e57600080fd5b506040015191505b612467565b8460000b600114156123e95760085460408051634a4ecebb60e11b81526004810189905290516001600160a01b039092169163949d9d7691602480820192606092909190829003018186803b1580156123b357600080fd5b505afa1580156123c7573d6000803e3d6000fd5b505050506040513d60608110156123dd57600080fd5b50602001519150612467565b60085460408051634a4ecebb60e11b81526004810189905290516001600160a01b039092169163949d9d7691602480820192606092909190829003018186803b15801561243557600080fd5b505afa158015612449573d6000803e3d6000fd5b505050506040513d606081101561245f57600080fd5b506040015191505b61249b61247b83606463ffffffff61257716565b6106d48561248f8b8d63ffffffff61257716565b9063ffffffff61257716565b99505b50505050505093509350935093565b600d546001600160a01b031681565b60006208aa8960eb1b6001600160e01b0319831614156124f1575073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee6112aa565b6244414960e81b6001600160e01b03198316141561251b5750600d546001600160a01b03166112aa565b6040805162461bcd60e51b815260206004820152601b60248201527f436c61696d735265776172643a20756e6b6e6f776e2061737365740000000000604482015290519081900360640190fd5b6001546001600160a01b031681565b600082612586575060006125d3565b8282028284828161259357fe5b04146125d05760405162461bcd60e51b81526004018080602001828103825260218152602001806148c56021913960400191505060405180910390fd5b90505b92915050565b60006125d083836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f0000000000008152506141dc565b60006125d083836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f77000081525061427e565b6000828201838110156125d0576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b60085460408051630d3c678b60e21b815233600482015290516000926001600160a01b0316916334f19e2c916024808301926020929190829003018186803b15801561270257600080fd5b505afa158015612716573d6000803e3d6000fd5b505050506040513d602081101561272c57600080fd5b5051600854604080516304ecffeb60e01b8152336004820152815193945060009384936001600160a01b0316926304ecffeb9260248082019391829003018186803b15801561277a57600080fd5b505afa15801561278e573d6000803e3d6000fd5b505050506040513d60408110156127a457600080fd5b50516005546040805163034b044160e21b815290519293506000928392839283926001600160a01b0390921691630d2c110491600480820192602092909190829003018186803b1580156127f757600080fd5b505afa15801561280b573d6000803e3d6000fd5b505050506040513d602081101561282157600080fd5b505190506000808080888b5b8c8210801561283b57508d85105b15612bac576008546040805163fc57c9bb60e01b81523360048201526024810185905290516001600160a01b039092169163fc57c9bb91604480820192602092909190829003018186803b15801561289257600080fd5b505afa1580156128a6573d6000803e3d6000fd5b505050506040513d60208110156128bc57600080fd5b50519b506128cd60018d6000611fc4565b929b5090995090935050808d1480156128e857506001881515145b156128f05750805b600854604080516354a1b43160e01b8152600481018f905290516001600160a01b03909216916354a1b43191602480820192608092909190829003018186803b15801561293c57600080fd5b505afa158015612950573d6000803e3d6000fd5b505050506040513d608081101561296657600080fd5b506020810151606090910151965093508215801590612983575085155b156129ff576008546040805163f27a104760e01b8152600481018f9052600160248201819052915191909701966001600160a01b039092169163f27a104791604480830192600092919082900301818387803b1580156129e257600080fd5b505af11580156129f6573d6000803e3d6000fd5b50505050612b88565b82158015612a86575060085460408051631381b00560e21b81526004810187905290516001600160a01b0390921691634e06c01491602480820192602092909190829003018186803b158015612a5457600080fd5b505afa158015612a68573d6000803e3d6000fd5b505050506040513d6020811015612a7e57600080fd5b505160000b15155b8015612a90575085155b15612b88576008546040805163c49a8b1960e01b81526004810187905290516001600160a01b039092169163c49a8b1991602480820192606092909190829003018186803b158015612ae157600080fd5b505afa158015612af5573d6000803e3d6000fd5b505050506040513d6060811015612b0b57600080fd5b5051925082612b1b576001909401935b6008546040805163f27a104760e01b8152600481018f90526001602482015290516001600160a01b039092169163f27a10479160448082019260009290919082900301818387803b158015612b6f57600080fd5b505af1158015612b83573d6000803e3d6000fd5b505050505b8815612ba157612b9e898b63ffffffff61265d16565b99505b60019091019061282d565b8c811415612c245760085460408051632f9f2f1760e21b81523360048201526024810185905290516001600160a01b039092169163be7cbc5c9160448082019260009290919082900301818387803b158015612c0757600080fd5b505af1158015612c1b573d6000803e3d6000fd5b50505050612c90565b60085460408051632f9f2f1760e21b81523360048201526024810184905290516001600160a01b039092169163be7cbc5c9160448082019260009290919082900301818387803b158015612c7757600080fd5b505af1158015612c8b573d6000803e3d6000fd5b505050505b6008546040805163274d865f60e01b815233600482015290516001600160a01b039092169163274d865f91602480820192602092909190829003018186803b158015612cdb57600080fd5b505afa158015612cef573d6000803e3d6000fd5b505050506040513d6020811015612d0557600080fd5b50519c508c9050612d1c878663ffffffff61257716565b6003546040805163179e91f160e01b815233600482015262434c4160e81b602482015242604482015290519299506000926001600160a01b039092169163179e91f191606480820192602092909190829003018186803b158015612d7f57600080fd5b505afa158015612d93573d6000803e3d6000fd5b505050506040513d6020811015612da957600080fd5b50511115612e295760035460408051631fea558f60e01b815233600482015262434c4160e81b6024820152604481018a905290516001600160a01b0390921691631fea558f9160648082019260009290919082900301818387803b158015612e1057600080fd5b505af1158015612e24573d6000803e3d6000fd5b505050505b600854604080516304ecffeb60e01b815233600482015281516001600160a01b03909316926304ecffeb92602480840193919291829003018186803b158015612e7157600080fd5b505afa158015612e85573d6000803e3d6000fd5b505050506040513d6040811015612e9b57600080fd5b50602001519a50600094508a91508c90505b8c82108015612ebb57508d85105b156131155760085460408051637968b71560e11b81523360048201526024810185905290516001600160a01b039092169163f2d16e2a91604480820192602092909190829003018186803b158015612f1257600080fd5b505afa158015612f26573d6000803e3d6000fd5b505050506040513d6020811015612f3c57600080fd5b50519b50612f4c60008d81611fc4565b50919a50985050808d148015612f6457506001881515145b15612f6c5750805b600854604080516354a1b43160e01b8152600481018f905290516001600160a01b03909216916354a1b43191602480820192608092909190829003018186803b158015612fb857600080fd5b505afa158015612fcc573d6000803e3d6000fd5b505050506040513d6080811015612fe257600080fd5b5060208101516060909101519650935085158015613079575060085460408051631381b00560e21b81526004810187905290516001600160a01b0390921691634e06c01491602480820192602092909190829003018186803b15801561304757600080fd5b505afa15801561305b573d6000803e3d6000fd5b505050506040513d602081101561307157600080fd5b505160000b15155b156130f1576008546040805163f27a104760e01b8152600481018f90526001602482015290516001600160a01b039092169163f27a10479160448082019260009290919082900301818387803b1580156130d257600080fd5b505af11580156130e6573d6000803e3d6000fd5b505060019096019550505b881561310a57613107898b63ffffffff61265d16565b99505b600190910190612ead565b89156131a4576002546040805163a9059cbb60e01b8152336004820152602481018d905290516001600160a01b039092169163a9059cbb916044808201926020929091908290030181600087803b15801561316f57600080fd5b505af1158015613183573d6000803e3d6000fd5b505050506040513d602081101561319957600080fd5b50516131a457600080fd5b8c81141561321c5760085460408051630f305e9560e21b81523360048201526024810185905290516001600160a01b0390921691633cc17a549160448082019260009290919082900301818387803b1580156131ff57600080fd5b505af1158015613213573d6000803e3d6000fd5b50505050613288565b60085460408051630f305e9560e21b81523360048201526024810184905290516001600160a01b0390921691633cc17a549160448082019260009290919082900301818387803b15801561326f57600080fd5b505af1158015613283573d6000803e3d6000fd5b505050505b5050505050505050505050505050565b6007546040805163778aa67960e11b81526004810186905290516001600160a01b039092169163ef154cf291602480820192602092909190829003018186803b1580156132e457600080fd5b505afa1580156132f8573d6000803e3d6000fd5b505050506040513d602081101561330e57600080fd5b505160000b6001141561372257600754604080516357af051560e01b815260048101869052600060248201819052915191926001600160a01b0316916357af051591604480820192602092909190829003018186803b15801561337057600080fd5b505afa158015613384573d6000803e3d6000fd5b505050506040513d602081101561339a57600080fd5b505160085460408051631509d4c960e21b81526004810188905260016024820152815193945060009384938493849384936001600160a01b0390931692635427532492604480840193919291829003018186803b1580156133fa57600080fd5b505afa15801561340e573d6000803e3d6000fd5b505050506040513d604081101561342457600080fd5b506020015160085460408051631509d4c960e21b8152600481018d9052600019602482015281519398506001600160a01b03909216926354275324926044808201939291829003018186803b15801561347c57600080fd5b505afa158015613490573d6000803e3d6000fd5b505050506040513d60408110156134a657600080fd5b506020015193506134bd858563ffffffff61265d16565b92506134d085606463ffffffff61257716565b94506134e384606463ffffffff61257716565b9350856134f3576003965061369e565b60065460408051631ce48f3d60e01b8152600481018b9052905161354d92670de0b6b3a7640000926001600160a01b0390911691631ce48f3d91602480820192602092909190829003018186803b15801561069c57600080fd5b905061356081600563ffffffff61257716565b86111561366a576046613579868563ffffffff6125d916565b11156135fc57600654600797506001600160a01b031663ae1c5eca8960015b6040518363ffffffff1660e01b8152600401808381526020018260ff1660ff16815260200192505050600060405180830381600087803b1580156135db57600080fd5b505af11580156135ef573d6000803e3d6000fd5b5050505060019150613665565b604661360e858563ffffffff6125d916565b111561363157600680549097506001600160a01b031663ae1c5eca896002613598565b613641848463ffffffff6125d916565b613651868563ffffffff6125d916565b11156136605760049650613665565b600596505b61369e565b61367a848463ffffffff6125d916565b61368a868563ffffffff6125d916565b1115613699576002965061369e565b600396505b60075460408051637a88deff60e11b8152600481018c9052602481018a905290516001600160a01b039092169163f511bdfe9160448082019260009290919082900301818387803b1580156136f257600080fd5b505af1158015613706573d6000803e3d6000fd5b50505050811561371b5761371b8989896142d8565b5050505050505b505050565b6007546040805163778aa67960e11b81526004810186905290516001600160a01b039092169163ef154cf291602480820192602092909190829003018186803b15801561377357600080fd5b505afa158015613787573d6000803e3d6000fd5b505050506040513d602081101561379d57600080fd5b505160000b6001141561372257600754604080516357af051560e01b815260048101869052600160248201529051600092849284926001600160a01b03909216916357af051591604480820192602092909190829003018186803b15801561380457600080fd5b505afa158015613818573d6000803e3d6000fd5b505050506040513d602081101561382e57600080fd5b505160065460408051631ce48f3d60e01b815260048101899052905192935060009261389092670de0b6b3a7640000926001600160a01b0390911691631ce48f3d91602480820192602092909190829003018186803b15801561069c57600080fd5b905060006138a582600563ffffffff61257716565b8310156138b0575060015b60085460408051635ca1ce8b60e01b8152600481018b90526001602482015281516000936001600160a01b031692635ca1ce8b9260448082019391829003018186803b1580156138ff57600080fd5b505afa158015613913573d6000803e3d6000fd5b505050506040513d604081101561392957600080fd5b506020015160085460408051635ca1ce8b60e01b8152600481018d9052600019602482015281519394506000936001600160a01b0390931692635ca1ce8b92604480840193919291829003018186803b15801561398557600080fd5b505afa158015613999573d6000803e3d6000fd5b505050506040513d60408110156139af57600080fd5b5060200151905060006139c8838363ffffffff61265d16565b1115613a845760326139f46139e3848463ffffffff61265d16565b6106d485606463ffffffff61257716565b10158015613a025750600186115b8015613a0f575060058611155b8015613a19575082155b15613a2b576008975060019650613a84565b6032613a51613a40848463ffffffff61265d16565b6106d484606463ffffffff61257716565b10158015613a5f5750600186115b8015613a6c575060058611155b8015613a76575082155b15613a845760099750600296505b826001148015613a9e57508560021480613a9e5750856004145b15613ab057600a975060019650613ae3565b826001148015613ad557508560051480613aca5750856003145b80613ad55750856001145b15613ae357600b9750600296505b60075460408051637a88deff60e11b8152600481018d9052602481018b905290516001600160a01b039092169163f511bdfe9160448082019260009290919082900301818387803b158015613b3757600080fd5b505af1158015613b4b573d6000803e3d6000fd5b50506006546040805163570e2f6560e11b8152600481018e905260ff8c16602482015290516001600160a01b03909216935063ae1c5eca925060448082019260009290919082900301818387803b158015613ba557600080fd5b505af1158015613bb9573d6000803e3d6000fd5b50505050613bc88a8a8a6142d8565b50505050505050505050565b60065460408051631ce48f3d60e01b815260048101849052905160009283926001600160a01b0390911691631ce48f3d91602480820192602092909190829003018186803b158015613c2557600080fd5b505afa158015613c39573d6000803e3d6000fd5b505050506040513d6020811015613c4f57600080fd5b505190506000613c6d82670de0b6b3a764000063ffffffff61257716565b600654604080516322d5c8bd60e21b81526004810188905290519293506000926001600160a01b0390921691638b5722f491602480820192602092909190829003018186803b158015613cbf57600080fd5b505afa158015613cd3573d6000803e3d6000fd5b505050506040513d6020811015613ce957600080fd5b505190506000613cf8826124bc565b60065460408051633c2d4daf60e01b8152600481018a905290519293506000926001600160a01b0390921691633c2d4daf91602480820192602092909190829003018186803b158015613d4a57600080fd5b505afa158015613d5e573d6000803e3d6000fd5b505050506040513d6020811015613d7457600080fd5b5051600c5460408051621d624360e71b81526001600160a01b03808516600483015291519394506000939190921691630eb12180916024808301926020929190829003018186803b158015613dc857600080fd5b505afa158015613ddc573d6000803e3d6000fd5b505050506040513d6020811015613df257600080fd5b5051600954604080516375466b9b60e01b81526001600160a01b0387811660048301528085166024830152604482018a9052915193945060009391909216916375466b9b91606480830192602092919082900301818787803b158015613e5757600080fd5b505af1158015613e6b573d6000803e3d6000fd5b505050506040513d6020811015613e8157600080fd5b5051905080156141cd5760065460408051638d16a10560e01b8152600481018c905281516000936001600160a01b031692638d16a1059260248082019391829003018186803b158015613ed357600080fd5b505afa158015613ee7573d6000803e3d6000fd5b505050506040513d6040811015613efd57600080fd5b5060209081015160095460408051630681320d60e51b81526001600160a01b038a811660048301529151939550600094919092169263d02641a0926024808201939291829003018186803b158015613f5457600080fd5b505afa158015613f68573d6000803e3d6000fd5b505050506040513d6020811015613f7e57600080fd5b505190506000613fa0826106d48b670de0b6b3a764000063ffffffff61257716565b600b54604080516368989a5960e01b81526001600160a01b0387811660048301526024820185905291519394509116916368989a599160448082019260009290919082900301818387803b158015613ff757600080fd5b505af115801561400b573d6000803e3d6000fd5b505050506000600660009054906101000a90046001600160a01b03166001600160a01b0316638d16a1058e6040518263ffffffff1660e01b815260040180828152602001915050604080518083038186803b15801561406957600080fd5b505afa15801561407d573d6000803e3d6000fd5b505050506040513d604081101561409357600080fd5b5060200151600654604080516360a230e760e01b81526001600160e01b03198d166004820152602481018f905290519293506001600160a01b03909116916360a230e79160448082019260009290919082900301818387803b1580156140f857600080fd5b505af115801561410c573d6000803e3d6000fd5b50505050600660009054906101000a90046001600160a01b03166001600160a01b03166381d760ae828b8e6040518463ffffffff1660e01b815260040180846001600160a01b03166001600160a01b03168152602001836001600160e01b0319166001600160e01b03191681526020018281526020019350505050600060405180830381600087803b1580156141a157600080fd5b505af11580156141b5573d6000803e3d6000fd5b5050505060019b5050505050505050505050506112aa565b50600098975050505050505050565b600081836142685760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561422d578181015183820152602001614215565b50505050905090810190601f16801561425a5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b50600083858161427457fe5b0495945050505050565b600081848411156142d05760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561422d578181015183820152602001614215565b505050900390565b6006546040805163ba38de3560e01b81526004810185905290516000926001600160a01b03169163ba38de35916024808301926020929190829003018186803b15801561432457600080fd5b505afa158015614338573d6000803e3d6000fd5b505050506040513d602081101561434e57600080fd5b505160085460408051635459cad560e11b815290519293506000926143df926064926106d4926001600160a01b039092169163a8b395aa91600480820192602092909190829003018186803b1580156143a657600080fd5b505afa1580156143ba573d6000803e3d6000fd5b505050506040513d60208110156143d057600080fd5b5051859063ffffffff61257716565b60085460408051635f39579d60e01b815260048101879052815193945060009384936001600160a01b031692635f39579d9260248082019391829003018186803b15801561442c57600080fd5b505afa158015614440573d6000803e3d6000fd5b505050506040513d604081101561445657600080fd5b50805160209091015160085460408051636339392560e11b8152600481018c905260248101859052604481018490526064810188905290519395509193506001600160a01b03169163c672724a9160848082019260009290919082900301818387803b1580156144c557600080fd5b505af11580156144d9573d6000803e3d6000fd5b5050505060008211806144ec5750600081115b1561455d57600354604080516340c10f1960e01b81523060048201526024810186905290516001600160a01b03909216916340c10f199160448082019260009290919082900301818387803b15801561454457600080fd5b505af1158015614558573d6000803e3d6000fd5b505050505b846006148061456c5750846009145b80614577575084600b145b156146d057600854604080516346f4da5760e01b8152600481018a9052600019602482015290516001600160a01b03909216916346f4da579160448082019260009290919082900301818387803b1580156145d157600080fd5b505af11580156145e5573d6000803e3d6000fd5b5050600554604080516308831d5960e41b8152600481018b905260006024820181905291516001600160a01b039093169450638831d59093506044808201939182900301818387803b15801561463a57600080fd5b505af115801561464e573d6000803e3d6000fd5b50506004805460408051637dded30d60e01b81529283018b9052516001600160a01b039091169350637dded30d925060248083019260209291908290030181600087803b15801561469e57600080fd5b505af11580156146b2573d6000803e3d6000fd5b505050506040513d60208110156146c857600080fd5b50611f0b9050565b84600714806146df5750846008145b806146ea575084600a145b15611f0b57600854604080516346f4da5760e01b8152600481018a90526001602482015290516001600160a01b03909216916346f4da579160448082019260009290919082900301818387803b15801561474357600080fd5b505af1158015614757573d6000803e3d6000fd5b5050600554604080516308831d5960e41b8152600481018b905260006024820181905291516001600160a01b039093169450638831d59093506044808201939182900301818387803b1580156147ac57600080fd5b505af11580156147c0573d6000803e3d6000fd5b5050600480546040805163217d488160e21b81529283018b9052516001600160a01b0390911693506385f522049250602480830192600092919082900301818387803b15801561480f57600080fd5b505af1158015614823573d6000803e3d6000fd5b50505050600061483287613bd4565b905060008161484257600c614845565b600e5b60075460408051637a88deff60e11b8152600481018d905260ff939093166024840181905290519093506001600160a01b039091169163f511bdfe91604480830192600092919082900301818387803b1580156148a157600080fd5b505af11580156148b5573d6000803e3d6000fd5b5050505050505050505050505056fe536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77a265627a7a72315820f41a8a8ea947710cff64207aa72b5df28a6495481a3c4ee214437fd5eb52634c64736f6c63430005110032
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000001bfd82675dbcc7762c84019ca518e701c0cd07e0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f
-----Decoded View---------------
Arg [0] : masterAddress (address): 0x01BFd82675DBCc7762C84019cA518e701C0cD07e
Arg [1] : _daiAddress (address): 0x6B175474E89094C44Da98b954EedeAC495271d0F
-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 00000000000000000000000001bfd82675dbcc7762c84019ca518e701c0cd07e
Arg [1] : 0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f
Deployed Bytecode Sourcemap
222668:18592:0:-;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;222668:18592:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;239909:1346;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;239909:1346:0;;;;;;-1:-1:-1;;;;;239909:1346:0;;:::i;:::-;;228588:186;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;228588:186:0;-1:-1:-1;;;;;228588:186:0;;:::i;223411:625::-;;;:::i;231434:332::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;231434:332:0;-1:-1:-1;;;;;231434:332:0;;:::i;:::-;;;;;;;;;;;;;;;;229906:831;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;229906:831:0;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;228883:821;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;228883:821:0;-1:-1:-1;;;;;228883:821:0;;:::i;230920:338::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;230920:338:0;;:::i;224107:766::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;224107:766:0;;:::i;223136:72::-;;;:::i;:::-;;;;-1:-1:-1;;;;;223136:72:0;;;;;;;;;;;;;;24500:20;;;:::i;25246:247::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;25246:247:0;-1:-1:-1;;;;;25246:247:0;;:::i;227110:1384::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;227110:1384:0;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;223095:18;;;:::i;224879:248::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;224879:248:0;-1:-1:-1;;;;;;224879:248:0;;:::i;24525:30::-;;;:::i;239909:1346::-;24599:2;;:25;;;-1:-1:-1;;;24599:25:0;;24613:10;24599:25;;;;;;-1:-1:-1;;;;;24599:2:0;;;;:13;;:25;;;;;;;;;;;;;;;:2;:25;;;5:2:-1;;;;30:1;27;20:12;5:2;24599:25:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;24599:25:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;24599:25:0;24591:34;;;;;;240031:2;;:39;;;-1:-1:-1;;;240031:39:0;;-1:-1:-1;;;;;240031:39:0;;;;;;;;;239999:10;;;;240031:2;;:32;;:39;;;;;;;;;;;;;;:2;:39;;;5:2:-1;;;;30:1;27;20:12;5:2;240031:39:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;240031:39:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;240031:39:0;240113:2;;:38;;;-1:-1:-1;;;240113:38:0;;-1:-1:-1;;;;;240113:38:0;;;;;;;;;240031:39;;-1:-1:-1;240077:33:0;;240113:2;;;;;:31;;:38;;;;;240031:39;;240113:38;;;;;;;:2;:38;;;5:2:-1;;;;30:1;27;20:12;5:2;240113:38:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;240113:38:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;240113:38:0;;-1:-1:-1;240158:21:0;;;240270:3;240158:21;240113:38;240314:649;240357:3;240353:1;:7;:29;;;;;240374:8;240364:7;:18;240353:29;240314:649;;;240419:2;;:45;;;-1:-1:-1;;;240419:45:0;;-1:-1:-1;;;;;240419:45:0;;;;;;;;;;;;;;;:2;;;;;:35;;:45;;;;;;;;;;;;;;:2;:45;;;5:2:-1;;;;30:1;27;20:12;5:2;240419:45:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;240419:45:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;240419:45:0;240492:2;;:43;;;-1:-1:-1;;;240492:43:0;;-1:-1:-1;;;;;240492:43:0;;;;;;;;;;;;;;;240419:45;;-1:-1:-1;240492:2:0;;;:33;;:43;;;;;240419:45;;240492:43;;;;;;;;:2;:43;;;5:2:-1;;;;30:1;27;20:12;5:2;240492:43:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;240492:43:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;240492:43:0;240626:2;;:27;;;-1:-1:-1;;;240626:27:0;;;;240492:43;;-1:-1:-1;240560:103:0;;240659:3;;240560:94;;-1:-1:-1;;;;;240626:2:0;;;;:25;;:27;;;;;240492:43;;240626:27;;;;;;;;:2;:27;;;5:2:-1;;;;30:1;27;20:12;5:2;240626:27:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;240626:27:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;240626:27:0;;;;;;;;;;;;;;;;240560:2;;;;;;;;;-1:-1:-1;;;;;240560:2:0;-1:-1:-1;;;;;240560:41:0;;240612:5;240619:1;240560:61;;;;;;;;;;;;;-1:-1:-1;;;;;240560:61:0;-1:-1:-1;;;;;240560:61:0;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;240560:61:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;240560:61:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;240560:61:0;;:94;:65;:94;:::i;:::-;:98;:103;:98;:103;:::i;:::-;240544:119;;240701:3;240676:21;:28;:65;;;;;240725:16;240708:13;:33;;240676:65;240672:105;;;240776:1;240752:25;;240672:105;240786:2;;-1:-1:-1;;;;;240786:2:0;:31;240818:5;240825:1;240828:40;:16;240849:18;240828:40;:20;:40;:::i;:::-;240786:83;;;;;;;;;;;;;-1:-1:-1;;;;;240786:83:0;-1:-1:-1;;;;;240786:83:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;240786:83:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;240786:83:0;;;;240886:51;240896:40;240917:18;240896:16;:20;;:40;;;;:::i;:::-;240886:5;;:51;:9;:51;:::i;:::-;240878:59;-1:-1:-1;240946:9:0;;;;;240384:3;240314:649;;;240998:3;240973:21;:28;240969:192;;;241012:2;;:49;;;-1:-1:-1;;;241012:49:0;;-1:-1:-1;;;;;241012:49:0;;;;;;;;;;;;;;;:2;;;;;:39;;:49;;;;;:2;;:49;;;;;;;:2;;:49;;;5:2:-1;;;;30:1;27;20:12;5:2;241012:49:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;241012:49:0;;;;240969:192;;;241084:2;;:69;;;-1:-1:-1;;;241084:69:0;;-1:-1:-1;;;;;241084:69:0;;;;;;;;;;;;;;;:2;;;;;:39;;:69;;;;;:2;;:69;;;;;;;:2;;:69;;;5:2:-1;;;;30:1;27;20:12;5:2;241084:69:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;241084:69:0;;;;240969:192;241173:9;;241169:56;;241199:2;;:25;;;-1:-1:-1;;;241199:25:0;;-1:-1:-1;;;;;241199:25:0;;;;;;;;;;;;;;;:2;;;;;:11;;:25;;;;;;;;;;;;;;:2;;:25;;;5:2:-1;;;;30:1;27;20:12;5:2;241199:25:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;241199:25:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;241199:25:0;241191:34;;;;;;24632:1;;;;;;;;;239909:1346;;:::o;228588:186::-;24599:2;;:25;;;-1:-1:-1;;;24599:25:0;;24613:10;24599:25;;;;;;-1:-1:-1;;;;;24599:2:0;;;;:13;;:25;;;;;;;;;;;;;;;:2;:25;;;5:2:-1;;;;30:1;27;20:12;5:2;24599:25:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;24599:25:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;24599:25:0;24591:34;;;;;;228663:2;;:27;;;-1:-1:-1;;;228663:27:0;;228684:4;228663:27;;;;;;228649:11;;-1:-1:-1;;;;;228663:2:0;;:12;;:27;;;;;;;;;;;;;;:2;:27;;;5:2:-1;;;;30:1;27;20:12;5:2;228663:27:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;228663:27:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;228663:27:0;;-1:-1:-1;228701:10:0;;228697:70;;228730:2;;:28;;;-1:-1:-1;;;228730:28:0;;-1:-1:-1;;;;;228730:28:0;;;;;;;;;;;;;;;:2;;;;;:11;;:28;;;;;;;;;;;;;;:2;;:28;;;5:2:-1;;;;30:1;27;20:12;5:2;228730:28:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;228730:28:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;228730:28:0;228722:37;;;;;;24632:1;228588:186;:::o;223411:625::-;24599:2;;:25;;;-1:-1:-1;;;24599:25:0;;24613:10;24599:25;;;;;;-1:-1:-1;;;;;24599:2:0;;;;:13;;:25;;;;;;;;;;;;;;;:2;:25;;;5:2:-1;;;;30:1;27;20:12;5:2;24599:25:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;24599:25:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;24599:25:0;24591:34;;;;;;223492:2;;:25;;;-1:-1:-1;;;223492:25:0;;-1:-1:-1;;;223492:25:0;;;;;;-1:-1:-1;;;;;223492:2:0;;;;:19;;:25;;;;;;;;;;;;;;;:2;:25;;;5:2:-1;;;;30:1;27;20:12;5:2;223492:25:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;223492:25:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;223492:25:0;223480:2;:38;;-1:-1:-1;;;;;;223480:38:0;-1:-1:-1;;;;;223480:38:0;;;;;;-1:-1:-1;223541:2:0;:25;;;-1:-1:-1;;;223541:25:0;;-1:-1:-1;;;223541:25:0;;;;;;:2;;;;;:19;;:25;;;;;223492;;223541;;;;;;;:2;:25;;;5:2:-1;;;;30:1;27;20:12;5:2;223541:25:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;223541:25:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;223541:25:0;223525:2;:42;;-1:-1:-1;;;;;;223525:42:0;-1:-1:-1;;;;;223525:42:0;;;;;;-1:-1:-1;223588:2:0;:17;;;-1:-1:-1;;;223588:17:0;;;;:2;;;;;:15;;:17;;;;;223541:25;;223588:17;;;;;;;:2;:17;;;5:2:-1;;;;30:1;27;20:12;5:2;223588:17:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;223588:17:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;223588:17:0;223574:2;:32;;-1:-1:-1;;;;;;223574:32:0;-1:-1:-1;;;;;223574:32:0;;;;;;-1:-1:-1;223634:2:0;:25;;;-1:-1:-1;;;223634:25:0;;-1:-1:-1;;;223634:25:0;;;;;;:2;;;;;:19;;:25;;;;;223588:17;;223634:25;;;;;;;:2;:25;;;5:2:-1;;;;30:1;27;20:12;5:2;223634:25:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;223634:25:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;223634:25:0;223613:2;:47;;-1:-1:-1;;;;;;223613:47:0;-1:-1:-1;;;;;223613:47:0;;;;;;-1:-1:-1;223682:2:0;:25;;;-1:-1:-1;;;223682:25:0;;-1:-1:-1;;;223682:25:0;;;;;;:2;;;;;:19;;:25;;;;;223634;;223682;;;;;;;:2;:25;;;5:2:-1;;;;30:1;27;20:12;5:2;223682:25:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;223682:25:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;223682:25:0;223667:2;:41;;-1:-1:-1;;;;;;223667:41:0;-1:-1:-1;;;;;223667:41:0;;;;;;-1:-1:-1;223735:2:0;:25;;;-1:-1:-1;;;223735:25:0;;-1:-1:-1;;;223735:25:0;;;;;;:2;;;;;:19;;:25;;;;;223682;;223735;;;;;;;:2;:25;;;5:2:-1;;;;30:1;27;20:12;5:2;223735:25:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;223735:25:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;223735:25:0;223715:2;:46;;-1:-1:-1;;;;;;223715:46:0;-1:-1:-1;;;;;223715:46:0;;;;;;-1:-1:-1;223787:2:0;:25;;;-1:-1:-1;;;223787:25:0;;-1:-1:-1;;;223787:25:0;;;;;;;;:2;;;:19;;:25;;;;;223735;;223787;;;;;;;;:2;:25;;;5:2:-1;;;;30:1;27;20:12;5:2;223787:25:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;223787:25:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;223787:25:0;223768:2;:45;;-1:-1:-1;;;;;;223768:45:0;-1:-1:-1;;;;;223768:45:0;;;;;;-1:-1:-1;223836:2:0;:25;;;-1:-1:-1;;;223836:25:0;;-1:-1:-1;;;223836:25:0;;;;;;:2;;;;;:19;;:25;;;;;223787;;223836;;;;;;;:2;:25;;;5:2:-1;;;;30:1;27;20:12;5:2;223836:25:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;223836:25:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;223836:25:0;223820:2;:42;;-1:-1:-1;;;;;;223820:42:0;-1:-1:-1;;;;;223820:42:0;;;;;;-1:-1:-1;223900:2:0;:25;;;-1:-1:-1;;;223900:25:0;;-1:-1:-1;;;223900:25:0;;;;;;:2;;;;;:19;;:25;;;;;223836;;223900;;;;;;;:2;:25;;;5:2:-1;;;;30:1;27;20:12;5:2;223900:25:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;223900:25:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;223900:25:0;223869:13;:57;;-1:-1:-1;;;;;;223869:57:0;-1:-1:-1;;;;;223869:57:0;;;;;;-1:-1:-1;223959:2:0;:25;;;-1:-1:-1;;;223959:25:0;;-1:-1:-1;;;223959:25:0;;;;;;:2;;;;;:19;;:25;;;;;223900;;223959;;;;;;;:2;:25;;;5:2:-1;;;;30:1;27;20:12;5:2;223959:25:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;223959:25:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;223959:25:0;223933:11;:52;;-1:-1:-1;;;;;;223933:52:0;-1:-1:-1;;;;;223933:52:0;;;;;;-1:-1:-1;224004:2:0;:25;;;-1:-1:-1;;;224004:25:0;;-1:-1:-1;;;224004:25:0;;;;;;:2;;;;;:19;;:25;;;;;223959;;224004;;;;;;;:2;:25;;;5:2:-1;;;;30:1;27;20:12;5:2;224004:25:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;224004:25:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;224004:25:0;223992:4;:38;;-1:-1:-1;;;;;;223992:38:0;-1:-1:-1;;;;;223992:38:0;;;;;;;;;223411:625::o;231434:332::-;231504:4;231517:13;231533:36;231564:4;231533:30;:36::i;:::-;231603:13;;:32;;;-1:-1:-1;;;231603:32:0;;-1:-1:-1;;;;;231603:32:0;;;;;;;;;231517:52;;-1:-1:-1;231576:24:0;;231603:13;;;;;:26;;:32;;;;;;;;;;;;;;:13;:32;;;5:2:-1;;;;30:1;27;20:12;5:2;231603:32:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;231603:32:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;231603:32:0;231666:2;;:25;;;-1:-1:-1;;;231666:25:0;;-1:-1:-1;;;;;231666:25:0;;;;;;;;;231603:32;;-1:-1:-1;231642:21:0;;231666:2;;;;;:19;;:25;;;;;231603:32;;231666:25;;;;;;;:2;:25;;;5:2:-1;;;;30:1;27;20:12;5:2;231666:25:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;231666:25:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;231666:25:0;;-1:-1:-1;231705:55:0;231666:25;231705:33;:8;231718:19;231705:33;:12;:33;:::i;:::-;:37;:55;:37;:55;:::i;:::-;231698:62;;;;;231434:332;;;;:::o;229906:831::-;229988:11;230001:12;230022:11;230040:12;230059:15;230087:5;230096:1;230087:10;230083:590;;;230121:2;;:37;;;-1:-1:-1;;;230121:37:0;;230147:10;230121:37;;;;;;-1:-1:-1;;;;;230121:2:0;;;;:25;;:37;;;;;;;;;;;;;;;:2;:37;;;5:2:-1;;;;30:1;27;20:12;5:2;230121:37:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;230121:37:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;230121:37:0;;-1:-1:-1;230172:6:0;230167:205;230188:10;230184:1;:14;230167:205;;;230225:2;;:34;;;-1:-1:-1;;;230225:34:0;;230245:10;230225:34;;;;;;;;;;;;-1:-1:-1;;;;;230225:2:0;;;;:19;;:34;;;;;;;;;;;;;;;:2;:34;;;5:2:-1;;;;30:1;27;20:12;5:2;230225:34:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;230225:34:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;230225:34:0;230295:2;;:25;;;-1:-1:-1;;;230295:25:0;;;;;;;;;;230225:34;;-1:-1:-1;;;;;;230295:2:0;;;;:17;;:25;;;;;;;;;;;;;;;:2;:25;;;5:2:-1;;;;30:1;27;20:12;5:2;230295:25:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;230295:25:0;;;;;;;13:3:-1;8;5:12;2:2;;;30:1;27;20:12;2:2;-1:-1;230295:25:0;;;;;;;;;;-1:-1:-1;230295:25:0;-1:-1:-1;230335:18:0;;;230331:32;;;230356:5;;230331:32;230200:3;;230167:205;;;;230083:590;;;230407:2;;:41;;;-1:-1:-1;;;230407:41:0;;230437:10;230407:41;;;;;;-1:-1:-1;;;;;230407:2:0;;;;:29;;:41;;;;;;;;;;;;;;;:2;:41;;;5:2:-1;;;;30:1;27;20:12;5:2;230407:41:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;230407:41:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;230407:41:0;;-1:-1:-1;230462:6:0;230457:209;230478:10;230474:1;:14;230457:209;;;230515:2;;:38;;;-1:-1:-1;;;230515:38:0;;230539:10;230515:38;;;;;;;;;;;;-1:-1:-1;;;;;230515:2:0;;;;:23;;:38;;;;;;;;;;;;;;;:2;:38;;;5:2:-1;;;;30:1;27;20:12;5:2;230515:38:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;230515:38:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;230515:38:0;230589:2;;:25;;;-1:-1:-1;;;230589:25:0;;;;;;;;;;230515:38;;-1:-1:-1;;;;;;230589:2:0;;;;:17;;:25;;;;;;;;;;;;;;;:2;:25;;;5:2:-1;;;;30:1;27;20:12;5:2;230589:25:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;230589:25:0;;;;;;;13:3:-1;8;5:12;2:2;;;30:1;27;20:12;2:2;-1:-1;230589:25:0;;;;;;;;;;-1:-1:-1;230589:25:0;-1:-1:-1;230629:18:0;;;230625:32;;;230650:5;;230625:32;230490:3;;230457:209;;;;230083:590;230693:36;230712:5;230719:6;230727:1;230693:18;:36::i;:::-;-1:-1:-1;230679:50:0;;229906:831;;-1:-1:-1;229906:831:0;;-1:-1:-1;;;;;;;229906:831:0:o;228883:821::-;228995:2;;:31;;;-1:-1:-1;;;228995:31:0;;-1:-1:-1;;;;;228995:31:0;;;;;;;;;228958:10;;;;228995:2;;:25;;:31;;;;;;;;;;;;;;:2;:31;;;5:2:-1;;;;30:1;27;20:12;5:2;228995:31:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;228995:31:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;228995:31:0;229152:2;;:34;;;-1:-1:-1;;;229152:34:0;;-1:-1:-1;;;;;229152:34:0;;;;;;;;;228995:31;;-1:-1:-1;229033:16:0;;;;;;;;229152:2;;;;;:28;;:34;;;;;;;;;;;:2;:34;;;5:2:-1;;;;30:1;27;20:12;5:2;229152:34:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;229152:34:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;229152:34:0;;;;;;;;;-1:-1:-1;229152:34:0;-1:-1:-1;229152:34:0;229195:207;229226:10;229222:1;:14;229195:207;;;229261:2;;:28;;;-1:-1:-1;;;229261:28:0;;-1:-1:-1;;;;;229261:28:0;;;;;;;;;;;;;;;:2;;;;;:19;;:28;;;;;;;;;;;;;;:2;:28;;;5:2:-1;;;;30:1;27;20:12;5:2;229261:28:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;229261:28:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;229261:28:0;;-1:-1:-1;229320:32:0;229339:1;229261:28;229350:1;229320:18;:32::i;:::-;-1:-1:-1;229298:54:0;;-1:-1:-1;229369:25:0;;-1:-1:-1;229369:5:0;;-1:-1:-1;229298:54:0;229369:25;:9;:25;:::i;:::-;229361:33;-1:-1:-1;229238:3:0;;229195:207;;;-1:-1:-1;229423:2:0;;:35;;;-1:-1:-1;;;229423:35:0;;-1:-1:-1;;;;;229423:35:0;;;;;;;;;:2;;;;;:29;;:35;;;;;;;;;;;;;;:2;:35;;;5:2:-1;;;;30:1;27;20:12;5:2;229423:35:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;229423:35:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;229423:35:0;;-1:-1:-1;229481:11:0;229467:211;229498:10;229494:1;:14;229467:211;;;229533:2;;:32;;;-1:-1:-1;;;229533:32:0;;-1:-1:-1;;;;;229533:32:0;;;;;;;;;;;;;;;:2;;;;;:23;;:32;;;;;;;;;;;;;;:2;:32;;;5:2:-1;;;;30:1;27;20:12;5:2;229533:32:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;229533:32:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;229533:32:0;;-1:-1:-1;229596:32:0;229615:1;229533:32;229615:1;229596:18;:32::i;:::-;-1:-1:-1;229574:54:0;;-1:-1:-1;229645:25:0;;-1:-1:-1;229645:5:0;;-1:-1:-1;229574:54:0;229645:25;:9;:25;:::i;:::-;229637:33;-1:-1:-1;229510:3:0;;229467:211;;;-1:-1:-1;;;;;;228883:821:0;;;:::o;230920:338::-;24691:2;;;;;;;;;-1:-1:-1;;;;;24691:2:0;-1:-1:-1;;;;;24691:10:0;;:12;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;24691:12:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;24691:12:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;24691:12:0;:21;;;:56;;-1:-1:-1;24716:2:0;;:23;;;-1:-1:-1;;;24716:23:0;;24728:10;24716:23;;;;;;-1:-1:-1;;;;;24716:2:0;;;;:11;;:23;;;;;;;;;;;;;;;:2;:23;;;5:2:-1;;;;30:1;27;20:12;5:2;24716:23:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;24716:23:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;24716:23:0;:31;;24743:4;24716:31;24691:56;24683:65;;;;;;231001:36;231029:7;231001:27;:36::i;:::-;231044:13;;:40;;;-1:-1:-1;;;231044:40:0;;231073:10;231044:40;;;;;;-1:-1:-1;;;;;231044:13:0;;;;:28;;:40;;;;;:13;;:40;;;;;;;;:13;;:40;;;5:2:-1;;;;30:1;27;20:12;5:2;231044:40:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;-1:-1;;231116:2:0;;:35;;;-1:-1:-1;;;231116:35:0;;231131:10;231116:35;;;;;;;;;;;;231091:22;;-1:-1:-1;;;;;;231116:2:0;;;;-1:-1:-1;231116:14:0;;:35;;;;;;;;;;;;;;231091:22;231116:2;:35;;;5:2:-1;;;;30:1;27;20:12;5:2;231116:35:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;231116:35:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;231116:35:0;;-1:-1:-1;231162:21:0;;231158:95;;231202:2;;:42;;;-1:-1:-1;;;231202:42:0;;231214:10;231202:42;;;;;;;;;;;;-1:-1:-1;;;;;231202:2:0;;;;:11;;:42;;;;;;;;;;;;;;;:2;;:42;;;5:2:-1;;;;30:1;27;20:12;224107:766:0;24880:2;;;;;;;;;-1:-1:-1;;;;;24880:2:0;-1:-1:-1;;;;;24880:10:0;;:12;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;24880:12:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;24880:12:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;24880:12:0;:21;24872:30;;;;;;24599:2;;:25;;;-1:-1:-1;;;24599:25:0;;24613:10;24599:25;;;;;;-1:-1:-1;;;;;24599:2:0;;;;:13;;:25;;;;;;;;;;;;;;;:2;:25;;;5:2:-1;;;;30:1;27;20:12;5:2;24599:25:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;24599:25:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;24599:25:0;24591:34;;;;;;224207:2;;:27;;;-1:-1:-1;;;224207:27:0;;;;;;;;;;224191:12;;-1:-1:-1;;;;;224207:2:0;;:18;;:27;;;;;;;;;;;:2;:27;;;5:2:-1;;;;30:1;27;20:12;5:2;224207:27:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;224207:27:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;224207:27:0;;;224259:2;;224207:27;224259:32;;-1:-1:-1;;;224259:32:0;;;;;;;;;;224207:27;;-1:-1:-1;224244:11:0;;-1:-1:-1;;;;;224259:2:0;;;;:23;;:32;;;;;224207:27;;224259:32;;;;;;:2;:32;;;5:2:-1;;;;30:1;27;20:12;5:2;224259:32:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;224259:32:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;224259:32:0;;;;-1:-1:-1;224365:11:0;224361:470;;224387:46;224408:7;224417;224426:6;224387:20;:46::i;:::-;224361:470;;;224461:1;224451:6;:11;;:26;;;;;224476:1;224466:6;:11;;224451:26;224447:384;;;224488:46;224509:7;224518;224527:6;224488:20;:46::i;224447:384::-;224552:6;224562:2;224552:12;224548:283;;;224634:20;224657:27;224676:7;224657:18;:27::i;:::-;224634:50;;224699:15;224695:129;;;224727:2;;:30;;;-1:-1:-1;;;224727:30:0;;;;;;;;224754:2;224727:30;;;;;;-1:-1:-1;;;;;224727:2:0;;;;:17;;:30;;;;;:2;;:30;;;;;;;;:2;;:30;;;5:2:-1;;;;30:1;27;20:12;5:2;224727:30:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;224727:30:0;;;;224695:129;;;224784:2;;:30;;;-1:-1:-1;;;224784:30:0;;;;;;;;224811:2;224784:30;;;;;;-1:-1:-1;;;;;224784:2:0;;;;:17;;:30;;;;;:2;;:30;;;;;;;;:2;;:30;;;5:2:-1;;;;30:1;27;20:12;5:2;224784:30:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;224784:30:0;;;;224695:129;224548:283;;224839:2;;;;;;;;;-1:-1:-1;;;;;224839:2:0;-1:-1:-1;;;;;224839:26:0;;:28;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;224839:28:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;224839:28:0;;;;24632:1;;224107:766;:::o;223136:72::-;223166:42;223136:72;:::o;24500:20::-;;;-1:-1:-1;;;;;24500:20:0;;:::o;25246:247::-;25340:1;25325:2;-1:-1:-1;;;;;25325:2:0;25317:25;25313:96;;25369:2;;-1:-1:-1;;;;;25369:2:0;25376:10;25361:25;25353:48;;;;;-1:-1:-1;;;25353:48:0;;;;;;;;;;;;-1:-1:-1;;;25353:48:0;;;;;;;;;;;;;;;25417:2;:31;;-1:-1:-1;;;;;25417:31:0;;;-1:-1:-1;;;;;;25417:31:0;;;;;;;;;25455:32;;;;;;;;25246:247::o;227110:1384::-;227230:20;227257:21;227285:11;227303:9;227330:12;227349;227368;227387:19;227413:16;227474:2;;;;;;;;;-1:-1:-1;;;;;227474:2:0;-1:-1:-1;;;;;227474:17:0;;227492:6;227474:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;227474:25:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;227474:25:0;;;;;;;13:3:-1;8;5:12;2:2;;;30:1;27;20:12;2:2;-1:-1;227474:25:0;;;;;;;;;;;;;;;;;227557:2;;:27;;-1:-1:-1;;;227557:27:0;;;;;;;;;;227525:5;;-1:-1:-1;227474:25:0;;-1:-1:-1;227474:25:0;;-1:-1:-1;227474:25:0;;-1:-1:-1;227474:25:0;;-1:-1:-1;227525:5:0;;-1:-1:-1;;;;;227557:2:0;;;;:18;;:27;;;;;227474:25;;227557:27;;;;;;:2;:27;;;5:2:-1;;;;30:1;27;20:12;5:2;227557:27:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;227557:27:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;227557:27:0;;-1:-1:-1;227595:17:0;;;;227591:63;;227642:4;227623:23;;227591:63;227682:7;227666:23;;:12;:23;;;:58;;;;-1:-1:-1;227694:16:0;;;:29;;;227714:4;227722:1;227714:9;227694:29;227662:827;;;227741:5;227750:1;227741:10;227737:182;;;227791:2;;:32;;;-1:-1:-1;;;227791:32:0;;;;;;;;;;-1:-1:-1;;;;;227791:2:0;;;;:23;;:32;;;;;;;;;;;;;;;:2;:32;;;5:2:-1;;;;30:1;27;20:12;5:2;227791:32:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;227791:32:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;227791:32:0;;;;;;;;;-1:-1:-1;227791:32:0;-1:-1:-1;227737:182:0;;;227877:2;;:32;;;-1:-1:-1;;;227877:32:0;;;;;;;;;;-1:-1:-1;;;;;227877:2:0;;;;:23;;:32;;;;;;;;;;;;;;;:2;:32;;;5:2:-1;;;;30:1;27;20:12;5:2;227877:32:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;227877:32:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;227877:32:0;;;;;;;;;;;-1:-1:-1;227877:32:0;-1:-1:-1;227737:182:0;227933:8;;227929:553;;227958:5;227967:1;227958:10;227954:422;;;227987:7;:12;;227998:1;227987:12;227983:176;;;228035:2;;:28;;;-1:-1:-1;;;228035:28:0;;;;;;;;;;-1:-1:-1;;;;;228035:2:0;;;;:19;;:28;;;;;;;;;;;;;;;:2;:28;;;5:2:-1;;;;30:1;27;20:12;5:2;228035:28:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;228035:28:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;228035:28:0;;;;-1:-1:-1;227983:176:0;;;228117:2;;:28;;;-1:-1:-1;;;228117:28:0;;;;;;;;;;-1:-1:-1;;;;;228117:2:0;;;;:19;;:28;;;;;;;;;;;;;;;:2;:28;;;5:2:-1;;;;30:1;27;20:12;5:2;228117:28:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;228117:28:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;228117:28:0;;;;-1:-1:-1;227983:176:0;227954:422;;;228193:7;:12;;228204:1;228193:12;228189:176;;;228241:2;;:28;;;-1:-1:-1;;;228241:28:0;;;;;;;;;;-1:-1:-1;;;;;228241:2:0;;;;:19;;:28;;;;;;;;;;;;;;;:2;:28;;;5:2:-1;;;;30:1;27;20:12;5:2;228241:28:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;228241:28:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;228241:28:0;;;;-1:-1:-1;228189:176:0;;;228323:2;;:28;;;-1:-1:-1;;;228323:28:0;;;;;;;;;;-1:-1:-1;;;;;228323:2:0;;;;:19;;:28;;;;;;;;;;;;;;;:2;:28;;;5:2:-1;;;;30:1;27;20:12;5:2;228323:28:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;228323:28:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;228323:28:0;;;;-1:-1:-1;228189:176:0;228404:64;228447:20;:11;228463:3;228447:20;:15;:20;:::i;:::-;228405:36;228426:14;228405:16;:4;228414:6;228405:16;:8;:16;:::i;:::-;:20;:36;:20;:36;:::i;228404:64::-;228386:82;;227929:553;227110:1384;;;;;;;;;;;;;:::o;223095:18::-;;;-1:-1:-1;;;;;223095:18:0;;:::o;224879:248::-;224950:7;-1:-1:-1;;;;;;;;;224972:17:0;;;224968:50;;;-1:-1:-1;223166:42:0;225000:10;;224968:50;-1:-1:-1;;;;;;;;;225030:17:0;;;225026:50;;;-1:-1:-1;225065:3:0;;-1:-1:-1;;;;;225065:3:0;225058:10;;225026:50;225084:37;;;-1:-1:-1;;;225084:37:0;;;;;;;;;;;;;;;;;;;;;;;;;;;24525:30;;;-1:-1:-1;;;;;24525:30:0;;:::o;3145:471::-;3203:7;3448:6;3444:47;;-1:-1:-1;3478:1:0;3471:8;;3444:47;3515:5;;;3519:1;3515;:5;:1;3539:5;;;;;:10;3531:56;;;;-1:-1:-1;;;3531:56:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3607:1;-1:-1:-1;3145:471:0;;;;;:::o;4084:132::-;4142:7;4169:39;4173:1;4176;4169:39;;;;;;;;;;;;;;;;;:3;:39::i;2229:136::-;2287:7;2314:43;2318:1;2321;2314:43;;;;;;;;;;;;;;;;;:3;:43::i;1773:181::-;1831:7;1863:5;;;1887:6;;;;1879:46;;;;;-1:-1:-1;;;1879:46:0;;;;;;;;;;;;;;;;;;;;;;;;;;;237141:2677;237227:2;;:37;;;-1:-1:-1;;;237227:37:0;;237253:10;237227:37;;;;;;237209:15;;-1:-1:-1;;;;;237227:2:0;;:25;;:37;;;;;;;;;;;;;;:2;:37;;;5:2:-1;;;;30:1;27;20:12;5:2;237227:37:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;237227:37:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;237227:37:0;237325:2;;:40;;;-1:-1:-1;;;237325:40:0;;237354:10;237325:40;;;;;;237227:37;;-1:-1:-1;237271:11:0;;;;-1:-1:-1;;;;;237325:2:0;;:28;;:40;;;;;;;;;;;:2;:40;;;5:2:-1;;;;30:1;27;20:12;5:2;237325:40:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;237325:40:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;237325:40:0;237464:2;;237325:40;237464:15;;-1:-1:-1;;;237464:15:0;;;;237325:40;;-1:-1:-1;237372:10:0;;;;;;;;-1:-1:-1;;;;;237464:2:0;;;;:13;;:15;;;;;237325:40;;237464:15;;;;;;;;:2;:15;;;5:2:-1;;;;30:1;27;20:12;5:2;237464:15:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;237464:15:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;237464:15:0;;-1:-1:-1;237486:12:0;;;;237623:9;237595:10;237614:803;237638:10;237634:1;:14;:36;;;;;237662:8;237652:7;:18;237634:36;237614:803;;;237695:2;;:34;;;-1:-1:-1;;;237695:34:0;;237715:10;237695:34;;;;;;;;;;;;-1:-1:-1;;;;;237695:2:0;;;;:19;;:34;;;;;;;;;;;;;;;:2;:34;;;5:2:-1;;;;30:1;27;20:12;5:2;237695:34:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;237695:34:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;237695:34:0;;-1:-1:-1;237783:32:0;237802:1;237695:34;237813:1;237783:18;:32::i;:::-;237738:77;;-1:-1:-1;237738:77:0;;-1:-1:-1;237738:77:0;;-1:-1:-1;;237828:25:0;;;:53;;;;-1:-1:-1;237877:4:0;237857:24;;;;237828:53;237824:95;;;-1:-1:-1;237908:1:0;237824:95;237952:2;;:25;;;-1:-1:-1;;;237952:25:0;;;;;;;;;;-1:-1:-1;;;;;237952:2:0;;;;:17;;:25;;;;;;;;;;;;;;;:2;:25;;;5:2:-1;;;;30:1;27;20:12;5:2;237952:25:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;237952:25:0;;;;;;;13:3:-1;8;5:12;2:2;;;30:1;27;20:12;2:2;-1:-1;237952:25:0;;;;;;;;;;-1:-1:-1;237952:25:0;-1:-1:-1;237992:8:0;;;;;:20;;;238005:7;238004:8;237992:20;237988:336;;;238045:2;;:33;;;-1:-1:-1;;;238045:33:0;;;;;;;;238025:9;238045:33;;;;;;;;238025:9;;;;;-1:-1:-1;;;;;238045:2:0;;;;:19;;:33;;;;;:2;;:33;;;;;;;:2;;:33;;;5:2:-1;;;;30:1;27;20:12;5:2;238045:33:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;238045:33:0;;;;237988:336;;;238098:9;;:45;;;;-1:-1:-1;238111:2:0;;:27;;;-1:-1:-1;;;238111:27:0;;;;;;;;;;-1:-1:-1;;;;;238111:2:0;;;;:18;;:27;;;;;;;;;;;;;;;:2;:27;;;5:2:-1;;;;30:1;27;20:12;5:2;238111:27:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;238111:27:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;238111:27:0;:32;;;;238098:45;:57;;;;;238148:7;238147:8;238098:57;238094:230;;;238179:2;;:32;;;-1:-1:-1;;;238179:32:0;;;;;;;;;;-1:-1:-1;;;;;238179:2:0;;;;:23;;:32;;;;;;;;;;;;;;;:2;:32;;;5:2:-1;;;;30:1;27;20:12;5:2;238179:32:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;238179:32:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;238179:32:0;;-1:-1:-1;238226:9:0;238222:49;;238250:9;;;;;238222:49;238281:2;;:33;;;-1:-1:-1;;;238281:33:0;;;;;;;;:2;:33;;;;;;-1:-1:-1;;;;;238281:2:0;;;;:19;;:33;;;;;:2;;:33;;;;;;;;:2;;:33;;;5:2:-1;;;;30:1;27;20:12;5:2;238281:33:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;238281:33:0;;;;238094:230;238336:18;;238332:78;;238375:25;:14;238394:5;238375:25;:18;:25;:::i;:::-;238367:33;;238332:78;237672:3;;;;;237614:803;;;238442:10;238427:11;:25;238423:176;;;238463:2;;:45;;;-1:-1:-1;;;238463:45:0;;238494:10;238463:45;;;;;;;;;;;;-1:-1:-1;;;;;238463:2:0;;;;:30;;:45;;;;;:2;;:45;;;;;;;;:2;;:45;;;5:2:-1;;;;30:1;27;20:12;5:2;238463:45:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;238463:45:0;;;;238423:176;;;238536:2;;:55;;;-1:-1:-1;;;238536:55:0;;238567:10;238536:55;;;;;;;;;;;;-1:-1:-1;;;;;238536:2:0;;;;:30;;:55;;;;;:2;;:55;;;;;;;;:2;;:55;;;5:2:-1;;;;30:1;27;20:12;5:2;238536:55:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;238536:55:0;;;;238423:176;238618:2;;:41;;;-1:-1:-1;;;238618:41:0;;238648:10;238618:41;;;;;;-1:-1:-1;;;;;238618:2:0;;;;:29;;:41;;;;;;;;;;;;;;;:2;:41;;;5:2:-1;;;;30:1;27;20:12;5:2;238618:41:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;238618:41:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;238618:41:0;;-1:-1:-1;238618:41:0;;-1:-1:-1;238705:18:0;:5;238715:7;238705:18;:9;:18;:::i;:::-;238734:2;;:45;;;-1:-1:-1;;;238734:45:0;;238756:10;238734:45;;;;-1:-1:-1;;;238734:45:0;;;;238775:3;238734:45;;;;;;238697:26;;-1:-1:-1;238782:1:0;;-1:-1:-1;;;;;238734:2:0;;;;:21;;:45;;;;;;;;;;;;;;;:2;:45;;;5:2:-1;;;;30:1;27;20:12;5:2;238734:45:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;238734:45:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;238734:45:0;:49;238730:111;;;238794:2;;:39;;;-1:-1:-1;;;238794:39:0;;238808:10;238794:39;;;;-1:-1:-1;;;238794:39:0;;;;;;;;;;;;-1:-1:-1;;;;;238794:2:0;;;;:13;;:39;;;;;:2;;:39;;;;;;;;:2;;:39;;;5:2:-1;;;;30:1;27;20:12;5:2;238794:39:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;238794:39:0;;;;238730:111;238863:2;;:40;;;-1:-1:-1;;;238863:40:0;;238892:10;238863:40;;;;;;-1:-1:-1;;;;;238863:2:0;;;;:28;;:40;;;;;;;;;;;;;:2;:40;;;5:2:-1;;;;30:1;27;20:12;5:2;238863:40:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;238863:40:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;238863:40:0;;;;-1:-1:-1;238951:1:0;;-1:-1:-1;238863:40:0;;-1:-1:-1;238924:10:0;;-1:-1:-1;238959:595:0;238983:10;238979:1;:14;:36;;;;;239007:8;238997:7;:18;238979:36;238959:595;;;239040:2;;:38;;;-1:-1:-1;;;239040:38:0;;239064:10;239040:38;;;;;;;;;;;;-1:-1:-1;;;;;239040:2:0;;;;:23;;:38;;;;;;;;;;;;;;;:2;:38;;;5:2:-1;;;;30:1;27;20:12;5:2;239040:38:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;239040:38:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;239040:38:0;;-1:-1:-1;239126:32:0;239145:1;239040:38;239145:1;239126:18;:32::i;:::-;-1:-1:-1;239087:71:0;;-1:-1:-1;239087:71:0;-1:-1:-1;;239171:25:0;;;:53;;;;-1:-1:-1;239220:4:0;239200:24;;;;239171:53;239167:95;;;-1:-1:-1;239251:1:0;239167:95;239295:2;;:25;;;-1:-1:-1;;;239295:25:0;;;;;;;;;;-1:-1:-1;;;;;239295:2:0;;;;:17;;:25;;;;;;;;;;;;;;;:2;:25;;;5:2:-1;;;;30:1;27;20:12;5:2;239295:25:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;239295:25:0;;;;;;;13:3:-1;8;5:12;2:2;;;30:1;27;20:12;2:2;-1:-1;239295:25:0;;;;;;;;;;-1:-1:-1;239295:25:0;-1:-1:-1;239333:16:0;;;;:52;;-1:-1:-1;239353:2:0;;:27;;;-1:-1:-1;;;239353:27:0;;;;;;;;;;-1:-1:-1;;;;;239353:2:0;;;;:18;;:27;;;;;;;;;;;;;;;:2;:27;;;5:2:-1;;;;30:1;27;20:12;5:2;239353:27:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;239353:27:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;239353:27:0;:32;;;;239333:52;239329:132;;;239398:2;;:33;;;-1:-1:-1;;;239398:33:0;;;;;;;;:2;:33;;;;;;-1:-1:-1;;;;;239398:2:0;;;;:19;;:33;;;;;:2;;:33;;;;;;;;:2;;:33;;;5:2:-1;;;;30:1;27;20:12;5:2;239398:33:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;-1:-1;;239442:9:0;;;;;-1:-1:-1;;239329:132:0;239473:18;;239469:78;;239512:25;:14;239531:5;239512:25;:18;:25;:::i;:::-;239504:33;;239469:78;239017:3;;;;;238959:595;;;239564:9;;239560:71;;239592:2;;:30;;;-1:-1:-1;;;239592:30:0;;239604:10;239592:30;;;;;;;;;;;;-1:-1:-1;;;;;239592:2:0;;;;:11;;:30;;;;;;;;;;;;;;;:2;;:30;;;5:2:-1;;;;30:1;27;20:12;5:2;239592:30:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;239592:30:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;239592:30:0;239584:39;;;;;;239656:10;239641:11;:25;239637:176;;;239677:2;;:45;;;-1:-1:-1;;;239677:45:0;;239708:10;239677:45;;;;;;;;;;;;-1:-1:-1;;;;;239677:2:0;;;;:30;;:45;;;;;:2;;:45;;;;;;;;:2;;:45;;;5:2:-1;;;;30:1;27;20:12;5:2;239677:45:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;239677:45:0;;;;239637:176;;;239750:2;;:55;;;-1:-1:-1;;;239750:55:0;;239781:10;239750:55;;;;;;;;;;;;-1:-1:-1;;;;;239750:2:0;;;;:30;;:55;;;;;:2;;:55;;;;;;;;:2;;:55;;;5:2:-1;;;;30:1;27;20:12;5:2;239750:55:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;239750:55:0;;;;239637:176;237141:2677;;;;;;;;;;;;;;:::o;233215:1754::-;233354:2;;:28;;;-1:-1:-1;;;233354:28:0;;;;;;;;;;-1:-1:-1;;;;;233354:2:0;;;;:19;;:28;;;;;;;;;;;;;;;:2;:28;;;5:2:-1;;;;30:1;27;20:12;5:2;233354:28:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;233354:28:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;233354:28:0;:33;;233386:1;233354:33;233350:1614;;;233414:2;;:26;;;-1:-1:-1;;;233414:26:0;;;;;;;;233398:13;233414:26;;;;;;;;233398:13;;-1:-1:-1;;;;;233414:2:0;;:14;;:26;;;;;;;;;;;;;;;:2;:26;;;5:2:-1;;;;30:1;27;20:12;5:2;233414:26:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;233414:26:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;233414:26:0;233611:2;;:27;;;-1:-1:-1;;;233611:27:0;;;;;;;;:2;:27;;;;;;233414:26;;-1:-1:-1;233481:11:0;;;;;;;;;;-1:-1:-1;;;;;233611:2:0;;;;:15;;:27;;;;;;;;;;;;;:2;:27;;;5:2:-1;;;;30:1;27;20:12;5:2;233611:27:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;233611:27:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;233611:27:0;;;233658:2;;233611:27;233658:29;;-1:-1:-1;;;233658:29:0;;;;;;;;-1:-1:-1;;233658:29:0;;;;;;233611:27;;-1:-1:-1;;;;;;233658:2:0;;;;:15;;:29;;;;;233611:27;233658:29;;;;;;:2;:29;;;5:2:-1;;;;30:1;27;20:12;5:2;233658:29:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;233658:29:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;233658:29:0;;;;-1:-1:-1;233712:16:0;:6;233658:29;233712:16;:10;:16;:::i;:::-;233696:32;-1:-1:-1;233746:15:0;:6;233757:3;233746:15;:10;:15;:::i;:::-;233737:24;-1:-1:-1;233777:13:0;:4;233786:3;233777:13;:8;:13;:::i;:::-;233770:20;-1:-1:-1;233805:13:0;233801:1015;;233840:1;233831:10;;233801:1015;;;233881:2;;:30;;;-1:-1:-1;;;233881:30:0;;;;;;;;;;:47;;223249:14;;-1:-1:-1;;;;;233881:2:0;;;;:21;;:30;;;;;;;;;;;;;;;:2;:30;;;5:2:-1;;;;30:1;27;20:12;233881:47:0;233868:60;-1:-1:-1;234028:17:0;233868:60;234043:1;234028:17;:14;:17;:::i;:::-;234017:8;:28;234013:794;;;234094:2;234066:25;:6;234077:13;234066:25;:10;:25;:::i;:::-;:30;234062:559;;;234138:2;;234122:1;;-1:-1:-1;;;;;;234138:2:0;:22;234161:7;234138:2;234170:46;234138:79;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;234138:79:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;234138:79:0;;;;234249:4;234232:21;;234062:559;;;234303:2;234277:23;:4;234286:13;234277:23;:8;:23;:::i;:::-;:28;234273:348;;;234331:1;234347:2;;234331:1;;-1:-1:-1;;;;;;234347:2:0;:22;234370:7;234385:37;234379:44;;234273:348;234512:23;:4;234521:13;234512:23;:8;:23;:::i;:::-;234484:25;:6;234495:13;234484:25;:10;:25;:::i;:::-;:51;234480:141;;;234561:1;234552:10;;234480:141;;;234606:1;234597:10;;234480:141;234013:794;;;234687:23;:4;234696:13;234687:23;:8;:23;:::i;:::-;234659:25;:6;234670:13;234659:25;:10;:25;:::i;:::-;:51;234655:141;;;234736:1;234727:10;;234655:141;;;234781:1;234772:10;;234655:141;234826:2;;:34;;;-1:-1:-1;;;234826:34:0;;;;;;;;;;;;;;;;-1:-1:-1;;;;;234826:2:0;;;;:17;;:34;;;;;:2;;:34;;;;;;;;:2;;:34;;;5:2:-1;;;;30:1;27;20:12;5:2;234826:34:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;234826:34:0;;;;234875:14;234871:86;;;234902:45;234922:7;234931;234940:6;234902:19;:45::i;:::-;233350:1614;;;;;;;233215:1754;;;:::o;235046:2014::-;235187:2;;:28;;;-1:-1:-1;;;235187:28:0;;;;;;;;;;-1:-1:-1;;;;;235187:2:0;;;;:19;;:28;;;;;;;;;;;;;;;:2;:28;;;5:2:-1;;;;30:1;27;20:12;5:2;235187:28:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;235187:28:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;235187:28:0;:33;;235219:1;235187:33;235183:1872;;;235306:2;;:26;;;-1:-1:-1;;;235306:26:0;;;;;;;;:2;:26;;;;;;235231:17;;235275:6;;235231:17;;-1:-1:-1;;;;;235306:2:0;;;;:14;;:26;;;;;;;;;;;;;;;:2;:26;;;5:2:-1;;;;30:1;27;20:12;5:2;235306:26:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;235306:26:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;235306:26:0;235457:2;;:30;;;-1:-1:-1;;;235457:30:0;;;;;;;;;;235306:26;;-1:-1:-1;235439:15:0;;235457:47;;223249:14;;-1:-1:-1;;;;;235457:2:0;;;;:21;;:30;;;;;235306:26;;235457:30;;;;;;;;:2;:30;;;5:2:-1;;;;30:1;27;20:12;235457:47:0;235439:65;-1:-1:-1;235513:23:0;235702:17;235439:65;235717:1;235702:17;:14;:17;:::i;:::-;235691:8;:28;235687:77;;;-1:-1:-1;235753:1:0;235687:77;235807:2;;:28;;;-1:-1:-1;;;235807:28:0;;;;;;;;:2;:28;;;;;;235774:11;;-1:-1:-1;;;;;235807:2:0;;:16;;:28;;;;;;;;;;;:2;:28;;;5:2:-1;;;;30:1;27;20:12;5:2;235807:28:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;235807:28:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;235807:28:0;;;235873:2;;235807:28;235873:30;;-1:-1:-1;;;235873:30:0;;;;;;;;-1:-1:-1;;235873:30:0;;;;;;235807:28;;-1:-1:-1;235844:9:0;;-1:-1:-1;;;;;235873:2:0;;;;:16;;:30;;;;;235807:28;;235873:30;;;;;;:2;:30;;;5:2:-1;;;;30:1;27;20:12;5:2;235873:30:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;235873:30:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;235873:30:0;;;;-1:-1:-1;235937:1:0;235918:16;:6;235873:30;235918:16;:10;:16;:::i;:::-;:20;235914:508;;;235996:2;235955:37;235975:16;:6;235986:4;235975:16;:10;:16;:::i;:::-;235955:15;:6;235966:3;235955:15;:10;:15;:::i;:37::-;:43;;:61;;;;;236015:1;236002:10;:14;235955:61;:89;;;;;236043:1;236029:10;:15;;235955:89;:116;;;;-1:-1:-1;236048:23:0;;235955:116;235951:462;;;236095:1;;-1:-1:-1;236129:39:0;236109:60;;235951:462;;;236230:2;236191:35;236209:16;:6;236220:4;236209:16;:10;:16;:::i;:::-;236191:13;:4;236200:3;236191:13;:8;:13;:::i;:35::-;:41;;:59;;;;;236249:1;236236:10;:14;236191:59;:87;;;;;236277:1;236263:10;:15;;236191:87;:114;;;;-1:-1:-1;236282:23:0;;236191:114;236187:226;;;236329:1;;-1:-1:-1;236363:37:0;236343:58;;236187:226;236436:18;236458:1;236436:23;:63;;;;;236464:10;236478:1;236464:15;:34;;;;236483:10;236497:1;236483:15;236464:34;236432:367;;;236521:2;;-1:-1:-1;236554:39:0;236534:60;;236432:367;;;236614:18;236636:1;236614:23;:82;;;;;236642:10;236656:1;236642:15;:34;;;;236661:10;236675:1;236661:15;236642:34;:53;;;;236680:10;236694:1;236680:15;236642:53;236610:189;;;236718:2;;-1:-1:-1;236751:37:0;236731:58;;236610:189;236809:2;;:34;;;-1:-1:-1;;;236809:34:0;;;;;;;;;;;;;;;;-1:-1:-1;;;;;236809:2:0;;;;:17;;:34;;;;;:2;;:34;;;;;;;;:2;;:34;;;5:2:-1;;;;30:1;27;20:12;5:2;236809:34:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;-1:-1;;236852:2:0;;:51;;;-1:-1:-1;;;236852:51:0;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;236852:2:0;;;;-1:-1:-1;236852:22:0;;-1:-1:-1;236852:51:0;;;;;:2;;:51;;;;;;;;:2;;:51;;;5:2:-1;;;;30:1;27;20:12;5:2;236852:51:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;236852:51:0;;;;237002:45;237022:7;237031;237040:6;237002:19;:45::i;:::-;235183:1872;;;;;;;235046:2014;;;:::o;225133:1416::-;225234:2;;:30;;;-1:-1:-1;;;225234:30:0;;;;;;;;;;225193:12;;;;-1:-1:-1;;;;;225234:2:0;;;;:21;;:30;;;;;;;;;;;;;;;:2;:30;;;5:2:-1;;;;30:1;27;20:12;5:2;225234:30:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;225234:30:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;225234:30:0;;-1:-1:-1;225370:18:0;225391:20;225234:30;225406:4;225391:20;:14;:20;:::i;:::-;225469:2;;:30;;;-1:-1:-1;;;225469:30:0;;;;;;;;;;225370:41;;-1:-1:-1;225446:20:0;;-1:-1:-1;;;;;225469:2:0;;;;:21;;:30;;;;;;;;;;;;;;;:2;:30;;;5:2:-1;;;;30:1;27;20:12;5:2;225469:30:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;225469:30:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;225469:30:0;;-1:-1:-1;225506:13:0;225522:38;225469:30;225522:23;:38::i;:::-;225626:2;;:33;;;-1:-1:-1;;;225626:33:0;;;;;;;;;;225506:54;;-1:-1:-1;225596:27:0;;-1:-1:-1;;;;;225626:2:0;;;;:24;;:33;;;;;;;;;;;;;;;:2;:33;;;5:2:-1;;;;30:1;27;20:12;5:2;225626:33:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;225626:33:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;225626:33:0;225698:11;;:46;;;-1:-1:-1;;;225698:46:0;;-1:-1:-1;;;;;225698:46:0;;;;;;;;;225626:33;;-1:-1:-1;225666:29:0;;225698:11;;;;;:33;;:46;;;;;225626:33;;225698:46;;;;;;;:11;:46;;;5:2:-1;;;;30:1;27;20:12;5:2;225698:46:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;225698:46:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;225698:46:0;225803:4;;:57;;;-1:-1:-1;;;225803:57:0;;-1:-1:-1;;;;;225803:57:0;;;;;;;;;;;;;;;;;;;;;;225698:46;;-1:-1:-1;225780:20:0;;225803:4;;;;;:20;;:57;;;;;225698:46;;225803:57;;;;;;;225780:20;225803:4;:57;;;5:2:-1;;;;30:1;27;20:12;5:2;225803:57:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;225803:57:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;225803:57:0;;-1:-1:-1;225869:654:0;;;;225954:2;;:31;;;-1:-1:-1;;;225954:31:0;;;;;;;;;;225933:17;;-1:-1:-1;;;;;225954:2:0;;:22;;:31;;;;;;;;;;;:2;:31;;;5:2:-1;;;;30:1;27;20:12;5:2;225954:31:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;225954:31:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;225954:31:0;;;;;226012:4;;225954:31;226012:25;;-1:-1:-1;;;226012:25:0;;-1:-1:-1;;;;;226012:25:0;;;;;;;;;225954:31;;-1:-1:-1;225994:15:0;;226012:4;;;;;:18;;:25;;;;;225954:31;226012:25;;;;;;:4;:25;;;5:2:-1;;;;30:1;27;20:12;5:2;226012:25:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;226012:25:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;226012:25:0;;-1:-1:-1;226132:18:0;226153:39;226012:25;226153:23;:13;226171:4;226153:23;:17;:23;:::i;:39::-;226201:13;;:48;;;-1:-1:-1;;;226201:48:0;;-1:-1:-1;;;;;226201:48:0;;;;;;;;;;;;;;;226132:60;;-1:-1:-1;226201:13:0;;;:22;;:48;;;;;:13;;:48;;;;;;;;:13;;:48;;;5:2:-1;;;;30:1;27;20:12;5:2;226201:48:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;226201:48:0;;;;226298:21;226323:2;;;;;;;;;-1:-1:-1;;;;;226323:2:0;-1:-1:-1;;;;;226323:22:0;;226346:7;226323:31;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;226323:31:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;226323:31:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;226323:31:0;;;226363:2;;226323:31;226363:52;;-1:-1:-1;;;226363:52:0;;-1:-1:-1;;;;;;226363:52:0;;;;;;;;;;;;;;226323:31;;-1:-1:-1;;;;;;226363:2:0;;;;:25;;:52;;;;;:2;;:52;;;;;;;;:2;;:52;;;5:2:-1;;;;30:1;27;20:12;5:2;226363:52:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;226363:52:0;;;;226424:2;;;;;;;;;-1:-1:-1;;;;;226424:2:0;-1:-1:-1;;;;;226424:27:0;;226452:13;226467;226482:10;226424:69;;;;;;;;;;;;;-1:-1:-1;;;;;226424:69:0;-1:-1:-1;;;;;226424:69:0;;;;;;-1:-1:-1;;;;;226424:69:0;;-1:-1:-1;;;;;226424:69:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;226424:69:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;226424:69:0;;;;226511:4;226504:11;;;;;;;;;;;;;;;225869:654;-1:-1:-1;226538:5:0;;225133:1416;-1:-1:-1;;;;;;;;225133:1416:0:o;4746:345::-;4832:7;4934:12;4927:5;4919:28;;;;-1:-1:-1;;;4919:28:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23:1:-1;8:100;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;4919:28:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4958:9;4974:1;4970;:5;;;;;;;4746:345;-1:-1:-1;;;;;4746:345:0:o;2702:192::-;2788:7;2824:12;2816:6;;;;2808:29;;;;-1:-1:-1;;;2808:29:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;27:10:-1;;8:100;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;2808:29:0;-1:-1:-1;;;2860:5:0;;;2702:192::o;231967:1162::-;232073:2;;:30;;;-1:-1:-1;;;232073:30:0;;;;;;;;;;232055:15;;-1:-1:-1;;;;;232073:2:0;;:21;;:30;;;;;;;;;;;;;;:2;:30;;;5:2:-1;;;;30:1;27;20:12;5:2;232073:30:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;232073:30:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;232073:30:0;232152:2;;:20;;;-1:-1:-1;;;232152:20:0;;;;232073:30;;-1:-1:-1;232110:24:0;;232137:45;;232178:3;;232137:36;;-1:-1:-1;;;;;232152:2:0;;;;:18;;:20;;;;;232073:30;;232152:20;;;;;;;;:2;:20;;;5:2:-1;;;;30:1;27;20:12;5:2;232152:20:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;232152:20:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;232152:20:0;232137:10;;:36;:14;:36;:::i;:45::-;232266:2;;:26;;;-1:-1:-1;;;232266:26:0;;;;;;;;;;232110:72;;-1:-1:-1;232209:11:0;;;;-1:-1:-1;;;;;232266:2:0;;:18;;:26;;;;;;;;;;;:2;:26;;;5:2:-1;;;;30:1;27;20:12;5:2;232266:26:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;232266:26:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;232266:26:0;;;;;;;232299:2;;232266:26;232299:69;;-1:-1:-1;;;232299:69:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;232266:26;;-1:-1:-1;232266:26:0;;-1:-1:-1;;;;;;232299:2:0;;:23;;:69;;;;;:2;;:69;;;;;;;;:2;;:69;;;5:2:-1;;;;30:1;27;20:12;5:2;232299:69:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;232299:69:0;;;;232390:1;232381:6;:10;:24;;;;232404:1;232395:6;:10;232381:24;232377:90;;;232416:2;;:43;;;-1:-1:-1;;;232416:43:0;;232432:4;232416:43;;;;;;;;;;;;-1:-1:-1;;;;;232416:2:0;;;;:7;;:43;;;;;:2;;:43;;;;;;;;:2;;:43;;;5:2:-1;;;;30:1;27;20:12;5:2;232416:43:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;232416:43:0;;;;232377:90;232494:6;232504:1;232494:11;:26;;;;232509:6;232519:1;232509:11;232494:26;:42;;;;232524:6;232534:2;232524:12;232494:42;232490:634;;;232549:2;;:35;;;-1:-1:-1;;;232549:35:0;;;;;;;;-1:-1:-1;;232549:35:0;;;;;;-1:-1:-1;;;;;232549:2:0;;;;:21;;:35;;;;;:2;;:35;;;;;;;;:2;;:35;;;5:2:-1;;;;30:1;27;20:12;5:2;232549:35:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;-1:-1;;232593:2:0;;:31;;;-1:-1:-1;;;232593:31:0;;;;;;;;:2;:31;;;;;;;;-1:-1:-1;;;;;232593:2:0;;;;-1:-1:-1;232593:15:0;;-1:-1:-1;232593:31:0;;;;;;;;;;;:2;;:31;;;5:2:-1;;;;30:1;27;20:12;5:2;232593:31:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;-1:-1;;232647:2:0;;;:25;;;-1:-1:-1;;;232647:25:0;;;;;;;;;-1:-1:-1;;;;;232647:2:0;;;;-1:-1:-1;232647:16:0;;-1:-1:-1;232647:25:0;;;;;;;;;;;;;;:2;;:25;;;5:2:-1;;;;30:1;27;20:12;5:2;232647:25:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;232647:25:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;232490:634:0;;-1:-1:-1;232490:634:0;;232730:6;232740:1;232730:11;:26;;;;232745:6;232755:1;232745:11;232730:26;:42;;;;232760:6;232770:2;232760:12;232730:42;232726:398;;;232785:2;;:33;;;-1:-1:-1;;;232785:33:0;;;;;;;;:2;:33;;;;;;-1:-1:-1;;;;;232785:2:0;;;;:21;;:33;;;;;:2;;:33;;;;;;;;:2;;:33;;;5:2:-1;;;;30:1;27;20:12;5:2;232785:33:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;-1:-1;;232827:2:0;;:31;;;-1:-1:-1;;;232827:31:0;;;;;;;;:2;:31;;;;;;;;-1:-1:-1;;;;;232827:2:0;;;;-1:-1:-1;232827:15:0;;-1:-1:-1;232827:31:0;;;;;;;;;;;:2;;:31;;;5:2:-1;;;;30:1;27;20:12;5:2;232827:31:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;-1:-1;;232881:2:0;;;:20;;;-1:-1:-1;;;232881:20:0;;;;;;;;;-1:-1:-1;;;;;232881:2:0;;;;-1:-1:-1;232881:11:0;;-1:-1:-1;232881:20:0;;;;;:2;;:20;;;;;;;:2;;:20;;;5:2:-1;;;;30:1;27;20:12;5:2;232881:20:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;232881:20:0;;;;232912;232935:27;232954:7;232935:18;:27::i;:::-;232912:50;;233026:15;233044;:25;;233067:2;233044:25;;;233062:2;233044:25;233078:2;;:38;;;-1:-1:-1;;;233078:38:0;;;;;;;;233026:43;;;;;233078:38;;;;;;;;233026:43;;-1:-1:-1;;;;;;233078:2:0;;;;:17;;:38;;;;;:2;;:38;;;;;;;:2;;:38;;;5:2:-1;;;;30:1;27;20:12;5:2;233078:38:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;233078:38:0;;;;232726:398;;231967:1162;;;;;;;:::o
Swarm Source
bzzr://f41a8a8ea947710cff64207aa72b5df28a6495481a3c4ee214437fd5eb52634c
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.