Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
- Contract name:
- Ftso
- Optimization enabled
- true
- Compiler version
- v0.7.6+commit.7338295f
- Optimization runs
- 200
- EVM Version
- default
- Verified at
- 2022-05-06T07:13:30.355178Z
Constructor Arguments
00000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000100000000000000000000000000000000000000300000000000000000000000002f0826ef6ad107cfc861152b32b52fd11bab9ed000000000000000000000000bfa12e4e1411b62eda8b035d71735667422a6a9e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005dc00000000000000000000000000000000000000000000000000000000000000c800000000000000000000000000000000000000000000000000000000000000034254430000000000000000000000000000000000000000000000000000000000
Arg [0] (string) : BTC
Arg [1] (address) : 0x1000000000000000000000000000000000000003
Arg [2] (address) : 0x02f0826ef6ad107cfc861152b32b52fd11bab9ed
Arg [3] (address) : 0xbfa12e4e1411b62eda8b035d71735667422a6a9e
Arg [4] (uint256) : 0
Arg [5] (uint256) : 1500
Arg [6] (uint256) : 200
Contract source code
// Sources flattened with hardhat v2.3.0 https://hardhat.org // File @openzeppelin/contracts/token/ERC20/[email protected] // SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ 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 contracts/userInterfaces/IGovernanceVotePower.sol // pragma solidity 0.7.6; interface IGovernanceVotePower { /** * @notice Delegate all governance vote power of `msg.sender` to `_to`. * @param _to The address of the recipient **/ function delegate(address _to) external; /** * @notice Undelegate all governance vote power that `msg.sender` has delegated. **/ function undelegate() external; /** * @notice Get the governance vote power of `_who` at block `_blockNumber` * @param _who The address to get voting power. * @param _blockNumber The block number at which to fetch. * @return Governance vote power of `_who` at `_blockNumber`. */ function votePowerOfAt(address _who, uint256 _blockNumber) external view returns(uint256); } // File contracts/userInterfaces/IVPContractEvents.sol // pragma solidity 0.7.6; interface IVPContractEvents { /** * Event triggered when an account delegates or undelegates another account. * Definition: `votePowerFromTo(from, to)` is `changed` from `priorVotePower` to `newVotePower`. * For undelegation, `newVotePower` is 0. * * Note: the event is always emitted from VPToken's `writeVotePowerContract`. */ event Delegate(address indexed from, address indexed to, uint256 priorVotePower, uint256 newVotePower); /** * Event triggered only when account `delegator` revokes delegation to `delegatee` * for a single block in the past (typically the current vote block). * * Note: the event is always emitted from VPToken's `writeVotePowerContract` and/or `readVotePowerContract`. */ event Revoke(address indexed delegator, address indexed delegatee, uint256 votePower, uint256 blockNumber); } // File contracts/userInterfaces/IVPToken.sol // pragma solidity 0.7.6; interface IVPToken is IERC20 { /** * @notice Delegate by percentage `_bips` of voting power to `_to` from `msg.sender`. * @param _to The address of the recipient * @param _bips The percentage of voting power to be delegated expressed in basis points (1/100 of one percent). * Not cummulative - every call resets the delegation value (and value of 0 undelegates `to`). **/ function delegate(address _to, uint256 _bips) external; /** * @notice Explicitly delegate `_amount` of voting power to `_to` from `msg.sender`. * @param _to The address of the recipient * @param _amount An explicit vote power amount to be delegated. * Not cummulative - every call resets the delegation value (and value of 0 undelegates `to`). **/ function delegateExplicit(address _to, uint _amount) external; /** * @notice Revoke all delegation from sender to `_who` at given block. * Only affects the reads via `votePowerOfAtCached()` in the block `_blockNumber`. * Block `_blockNumber` must be in the past. * This method should be used only to prevent rogue delegate voting in the current voting block. * To stop delegating use delegate/delegateExplicit with value of 0 or undelegateAll/undelegateAllExplicit. * @param _who Address of the delegatee * @param _blockNumber The block number at which to revoke delegation. */ function revokeDelegationAt(address _who, uint _blockNumber) external; /** * @notice Undelegate all voting power for delegates of `msg.sender` * Can only be used with percentage delegation. * Does not reset delegation mode back to NOTSET. **/ function undelegateAll() external; /** * @notice Undelegate all explicit vote power by amount delegates for `msg.sender`. * Can only be used with explicit delegation. * Does not reset delegation mode back to NOTSET. * @param _delegateAddresses Explicit delegation does not store delegatees' addresses, * so the caller must supply them. * @return The amount still delegated (in case the list of delegates was incomplete). */ function undelegateAllExplicit(address[] memory _delegateAddresses) external returns (uint256); /** * @dev Should be compatible with ERC20 method */ function name() external view returns (string memory); /** * @dev Should be compatible with ERC20 method */ function symbol() external view returns (string memory); /** * @dev Should be compatible with ERC20 method */ function decimals() external view returns (uint8); /** * @notice Total amount of tokens at a specific `_blockNumber`. * @param _blockNumber The block number when the totalSupply is queried * @return The total amount of tokens at `_blockNumber` **/ function totalSupplyAt(uint _blockNumber) external view returns(uint256); /** * @dev Queries the token balance of `_owner` at a specific `_blockNumber`. * @param _owner The address from which the balance will be retrieved. * @param _blockNumber The block number when the balance is queried. * @return The balance at `_blockNumber`. **/ function balanceOfAt(address _owner, uint _blockNumber) external view returns (uint256); /** * @notice Get the current total vote power. * @return The current total vote power (sum of all accounts' vote powers). */ function totalVotePower() external view returns(uint256); /** * @notice Get the total vote power at block `_blockNumber` * @param _blockNumber The block number at which to fetch. * @return The total vote power at the block (sum of all accounts' vote powers). */ function totalVotePowerAt(uint _blockNumber) external view returns(uint256); /** * @notice Get the current vote power of `_owner`. * @param _owner The address to get voting power. * @return Current vote power of `_owner`. */ function votePowerOf(address _owner) external view returns(uint256); /** * @notice Get the vote power of `_owner` at block `_blockNumber` * @param _owner The address to get voting power. * @param _blockNumber The block number at which to fetch. * @return Vote power of `_owner` at `_blockNumber`. */ function votePowerOfAt(address _owner, uint256 _blockNumber) external view returns(uint256); /** * @notice Get the delegation mode for '_who'. This mode determines whether vote power is * allocated by percentage or by explicit value. Once the delegation mode is set, * it never changes, even if all delegations are removed. * @param _who The address to get delegation mode. * @return delegation mode: 0 = NOTSET, 1 = PERCENTAGE, 2 = AMOUNT (i.e. explicit) */ function delegationModeOf(address _who) external view returns(uint256); /** * @notice Get current delegated vote power `_from` delegator delegated `_to` delegatee. * @param _from Address of delegator * @param _to Address of delegatee * @return The delegated vote power. */ function votePowerFromTo(address _from, address _to) external view returns(uint256); /** * @notice Get delegated the vote power `_from` delegator delegated `_to` delegatee at `_blockNumber`. * @param _from Address of delegator * @param _to Address of delegatee * @param _blockNumber The block number at which to fetch. * @return The delegated vote power. */ function votePowerFromToAt(address _from, address _to, uint _blockNumber) external view returns(uint256); /** * @notice Compute the current undelegated vote power of `_owner` * @param _owner The address to get undelegated voting power. * @return The unallocated vote power of `_owner` */ function undelegatedVotePowerOf(address _owner) external view returns(uint256); /** * @notice Get the undelegated vote power of `_owner` at given block. * @param _owner The address to get undelegated voting power. * @param _blockNumber The block number at which to fetch. * @return The undelegated vote power of `_owner` (= owner's own balance minus all delegations from owner) */ function undelegatedVotePowerOfAt(address _owner, uint256 _blockNumber) external view returns(uint256); /** * @notice Get the vote power delegation `delegationAddresses` * and `_bips` of `_who`. Returned in two separate positional arrays. * @param _who The address to get delegations. * @return _delegateAddresses Positional array of delegation addresses. * @return _bips Positional array of delegation percents specified in basis points (1/100 or 1 percent) * @return _count The number of delegates. * @return _delegationMode The mode of the delegation (NOTSET=0, PERCENTAGE=1, AMOUNT=2). */ function delegatesOf(address _who) external view returns ( address[] memory _delegateAddresses, uint256[] memory _bips, uint256 _count, uint256 _delegationMode ); /** * @notice Get the vote power delegation `delegationAddresses` * and `pcts` of `_who`. Returned in two separate positional arrays. * @param _who The address to get delegations. * @param _blockNumber The block for which we want to know the delegations. * @return _delegateAddresses Positional array of delegation addresses. * @return _bips Positional array of delegation percents specified in basis points (1/100 or 1 percent) * @return _count The number of delegates. * @return _delegationMode The mode of the delegation (NOTSET=0, PERCENTAGE=1, AMOUNT=2). */ function delegatesOfAt(address _who, uint256 _blockNumber) external view returns ( address[] memory _delegateAddresses, uint256[] memory _bips, uint256 _count, uint256 _delegationMode ); /** * Returns VPContract used for readonly operations (view methods). * The only non-view method that might be called on it is `revokeDelegationAt`. * * @notice `readVotePowerContract` is almost always equal to `writeVotePowerContract` * except during upgrade from one VPContract to a new version (which should happen * rarely or never and will be anounced before). * * @notice You shouldn't call any methods on VPContract directly, all are exposed * via VPToken (and state changing methods are forbidden from direct calls). * This is the reason why this method returns `IVPContractEvents` - it should only be used * for listening to events (`Revoke` only). */ function readVotePowerContract() external view returns (IVPContractEvents); /** * Returns VPContract used for state changing operations (non-view methods). * The only non-view method that might be called on it is `revokeDelegationAt`. * * @notice `writeVotePowerContract` is almost always equal to `readVotePowerContract` * except during upgrade from one VPContract to a new version (which should happen * rarely or never and will be anounced before). In the case of upgrade, * `writeVotePowerContract` will be replaced first to establish delegations, and * after some perio (e.g. after a reward epoch ends) `readVotePowerContract` will be set equal to it. * * @notice You shouldn't call any methods on VPContract directly, all are exposed * via VPToken (and state changing methods are forbidden from direct calls). * This is the reason why this method returns `IVPContractEvents` - it should only be used * for listening to events (`Delegate` and `Revoke` only). */ function writeVotePowerContract() external view returns (IVPContractEvents); /** * When set, allows token owners to participate in governance voting * and delegate governance vote power. */ function governanceVotePower() external view returns (IGovernanceVotePower); } // File contracts/token/interface/IICleanable.sol // pragma solidity 0.7.6; interface IICleanable { /** * Set the contract that is allowed to call history cleaning methods. */ function setCleanerContract(address _cleanerContract) external; /** * Set the cleanup block number. * Historic data for the blocks before `cleanupBlockNumber` can be erased, * history before that block should never be used since it can be inconsistent. * In particular, cleanup block number must be before current vote power block. * @param _blockNumber The new cleanup block number. */ function setCleanupBlockNumber(uint256 _blockNumber) external; /** * Set the contract that is allowed to set cleanupBlockNumber. * Usually this will be an instance of CleanupBlockNumberManager. */ function setCleanupBlockNumberManager(address _cleanupBlockNumberManager) external; /** * Get the current cleanup block number. */ function cleanupBlockNumber() external view returns (uint256); } // File contracts/token/interface/IIVPContract.sol // pragma solidity 0.7.6; interface IIVPContract is IICleanable, IVPContractEvents { /** * Update vote powers when tokens are transfered. * Also update delegated vote powers for percentage delegation * and check for enough funds for explicit delegations. **/ function updateAtTokenTransfer( address _from, address _to, uint256 _fromBalance, uint256 _toBalance, uint256 _amount ) external; /** * @notice Delegate `_bips` percentage of voting power to `_to` from `_from` * @param _from The address of the delegator * @param _to The address of the recipient * @param _balance The delegator's current balance * @param _bips The percentage of voting power to be delegated expressed in basis points (1/100 of one percent). * Not cummulative - every call resets the delegation value (and value of 0 revokes delegation). **/ function delegate( address _from, address _to, uint256 _balance, uint256 _bips ) external; /** * @notice Explicitly delegate `_amount` of voting power to `_to` from `msg.sender`. * @param _from The address of the delegator * @param _to The address of the recipient * @param _balance The delegator's current balance * @param _amount An explicit vote power amount to be delegated. * Not cummulative - every call resets the delegation value (and value of 0 undelegates `to`). **/ function delegateExplicit( address _from, address _to, uint256 _balance, uint _amount ) external; /** * @notice Revoke all delegation from sender to `_who` at given block. * Only affects the reads via `votePowerOfAtCached()` in the block `_blockNumber`. * Block `_blockNumber` must be in the past. * This method should be used only to prevent rogue delegate voting in the current voting block. * To stop delegating use delegate/delegateExplicit with value of 0 or undelegateAll/undelegateAllExplicit. * @param _from The address of the delegator * @param _who Address of the delegatee * @param _balance The delegator's current balance * @param _blockNumber The block number at which to revoke delegation. **/ function revokeDelegationAt( address _from, address _who, uint256 _balance, uint _blockNumber ) external; /** * @notice Undelegate all voting power for delegates of `msg.sender` * Can only be used with percentage delegation. * Does not reset delegation mode back to NOTSET. * @param _from The address of the delegator **/ function undelegateAll( address _from, uint256 _balance ) external; /** * @notice Undelegate all explicit vote power by amount delegates for `msg.sender`. * Can only be used with explicit delegation. * Does not reset delegation mode back to NOTSET. * @param _from The address of the delegator * @param _delegateAddresses Explicit delegation does not store delegatees' addresses, * so the caller must supply them. * @return The amount still delegated (in case the list of delegates was incomplete). */ function undelegateAllExplicit( address _from, address[] memory _delegateAddresses ) external returns (uint256); /** * @notice Get the vote power of `_who` at block `_blockNumber` * Reads/updates cache and upholds revocations. * @param _who The address to get voting power. * @param _blockNumber The block number at which to fetch. * @return Vote power of `_who` at `_blockNumber`. */ function votePowerOfAtCached(address _who, uint256 _blockNumber) external returns(uint256); /** * @notice Get the current vote power of `_who`. * @param _who The address to get voting power. * @return Current vote power of `_who`. */ function votePowerOf(address _who) external view returns(uint256); /** * @notice Get the vote power of `_who` at block `_blockNumber` * @param _who The address to get voting power. * @param _blockNumber The block number at which to fetch. * @return Vote power of `_who` at `_blockNumber`. */ function votePowerOfAt(address _who, uint256 _blockNumber) external view returns(uint256); /** * Return vote powers for several addresses in a batch. * @param _owners The list of addresses to fetch vote power of. * @param _blockNumber The block number at which to fetch. * @return A list of vote powers. */ function batchVotePowerOfAt( address[] memory _owners, uint256 _blockNumber ) external view returns(uint256[] memory); /** * @notice Get current delegated vote power `_from` delegator delegated `_to` delegatee. * @param _from Address of delegator * @param _to Address of delegatee * @param _balance The delegator's current balance * @return The delegated vote power. */ function votePowerFromTo( address _from, address _to, uint256 _balance ) external view returns(uint256); /** * @notice Get delegated the vote power `_from` delegator delegated `_to` delegatee at `_blockNumber`. * @param _from Address of delegator * @param _to Address of delegatee * @param _balance The delegator's current balance * @param _blockNumber The block number at which to fetch. * @return The delegated vote power. */ function votePowerFromToAt( address _from, address _to, uint256 _balance, uint _blockNumber ) external view returns(uint256); /** * @notice Compute the current undelegated vote power of `_owner` * @param _owner The address to get undelegated voting power. * @param _balance Owner's current balance * @return The unallocated vote power of `_owner` */ function undelegatedVotePowerOf( address _owner, uint256 _balance ) external view returns(uint256); /** * @notice Get the undelegated vote power of `_owner` at given block. * @param _owner The address to get undelegated voting power. * @param _blockNumber The block number at which to fetch. * @return The undelegated vote power of `_owner` (= owner's own balance minus all delegations from owner) */ function undelegatedVotePowerOfAt( address _owner, uint256 _balance, uint256 _blockNumber ) external view returns(uint256); /** * @notice Get the delegation mode for '_who'. This mode determines whether vote power is * allocated by percentage or by explicit value. * @param _who The address to get delegation mode. * @return Delegation mode (NOTSET=0, PERCENTAGE=1, AMOUNT=2)) */ function delegationModeOf(address _who) external view returns (uint256); /** * @notice Get the vote power delegation `_delegateAddresses` * and `pcts` of an `_owner`. Returned in two separate positional arrays. * @param _owner The address to get delegations. * @return _delegateAddresses Positional array of delegation addresses. * @return _bips Positional array of delegation percents specified in basis points (1/100 or 1 percent) * @return _count The number of delegates. * @return _delegationMode The mode of the delegation (NOTSET=0, PERCENTAGE=1, AMOUNT=2). */ function delegatesOf( address _owner ) external view returns ( address[] memory _delegateAddresses, uint256[] memory _bips, uint256 _count, uint256 _delegationMode ); /** * @notice Get the vote power delegation `delegationAddresses` * and `pcts` of an `_owner`. Returned in two separate positional arrays. * @param _owner The address to get delegations. * @param _blockNumber The block for which we want to know the delegations. * @return _delegateAddresses Positional array of delegation addresses. * @return _bips Positional array of delegation percents specified in basis points (1/100 or 1 percent) * @return _count The number of delegates. * @return _delegationMode The mode of the delegation (NOTSET=0, PERCENTAGE=1, AMOUNT=2). */ function delegatesOfAt( address _owner, uint256 _blockNumber ) external view returns ( address[] memory _delegateAddresses, uint256[] memory _bips, uint256 _count, uint256 _delegationMode ); /** * The VPToken (or some other contract) that owns this VPContract. * All state changing methods may be called only from this address. * This is because original msg.sender is sent in `_from` parameter * and we must be sure that it cannot be faked by directly calling VPContract. * Owner token is also used in case of replacement to recover vote powers from balances. */ function ownerToken() external view returns (IVPToken); /** * Return true if this IIVPContract is configured to be used as a replacement for other contract. * It means that vote powers are not necessarily correct at the initialization, therefore * every method that reads vote power must check whether it is initialized for that address and block. */ function isReplacement() external view returns (bool); } // File contracts/token/interface/IIGovernanceVotePower.sol // pragma solidity 0.7.6; interface IIGovernanceVotePower is IGovernanceVotePower { /** * Update vote powers when tokens are transfered. **/ function updateAtTokenTransfer( address _from, address _to, uint256 _fromBalance, uint256 _toBalance, uint256 _amount ) external; /** * Set the cleanup block number. * Historic data for the blocks before `cleanupBlockNumber` can be erased, * history before that block should never be used since it can be inconsistent. * In particular, cleanup block number must be before current vote power block. * @param _blockNumber The new cleanup block number. */ function setCleanupBlockNumber(uint256 _blockNumber) external; /** * Set the contract that is allowed to call history cleaning methods. */ function setCleanerContract(address _cleanerContract) external; /** * @notice Get the token that this governance vote power contract belongs to. */ function ownerToken() external view returns(IVPToken); } // File contracts/token/interface/IIVPToken.sol // pragma solidity 0.7.6; interface IIVPToken is IVPToken, IICleanable { /** * Sets new governance vote power contract that allows token owners to participate in governance voting * and delegate governance vote power. */ function setGovernanceVotePower(IIGovernanceVotePower _governanceVotePower) external; /** * @notice Get the total vote power at block `_blockNumber` using cache. * It tries to read the cached value and if not found, reads the actual value and stores it in cache. * Can only be used if `_blockNumber` is in the past, otherwise reverts. * @param _blockNumber The block number at which to fetch. * @return The total vote power at the block (sum of all accounts' vote powers). */ function totalVotePowerAtCached(uint256 _blockNumber) external returns(uint256); /** * @notice Get the vote power of `_owner` at block `_blockNumber` using cache. * It tries to read the cached value and if not found, reads the actual value and stores it in cache. * Can only be used if _blockNumber is in the past, otherwise reverts. * @param _owner The address to get voting power. * @param _blockNumber The block number at which to fetch. * @return Vote power of `_owner` at `_blockNumber`. */ function votePowerOfAtCached(address _owner, uint256 _blockNumber) external returns(uint256); /** * Return vote powers for several addresses in a batch. * @param _owners The list of addresses to fetch vote power of. * @param _blockNumber The block number at which to fetch. * @return A list of vote powers. */ function batchVotePowerOfAt( address[] memory _owners, uint256 _blockNumber ) external view returns(uint256[] memory); } // File contracts/genesis/interface/IFtsoGenesis.sol // pragma solidity 0.7.6; interface IFtsoGenesis { /** * @notice Submits price hash for current epoch - only price submitter * @param _sender Sender address * @param _epochId Target epoch id to which hashes are submitted * @param _hash Hashed price and random number * @notice Emits PriceHashSubmitted event. */ function submitPriceHashSubmitter(address _sender, uint256 _epochId, bytes32 _hash) external; /** * @notice Reveals submitted price during epoch reveal period - only price submitter * @param _voter Voter address * @param _epochId Id of the epoch in which the price hash was submitted * @param _price Submitted price in USD * @param _random Submitted random number * @notice The hash of _price and _random must be equal to the submitted hash * @notice Emits PriceRevealed event */ function revealPriceSubmitter( address _voter, uint256 _epochId, uint256 _price, uint256 _random, uint256 _wNatVP ) external; /** * @notice Get (and cache) wNat vote power for specified voter and given epoch id * @param _voter Voter address * @param _epochId Id of the epoch in which the price hash was submitted * @return wNat vote power */ function wNatVotePowerCached(address _voter, uint256 _epochId) external returns (uint256); } // File contracts/userInterfaces/IFtso.sol // pragma solidity 0.7.6; interface IFtso { enum PriceFinalizationType { // initial state NOT_FINALIZED, // median calculation used to decide price WEIGHTED_MEDIAN, // low turnout - price decided from average of trusted addresses TRUSTED_ADDRESSES, // low turnout + no votes from trusted addresses - price copied from previous epoch PREVIOUS_PRICE_COPIED, // price decided from average of trusted addresses - triggered due to an exception TRUSTED_ADDRESSES_EXCEPTION, // previous price copied - triggered due to an exception PREVIOUS_PRICE_COPIED_EXCEPTION } // events event PriceHashSubmitted( address indexed submitter, uint256 indexed epochId, bytes32 hash, uint256 timestamp ); event PriceRevealed( address indexed voter, uint256 indexed epochId, uint256 price, uint256 random, uint256 timestamp, uint256 votePowerNat, uint256 votePowerAsset ); event PriceFinalized( uint256 indexed epochId, uint256 price, bool rewardedFtso, uint256 lowRewardPrice, uint256 highRewardPrice, PriceFinalizationType finalizationType, uint256 timestamp ); event PriceEpochInitializedOnFtso( uint256 indexed epochId, uint256 endTime, uint256 timestamp ); event LowTurnout( uint256 indexed epochId, uint256 natTurnout, uint256 lowNatTurnoutThresholdBIPS, uint256 timestamp ); /** * @notice Returns if FTSO is active */ function active() external view returns (bool); /** * @notice Returns the FTSO symbol */ function symbol() external view returns (string memory); /** * @notice Returns current epoch id */ function getCurrentEpochId() external view returns (uint256); /** * @notice Returns id of the epoch which was opened for price submission at the specified timestamp * @param _timestamp Timestamp as seconds from unix epoch */ function getEpochId(uint256 _timestamp) external view returns (uint256); /** * @notice Returns random number of the specified epoch * @param _epochId Id of the epoch */ function getRandom(uint256 _epochId) external view returns (uint256); /** * @notice Returns asset price consented in specific epoch * @param _epochId Id of the epoch * @return Price in USD multiplied by ASSET_PRICE_USD_DECIMALS */ function getEpochPrice(uint256 _epochId) external view returns (uint256); /** * @notice Returns current epoch data * @return _epochId Current epoch id * @return _epochSubmitEndTime End time of the current epoch price submission as seconds from unix epoch * @return _epochRevealEndTime End time of the current epoch price reveal as seconds from unix epoch * @return _votePowerBlock Vote power block for the current epoch * @return _fallbackMode Current epoch in fallback mode - only votes from trusted addresses will be used * @dev half-closed intervals - end time not included */ function getPriceEpochData() external view returns ( uint256 _epochId, uint256 _epochSubmitEndTime, uint256 _epochRevealEndTime, uint256 _votePowerBlock, bool _fallbackMode ); /** * @notice Returns current epoch data * @return _firstEpochStartTime First epoch start time * @return _submitPeriod Submit period in seconds * @return _revealPeriod Reveal period in seconds */ function getPriceEpochConfiguration() external view returns ( uint256 _firstEpochStartTime, uint256 _submitPeriod, uint256 _revealPeriod ); /** * @notice Returns asset price submitted by voter in specific epoch * @param _epochId Id of the epoch * @param _voter Address of the voter * @return Price in USD multiplied by ASSET_PRICE_USD_DECIMALS */ function getEpochPriceForVoter(uint256 _epochId, address _voter) external view returns (uint256); /** * @notice Returns current asset price * @return _price Price in USD multiplied by ASSET_PRICE_USD_DECIMALS * @return _timestamp Time when price was updated for the last time */ function getCurrentPrice() external view returns (uint256 _price, uint256 _timestamp); /** * @notice Returns current random number */ function getCurrentRandom() external view returns (uint256); } // File contracts/ftso/interface/IIFtso.sol // pragma solidity 0.7.6; interface IIFtso is IFtso, IFtsoGenesis { /// function finalizePriceReveal /// called by reward manager only on correct timing. /// if price reveal period for epoch x ended. finalize. /// iterate list of price submissions /// find weighted median /// find adjucant 50% of price submissions. /// Allocate reward for any price submission which is same as a "winning" submission function finalizePriceEpoch(uint256 _epochId, bool _returnRewardData) external returns( address[] memory _eligibleAddresses, uint256[] memory _natWeights, uint256 _totalNatWeight ); function averageFinalizePriceEpoch(uint256 _epochId) external; function forceFinalizePriceEpoch(uint256 _epochId) external; // activateFtso will be called by ftso manager once ftso is added // before this is done, FTSO can't run function activateFtso( uint256 _firstEpochStartTs, uint256 _epochPeriod, uint256 _revealPeriod ) external; function deactivateFtso() external; // update initial price and timestamp - only if not active function updateInitialPrice(uint256 _initialPriceUSD, uint256 _initialPriceTimestamp) external; function configureEpochs( uint256 _maxVotePowerNatThresholdFraction, uint256 _maxVotePowerAssetThresholdFraction, uint256 _lowAssetUSDThreshold, uint256 _highAssetUSDThreshold, uint256 _highAssetTurnoutThresholdBIPS, uint256 _lowNatTurnoutThresholdBIPS, address[] memory _trustedAddresses ) external; function setAsset(IIVPToken _asset) external; function setAssetFtsos(IIFtso[] memory _assetFtsos) external; // current vote power block will update per reward epoch. // the FTSO doesn't have notion of reward epochs. // reward manager only can set this data. function setVotePowerBlock(uint256 _blockNumber) external; function initializeCurrentEpochStateForReveal(uint256 _circulatingSupplyNat, bool _fallbackMode) external; /** * @notice Returns the FTSO asset * @dev Asset is null in case of multi-asset FTSO */ function getAsset() external view returns (IIVPToken); /** * @notice Returns the Asset FTSOs * @dev AssetFtsos is not null only in case of multi-asset FTSO */ function getAssetFtsos() external view returns (IIFtso[] memory); /** * @notice Returns current configuration of epoch state * @return _maxVotePowerNatThresholdFraction High threshold for native token vote power per voter * @return _maxVotePowerAssetThresholdFraction High threshold for asset vote power per voter * @return _lowAssetUSDThreshold Threshold for low asset vote power * @return _highAssetUSDThreshold Threshold for high asset vote power * @return _highAssetTurnoutThresholdBIPS Threshold for high asset turnout * @return _lowNatTurnoutThresholdBIPS Threshold for low nat turnout * @return _trustedAddresses Trusted addresses - use their prices if low nat turnout is not achieved */ function epochsConfiguration() external view returns ( uint256 _maxVotePowerNatThresholdFraction, uint256 _maxVotePowerAssetThresholdFraction, uint256 _lowAssetUSDThreshold, uint256 _highAssetUSDThreshold, uint256 _highAssetTurnoutThresholdBIPS, uint256 _lowNatTurnoutThresholdBIPS, address[] memory _trustedAddresses ); /** * @notice Returns parameters necessary for approximately replicating vote weighting. * @return _assets the list of Assets that are accounted in vote * @return _assetMultipliers weight of each asset in (multiasset) ftso, mutiplied by TERA * @return _totalVotePowerNat total native token vote power at block * @return _totalVotePowerAsset total combined asset vote power at block * @return _assetWeightRatio ratio of combined asset vp vs. native token vp (in BIPS) * @return _votePowerBlock vote powewr block for given epoch */ function getVoteWeightingParameters() external view returns ( IIVPToken[] memory _assets, uint256[] memory _assetMultipliers, uint256 _totalVotePowerNat, uint256 _totalVotePowerAsset, uint256 _assetWeightRatio, uint256 _votePowerBlock ); function wNat() external view returns (IIVPToken); } // File contracts/genesis/interface/IFtsoRegistryGenesis.sol // pragma solidity 0.7.6; interface IFtsoRegistryGenesis { function getFtsos(uint256[] memory _indices) external view returns(IFtsoGenesis[] memory _ftsos); } // File contracts/userInterfaces/IPriceSubmitter.sol // pragma solidity 0.7.6; interface IPriceSubmitter { /** * Event emitted when price hashes were submitted through PriceSubmitter. * @param submitter the address of the sender * @param epochId current price epoch id * @param ftsos array of ftsos that correspond to the indexes in call * @param hashes the submitted hashes * @param timestamp current block timestamp */ event PriceHashesSubmitted( address indexed submitter, uint256 indexed epochId, IFtsoGenesis[] ftsos, bytes32[] hashes, uint256 timestamp ); /** * Event emitted when prices were revealed through PriceSubmitter. * @param voter the address of the sender * @param epochId id of the epoch in which the price hash was submitted * @param ftsos array of ftsos that correspond to the indexes in the call * @param prices the submitted prices * @param timestamp current block timestamp */ event PricesRevealed( address indexed voter, uint256 indexed epochId, IFtsoGenesis[] ftsos, uint256[] prices, uint256[] randoms, uint256 timestamp ); /** * @notice Submits price hashes for current epoch * @param _epochId Target epoch id to which hashes are submitted * @param _ftsoIndices List of ftso indices * @param _hashes List of hashed price and random number * @notice Emits PriceHashesSubmitted event */ function submitPriceHashes( uint256 _epochId, uint256[] memory _ftsoIndices, bytes32[] memory _hashes ) external; /** * @notice Reveals submitted prices during epoch reveal period * @param _epochId Id of the epoch in which the price hashes was submitted * @param _ftsoIndices List of ftso indices * @param _prices List of submitted prices in USD * @param _randoms List of submitted random numbers * @notice The hash of _price and _random must be equal to the submitted hash * @notice Emits PricesRevealed event */ function revealPrices( uint256 _epochId, uint256[] memory _ftsoIndices, uint256[] memory _prices, uint256[] memory _randoms ) external; /** * Returns bitmap of all ftso's for which `_voter` is allowed to submit prices/hashes. * If voter is allowed to vote for ftso at index (see *_FTSO_INDEX), the corrsponding * bit in the result will be 1. */ function voterWhitelistBitmap(address _voter) external view returns (uint256); function getVoterWhitelister() external view returns (address); function getFtsoRegistry() external view returns (IFtsoRegistryGenesis); function getFtsoManager() external view returns (address); } // File contracts/userInterfaces/IFtsoManager.sol // pragma solidity 0.7.6; interface IFtsoManager { event FtsoAdded(IIFtso ftso, bool add); event FallbackMode(bool fallbackMode); event FtsoFallbackMode(IIFtso ftso, bool fallbackMode); event RewardEpochFinalized(uint256 votepowerBlock, uint256 startBlock); event PriceEpochFinalized(address chosenFtso, uint256 rewardEpochId); event InitializingCurrentEpochStateForRevealFailed(IIFtso ftso, uint256 epochId); event FinalizingPriceEpochFailed(IIFtso ftso, uint256 epochId, IFtso.PriceFinalizationType failingType); event DistributingRewardsFailed(address ftso, uint256 epochId); function active() external view returns (bool); function getPriceSubmitter() external view returns (IPriceSubmitter); function getCurrentRewardEpoch() external view returns (uint256); function getRewardEpochVotePowerBlock(uint256 _rewardEpoch) external view returns (uint256); function getCurrentPriceEpochData() external view returns ( uint256 _priceEpochId, uint256 _priceEpochStartTimestamp, uint256 _priceEpochEndTimestamp, uint256 _priceEpochRevealEndTimestamp, uint256 _currentTimestamp ); function getFtsos() external view returns (IIFtso[] memory _ftsos); function getPriceEpochConfiguration() external view returns ( uint256 _firstPriceEpochStartTs, uint256 _priceEpochDurationSeconds, uint256 _revealEpochDurationSeconds ); function getFallbackMode() external view returns ( bool _fallbackMode, IIFtso[] memory _ftsos, bool[] memory _ftsoInFallbackMode ); } // File contracts/ftso/interface/IIFtsoManager.sol // pragma solidity 0.7.6; interface IIFtsoManager is IFtsoManager { event ClosingExpiredRewardEpochFailed(uint256 _rewardEpoch); event CleanupBlockNumberManagerUnset(); event CleanupBlockNumberManagerFailedForBlock(uint256 blockNumber); function activate() external; function setGovernanceParameters( uint256 _maxVotePowerNatThresholdFraction, uint256 _maxVotePowerAssetThresholdFraction, uint256 _lowAssetUSDThreshold, uint256 _highAssetUSDThreshold, uint256 _highAssetTurnoutThresholdBIPS, uint256 _lowNatTurnoutThresholdBIPS, uint256 _rewardExpiryOffsetSeconds, address[] memory _trustedAddresses ) external; function addFtso(IIFtso _ftso) external; function removeFtso(IIFtso _ftso) external; function replaceFtso( IIFtso _ftsoToRemove, IIFtso _ftsoToAdd, bool copyCurrentPrice, bool copyAssetOrAssetFtsos ) external; function setFtsoAsset(IIFtso _ftso, IIVPToken _asset) external; function setFtsoAssetFtsos(IIFtso _ftso, IIFtso[] memory _assetFtsos) external; function setFallbackMode(bool _fallbackMode) external; function setFtsoFallbackMode(IIFtso _ftso, bool _fallbackMode) external; } // File @openzeppelin/contracts/math/[email protected] // pragma solidity >=0.6.0 <0.8.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, with an overflow flag. * * _Available since v3.4._ */ function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { uint256 c = a + b; if (c < a) return (false, 0); return (true, c); } /** * @dev Returns the substraction of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b > a) return (false, 0); return (true, a - b); } /** * @dev Returns the multiplication of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryMul(uint256 a, uint256 b) internal pure returns (bool, 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 (true, 0); uint256 c = a * b; if (c / a != b) return (false, 0); return (true, c); } /** * @dev Returns the division of two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b == 0) return (false, 0); return (true, a / b); } /** * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b == 0) return (false, 0); return (true, a % b); } /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { require(b <= a, "SafeMath: subtraction overflow"); return a - b; } /** * @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) { 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, reverting 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) { require(b > 0, "SafeMath: division by zero"); return a / b; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { require(b > 0, "SafeMath: modulo by zero"); return a % b; } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {trySub}. * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b <= a, errorMessage); return a - b; } /** * @dev Returns the integer division of two unsigned integers, reverting with custom message on * division by zero. The result is rounded towards zero. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryDiv}. * * 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, string memory errorMessage) internal pure returns (uint256) { require(b > 0, errorMessage); return a / b; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting with custom message when dividing by zero. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryMod}. * * 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, string memory errorMessage) internal pure returns (uint256) { require(b > 0, errorMessage); return a % b; } } // File contracts/utils/implementation/SafePct.sol // pragma solidity 0.7.6; /** * @dev Compute percentages safely without phantom overflows. * * Intermediate operations can overflow even when the result will always * fit into computed type. Developers usually * assume that overflows raise errors. `SafePct` restores this intuition by * reverting the transaction when such 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. * * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing * all math on `uint256` and `int256` and then downcasting. */ library SafePct { using SafeMath for uint256; /** * Requirements: * * - intermediate operations must revert on overflow */ function mulDiv(uint256 x, uint256 y, uint256 z) internal pure returns (uint256) { require(z > 0, "Division by zero"); if (x == 0) return 0; uint256 xy = x * y; if (xy / x == y) { // no overflow happened - same as in SafeMath mul return xy / z; } //slither-disable-next-line divide-before-multiply uint256 a = x / z; uint256 b = x % z; // x = a * z + b //slither-disable-next-line divide-before-multiply uint256 c = y / z; uint256 d = y % z; // y = c * z + d return (a.mul(c).mul(z)).add(a.mul(d)).add(b.mul(c)).add(b.mul(d).div(z)); } } // File @openzeppelin/contracts/utils/[email protected] // pragma solidity >=0.6.0 <0.8.0; /** * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow * checks. * * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can * easily result in undesired exploitation or bugs, since developers usually * assume that overflows raise errors. `SafeCast` restores this intuition by * reverting the transaction when such 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. * * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing * all math on `uint256` and `int256` and then downcasting. */ library SafeCast { /** * @dev Returns the downcasted uint128 from uint256, reverting on * overflow (when the input is greater than largest uint128). * * Counterpart to Solidity's `uint128` operator. * * Requirements: * * - input must fit into 128 bits */ function toUint128(uint256 value) internal pure returns (uint128) { require(value < 2**128, "SafeCast: value doesn\'t fit in 128 bits"); return uint128(value); } /** * @dev Returns the downcasted uint64 from uint256, reverting on * overflow (when the input is greater than largest uint64). * * Counterpart to Solidity's `uint64` operator. * * Requirements: * * - input must fit into 64 bits */ function toUint64(uint256 value) internal pure returns (uint64) { require(value < 2**64, "SafeCast: value doesn\'t fit in 64 bits"); return uint64(value); } /** * @dev Returns the downcasted uint32 from uint256, reverting on * overflow (when the input is greater than largest uint32). * * Counterpart to Solidity's `uint32` operator. * * Requirements: * * - input must fit into 32 bits */ function toUint32(uint256 value) internal pure returns (uint32) { require(value < 2**32, "SafeCast: value doesn\'t fit in 32 bits"); return uint32(value); } /** * @dev Returns the downcasted uint16 from uint256, reverting on * overflow (when the input is greater than largest uint16). * * Counterpart to Solidity's `uint16` operator. * * Requirements: * * - input must fit into 16 bits */ function toUint16(uint256 value) internal pure returns (uint16) { require(value < 2**16, "SafeCast: value doesn\'t fit in 16 bits"); return uint16(value); } /** * @dev Returns the downcasted uint8 from uint256, reverting on * overflow (when the input is greater than largest uint8). * * Counterpart to Solidity's `uint8` operator. * * Requirements: * * - input must fit into 8 bits. */ function toUint8(uint256 value) internal pure returns (uint8) { require(value < 2**8, "SafeCast: value doesn\'t fit in 8 bits"); return uint8(value); } /** * @dev Converts a signed int256 into an unsigned uint256. * * Requirements: * * - input must be greater than or equal to 0. */ function toUint256(int256 value) internal pure returns (uint256) { require(value >= 0, "SafeCast: value must be positive"); return uint256(value); } /** * @dev Returns the downcasted int128 from int256, reverting on * overflow (when the input is less than smallest int128 or * greater than largest int128). * * Counterpart to Solidity's `int128` operator. * * Requirements: * * - input must fit into 128 bits * * _Available since v3.1._ */ function toInt128(int256 value) internal pure returns (int128) { require(value >= -2**127 && value < 2**127, "SafeCast: value doesn\'t fit in 128 bits"); return int128(value); } /** * @dev Returns the downcasted int64 from int256, reverting on * overflow (when the input is less than smallest int64 or * greater than largest int64). * * Counterpart to Solidity's `int64` operator. * * Requirements: * * - input must fit into 64 bits * * _Available since v3.1._ */ function toInt64(int256 value) internal pure returns (int64) { require(value >= -2**63 && value < 2**63, "SafeCast: value doesn\'t fit in 64 bits"); return int64(value); } /** * @dev Returns the downcasted int32 from int256, reverting on * overflow (when the input is less than smallest int32 or * greater than largest int32). * * Counterpart to Solidity's `int32` operator. * * Requirements: * * - input must fit into 32 bits * * _Available since v3.1._ */ function toInt32(int256 value) internal pure returns (int32) { require(value >= -2**31 && value < 2**31, "SafeCast: value doesn\'t fit in 32 bits"); return int32(value); } /** * @dev Returns the downcasted int16 from int256, reverting on * overflow (when the input is less than smallest int16 or * greater than largest int16). * * Counterpart to Solidity's `int16` operator. * * Requirements: * * - input must fit into 16 bits * * _Available since v3.1._ */ function toInt16(int256 value) internal pure returns (int16) { require(value >= -2**15 && value < 2**15, "SafeCast: value doesn\'t fit in 16 bits"); return int16(value); } /** * @dev Returns the downcasted int8 from int256, reverting on * overflow (when the input is less than smallest int8 or * greater than largest int8). * * Counterpart to Solidity's `int8` operator. * * Requirements: * * - input must fit into 8 bits. * * _Available since v3.1._ */ function toInt8(int256 value) internal pure returns (int8) { require(value >= -2**7 && value < 2**7, "SafeCast: value doesn\'t fit in 8 bits"); return int8(value); } /** * @dev Converts an unsigned uint256 into a signed int256. * * Requirements: * * - input must be less than or equal to maxInt256. */ function toInt256(uint256 value) internal pure returns (int256) { require(value < 2**255, "SafeCast: value doesn't fit in an int256"); return int256(value); } } // File contracts/ftso/lib/FtsoVote.sol // pragma solidity 0.7.6; /** * @title A library used for FTSO vote management * @dev Every vote corresponds to a specific FTSO epoch */ library FtsoVote { using SafePct for uint256; struct Instance { // struct holding vote data uint128 price; // submitted price in USD uint64 weightNat; // native token weight uint64 weightAsset; // asset weight address voter; // the sender of this vote uint32 index; } uint256 internal constant TERA = 10**12; // 10^12 /** * @notice Creates a vote instance and stores data associated with the vote * @param _voter Sender of the vote * @param _votePowerNat Native token vote power * @param _votePowerAsset Asset vote power * @param _totalVotePowerNat Total native token vote power in epoch * @param _totalVotePowerAsset Total asset vote power in epoch * @param _price Price in USD submitted in a vote * @return vote The combined vote */ function _createInstance( address _voter, uint256 _votePowerNat, uint256 _votePowerAsset, uint256 _totalVotePowerNat, uint256 _totalVotePowerAsset, uint256 _price ) internal pure returns (Instance memory vote) { vote.voter = _voter; vote.weightNat = _getWeight(_votePowerNat, _totalVotePowerNat); vote.weightAsset = _getWeight(_votePowerAsset, _totalVotePowerAsset); vote.price = uint128(_price); } /** * @notice Returns the vote weight (NAT or asset) computed based on vote power * @param _votePower Vote power * @param _totalVotePower Total vote power in epoch * @return Vote weight * @dev Vote power is adjusted to uint64 and is a number between 0 and TERA */ function _getWeight(uint256 _votePower, uint256 _totalVotePower) private pure returns (uint64) { if (_totalVotePower == 0 || _votePower == 0) { return 0; } else { return uint64(_votePower.mulDiv(TERA, _totalVotePower)); } } } // File contracts/ftso/lib/FtsoEpoch.sol // pragma solidity 0.7.6; /** * @title A library used for FTSO epoch management */ library FtsoEpoch { using SafeMath for uint256; using SafePct for uint256; using SafeCast for uint256; struct State { // struct holding storage and settings related to epochs // storage mapping(uint256 => Instance) instance; // mapping from epoch id to instance mapping(IIVPToken => uint256) assetNorm; // mapping from asset address to its normalization // immutable settings uint256 firstEpochStartTime; // start time of the first epoch instance uint256 submitPeriod; // duration of price submission for an epoch instance uint256 revealPeriod; // duration of price reveal for an apoch instance // configurable settings uint240 votePowerBlock; // current block at which the vote power is checked uint256 maxVotePowerNatThresholdFraction; // high threshold for native token vote power per voter uint256 maxVotePowerAssetThresholdFraction; // high threshold for asset vote power per voter uint256 lowAssetUSDThreshold; // threshold for low asset vote power (in scaled USD) uint256 highAssetUSDThreshold; // threshold for high asset vote power (in scaled USD) uint256 highAssetTurnoutThresholdBIPS; // threshold for high asset turnout (in BIPS) uint256 lowNatTurnoutThresholdBIPS; // threshold for low nat turnout (in BIPS) address[] trustedAddresses; // trusted addresses - use their prices if low turnout is not achieved mapping(address => bool) trustedAddressesMapping; // for checking addresses in fallback mode } struct Instance { // struct holding epoch votes and results uint128 maxVotePowerNat; // max native token vote power required for voting uint128 maxVotePowerAsset; // max asset vote power required for voting uint240 votePowerBlock; // block used to obtain vote weights in epoch bool initializedForReveal; // whether epoch instance is initialized for reveal bool fallbackMode; // current epoch in fallback mode uint256 epochId; // current epoch id uint256 highAssetTurnoutThresholdBIPS; // threshold for high asset turnout (in BIPS) uint256 lowNatTurnoutThresholdBIPS; // threshold for low nat turnout (in BIPS) uint256 circulatingSupplyNat; // total native token circulating supply at votePowerBlock uint256 votePowerNat; // total native token vote power at votePowerBlock uint256 votePowerAsset; // total asset vote power at votePowerBlock uint256 accumulatedVotePowerNat; // total native token vote power accumulated from votes in epoch // base weight ratio between asset and native token used to combine weights uint256 baseWeightRatio; uint256 random; // random number associated with the epoch IIVPToken[] assets; // list of assets uint256[] assetWeightedPrices; // prices that determine the contributions of assets to vote power uint256 nextVoteIndex; mapping(uint256 => FtsoVote.Instance) votes; // array of all votes in epoch uint256 price; // consented epoch asset price IFtso.PriceFinalizationType finalizationType; // finalization type } uint256 internal constant BIPS100 = 1e4; // 100% in basis points uint256 internal constant BIPS50 = BIPS100 / 2; // 50% in basis points uint256 internal constant BIPS45 = (45 * BIPS100) / 100; // 45% in basis points uint256 internal constant BIPS5 = (5 * BIPS100) / 100; // 5% in basis points uint256 internal constant TERA = 10**12; // 10^12 /** * @notice Initializes a new epoch instance for reveal with instance specific settings * @param _state Epoch state * @param _instance Epoch instance * @param _circulatingSupplyNat Epoch native token circulating supply * @param _votePowerNat Epoch native token vote power * @param _assets List of assets * @param _assetVotePowers List of asset vote powers * @param _assetPrices List of asset prices * @dev _votePowerNat is assumed to be smaller than 2**128 to avoid overflows in computations * @dev computed votePowerAsset is assumed to be smaller than 2**128 to avoid overflows in computations */ function _initializeInstanceForReveal( State storage _state, Instance storage _instance, uint256 _circulatingSupplyNat, uint256 _votePowerNat, IIVPToken[] memory _assets, uint256[] memory _assetVotePowers, uint256[] memory _assetPrices ) internal { // all divisions guaranteed not to divide with 0 - checked in ftso manager setGovernanceParameters(...) _setAssets(_state, _instance, _assets, _assetVotePowers, _assetPrices); _instance.highAssetTurnoutThresholdBIPS = _state.highAssetTurnoutThresholdBIPS; _instance.lowNatTurnoutThresholdBIPS = _state.lowNatTurnoutThresholdBIPS; _instance.circulatingSupplyNat = _circulatingSupplyNat; _instance.votePowerNat = _votePowerNat; _instance.maxVotePowerNat = (_votePowerNat / _state.maxVotePowerNatThresholdFraction).toUint128(); _instance.maxVotePowerAsset = (_instance.votePowerAsset / _state.maxVotePowerAssetThresholdFraction).toUint128(); _instance.initializedForReveal = true; } /** * @notice Adds a vote to the vote array of an epoch instance * @param _instance Epoch instance * @param _votePowerNat Vote power for native token * @param _votePowerAsset Vote power for asset * @param _price Price in USD submitted in a vote * @param _random Random number associated with the vote */ function _addVote( Instance storage _instance, address _voter, uint256 _votePowerNat, uint256 _votePowerAsset, uint256 _price, uint256 _random ) internal { uint256 index = _instance.nextVoteIndex; FtsoVote.Instance memory vote = FtsoVote._createInstance( _voter, _votePowerNat, _votePowerAsset, _instance.votePowerNat, _instance.votePowerAsset, _price); // cast legal current max voters are ~100 extreme max can be 2000 vote.index = uint32(index); _instance.votes[index] = vote; _instance.nextVoteIndex = index + 1; _instance.accumulatedVotePowerNat = _instance.accumulatedVotePowerNat.add(_votePowerNat); _instance.random += _random; } /** * @notice Sets epoch instance data related to assets * @param _state Epoch state * @param _instance Epoch instance * @param _assets List of assets * @param _assetVotePowers List of asset vote powers * @param _assetPrices List of asset prices */ function _setAssets( State storage _state, Instance storage _instance, IIVPToken[] memory _assets, uint256[] memory _assetVotePowers, uint256[] memory _assetPrices ) internal { _instance.assets = _assets; _instance.assetWeightedPrices = _getAssetWeightedPrices(_state, _assets, _assetVotePowers, _assetPrices); // compute vote power uint256 votePower = _getAssetVotePower(_state, _instance, _assetVotePowers); _instance.votePowerAsset = votePower; // compute base weight ratio between asset and native token _instance.baseWeightRatio = _getAssetBaseWeightRatio(_state, votePower); } function _getAssetWeightedPrices( State storage _state, IIVPToken[] memory _assets, uint256[] memory _assetVotePowers, uint256[] memory _assetPrices ) internal view returns (uint256[] memory) { uint256 count = _assets.length; uint256[] memory values = new uint256[](count); // array of values which eventually contains weighted prices // compute sum of vote powers in USD uint256 votePowerSumUSD = 0; for (uint256 i = 0; i < count; i++) { if (address(_assets[i]) == address(0)) { continue; } uint256 votePowerUSD = _assetVotePowers[i].mulDiv(_assetPrices[i], _state.assetNorm[_assets[i]]); values[i] = votePowerUSD; votePowerSumUSD = votePowerSumUSD.add(votePowerUSD); } // determine asset weighted prices if (votePowerSumUSD > 0) { // determine shares based on asset vote powers in USD for (uint256 i = 0; i < count; i++) { // overriding/reusing array slots values[i] = values[i].mulDiv(_assetPrices[i].mul(BIPS100), votePowerSumUSD); } } return values; } /** * @notice Returns the id of the epoch opened for price submission at the given timestamp * @param _state Epoch state * @param _timestamp Timestamp as seconds since unix epoch * @return Epoch id * @dev Should never revert */ function _getEpochId(State storage _state, uint256 _timestamp) internal view returns (uint256) { if (_timestamp < _state.firstEpochStartTime) { return 0; } else { return (_timestamp - _state.firstEpochStartTime) / _state.submitPeriod; } } /** * @notice Returns start time of price submission for an epoch instance * @param _state Epoch state * @param _epochId Id of epoch instance * @return Timestamp as seconds since unix epoch */ function _epochSubmitStartTime(State storage _state, uint256 _epochId) internal view returns (uint256) { return _state.firstEpochStartTime + _epochId * _state.submitPeriod; } /** * @notice Returns end time of price submission for an epoch instance = reveal start time * @param _state Epoch state * @param _epochId Id of epoch instance * @return Timestamp as seconds since unix epoch * @dev half-closed interval - end time not included */ function _epochSubmitEndTime(State storage _state, uint256 _epochId) internal view returns (uint256) { return _state.firstEpochStartTime + (_epochId + 1) * _state.submitPeriod; } /** * @notice Returns end time of price reveal for an epoch instance * @param _state Epoch state * @param _epochId Id of epoch instance * @return Timestamp as seconds since unix epoch * @dev half-closed interval - end time not included */ function _epochRevealEndTime(State storage _state, uint256 _epochId) internal view returns (uint256) { return _epochSubmitEndTime(_state, _epochId) + _state.revealPeriod; } /** * @notice Determines if the epoch with the given id is currently in the reveal process * @param _state Epoch state * @param _epochId Id of epoch * @return True if epoch reveal is in process and false otherwise */ function _epochRevealInProcess(State storage _state, uint256 _epochId) internal view returns (bool) { uint256 revealStartTime = _epochSubmitEndTime(_state, _epochId); return revealStartTime <= block.timestamp && block.timestamp < revealStartTime + _state.revealPeriod; } /** * @notice Returns combined asset vote power * @param _state Epoch state * @param _instance Epoch instance * @param _votePowers Array of asset vote powers * @dev Asset vote power is specified in USD and weighted among assets */ function _getAssetVotePower( FtsoEpoch.State storage _state, FtsoEpoch.Instance storage _instance, uint256[] memory _votePowers ) internal view returns (uint256) { uint256 votePower = 0; for (uint256 i = 0; i < _instance.assets.length; i++) { if (address(_instance.assets[i]) == address(0)) { continue; } votePower = votePower.add( _instance.assetWeightedPrices[i].mulDiv( _votePowers[i], _state.assetNorm[_instance.assets[i]] ) / BIPS100 ); } return votePower; } /** * @notice Returns combined asset vote power, same as _getAssetVotePower(...), but doesn't need _instance * @param _state Epoch state * @param _assets The list of assets, some may be address(0) * @param _votePowers Array of asset vote powers * @param _assetWeightedPrices Asset weighted prices as calculated by _getAssetWeightedPrices * @dev Asset vote power is specified in USD and weighted among assets */ function _calculateAssetVotePower( FtsoEpoch.State storage _state, IIVPToken[] memory _assets, uint256[] memory _votePowers, uint256[] memory _assetWeightedPrices ) internal view returns (uint256) { uint256 votePower = 0; for (uint256 i = 0; i < _assets.length; i++) { if (address(_assets[i]) != address(0)) { uint256 vp = _assetWeightedPrices[i].mulDiv(_votePowers[i], _state.assetNorm[_assets[i]]) / BIPS100; votePower = votePower.add(vp); } } return votePower; } /** * Get multipliers for converting asset vote powers to asset vote power weights as in * FTSO price calculation. Weights are multiplied by (TERA / BIPS100 * 1e18). * Used in VoterWhitelister to emulate ftso weight calculation. */ function _getAssetVoteMultipliers( FtsoEpoch.State storage _state, IIVPToken[] memory _assets, uint256[] memory _assetWeightedPrices ) internal view returns (uint256[] memory _assetMultipliers) { uint256 numAssets = _assets.length; _assetMultipliers = new uint256[](numAssets); for (uint256 i = 0; i < numAssets; i++) { if (address(_assets[i]) != address(0)) { uint256 divisor = _state.assetNorm[_assets[i]]; // Since we divide by `_state.assetNorm[_instance.assets[i]]` we multiply by 1e18 to prevent underflow // (we assume that assetNorm is never much bigger than that) // The value is only used in VoterWhitelister._getAssetVotePowerWeights, where we divide by 1e18 _assetMultipliers[i] = _assetWeightedPrices[i].mulDiv(TERA / BIPS100 * 1e18, divisor); } else { _assetMultipliers[i] = 0; } } } /** * @notice Computes the base asset weight ratio * @param _state Epoch state * @param _assetVotePowerUSD Price of the asset in USD * @return Base weight ratio for asset in BIPS (a number between 0 and BIPS) */ function _getAssetBaseWeightRatio( State storage _state, uint256 _assetVotePowerUSD ) internal view returns (uint256) { // highAssetUSDThreshold >= lowAssetUSDThreshold - checked in ftso manager uint256 ratio; if (_assetVotePowerUSD < _state.lowAssetUSDThreshold) { // 0 % ratio = 0; } else if (_assetVotePowerUSD >= _state.highAssetUSDThreshold) { // 50 % ratio = BIPS50; } else { // between 5% and 50% (linear function) ratio = (BIPS45 * (_assetVotePowerUSD - _state.lowAssetUSDThreshold)) / (_state.highAssetUSDThreshold - _state.lowAssetUSDThreshold) + BIPS5; } return ratio; } /** * @notice Computes the weight ratio between native token and asset weight that specifies a unified vote weight * @param _instance Epoch instance * @return Weight ratio for asset in BIPS (a number between 0 and BIPS) * @dev Weight ratio for native token is supposed to be (BIPS - weight ratio for asset) */ function _getWeightRatio( Instance storage _instance, uint256 _weightNatSum, uint256 _weightAssetSum ) internal view returns (uint256) { if (_weightAssetSum == 0) { return 0; } else if (_weightNatSum == 0) { return BIPS100; } uint256 turnout = _weightAssetSum.mulDiv(BIPS100, TERA); if (turnout >= _instance.highAssetTurnoutThresholdBIPS) { return _instance.baseWeightRatio; } else { return _instance.baseWeightRatio.mulDiv(turnout, _instance.highAssetTurnoutThresholdBIPS); } } /** * @notice Computes vote weights in epoch * @param _instance Epoch instance * @param _weightsNat Array of native token weights * @param _weightsAsset Array of asset weights * @return _weights Array of combined weights * @dev All weight parameters and variables are in BIPS */ function _computeWeights( FtsoEpoch.Instance storage _instance, uint256[] memory _weightsNat, uint256[] memory _weightsAsset ) internal view returns (uint256[] memory _weights) { uint256 length = _instance.nextVoteIndex; _weights = new uint256[](length); uint256 weightNatSum = _arraySum(_weightsNat); uint256 weightAssetSum = _arraySum(_weightsAsset); // set weight distribution according to weight sums and weight ratio uint256 weightNatShare = 0; uint256 weightAssetShare = _getWeightRatio(_instance, weightNatSum, weightAssetSum); if (weightNatSum > 0) { weightNatShare = BIPS100 - weightAssetShare; } for (uint256 i = 0; i < length; i++) { uint256 weightNat = 0; if (weightNatShare > 0) { weightNat = weightNatShare.mulDiv(TERA * _weightsNat[i], weightNatSum * BIPS100); } uint256 weightAsset = 0; if (weightAssetShare > 0) { weightAsset = weightAssetShare.mulDiv(TERA * _weightsAsset[i], weightAssetSum * BIPS100); } _weights[i] = weightNat + weightAsset; } } /** * @notice Computes price deviation from the previous epoch in BIPS * @param _state Epoch state * @param _epochId Epoch id * @param _epochPrice Epoch price */ function _getPriceDeviation( State storage _state, uint256 _epochId, uint256 _epochPrice, uint256 _priceEpochCyclicBufferSize ) internal view returns (uint256) { if (_epochId == 0) { return 0; } uint256 previousEpochPrice = _state.instance[(_epochId - 1) % _priceEpochCyclicBufferSize].price; if (_epochPrice == previousEpochPrice) { return 0; } if (_epochPrice == 0) { return TERA; // "infinity" } uint256 priceEpochDiff; if (previousEpochPrice > _epochPrice) { priceEpochDiff = previousEpochPrice - _epochPrice; } else { priceEpochDiff = _epochPrice - previousEpochPrice; } return priceEpochDiff.mulDiv(BIPS100, _epochPrice); } function _findVoteOf(Instance storage _epoch, address _voter) internal view returns (uint256) { uint256 length = _epoch.nextVoteIndex; for (uint256 i = 0; i < length; i++) { if (_epoch.votes[i].voter == _voter) { return i + 1; } } return 0; } /** * Calculate sum of all values in an array. */ function _arraySum(uint256[] memory array) private pure returns (uint256) { uint256 result = 0; for (uint256 i = 0; i < array.length; i++) { result = result.add(array[i]); } return result; } } // File contracts/ftso/lib/FtsoMedian.sol // pragma solidity 0.7.6; library FtsoMedian { struct Data { // used for storing the results of weighted median calculation uint256 medianIndex; // index of the median price uint256 quartile1Index; // index of the first price corresponding to the first quartile price uint256 quartile3Index; // index of the last price corresponding to the third quartil price uint256 leftSum; // auxiliary sum of weights left from the median price uint256 rightSum; // auxiliary sum of weights right from the median price uint256 medianWeight; // weight of the median price uint256 lowWeightSum; // sum of weights corresponding to the prices too low for reward uint256 rewardedWeightSum; // sum of weights corresponding to the prices eligible for reward uint256 highWeightSum; // sum of weights corresponding to the prices too high for reward uint256 finalMedianPrice; // median price uint256 quartile1Price; // first quartile price uint256 quartile3Price; // third quartile price } struct QSVariables { // used for storing variables in quick select algorithm uint256 leftSum; // sum of values left to the current position uint256 rightSum; // sum of values right to the current position uint256 newLeftSum; // updated sum of values left to the current position uint256 newRightSum; // updated sum of values right to the current position uint256 pivotWeight; // weight associated with the pivot index uint256 leftMedianWeight; // sum of weights left to the median uint256 rightMedianWeight; // sum of weights right to the median } struct QSPositions { // used for storing positions in quick select algorithm uint256 pos; // position index uint256 left; // index left to the position index uint256 right; // index right to the position index uint256 pivotId; // pivot index } /** * @notice Computes the weighted median price and accompanying data * @param _price positional array of prices * @param _weight positional array of weights * @return _index permutation of indices of the input arrays that determines the sorting of _price * @return _d struct storing the weighted median price and accompanying data */ function _compute( uint256[] memory _price, uint256[] memory _weight ) internal view returns ( uint256[] memory _index, Data memory _d ) { uint256 count = _price.length; // initial index state _index = new uint256[](count); for (uint256 i = 0; i < count; i++) { _index[i] = i; } // quick select algorithm to find the weighted median (_d.medianIndex, _d.leftSum, _d.rightSum) = _quickSelect( 2, 0, count - 1, 0, 0, _index, _price, _weight ); _d.medianWeight = _weight[_index[_d.medianIndex]]; uint256 totalSum = _d.medianWeight + _d.leftSum + _d.rightSum; // procedure to find the first quartile bound if (_d.medianIndex == 0) { // first quartile index is 0 (_d.quartile1Index, _d.lowWeightSum, ) = (_d.medianIndex, 0, _d.rightSum); } else if (_d.leftSum <= totalSum / 4) { // left sum for median is below the first quartile threshold (_d.quartile1Index, _d.lowWeightSum, ) = (_d.medianIndex, _d.leftSum, _d.rightSum); } else { // quick select algorithm to find the first quartile bound (without moving the median index) (_d.quartile1Index, _d.lowWeightSum, ) = _quickSelect( 1, 0, _d.medianIndex - 1, 0, _d.rightSum + _d.medianWeight, _index, _price, _weight ); } // procedure to find the third quartile bound if (_d.medianIndex == count - 1) { // third quartile index is count - 1 (_d.quartile3Index, , _d.highWeightSum) = (_d.medianIndex, _d.leftSum, 0); } else if (_d.rightSum <= totalSum / 4) { // right sum for median is below the third quartile threshold (_d.quartile3Index, , _d.highWeightSum) = (_d.medianIndex, _d.leftSum, _d.rightSum); } else { // quick select algorithm to find the third quartile bound (without moving the median index) (_d.quartile3Index, , _d.highWeightSum) = _quickSelect( 3, _d.medianIndex + 1, count - 1, _d.leftSum + _d.medianWeight, 0, _index, _price, _weight ); } // final median price computation _d.finalMedianPrice = _price[_index[_d.medianIndex]]; if (_d.leftSum + _d.medianWeight == totalSum / 2 && totalSum % 2 == 0) { // if median is "in the middle", take the average price of the two consecutive prices _d.finalMedianPrice = (_d.finalMedianPrice + _closestPriceFix(_d.medianIndex, count - 1, _index, _price)) / 2; } // calculation of first and third quartile index to include indices with the same price (_d.quartile1Index, _d.lowWeightSum) = _samePriceFix( _d.quartile1Index, 0, -1, _d.lowWeightSum, _index, _price, _weight); (_d.quartile3Index, _d.highWeightSum) = _samePriceFix( _d.quartile3Index, count - 1, 1, _d.highWeightSum, _index, _price, _weight); // store the first and third quartile prices _d.quartile1Price = _price[_index[_d.quartile1Index]]; _d.quartile3Price = _price[_index[_d.quartile3Index]]; // reward weight sum _d.rewardedWeightSum = totalSum - _d.lowWeightSum - _d.highWeightSum; } /** * @notice Performs quick select algorithm */ function _quickSelect( uint256 _k, uint256 _start, uint256 _end, uint256 _leftSumInit, uint256 _rightSumInit, uint256[] memory _index, uint256[] memory _price, uint256[] memory _weight ) internal view returns (uint256, uint256, uint256) { if (_start == _end) { return (_start, _leftSumInit, _rightSumInit); } QSVariables memory s; s.leftSum = _leftSumInit; s.rightSum = _rightSumInit; QSPositions memory p; p.left = _start; p.right = _end; uint256 random = uint256(keccak256(abi.encode(block.difficulty, block.timestamp))); uint256 totalSum; while (true) { // guarantee: pos is in [left,right] and newLeftSum >= leftSum, newRightSum >= rightSum !!! //slither-disable-next-line weak-prng // no need for secure random, at worst more gas used (p.pos, s.newLeftSum, s.newRightSum) = _partition( p.left, p.right, (random % (p.right - p.left + 1)) + p.left, // pivot randomization s.leftSum, s.rightSum, _index, _price, _weight ); p.pivotId = _index[p.pos]; s.pivotWeight = _weight[p.pivotId]; totalSum = s.pivotWeight + s.newLeftSum + s.newRightSum; if (_k == 2) { // last element of s.leftMedianWeight is the real median s.leftMedianWeight = totalSum / 2 + (totalSum % 2); s.rightMedianWeight = totalSum - s.leftMedianWeight; // if newSumLeft is contains the median weight! if (s.newLeftSum >= s.leftMedianWeight && s.leftMedianWeight > _leftSumInit) { p.right = p.pos - 1; s.rightSum = s.pivotWeight + s.newRightSum; } else if (s.newRightSum > s.rightMedianWeight && s.rightMedianWeight > _rightSumInit) { p.left = p.pos + 1; s.leftSum = s.pivotWeight + s.newLeftSum; } else { return (p.pos, s.newLeftSum, s.newRightSum); } } else if (_k == 1) { s.leftMedianWeight = totalSum / 4; // rightMedianWeight contains the correct first weight s.rightMedianWeight = totalSum - s.leftMedianWeight; if (s.newLeftSum > s.leftMedianWeight && s.leftMedianWeight > _leftSumInit) { p.right = p.pos - 1; s.rightSum = s.pivotWeight + s.newRightSum; } else if (s.newRightSum >= s.rightMedianWeight && s.rightMedianWeight > _rightSumInit) { p.left = p.pos + 1; s.leftSum = s.pivotWeight + s.newLeftSum; } else { return (p.pos, s.newLeftSum, s.newRightSum); } } else { // k = 3 - outward bias due to division s.rightMedianWeight = totalSum / 4; // leftMedianWeight contains the correct last weight s.leftMedianWeight = totalSum - s.rightMedianWeight; if (s.newLeftSum >= s.leftMedianWeight && s.leftMedianWeight > _leftSumInit) { p.right = p.pos - 1; s.rightSum = s.pivotWeight + s.newRightSum; } else if (s.newRightSum > s.rightMedianWeight && s.rightMedianWeight > _rightSumInit) { p.left = p.pos + 1; s.leftSum = s.pivotWeight + s.newLeftSum; } else { return (p.pos, s.newLeftSum, s.newRightSum); } } } // should never happen assert(false); return (0, 0, 0); } /** * @notice Partitions the index array `index` according to the pivot */ function _partition( uint256 left0, uint256 right0, uint256 pivotId, uint256 leftSum0, uint256 rightSum0, uint256[] memory index, uint256[] memory price, uint256[] memory weight ) internal pure returns (uint256, uint256, uint256) { uint256 pivotValue = price[index[pivotId]]; uint256[] memory sums = new uint256[](2); sums[0] = leftSum0; sums[1] = rightSum0; uint256 left = left0; uint256 right = right0; _swap(pivotId, right, index); uint256 storeIndex = left; for (uint256 i = left; i < right; i++) { uint256 eltId = index[i]; if (price[eltId] < pivotValue) { sums[0] += weight[eltId]; // move index to the left _swap(storeIndex, i, index); storeIndex++; } else { sums[1] += weight[eltId]; } } _swap(right, storeIndex, index); return (storeIndex, sums[0], sums[1]); } /** * @notice Swaps indices `_i` and `_j` in the index array `_index` */ function _swap(uint256 _i, uint256 _j, uint256[] memory _index) internal pure { if (_i == _j) return; (_index[_i], _index[_j]) = (_index[_j], _index[_i]); } /** * @notice Handles the same price at the first or third quartile index */ function _samePriceFix( uint256 _start, uint256 _end, int256 _direction, uint256 _sumInit, uint256[] memory _index, uint256[] memory _price, uint256[] memory _weight ) internal pure returns (uint256, uint256) { uint256 weightSum = _sumInit; if ((int256(_start) - int256(_end)) * _direction >= 0) return (_start, _sumInit); uint256 thePrice = _price[_index[_start]]; int256 storeIndex = int256(_start) + _direction; uint256 eltId; for (int256 i = int256(_start) + _direction; (i - int256(_end)) * _direction <= 0; i += _direction) { eltId = _index[uint256(i)]; if (_price[eltId] == thePrice) { weightSum -= _weight[eltId]; _swap(uint256(storeIndex), uint256(i), _index); storeIndex += _direction; } } return (uint256(storeIndex - _direction), weightSum); } /** * @notice Finds the price between `_start + 1` and `_end`that is the closest to the price at `_start` index * @dev If _start = _end, _price[_start] is returned */ function _closestPriceFix( uint256 _start, uint256 _end, uint256[] memory _index, uint256[] memory _price ) internal pure returns (uint256) { if (_start == _end) { // special case return _price[_index[_start]]; } // find the closest price between `_start + 1` and `_end` uint256 closestPrice = _price[_index[_start + 1]]; uint256 newPrice; for (uint256 i = _start + 2; i <= _end; i++) { newPrice = _price[_index[i]]; // assumes all the elements to the right of start are greater or equal if (newPrice < closestPrice) { closestPrice = newPrice; } } return closestPrice; } } // File contracts/ftso/implementation/Ftso.sol // pragma solidity 0.7.6; /** * @title A contract implementing Flare Time Series Oracle */ contract Ftso is IIFtso { using FtsoEpoch for FtsoEpoch.State; // number of decimal places in Asset USD price // note that the actual USD price is the integer value divided by 10^ASSET_PRICE_USD_DECIMALS uint256 public constant ASSET_PRICE_USD_DECIMALS = 5; // errors string internal constant ERR_NOT_ACTIVE = "FTSO not active"; string internal constant ERR_ALREADY_ACTIVATED = "FTSO already activated"; string internal constant ERR_NO_ACCESS = "Access denied"; string internal constant ERR_PRICE_TOO_HIGH = "Price too high"; string internal constant ERR_PRICE_REVEAL_FAILURE = "Reveal period not active"; string internal constant ERR_PRICE_INVALID = "Price already revealed or not valid"; string internal constant ERR_EPOCH_FINALIZATION_FAILURE = "Epoch not ready for finalization"; string internal constant ERR_EPOCH_ALREADY_FINALIZED = "Epoch already finalized"; string internal constant ERR_VOTEPOWER_INSUFFICIENT = "Insufficient vote power to submit vote"; string internal constant ERR_EPOCH_NOT_INITIALIZED_FOR_REVEAL = "Epoch not initialized for reveal"; string internal constant ERR_EPOCH_DATA_NOT_AVAILABLE = "Epoch data not available"; string internal constant ERR_WRONG_EPOCH_ID = "Wrong epoch id"; string internal constant ERR_DUPLICATE_SUBMIT_IN_EPOCH = "Duplicate submit in epoch"; // storage uint256 public immutable priceDeviationThresholdBIPS; // threshold for price deviation between consecutive epochs uint256 public immutable priceEpochCyclicBufferSize; bool public override active; // activation status of FTSO string public override symbol; // asset symbol that identifies FTSO uint256 internal assetPriceUSD; // current asset USD price uint256 internal assetPriceTimestamp; // time when price was updated FtsoEpoch.State internal epochs; // epoch storage mapping(uint256 => mapping(address => bytes32)) internal epochVoterHash; uint256 internal lastRevealEpochId; // external contracts IIVPToken public immutable override wNat; // wrapped native token IIFtsoManager public immutable ftsoManager; // FTSO manager contract IPriceSubmitter public immutable priceSubmitter; // Price submitter contract IIVPToken[] public assets; // array of assets IIFtso[] public assetFtsos; // FTSOs for assets (for a multi-asset FTSO) // Revert strings get inlined and take a lot of contract space // Calling them from auxiliary functions removes used space modifier whenActive { if (!active) { revertNotActive(); } _; } modifier onlyFtsoManager { if (msg.sender != address(ftsoManager)) { revertNoAccess(); } _; } modifier onlyPriceSubmitter { if (msg.sender != address(priceSubmitter)) { revertNoAccess(); } _; } constructor( string memory _symbol, IPriceSubmitter _priceSubmitter, IIVPToken _wNat, IIFtsoManager _ftsoManager, uint256 _initialPriceUSD, uint256 _priceDeviationThresholdBIPS, uint256 _cyclicBufferSize ) { symbol = _symbol; priceSubmitter = _priceSubmitter; wNat = _wNat; ftsoManager = _ftsoManager; assetPriceUSD = _initialPriceUSD; assetPriceTimestamp = block.timestamp; priceDeviationThresholdBIPS = _priceDeviationThresholdBIPS; priceEpochCyclicBufferSize = _cyclicBufferSize; } /** * @notice Submits price hash for current epoch * @param _sender Sender address * @param _epochId Target epoch id to which hashes are submitted * @param _hash Hashed price and random number * @notice Emits PriceHashSubmitted event */ function submitPriceHashSubmitter( address _sender, uint256 _epochId, bytes32 _hash ) external override whenActive onlyPriceSubmitter { _submitPriceHash(_sender, _epochId, _hash); } /** * @notice Reveals submitted price during epoch reveal period * @param _voter Voter address * @param _epochId Id of the epoch in which the price hash was submitted * @param _price Submitted price in USD * @param _random Submitted random number * @notice The hash of _price and _random must be equal to the submitted hash * @notice Emits PriceRevealed event */ function revealPriceSubmitter( address _voter, uint256 _epochId, uint256 _price, uint256 _random, uint256 _voterWNatVP ) external override whenActive onlyPriceSubmitter { _revealPrice(_voter, _epochId, _price, _random, _voterWNatVP); } /** * @notice Computes epoch price based on gathered votes * @param _epochId Id of the epoch * @param _returnRewardData Parameter that determines if the reward data is returned * @return _eligibleAddresses List of addresses eligible for reward * @return _natWeights List of native token weights corresponding to the eligible addresses * @return _natWeightsSum Sum of weights in _natWeights */ function finalizePriceEpoch( uint256 _epochId, bool _returnRewardData ) external override onlyFtsoManager returns ( address[] memory _eligibleAddresses, uint256[] memory _natWeights, uint256 _natWeightsSum ) { FtsoEpoch.Instance storage epoch = _getEpochForFinalization(_epochId); epoch.initializedForReveal = false; // set back to false for next usage uint256 natTurnout = 0; if (epoch.circulatingSupplyNat > 0) { // no overflow - epoch.accumulatedVotePowerNat is the sum of all wNats of voters for given vote power block natTurnout = epoch.accumulatedVotePowerNat * FtsoEpoch.BIPS100 / epoch.circulatingSupplyNat; } if (epoch.fallbackMode || natTurnout <= epoch.lowNatTurnoutThresholdBIPS) { if (!epoch.fallbackMode) { emit LowTurnout(_epochId, natTurnout, epoch.lowNatTurnoutThresholdBIPS, block.timestamp); } _averageFinalizePriceEpoch(_epochId, epoch, false); // return empty reward data return (_eligibleAddresses, _natWeights, _natWeightsSum); } // finalizationType = PriceFinalizationType.WEIGHTED_MEDIAN // extract data from epoch votes to memory uint256[] memory price; uint256[] memory weight; uint256[] memory weightNat; (price, weight, weightNat) = _readVotes(epoch); // compute weighted median and truncated quartiles uint256[] memory index; FtsoMedian.Data memory data; (index, data) = FtsoMedian._compute(price, weight); // check price deviation if (epochs._getPriceDeviation(_epochId, data.finalMedianPrice, priceEpochCyclicBufferSize) > priceDeviationThresholdBIPS) { // revert to average price calculation _averageFinalizePriceEpoch(_epochId, epoch, false); // return empty reward data return (_eligibleAddresses, _natWeights, _natWeightsSum); } // store epoch results epoch.finalizationType = PriceFinalizationType.WEIGHTED_MEDIAN; epoch.price = data.finalMedianPrice; // update price assetPriceUSD = data.finalMedianPrice; assetPriceTimestamp = block.timestamp; // return reward data if requested bool rewardedFtso = false; if (_returnRewardData) { (_eligibleAddresses, _natWeights, _natWeightsSum) = _readRewardData(epoch, data, index, weightNat); if (_eligibleAddresses.length > 0) { rewardedFtso = true; } } // allow saving some informational data (no-op here) _writeEpochPriceData(_epochId, data, index, rewardedFtso); // inform about epoch result emit PriceFinalized(_epochId, epoch.price, rewardedFtso, data.quartile1Price, data.quartile3Price, epoch.finalizationType, block.timestamp); epoch.fallbackMode = false; // set back to false for next usage } /** * @notice Forces finalization of price epoch calculating average price from trusted addresses * @param _epochId Id of the epoch to finalize * @dev Used as a fallback method if epoch finalization is failing */ function averageFinalizePriceEpoch(uint256 _epochId) external override onlyFtsoManager { FtsoEpoch.Instance storage epoch = _getEpochForFinalization(_epochId); epoch.initializedForReveal = false; // set back to false for next usage _averageFinalizePriceEpoch(_epochId, epoch, true); } /** * @notice Forces finalization of price epoch - only called when exception happened * @param _epochId Id of the epoch to finalize * @dev Used as a fallback method if epoch finalization is failing */ function forceFinalizePriceEpoch(uint256 _epochId) external override onlyFtsoManager { FtsoEpoch.Instance storage epoch = _getEpochForFinalization(_epochId); epoch.initializedForReveal = false; // set back to false for next usage _forceFinalizePriceEpoch(_epochId, epoch, true); } /** * @notice Initializes ftso immutable settings and activates oracle * @param _firstEpochStartTime Timestamp of the first epoch as seconds from unix epoch * @param _submitPeriod Duration of epoch submission period in seconds * @param _revealPeriod Duration of epoch reveal period in seconds */ function activateFtso( uint256 _firstEpochStartTime, uint256 _submitPeriod, uint256 _revealPeriod ) external override onlyFtsoManager { require(!active, ERR_ALREADY_ACTIVATED); epochs.firstEpochStartTime = _firstEpochStartTime; epochs.submitPeriod = _submitPeriod; epochs.revealPeriod = _revealPeriod; active = true; } /** * @notice Deactivates oracle */ function deactivateFtso() external override whenActive onlyFtsoManager { active = false; } /** * Updates initial/current Asset price, but only if not active */ function updateInitialPrice( uint256 _initialPriceUSD, uint256 _initialPriceTimestamp ) external override onlyFtsoManager { require(!active, ERR_ALREADY_ACTIVATED); assetPriceUSD = _initialPriceUSD; assetPriceTimestamp = _initialPriceTimestamp; } /** * @notice Sets configurable settings related to epochs * @param _maxVotePowerNatThresholdFraction high threshold for native token vote power per voter * @param _maxVotePowerAssetThresholdFraction high threshold for asset vote power per voter * @param _lowAssetUSDThreshold threshold for low asset vote power * @param _highAssetUSDThreshold threshold for high asset vote power * @param _highAssetTurnoutThresholdBIPS threshold for high asset turnout * @param _lowNatTurnoutThresholdBIPS threshold for low nat turnout * @param _trustedAddresses trusted addresses - use their prices if low nat turnout is not achieved * @dev Should never revert if called from ftso manager */ function configureEpochs( uint256 _maxVotePowerNatThresholdFraction, uint256 _maxVotePowerAssetThresholdFraction, uint256 _lowAssetUSDThreshold, uint256 _highAssetUSDThreshold, uint256 _highAssetTurnoutThresholdBIPS, uint256 _lowNatTurnoutThresholdBIPS, address[] memory _trustedAddresses ) external override onlyFtsoManager { epochs.maxVotePowerNatThresholdFraction = _maxVotePowerNatThresholdFraction; epochs.maxVotePowerAssetThresholdFraction = _maxVotePowerAssetThresholdFraction; epochs.lowAssetUSDThreshold = _lowAssetUSDThreshold; epochs.highAssetUSDThreshold = _highAssetUSDThreshold; epochs.highAssetTurnoutThresholdBIPS = _highAssetTurnoutThresholdBIPS; epochs.lowNatTurnoutThresholdBIPS = _lowNatTurnoutThresholdBIPS; // remove old addresses mapping uint256 len = epochs.trustedAddresses.length; for (uint256 i = 0; i < len; i++) { epochs.trustedAddressesMapping[epochs.trustedAddresses[i]] = false; } // set new addresses mapping len = _trustedAddresses.length; for (uint256 i = 0; i < len; i++) { epochs.trustedAddressesMapping[_trustedAddresses[i]] = true; } epochs.trustedAddresses = _trustedAddresses; } /** * @notice Sets current vote power block * @param _votePowerBlock Vote power block */ function setVotePowerBlock(uint256 _votePowerBlock) external override onlyFtsoManager { // votePowerBlock must be in the past to prevent flash loan attacks require(_votePowerBlock < block.number); require(_votePowerBlock < 2 ** 240); epochs.votePowerBlock = uint240(_votePowerBlock); } /** * @notice Sets asset for FTSO to operate as single-asset oracle * @param _asset Asset */ function setAsset(IIVPToken _asset) external override onlyFtsoManager { assetFtsos = [ IIFtso(this) ]; assets = [ _asset ]; epochs.assetNorm[_asset] = 10**_asset.decimals(); } /** * @notice Sets an array of FTSOs for FTSO to operate as multi-asset oracle * @param _assetFtsos Array of FTSOs * @dev FTSOs implicitly determine the FTSO assets */ function setAssetFtsos(IIFtso[] memory _assetFtsos) external override onlyFtsoManager { assert(_assetFtsos.length > 0); assert(_assetFtsos.length > 1 || _assetFtsos[0] != this); assetFtsos = _assetFtsos; assets = new IIVPToken[](_assetFtsos.length); _refreshAssets(); } /** * @notice Initializes current epoch instance for reveal * @param _circulatingSupplyNat Epoch native token circulating supply * @param _fallbackMode Current epoch in fallback mode */ function initializeCurrentEpochStateForReveal( uint256 _circulatingSupplyNat, bool _fallbackMode ) external override onlyFtsoManager { uint256 epochId = getCurrentEpochId(); //slither-disable-next-line weak-prng // not used for random FtsoEpoch.Instance storage epoch = epochs.instance[epochId % priceEpochCyclicBufferSize]; // reset values for current epoch epoch.finalizationType = IFtso.PriceFinalizationType.NOT_FINALIZED; epoch.accumulatedVotePowerNat = 0; epoch.random = 0; // for easier testing. epoch.nextVoteIndex = 0; epoch.votePowerBlock = epochs.votePowerBlock; epoch.fallbackMode = _fallbackMode; epoch.epochId = epochId; if (_fallbackMode) { return; } uint256[] memory assetVotePowers; uint256[] memory assetPrices; (, assetVotePowers, assetPrices) = _getAssetData(); epochs._initializeInstanceForReveal( epoch, _circulatingSupplyNat, _getVotePowerAt(wNat, epochs.votePowerBlock), assets, assetVotePowers, assetPrices ); lastRevealEpochId = epochId; emit PriceEpochInitializedOnFtso(epochId, epochs._epochSubmitEndTime(epochId), block.timestamp); } /** * @notice Returns current epoch data * @return _firstEpochStartTime First epoch start time * @return _submitPeriod Submit period in seconds * @return _revealPeriod Reveal period in seconds */ function getPriceEpochConfiguration() external view override returns ( uint256 _firstEpochStartTime, uint256 _submitPeriod, uint256 _revealPeriod ) { return ( epochs.firstEpochStartTime, epochs.submitPeriod, epochs.revealPeriod ); } /** * @notice Returns current configuration of epoch state */ function epochsConfiguration() external view override returns ( uint256 _maxVotePowerNatThresholdFraction, uint256 _maxVotePowerAssetThresholdFraction, uint256 _lowAssetUSDThreshold, uint256 _highAssetUSDThreshold, uint256 _highAssetTurnoutThresholdBIPS, uint256 _lowNatTurnoutThresholdBIPS, address[] memory _trustedAddresses ) { return ( epochs.maxVotePowerNatThresholdFraction, epochs.maxVotePowerAssetThresholdFraction, epochs.lowAssetUSDThreshold, epochs.highAssetUSDThreshold, epochs.highAssetTurnoutThresholdBIPS, epochs.lowNatTurnoutThresholdBIPS, epochs.trustedAddresses ); } /** * @notice Returns the FTSO asset * @dev asset is null in case of multi-asset FTSO */ function getAsset() external view override returns (IIVPToken) { return assets.length == 1 && assetFtsos.length == 1 && assetFtsos[0] == this ? assets[0] : IIVPToken(address(0)); } /** * @notice Returns the asset FTSOs * @dev AssetFtsos is not null only in case of multi-asset FTSO */ function getAssetFtsos() external view override returns (IIFtso[] memory) { return assets.length == 1 && assetFtsos.length == 1 && assetFtsos[0] == this ? new IIFtso[](0) : assetFtsos; } /** * @notice Returns current asset price * @return _price Price in USD multiplied by ASSET_PRICE_USD_DECIMALS * @return _timestamp Time when price was updated for the last time */ function getCurrentPrice() external view override returns (uint256 _price, uint256 _timestamp) { return (assetPriceUSD, assetPriceTimestamp); } /** * @notice Returns asset price consented in specific epoch * @param _epochId Id of the epoch * @return Price in USD multiplied by ASSET_PRICE_USD_DECIMALS */ function getEpochPrice(uint256 _epochId) external view override returns (uint256) { return _getEpochInstance(_epochId).price; } /** * @notice Returns asset price submitted by voter in specific epoch * @param _epochId Id of the epoch * @param _voter Address of the voter * @return Price in USD multiplied by ASSET_PRICE_USD_DECIMALS */ function getEpochPriceForVoter(uint256 _epochId, address _voter) external view override returns (uint256) { FtsoEpoch.Instance storage epoch = _getEpochInstance(_epochId); // only used off-chain, so loop should be ok uint256 voteInd = FtsoEpoch._findVoteOf(epoch, _voter); if (voteInd == 0) return 0; // no vote from _voter return epoch.votes[voteInd - 1].price; } /** * @notice Returns current random number * @return Random number * @dev Should never revert */ function getCurrentRandom() external view override returns (uint256) { uint256 currentEpochId = getCurrentEpochId(); if (currentEpochId == 0) { return 0; } //slither-disable-next-line weak-prng // not used for random return epochs.instance[(currentEpochId - 1) % priceEpochCyclicBufferSize].random; } /** * @notice Returns random number of the specified epoch * @param _epochId Id of the epoch * @return Random number */ function getRandom(uint256 _epochId) external view override returns (uint256) { return _getEpochInstance(_epochId).random; } /** * @notice Returns current epoch data * @return _epochId Current epoch id * @return _epochSubmitEndTime End time of the current epoch price submission as seconds from unix epoch * @return _epochRevealEndTime End time of the current epoch price reveal as seconds from unix epoch * @return _votePowerBlock Vote power block for the current epoch * @return _fallbackMode Current epoch in fallback mode - only votes from trusted addresses will be used * @dev half-closed intervals - end time not included */ function getPriceEpochData() external view override returns ( uint256 _epochId, uint256 _epochSubmitEndTime, uint256 _epochRevealEndTime, uint256 _votePowerBlock, bool _fallbackMode ) { _epochId = getCurrentEpochId(); _epochSubmitEndTime = epochs._epochSubmitEndTime(_epochId); _epochRevealEndTime = _epochSubmitEndTime + epochs.revealPeriod; //slither-disable-next-line weak-prng // not used for random FtsoEpoch.Instance storage epoch = epochs.instance[_epochId % priceEpochCyclicBufferSize]; _votePowerBlock = epoch.votePowerBlock; _fallbackMode = epoch.fallbackMode; } /** * @notice Returns parameters necessary for replicating vote weighting (used in VoterWhitelister). * @return _assets the list of assets that are accounted in vote * @return _assetMultipliers weight multiplier of each asset in (multiasset) ftso * @return _totalVotePowerNat total native token vote power at block * @return _totalVotePowerAsset total combined asset vote power at block * @return _assetWeightRatio ratio of combined asset vp vs. native token vp (in BIPS) * @return _votePowerBlock vote powewr block for given epoch */ function getVoteWeightingParameters() external view override returns ( IIVPToken[] memory _assets, uint256[] memory _assetMultipliers, uint256 _totalVotePowerNat, uint256 _totalVotePowerAsset, uint256 _assetWeightRatio, uint256 _votePowerBlock ) { _assets = assets; _votePowerBlock = epochs.votePowerBlock; uint256[] memory assetVotePowers = new uint256[](_assets.length); uint256[] memory assetPrices = new uint256[](_assets.length); for (uint256 i = 0; i < _assets.length; i++) { assetVotePowers[i] = address(_assets[i]) != address(0) ? _assets[i].totalVotePowerAt(_votePowerBlock) : 0; (assetPrices[i], ) = assetFtsos[i].getCurrentPrice(); } uint256[] memory assetWeightedPrices = epochs._getAssetWeightedPrices(_assets, assetVotePowers, assetPrices); _assetMultipliers = epochs._getAssetVoteMultipliers(_assets, assetWeightedPrices); _totalVotePowerNat = wNat.totalVotePowerAt(_votePowerBlock); _totalVotePowerAsset = epochs._calculateAssetVotePower(_assets, assetVotePowers, assetWeightedPrices); _assetWeightRatio = epochs._getAssetBaseWeightRatio(_totalVotePowerAsset); } /** * @notice Returns wNat vote power for the specified owner and the given epoch id * @param _owner Owner address * @param _epochId Id of the epoch */ function wNatVotePowerCached(address _owner, uint256 _epochId) public override returns (uint256) { return _getVotePowerOfAt(wNat, _owner, _getEpochInstance(_epochId).votePowerBlock); } /** * @notice Returns current epoch id * @dev Should never revert */ function getCurrentEpochId() public view override returns (uint256) { return getEpochId(block.timestamp); } /** * @notice Returns id of the epoch which was opened for price submission at the specified timestamp * @param _timestamp Timestamp as seconds from unix epoch * @dev Should never revert */ function getEpochId(uint256 _timestamp) public view override returns (uint256) { return epochs._getEpochId(_timestamp); } /** * @notice Submits price hash for current epoch * @param _sender Sender address * @param _epochId Target epoch id to which hashes are submitted * @param _hash Hashed price and random number * @notice Emits PriceHashSubmitted event */ function _submitPriceHash(address _sender, uint256 _epochId, bytes32 _hash) internal { require(_epochId == getCurrentEpochId(), ERR_WRONG_EPOCH_ID); require(epochVoterHash[_epochId][_sender] == 0, ERR_DUPLICATE_SUBMIT_IN_EPOCH); epochVoterHash[_epochId][_sender] = _hash; emit PriceHashSubmitted(_sender, _epochId, _hash, block.timestamp); } /** * @notice Reveals submitted price during epoch reveal period * @param _voter Voter address * @param _epochId Id of the epoch in which the price hash was submitted * @param _price Submitted price in USD * @param _random Submitted random number * @notice The hash of _price and _random must be equal to the submitted hash * @notice Emits PriceRevealed event */ function _revealPrice( address _voter, uint256 _epochId, uint256 _price, uint256 _random, uint256 _voterWNatVP ) internal { require(_price < 2**128, ERR_PRICE_TOO_HIGH); require(epochs._epochRevealInProcess(_epochId), ERR_PRICE_REVEAL_FAILURE); require(epochVoterHash[_epochId][_voter] == keccak256(abi.encode(_price, _random, _voter)), ERR_PRICE_INVALID); // get epoch //slither-disable-next-line weak-prng // not used for random FtsoEpoch.Instance storage epoch = epochs.instance[_epochId % priceEpochCyclicBufferSize]; // read all storage from one slot bool fallbackMode = epoch.fallbackMode; bool initializedForReveal = epoch.initializedForReveal; uint256 votePowerBlock = uint256(epoch.votePowerBlock); require(initializedForReveal || (fallbackMode && epochs.trustedAddressesMapping[_voter]), ERR_EPOCH_NOT_INITIALIZED_FOR_REVEAL); // register vote (uint256 votePowerNat, uint256 votePowerAsset) = _getVotePowerOf( epoch, _voter, _voterWNatVP, fallbackMode, votePowerBlock ); FtsoEpoch._addVote( epoch, _voter, votePowerNat, votePowerAsset, _price, uint256(keccak256(abi.encode(_random, _price))) ); // prevent price submission from being revealed twice delete epochVoterHash[_epochId][_voter]; // inform about price reveal result emit PriceRevealed(_voter, _epochId, _price, _random, block.timestamp, votePowerNat, votePowerAsset); } /** * @notice Returns the list of assets and its vote powers * @return _assets List of assets * @return _votePowers List of vote powers * @return _prices List of asset prices */ function _getAssetData() internal returns ( IIVPToken[] memory _assets, uint256[] memory _votePowers, uint256[] memory _prices ) { _refreshAssets(); _assets = assets; // compute vote power for each epoch _votePowers = new uint256[](_assets.length); _prices = new uint256[](_assets.length); for (uint256 i = 0; i < _assets.length; i++) { _votePowers[i] = _getVotePowerAt(_assets[i], epochs.votePowerBlock); (_prices[i], ) = assetFtsos[i].getCurrentPrice(); } } /** * @notice Refreshes epoch state assets if FTSO is in multi-asset mode * @dev Assets are determined by other single-asset FTSOs on which the asset may change at any time */ function _refreshAssets() internal { if (assetFtsos.length == 1 && assetFtsos[0] == this) { return; } else { for (uint256 i = 0; i < assetFtsos.length; i++) { IIVPToken asset = assetFtsos[i].getAsset(); if (asset == assets[i]) { continue; } assets[i] = asset; if (address(asset) != address(0)) { epochs.assetNorm[asset] = 10**asset.decimals(); } } } } /** * @notice Forces finalization of the epoch calculating average price from trusted addresses * @param _epochId Epoch id * @param _epoch Epoch instance * @param _exception Indicates if the exception happened * @dev Sets the price to be the average of prices from trusted addresses or force finalize if no votes submitted */ function _averageFinalizePriceEpoch( uint256 _epochId, FtsoEpoch.Instance storage _epoch, bool _exception ) internal { uint256 _priceSum; uint256 _count; // extract data from epoch trusted votes to memory (_priceSum, _count) = _readTrustedVotes(_epoch, epochs.trustedAddresses); if (_count > 0) { // finalizationType = PriceFinalizationType.TRUSTED_ADDRESSES _epoch.price = _priceSum / _count; _epoch.finalizationType = _exception ? PriceFinalizationType.TRUSTED_ADDRESSES_EXCEPTION : PriceFinalizationType.TRUSTED_ADDRESSES; // update price assetPriceUSD = _epoch.price; assetPriceTimestamp = block.timestamp; _writeFallbackEpochPriceData(_epochId); // inform about epoch result emit PriceFinalized(_epochId, _epoch.price, false, 0, 0, _epoch.finalizationType, block.timestamp); _epoch.fallbackMode = false; // set back to false for next usage } else { // finalizationType = PriceFinalizationType.PREVIOUS_PRICE_COPIED _forceFinalizePriceEpoch(_epochId, _epoch, _exception); } } /** * @notice Forces finalization of the epoch * @param _epochId Epoch id * @param _epoch Epoch instance * @param _exception Indicates if the exception happened * @dev Sets the median price to be equal to the price from the previous epoch (if epoch id is 0, price is 0) */ function _forceFinalizePriceEpoch( uint256 _epochId, FtsoEpoch.Instance storage _epoch, bool _exception ) internal { if (_epochId > 0) { _epoch.price = assetPriceUSD; } else { _epoch.price = 0; } _epoch.finalizationType = _exception ? PriceFinalizationType.PREVIOUS_PRICE_COPIED_EXCEPTION : PriceFinalizationType.PREVIOUS_PRICE_COPIED; _writeFallbackEpochPriceData(_epochId); emit PriceFinalized(_epochId, _epoch.price, false, 0, 0, _epoch.finalizationType, block.timestamp); _epoch.fallbackMode = false; // set back to false for next usage } /** * @notice Stores epoch data related to price * To be implemented in descendants */ function _writeEpochPriceData( uint256 /*_epochId*/, FtsoMedian.Data memory /*_data*/, uint256[] memory /*_index*/, bool /*rewardedFtso*/ ) internal virtual { /* empty block */ } /** * @notice Stores epoch data related to price (fallback / low turnout / forced mode) * To be implemented in descendants */ function _writeFallbackEpochPriceData(uint256 /*_epochId*/) internal virtual { } /** * @notice Returns native token and asset vote power for epoch - returns (0, 0) if in fallback mode * @param _epoch Epoch instance * @param _voter Voter (price provider) address * @param _voterWNatVP Voter nat vote power as queried by price submitter * @dev Checks if vote power is sufficient and adjusts vote power if it is too large */ function _getVotePowerOf( FtsoEpoch.Instance storage _epoch, address _voter, uint256 _voterWNatVP, bool _fallbackMode, uint256 _votePowerBlock ) internal returns ( uint256 _votePowerNat, uint256 _votePowerAsset ) { if (_fallbackMode) { return (0, 0); } _votePowerNat = _voterWNatVP; _votePowerAsset = _calculateAssetVotePower(_epoch, _voter, _votePowerBlock); uint256 maxVotePowerNat = _epoch.maxVotePowerNat; uint256 maxVotePowerAsset= _epoch.maxVotePowerAsset; if (_votePowerNat > maxVotePowerNat) { _votePowerNat = maxVotePowerNat; } if (_votePowerAsset > maxVotePowerAsset) { _votePowerAsset = maxVotePowerAsset; } } /** * @notice Returns vote power of the given token at the specified block * @param _vp Vote power token * @param _vpBlock Vote power block * @dev Returns 0 if vote power token is null */ function _getVotePowerAt(IIVPToken _vp, uint256 _vpBlock) internal returns (uint256) { if (address(_vp) == address(0)) { return 0; } else { return _vp.totalVotePowerAtCached(_vpBlock); } } /** * @notice Returns vote power of the given token at the specified block and for the specified owner * @param _vp Vote power token * @param _owner Owner address * @param _vpBlock Vote power block * @dev Returns 0 if vote power token is null */ function _getVotePowerOfAt(IIVPToken _vp, address _owner, uint256 _vpBlock) internal returns (uint256) { if (address(_vp) == address(0)) { return 0; } else { return _vp.votePowerOfAtCached(_owner, _vpBlock); } } function _calculateAssetVotePower(FtsoEpoch.Instance storage _epoch, address _owner, uint256 _votePowerBlock) internal returns (uint256 _votePowerAsset) { uint256[] memory votePowersAsset = new uint256[](_epoch.assets.length); for (uint256 i = 0; i < _epoch.assets.length; i++) { votePowersAsset[i] = _getVotePowerOfAt(_epoch.assets[i], _owner, _votePowerBlock); } _votePowerAsset = epochs._getAssetVotePower(_epoch, votePowersAsset); } /** * @notice Extract vote data from epoch * @param _epoch Epoch instance */ function _readVotes(FtsoEpoch.Instance storage _epoch) internal view returns ( uint256[] memory _price, uint256[] memory _weight, uint256[] memory _weightNat ) { uint256 length = _epoch.nextVoteIndex; _price = new uint256[](length); _weightNat = new uint256[](length); uint256[] memory weightAsset = new uint256[](length); for (uint256 i = 0; i < length; i++) { FtsoVote.Instance storage v = _epoch.votes[i]; _price[i] = v.price; _weightNat[i] = v.weightNat; weightAsset[i] = v.weightAsset; } _weight = FtsoEpoch._computeWeights(_epoch, _weightNat, weightAsset); } /** * @notice Extract trusted vote data from epoch * @param _epoch Epoch instance * @param _trustedAddresses List of trusted addresses * @return _priceSum Sum of all prices submitted by trusted addresses * @return _count Number of prices submitted by trusted addresses */ function _readTrustedVotes(FtsoEpoch.Instance storage _epoch, address[] memory _trustedAddresses) internal view returns ( uint256 _priceSum, uint256 _count ) { uint256 length = _epoch.nextVoteIndex; for (uint256 i = 0; i < length; i++) { address voter = _epoch.votes[i].voter; for (uint256 j = 0; j < _trustedAddresses.length; j++) { if (voter == _trustedAddresses[j]) { _priceSum += _epoch.votes[i].price; // no overflow as v.price < 2**128 _count++; } } } } /** * @notice Extracts reward data for epoch * @param _epoch The epoch instance to read data from * @param _data Median computation data * @param _index Array of vote indices * @param _weightNat Array of native token weights */ function _readRewardData( FtsoEpoch.Instance storage _epoch, FtsoMedian.Data memory _data, uint256[] memory _index, uint256[] memory _weightNat ) internal view returns ( address[] memory _eligibleAddresses, uint256[] memory _natWeights, uint256 _natWeightsSum ) { uint256 random = _epoch.random; uint256 voteRewardCount = 0; for (uint256 i = _data.quartile1Index; i <= _data.quartile3Index; i++) { uint256 idx = _index[i]; if (_weightNat[idx] > 0) { uint128 price = _epoch.votes[idx].price; if ((price == _data.quartile1Price || price == _data.quartile3Price) && ! _isAddressEligible(random, _epoch.votes[idx].voter)) { continue; } voteRewardCount++; } } _eligibleAddresses = new address[](voteRewardCount); _natWeights = new uint256[](voteRewardCount); uint256 cnt = 0; for (uint256 i = _data.quartile1Index; i <= _data.quartile3Index; i++) { uint256 idx = _index[i]; uint256 weight = _weightNat[idx]; if (weight > 0) { uint128 price = _epoch.votes[idx].price; if ((price == _data.quartile1Price || price == _data.quartile3Price) && ! _isAddressEligible(random, _epoch.votes[idx].voter)) { continue; } _eligibleAddresses[cnt] = _epoch.votes[idx].voter; _natWeights[cnt] = weight; _natWeightsSum += weight; cnt++; } } } /** * @notice Get epoch instance for given epoch id and check if it can be finished * @param _epochId Epoch id * @return _epoch Return epoch instance */ function _getEpochForFinalization(uint256 _epochId) internal view returns (FtsoEpoch.Instance storage _epoch) { require(block.timestamp >= epochs._epochRevealEndTime(_epochId), ERR_EPOCH_FINALIZATION_FAILURE); _epoch = _getEpochInstance(_epochId); require(_epoch.finalizationType == PriceFinalizationType.NOT_FINALIZED, ERR_EPOCH_ALREADY_FINALIZED); } /** * @notice Return epoch instance if epoch id exists in storage, reverts if it is already overwritten * @param _epochId Epoch id */ function _getEpochInstance(uint256 _epochId) internal view returns (FtsoEpoch.Instance storage _epoch) { //slither-disable-next-line weak-prng // not used for random _epoch = epochs.instance[_epochId % priceEpochCyclicBufferSize]; require(_epochId == _epoch.epochId, ERR_EPOCH_DATA_NOT_AVAILABLE); } /** * @notice Checks if an address is eligible for reward (for edge quartile cases) * @param _random Current random for this Ftso * @param _address Address that submitted the price * @return _eligible Return True if the address should be rewarded */ function _isAddressEligible(uint256 _random, address _address) internal pure returns (bool _eligible) { _eligible = ((uint256(keccak256(abi.encode(_random, _address))) % 2) == 1); } function revertNoAccess() internal pure { revert(ERR_NO_ACCESS); } function revertNotActive() internal pure { revert(ERR_NOT_ACTIVE); } }
Contract ABI
[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"string","name":"_symbol","internalType":"string"},{"type":"address","name":"_priceSubmitter","internalType":"contract IPriceSubmitter"},{"type":"address","name":"_wNat","internalType":"contract IIVPToken"},{"type":"address","name":"_ftsoManager","internalType":"contract IIFtsoManager"},{"type":"uint256","name":"_initialPriceUSD","internalType":"uint256"},{"type":"uint256","name":"_priceDeviationThresholdBIPS","internalType":"uint256"},{"type":"uint256","name":"_cyclicBufferSize","internalType":"uint256"}]},{"type":"event","name":"LowTurnout","inputs":[{"type":"uint256","name":"epochId","internalType":"uint256","indexed":true},{"type":"uint256","name":"natTurnout","internalType":"uint256","indexed":false},{"type":"uint256","name":"lowNatTurnoutThresholdBIPS","internalType":"uint256","indexed":false},{"type":"uint256","name":"timestamp","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"PriceEpochInitializedOnFtso","inputs":[{"type":"uint256","name":"epochId","internalType":"uint256","indexed":true},{"type":"uint256","name":"endTime","internalType":"uint256","indexed":false},{"type":"uint256","name":"timestamp","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"PriceFinalized","inputs":[{"type":"uint256","name":"epochId","internalType":"uint256","indexed":true},{"type":"uint256","name":"price","internalType":"uint256","indexed":false},{"type":"bool","name":"rewardedFtso","internalType":"bool","indexed":false},{"type":"uint256","name":"lowRewardPrice","internalType":"uint256","indexed":false},{"type":"uint256","name":"highRewardPrice","internalType":"uint256","indexed":false},{"type":"uint8","name":"finalizationType","internalType":"enum IFtso.PriceFinalizationType","indexed":false},{"type":"uint256","name":"timestamp","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"PriceHashSubmitted","inputs":[{"type":"address","name":"submitter","internalType":"address","indexed":true},{"type":"uint256","name":"epochId","internalType":"uint256","indexed":true},{"type":"bytes32","name":"hash","internalType":"bytes32","indexed":false},{"type":"uint256","name":"timestamp","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"PriceRevealed","inputs":[{"type":"address","name":"voter","internalType":"address","indexed":true},{"type":"uint256","name":"epochId","internalType":"uint256","indexed":true},{"type":"uint256","name":"price","internalType":"uint256","indexed":false},{"type":"uint256","name":"random","internalType":"uint256","indexed":false},{"type":"uint256","name":"timestamp","internalType":"uint256","indexed":false},{"type":"uint256","name":"votePowerNat","internalType":"uint256","indexed":false},{"type":"uint256","name":"votePowerAsset","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"ASSET_PRICE_USD_DECIMALS","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"activateFtso","inputs":[{"type":"uint256","name":"_firstEpochStartTime","internalType":"uint256"},{"type":"uint256","name":"_submitPeriod","internalType":"uint256"},{"type":"uint256","name":"_revealPeriod","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"active","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IIFtso"}],"name":"assetFtsos","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IIVPToken"}],"name":"assets","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"averageFinalizePriceEpoch","inputs":[{"type":"uint256","name":"_epochId","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"configureEpochs","inputs":[{"type":"uint256","name":"_maxVotePowerNatThresholdFraction","internalType":"uint256"},{"type":"uint256","name":"_maxVotePowerAssetThresholdFraction","internalType":"uint256"},{"type":"uint256","name":"_lowAssetUSDThreshold","internalType":"uint256"},{"type":"uint256","name":"_highAssetUSDThreshold","internalType":"uint256"},{"type":"uint256","name":"_highAssetTurnoutThresholdBIPS","internalType":"uint256"},{"type":"uint256","name":"_lowNatTurnoutThresholdBIPS","internalType":"uint256"},{"type":"address[]","name":"_trustedAddresses","internalType":"address[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"deactivateFtso","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"_maxVotePowerNatThresholdFraction","internalType":"uint256"},{"type":"uint256","name":"_maxVotePowerAssetThresholdFraction","internalType":"uint256"},{"type":"uint256","name":"_lowAssetUSDThreshold","internalType":"uint256"},{"type":"uint256","name":"_highAssetUSDThreshold","internalType":"uint256"},{"type":"uint256","name":"_highAssetTurnoutThresholdBIPS","internalType":"uint256"},{"type":"uint256","name":"_lowNatTurnoutThresholdBIPS","internalType":"uint256"},{"type":"address[]","name":"_trustedAddresses","internalType":"address[]"}],"name":"epochsConfiguration","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"address[]","name":"_eligibleAddresses","internalType":"address[]"},{"type":"uint256[]","name":"_natWeights","internalType":"uint256[]"},{"type":"uint256","name":"_natWeightsSum","internalType":"uint256"}],"name":"finalizePriceEpoch","inputs":[{"type":"uint256","name":"_epochId","internalType":"uint256"},{"type":"bool","name":"_returnRewardData","internalType":"bool"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"forceFinalizePriceEpoch","inputs":[{"type":"uint256","name":"_epochId","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IIFtsoManager"}],"name":"ftsoManager","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IIVPToken"}],"name":"getAsset","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address[]","name":"","internalType":"contract IIFtso[]"}],"name":"getAssetFtsos","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getCurrentEpochId","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"_price","internalType":"uint256"},{"type":"uint256","name":"_timestamp","internalType":"uint256"}],"name":"getCurrentPrice","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getCurrentRandom","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getEpochId","inputs":[{"type":"uint256","name":"_timestamp","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getEpochPrice","inputs":[{"type":"uint256","name":"_epochId","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getEpochPriceForVoter","inputs":[{"type":"uint256","name":"_epochId","internalType":"uint256"},{"type":"address","name":"_voter","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"_firstEpochStartTime","internalType":"uint256"},{"type":"uint256","name":"_submitPeriod","internalType":"uint256"},{"type":"uint256","name":"_revealPeriod","internalType":"uint256"}],"name":"getPriceEpochConfiguration","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"_epochId","internalType":"uint256"},{"type":"uint256","name":"_epochSubmitEndTime","internalType":"uint256"},{"type":"uint256","name":"_epochRevealEndTime","internalType":"uint256"},{"type":"uint256","name":"_votePowerBlock","internalType":"uint256"},{"type":"bool","name":"_fallbackMode","internalType":"bool"}],"name":"getPriceEpochData","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getRandom","inputs":[{"type":"uint256","name":"_epochId","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address[]","name":"_assets","internalType":"contract IIVPToken[]"},{"type":"uint256[]","name":"_assetMultipliers","internalType":"uint256[]"},{"type":"uint256","name":"_totalVotePowerNat","internalType":"uint256"},{"type":"uint256","name":"_totalVotePowerAsset","internalType":"uint256"},{"type":"uint256","name":"_assetWeightRatio","internalType":"uint256"},{"type":"uint256","name":"_votePowerBlock","internalType":"uint256"}],"name":"getVoteWeightingParameters","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"initializeCurrentEpochStateForReveal","inputs":[{"type":"uint256","name":"_circulatingSupplyNat","internalType":"uint256"},{"type":"bool","name":"_fallbackMode","internalType":"bool"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"priceDeviationThresholdBIPS","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"priceEpochCyclicBufferSize","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IPriceSubmitter"}],"name":"priceSubmitter","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"revealPriceSubmitter","inputs":[{"type":"address","name":"_voter","internalType":"address"},{"type":"uint256","name":"_epochId","internalType":"uint256"},{"type":"uint256","name":"_price","internalType":"uint256"},{"type":"uint256","name":"_random","internalType":"uint256"},{"type":"uint256","name":"_voterWNatVP","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setAsset","inputs":[{"type":"address","name":"_asset","internalType":"contract IIVPToken"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setAssetFtsos","inputs":[{"type":"address[]","name":"_assetFtsos","internalType":"contract IIFtso[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setVotePowerBlock","inputs":[{"type":"uint256","name":"_votePowerBlock","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"submitPriceHashSubmitter","inputs":[{"type":"address","name":"_sender","internalType":"address"},{"type":"uint256","name":"_epochId","internalType":"uint256"},{"type":"bytes32","name":"_hash","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"symbol","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updateInitialPrice","inputs":[{"type":"uint256","name":"_initialPriceUSD","internalType":"uint256"},{"type":"uint256","name":"_initialPriceTimestamp","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IIVPToken"}],"name":"wNat","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"wNatVotePowerCached","inputs":[{"type":"address","name":"_owner","internalType":"address"},{"type":"uint256","name":"_epochId","internalType":"uint256"}]}]
Contract Creation Code
0x6101206040523480156200001257600080fd5b506040516200507a3803806200507a833981810160405260e08110156200003857600080fd5b81019080805160405193929190846401000000008211156200005957600080fd5b9083019060208201858111156200006f57600080fd5b82516401000000008111828201881017156200008a57600080fd5b82525081516020918201929091019080838360005b83811015620000b95781810151838201526020016200009f565b50505050905090810190601f168015620000e75780820380516001836020036101000a031916815260200191505b506040908152602082810151918301516060840151608085015160a086015160c09096015189519598509296509094909390926200012b916001918a01906200016c565b506001600160601b0319606096871b81166101005294861b851660c0529290941b90921660e0526002919091554260035560809190915260a0525062000218565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282620001a45760008555620001ef565b82601f10620001bf57805160ff1916838001178555620001ef565b82800160010185558215620001ef579182015b82811115620001ef578251825591602001919060010190620001d2565b50620001fd92915062000201565b5090565b5b80821115620001fd576000815560010162000202565b60805160a05160c05160601c60e05160601c6101005160601c614da6620002d460003980610bcb528061111a5280611e745250806109c252806109ef5280610c135280610d245280610ded52806110c45280611620528061168752806117b55280611a535280611af75280611d475250806114d152806116e15280611c0a5280611cfa525080610f4f52806118d152806119ea5280611aca5280611b375280612f72528061315d525080610f2452806117635250614da66000f3fe608060405234801561001057600080fd5b50600436106102275760003560e01c8063974d7a6b11610130578063d89601fd116100b8578063f025bf661161007c578063f025bf661461088f578063f670ebe314610897578063f72cab28146108bc578063f7dba1f5146108e8578063f937d6ad146109af57610227565b8063d89601fd146107a9578063e3749e0c146107b1578063e3b3a3b31461081c578063e536f39614610851578063eb91d37e1461086e57610227565b8063c5d8b9e7116100ff578063c5d8b9e714610715578063cc245ab514610741578063cd4b691414610749578063cf35bdd014610766578063d0d552dd1461078357610227565b8063974d7a6b146106cb5780639de6f927146106e85780639edbf00714610705578063a29a839f1461070d57610227565b80635303548b116101b35780636b52b242116101825780636b52b2421461054f5780637d1d6f1214610557578063826cc76b146105745780638357d08c1461059157806395d89b411461064e57610227565b80635303548b146104d2578063555989da1461050157806355f7b69b146105095780635c222bad1461054757610227565b806318931c35116101fa57806318931c351461033757806327bd2ad51461038f5780632f0a6f3c146103c1578063306ba253146103ea57806340462a2d1461040d57610227565b806302fb0c5e1461022c57806311a7aaaa14610248578063131fdee21461026c578063144e159114610311575b600080fd5b6102346109b7565b604080519115158252519081900360200190f35b6102506109c0565b604080516001600160a01b039092168252519081900360200190f35b61030f6004803603602081101561028257600080fd5b81019060208101813564010000000081111561029d57600080fd5b8201836020820111156102af57600080fd5b803590602001918460208302840111640100000000831117156102d157600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295506109e4945050505050565b005b610319610ad9565b60408051938452602084019290925282820152519081900360600190f35b61033f610ae7565b60408051602080825283518183015283519192839290830191858101910280838360005b8381101561037b578181015183820152602001610363565b505050509050019250505060405180910390f35b61030f600480360360608110156103a557600080fd5b506001600160a01b038135169060208101359060400135610bae565b61030f600480360360608110156103d757600080fd5b5080359060208101359060400135610c08565b61030f6004803603604081101561040057600080fd5b5080359060200135610d19565b6104326004803603604081101561042357600080fd5b50803590602001351515610ddd565b604051808060200180602001848152602001838103835286818151815260200191508051906020019060200280838360005b8381101561047c578181015183820152602001610464565b50505050905001838103825285818151815260200191508051906020019060200280838360005b838110156104bb5781810151838201526020016104a3565b505050509050019550505050505060405180910390f35b6104ef600480360360208110156104e857600080fd5b5035611094565b60408051918252519081900360200190f35b61030f6110a7565b61030f600480360360a081101561051f57600080fd5b506001600160a01b0381351690602081013590604081013590606081013590608001356110fd565b61025061115b565b6104ef6111db565b6104ef6004803603602081101561056d57600080fd5b50356111e0565b6102506004803603602081101561058a57600080fd5b50356111f5565b61059961121f565b604051808060200180602001878152602001868152602001858152602001848152602001838103835289818151815260200191508051906020019060200280838360005b838110156105f55781810151838201526020016105dd565b50505050905001838103825288818151815260200191508051906020019060200280838360005b8381101561063457818101518382015260200161061c565b505050509050019850505050505050505060405180910390f35b610656611588565b6040805160208082528351818301528351919283929083019185019080838360005b83811015610690578181015183820152602001610678565b50505050905090810190601f1680156106bd5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61030f600480360360208110156106e157600080fd5b5035611615565b61030f600480360360208110156106fe57600080fd5b503561167c565b6102506116df565b6104ef611703565b6104ef6004803603604081101561072b57600080fd5b50803590602001356001600160a01b031661170e565b6104ef611761565b6104ef6004803603602081101561075f57600080fd5b5035611785565b6102506004803603602081101561077c57600080fd5b503561179a565b61030f6004803603602081101561079957600080fd5b50356001600160a01b03166117aa565b6104ef6118af565b6107b9611915565b6040518088815260200187815260200186815260200185815260200184815260200183815260200180602001828103825283818151815260200191508051906020019060200280838360008381101561063457818101518382015260200161061c565b6108246119bc565b60408051958652602086019490945284840192909252606084015215156080830152519081900360a00190f35b61030f6004803603602081101561086757600080fd5b5035611a48565b610876611abe565b6040805192835260208301919091528051918290030190f35b6104ef611ac8565b61030f600480360360408110156108ad57600080fd5b50803590602001351515611aec565b6104ef600480360360408110156108d257600080fd5b506001600160a01b038135169060200135611cf3565b61030f600480360360e08110156108fe57600080fd5b81359160208101359160408201359160608101359160808201359160a08101359181019060e0810160c082013564010000000081111561093d57600080fd5b82018360208201111561094f57600080fd5b8035906020019184602083028401116401000000008311171561097157600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550611d3c945050505050565b610250611e72565b60005460ff1681565b7f000000000000000000000000000000000000000000000000000000000000000081565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610a1c57610a1c611e96565b6000815111610a2757fe5b600181511180610a5d5750306001600160a01b031681600081518110610a4957fe5b60200260200101516001600160a01b031614155b610a6357fe5b8051610a76906015906020840190614b60565b5080516001600160401b0381118015610a8e57600080fd5b50604051908082528060200260200182016040528015610ab8578160200160208202803683370190505b508051610acd91601491602090910190614b60565b50610ad6611efe565b50565b600654600754600854909192565b6014546060906001148015610afe57506015546001145b8015610b345750306001600160a01b03166015600081548110610b1d57fe5b6000918252602090912001546001600160a01b0316145b610b98576015805480602002602001604051908101604052809291908181526020018280548015610b8e57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610b70575b5050505050610ba8565b6040805160008152602081019091525b90505b90565b60005460ff16610bc057610bc06120da565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610bf857610bf8611e96565b610c03838383612144565b505050565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610c4057610c40611e96565b600054604080518082019091526016815275119514d3c8185b1c9958591e481858dd1a5d985d195960521b60208201529060ff1615610cfd5760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015610cc2578181015183820152602001610caa565b50505050905090810190601f168015610cef5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b506006929092556007556008556000805460ff19166001179055565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610d5157610d51611e96565b600054604080518082019091526016815275119514d3c8185b1c9958591e481858dd1a5d985d195960521b60208201529060ff1615610dd15760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610cc2578181015183820152602001610caa565b50600291909155600355565b6060806000336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610e1a57610e1a611e96565b6000610e25866122cf565b60018101805460ff60f01b19169055600581015490915060009015610e5d57816005015461271083600801540281610e5957fe5b0490505b6001820154600160f81b900460ff1680610e7b575081600401548111155b15610eee576001820154600160f81b900460ff16610edb57600482015460408051838152602081019290925242828201525188917fbece8aa526cdc5e528cdaa56c1d03edc19da51e41845aa146f64a7071f74c65a919081900360600190a25b610ee78783600061240b565b505061108d565b6060806060610efc85612575565b919450925090506060610f0d614bc1565b610f17858561270a565b61012081015191935091507f000000000000000000000000000000000000000000000000000000000000000090610f73906004908f907f0000000000000000000000000000000000000000000000000000000000000000612a28565b1115610f9157610f858c88600061240b565b5050505050505061108d565b60108701805460ff19166001179055610120810151600f88018190556002554260035560008b15610fdb57610fc888838587612ab4565b8251929d50909b50995015610fdb575060015b610fe78d838584612d70565b8c7ffe8865c1fe85bbf124b9e0f16cccfeeb6f330454fd79475a31261c8fa250bc3089600f0154838561014001518661016001518d60100160009054906101000a900460ff164260405180878152602001861515815260200185815260200184815260200183600581111561105857fe5b8152602001828152602001965050505050505060405180910390a2505050600190940180546001600160f81b03169055505050505b9250925092565b60006110a1600483612d76565b92915050565b60005460ff166110b9576110b96120da565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146110f1576110f1611e96565b6000805460ff19169055565b60005460ff1661110f5761110f6120da565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461114757611147611e96565b6111548585858585612da7565b5050505050565b601454600090600114801561117257506015546001145b80156111a85750306001600160a01b0316601560008154811061119157fe5b6000918252602090912001546001600160a01b0316145b6111b3576000610ba8565b60146000815481106111c157fe5b6000918252602090912001546001600160a01b0316905090565b600581565b60006111eb82613156565b600f015492915050565b6015818154811061120557600080fd5b6000918252602090912001546001600160a01b0316905081565b606080600080600080601480548060200260200160405190810160405280929190818152602001828054801561127e57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611260575b50506009548451949a506001600160f01b031694506000939250506001600160401b038211905080156112b057600080fd5b506040519080825280602002602001820160405280156112da578160200160208202803683370190505b509050600087516001600160401b03811180156112f657600080fd5b50604051908082528060200260200182016040528015611320578160200160208202803683370190505b50905060005b88518110156114af5760006001600160a01b031689828151811061134657fe5b60200260200101516001600160a01b031614156113645760006113e8565b88818151811061137057fe5b60200260200101516001600160a01b0316633e5aa26a856040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b1580156113bb57600080fd5b505afa1580156113cf573d6000803e3d6000fd5b505050506040513d60208110156113e557600080fd5b50515b8382815181106113f457fe5b6020026020010181815250506015818154811061140d57fe5b9060005260206000200160009054906101000a90046001600160a01b03166001600160a01b031663eb91d37e6040518163ffffffff1660e01b8152600401604080518083038186803b15801561146257600080fd5b505afa158015611476573d6000803e3d6000fd5b505050506040513d604081101561148c57600080fd5b5051825183908390811061149c57fe5b6020908102919091010152600101611326565b5060006114bf60048a858561321e565b90506114cd60048a836133c4565b97507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316633e5aa26a856040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561153357600080fd5b505afa158015611547573d6000803e3d6000fd5b505050506040513d602081101561155d57600080fd5b5051965061156e60048a85846134f8565b955061157b6004876135c0565b9450505050909192939495565b60018054604080516020600284861615610100026000190190941693909304601f8101849004840282018401909252818152929183018282801561160d5780601f106115e25761010080835404028352916020019161160d565b820191906000526020600020905b8154815290600101906020018083116115f057829003601f168201915b505050505081565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461164d5761164d611e96565b6000611658826122cf565b6001808201805460ff60f01b191690559091506116789083908390613615565b5050565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146116b4576116b4611e96565b60006116bf826122cf565b6001808201805460ff60f01b19169055909150611678908390839061240b565b7f000000000000000000000000000000000000000000000000000000000000000081565b6000610ba842611094565b60008061171a84613156565b9050600061172882856136fd565b90508061173a576000925050506110a1565b600019016000908152600e90910160205260409020546001600160801b0316905092915050565b7f000000000000000000000000000000000000000000000000000000000000000081565b600061179082613156565b600a015492915050565b6014818154811061120557600080fd5b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146117e2576117e2611e96565b60408051602081019091523081526117fe906015906001614b60565b5060408051602081019091526001600160a01b0382168152611824906014906001614b60565b50806001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b15801561185e57600080fd5b505afa158015611872573d6000803e3d6000fd5b505050506040513d602081101561188857600080fd5b50516001600160a01b03909116600090815260056020526040902060ff909116600a0a9055565b6000806118ba611703565b9050806118cb576000915050610bab565b600460007f00000000000000000000000000000000000000000000000000000000000000006000198401816118fc57fe5b068152602001908152602001600020600a015491505090565b60008060008060008060606004600601546004600701546004600801546004600901546004600a01546004600b01546004600c018080548060200260200160405190810160405280929190818152602001828054801561199e57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611980575b50505050509050965096509650965096509650965090919293949596565b60008060008060006119cc611703565b94506119d9600486613755565b6008549094508401925060006004817f00000000000000000000000000000000000000000000000000000000000000008881611a1157fe5b0681526020810191909152604001600020600101549596949593946001600160f01b03851694600160f81b900460ff169350915050565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614611a8057611a80611e96565b438110611a8c57600080fd5b600160f01b8110611a9c57600080fd5b600980546001600160f01b0319166001600160f01b0392909216919091179055565b6002546003549091565b7f000000000000000000000000000000000000000000000000000000000000000081565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614611b2457611b24611e96565b6000611b2e611703565b905060006004817f00000000000000000000000000000000000000000000000000000000000000008481611b5e57fe5b0681526020810191909152604001600090812060108101805460ff1916905560088101829055600a8101829055600d81019190915560095460018201805486158015600160f81b026001600160f81b036001600160f01b03199093166001600160f01b03909516949094179190911692909217905560028201849055909150611be8575050611678565b606080611bf361376d565b6009549194509250611ca3915084908890611c38907f0000000000000000000000000000000000000000000000000000000000000000906001600160f01b0316613963565b6014805480602002602001604051908101604052809291908181526020018280548015611c8e57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611c70575b5060049796959493508a9250899150506139f4565b6013849055837fc0eaa359541c7c642d9947c9496507c134f3e4f8e1fd433313eb27dc48cb1fb7611cd5600483613755565b604080519182524260208301528051918290030190a2505050505050565b6000611d357f000000000000000000000000000000000000000000000000000000000000000084611d2385613156565b600101546001600160f01b0316613aa0565b9392505050565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614611d7457611d74611e96565b600a879055600b869055600c859055600d849055600e839055600f82905560105460005b81811015611df55760006004600d0160006004600c018481548110611db957fe5b6000918252602080832091909101546001600160a01b031683528201929092526040019020805460ff1916911515919091179055600101611d98565b5050805160005b81811015611e535760016004600d016000858481518110611e1957fe5b6020908102919091018101516001600160a01b03168252810191909152604001600020805460ff1916911515919091179055600101611dfc565b508151611e67906010906020850190614b60565b505050505050505050565b7f000000000000000000000000000000000000000000000000000000000000000081565b604080518082018252600d81526c1058d8d95cdcc819195b9a5959609a1b6020808301918252925162461bcd60e51b81526004810193845282516024820152825192939283926044909201919080838360008315610cc2578181015183820152602001610caa565b6015546001148015611f3a5750306001600160a01b03166015600081548110611f2357fe5b6000918252602090912001546001600160a01b0316145b15611f44576120d8565b60005b601554811015610ad657600060158281548110611f6057fe5b6000918252602091829020015460408051635c222bad60e01b815290516001600160a01b0390921692635c222bad92600480840193829003018186803b158015611fa957600080fd5b505afa158015611fbd573d6000803e3d6000fd5b505050506040513d6020811015611fd357600080fd5b5051601480549192509083908110611fe757fe5b6000918252602090912001546001600160a01b038281169116141561200c57506120d0565b806014838154811061201a57fe5b600091825260209091200180546001600160a01b0319166001600160a01b039283161790558116156120ce57806001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b15801561207f57600080fd5b505afa158015612093573d6000803e3d6000fd5b505050506040513d60208110156120a957600080fd5b50516001600160a01b038216600090815260056020526040902060ff909116600a0a90555b505b600101611f47565b565b604080518082018252600f81526e4654534f206e6f742061637469766560881b6020808301918252925162461bcd60e51b81526004810193845282516024820152825192939283926044909201919080838360008315610cc2578181015183820152602001610caa565b61214c611703565b82146040518060400160405280600e81526020016d15dc9bdb99c8195c1bd8da081a5960921b815250906121c15760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610cc2578181015183820152602001610caa565b5060008281526012602090815260408083206001600160a01b0387168452825291829020548251808401909352601983527f4475706c6963617465207375626d697420696e2065706f63680000000000000091830191909152156122665760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610cc2578181015183820152602001610caa565b5060008281526012602090815260408083206001600160a01b038716808552908352928190208490558051848152429281019290925280518593927f615c0184a2b16dbbcd09eae0bf239e28977aa4e6ff2204eda59c14016310bb6692908290030190a3505050565b60006122dc600483613b43565b4210156040518060400160405280602081526020017f45706f6368206e6f7420726561647920666f722066696e616c697a6174696f6e815250906123615760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610cc2578181015183820152602001610caa565b5061236b82613156565b90506000601082015460ff16600581111561238257fe5b146040518060400160405280601781526020017f45706f636820616c72656164792066696e616c697a6564000000000000000000815250906124055760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610cc2578181015183820152602001610caa565b50919050565b600080612475846004600c0180548060200260200160405190810160405280929190818152602001828054801561246b57602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161244d575b5050505050613b5c565b9092509050801561256a5780828161248957fe5b04600f8501558261249b57600261249e565b60045b60108501805460ff191660018360058111156124b657fe5b0217905550600f840154600255426003556124d085610ad6565b847ffe8865c1fe85bbf124b9e0f16cccfeeb6f330454fd79475a31261c8fa250bc3085600f015460008060008960100160009054906101000a900460ff164260405180878152602001861515815260200185815260200184815260200183600581111561253957fe5b8152602001828152602001965050505050505060405180910390a26001840180546001600160f81b03169055611154565b611154858585613615565b6060806060600084600d01549050806001600160401b038111801561259957600080fd5b506040519080825280602002602001820160405280156125c3578160200160208202803683370190505b509350806001600160401b03811180156125dc57600080fd5b50604051908082528060200260200182016040528015612606578160200160208202803683370190505b5091506000816001600160401b038111801561262157600080fd5b5060405190808252806020026020018201604052801561264b578160200160208202803683370190505b50905060005b828110156126f3576000818152600e880160205260409020805487516001600160801b039091169088908490811061268557fe5b602090810291909101015280548551600160801b9091046001600160401b0316908690849081106126b257fe5b602090810291909101015280548351600160c01b9091046001600160401b0316908490849081106126df57fe5b602090810291909101015250600101612651565b506126ff868483613c07565b935050509193909250565b6060612714614bc1565b8351806001600160401b038111801561272c57600080fd5b50604051908082528060200260200182016040528015612756578160200160208202803683370190505b50925060005b81811015612784578084828151811061277157fe5b602090810291909101015260010161275c565b5061279b6002600060018403600080888b8b613d1e565b6080850152606084015280835283518591859181106127b657fe5b6020026020010151815181106127c857fe5b602090810291909101015160a0830181905260808301516060840151845192010190612801578251600060c08501526020840152612855565b60048104836060015111612825578251606084015160c08501526020840152612855565b61284960016000600186600001510360008760a00151886080015101898c8c613d1e565b5060c085015260208401525b825160001983011415612876578251600061010085015260408401526128ce565b6004810483608001511161289b578251608084015161010085015260408401526128ce565b6128c160038460000151600101600185038660a001518760600151016000898c8c613d1e565b6101008601525060408401525b85848460000151815181106128df57fe5b6020026020010151815181106128f157fe5b6020908102919091010151610120840152600281048360a0015184606001510114801561291f575060028106155b1561294f576002612939846000015160018503878a613ff9565b846101200151018161294757fe5b046101208401525b612969836020015160006000198660c00151888b8b6140b9565b60c0850152602084015260408301516101008401516129949190600019850190600190888b8b6140b9565b6101008501526040840152602083015184518791869181106129b257fe5b6020026020010151815181106129c457fe5b60200260200101518361014001818152505085848460400151815181106129e757fe5b6020026020010151815181106129f957fe5b602090810291909101015161016084015261010083015160c08401519091030360e08301525090939092509050565b600083612a3757506000612aac565b6000858184600019880181612a4857fe5b068152602001908152602001600020600f0154905080841415612a6f576000915050612aac565b83612a825764e8d4a51000915050612aac565b600084821115612a955750838103612a9a565b508084035b612aa7816127108761418d565b925050505b949350505050565b600a840154602084015160609182916000919082905b88604001518111612b95576000888281518110612ae357fe5b602002602001015190506000888281518110612afb57fe5b60200260200101511115612b8b576000818152600e8c0160205260409020546101408b01516001600160801b0390911690811480612b4657508a6101600151816001600160801b0316145b8015612b7757506000828152600e8d016020526040902060010154612b759086906001600160a01b0316614299565b155b15612b83575050612b8d565b506001909201915b505b600101612aca565b50806001600160401b0381118015612bac57600080fd5b50604051908082528060200260200182016040528015612bd6578160200160208202803683370190505b509450806001600160401b0381118015612bef57600080fd5b50604051908082528060200260200182016040528015612c19578160200160208202803683370190505b5060208901519094506000905b89604001518111612d62576000898281518110612c3f57fe5b602002602001015190506000898281518110612c5757fe5b602002602001015190506000811115612d57576000828152600e8e0160205260409020546101408d01516001600160801b0390911690811480612ca757508c6101600151816001600160801b0316145b8015612cd857506000838152600e8f016020526040902060010154612cd69088906001600160a01b0316614299565b155b15612ce557505050612d5a565b6000838152600e8f0160205260409020600101548a516001600160a01b03909116908b9087908110612d1357fe5b60200260200101906001600160a01b031690816001600160a01b03168152505081898681518110612d4057fe5b602090810291909101015250958601956001909301925b50505b600101612c26565b505050509450945094915050565b50505050565b60008260020154821015612d8c575060006110a1565b82600301548360020154830381612d9f57fe5b0490506110a1565b60408051808201909152600e81526d0a0e4d2c6ca40e8dede40d0d2ced60931b6020820152600160801b8410612e1e5760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610cc2578181015183820152602001610caa565b50612e2a6004856142d4565b6040518060400160405280601881526020017f52657665616c20706572696f64206e6f7420616374697665000000000000000081525090612eac5760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610cc2578181015183820152602001610caa565b506040805160208082018690528183018590526001600160a01b0388166060808401829052845180850390910181526080840180865281519184019190912060008a81526012855286812093815292909352908490205460e08401909452602380825293909114929091614d2d9060a0013990612f6a5760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610cc2578181015183820152602001610caa565b5060006004817f00000000000000000000000000000000000000000000000000000000000000008781612f9957fe5b0681526020810191909152604001600020600181015490915060ff600160f81b8204811691600160f01b8104909116906001600160f01b03168180612ffe5750828015612ffe57506001600160a01b03891660009081526011602052604090205460ff165b6040518060400160405280602081526020017f45706f6368206e6f7420696e697469616c697a656420666f722072657665616c815250906130805760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610cc2578181015183820152602001610caa565b50600080613091868c8988876142ff565b915091506130d4868c84848d8d8f60405160200180838152602001828152602001925050506040516020818303038152906040528051906020012060001c61435f565b60008a81526012602090815260408083206001600160a01b038f168085529083528184209390935580518c81529182018b905242828201526060820185905260808201849052518c92917f408bc8c9d02102257c33373c2df5771b03067bd8ea7ec60c35f314ec4ee99d05919081900360a00190a35050505050505050505050565b60006004817f0000000000000000000000000000000000000000000000000000000000000000848161318457fe5b0681526020019081526020016000209050806002015482146040518060400160405280601881526020017f45706f63682064617461206e6f7420617661696c61626c650000000000000000815250906124055760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610cc2578181015183820152602001610caa565b82516060906000816001600160401b038111801561323b57600080fd5b50604051908082528060200260200182016040528015613265578160200160208202803683370190505b5090506000805b8381101561334e5760006001600160a01b031688828151811061328b57fe5b60200260200101516001600160a01b031614156132a757613346565b600061331e8783815181106132b857fe5b60200260200101518b60010160008c86815181106132d257fe5b60200260200101516001600160a01b03166001600160a01b03168152602001908152602001600020548a858151811061330757fe5b602002602001015161418d9092919063ffffffff16565b90508084838151811061332d57fe5b6020908102919091010152613342838261447c565b9250505b60010161326c565b5080156133b95760005b838110156133b75761339861338b61271088848151811061337557fe5b60200260200101516144d690919063ffffffff16565b8385848151811061330757fe5b8382815181106133a457fe5b6020908102919091010152600101613358565b505b509695505050505050565b8151606090806001600160401b03811180156133df57600080fd5b50604051908082528060200260200182016040528015613409578160200160208202803683370190505b50915060005b818110156134ef5760006001600160a01b031685828151811061342e57fe5b60200260200101516001600160a01b0316146134cc57600086600101600087848151811061345857fe5b60200260200101516001600160a01b03166001600160a01b031681526020019081526020016000205490506134ae61271064e8d4a510008161349657fe5b04670de0b6b3a7640000028287858151811061330757fe5b8483815181106134ba57fe5b602002602001018181525050506134e7565b60008382815181106134da57fe5b6020026020010181815250505b60010161340f565b50509392505050565b600080805b85518110156135b65760006001600160a01b031686828151811061351d57fe5b60200260200101516001600160a01b0316146135ae57600061271061359687848151811061354757fe5b60200260200101518a60010160008b878151811061356157fe5b60200260200101516001600160a01b03166001600160a01b031681526020019081526020016000205488868151811061330757fe5b8161359d57fe5b0490506135aa838261447c565b9250505b6001016134fd565b5095945050505050565b60008083600801548310156135d757506000611d35565b836009015483106135eb5750611388611d35565b600884015460098501546101f49190819003908503611194028161360b57fe5b0401949350505050565b821561362857600254600f830155613630565b6000600f8301555b8061363c57600361363f565b60055b60108301805460ff1916600183600581111561365757fe5b021790555061366583610ad6565b827ffe8865c1fe85bbf124b9e0f16cccfeeb6f330454fd79475a31261c8fa250bc3083600f015460008060008760100160009054906101000a900460ff16426040518087815260200186151581526020018581526020018481526020018360058111156136ce57fe5b8152602001828152602001965050505050505060405180910390a25060010180546001600160f81b0316905550565b600d820154600090815b8181101561374a576000818152600e860160205260409020600101546001600160a01b03858116911614156137425760010191506110a19050565b600101613707565b506000949350505050565b60038201546002830154600183019091020192915050565b606080606061377a611efe565b60148054806020026020016040519081016040528092919081815260200182805480156137d057602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116137b2575b5050505050925082516001600160401b03811180156137ee57600080fd5b50604051908082528060200260200182016040528015613818578160200160208202803683370190505b50915082516001600160401b038111801561383257600080fd5b5060405190808252806020026020018201604052801561385c578160200160208202803683370190505b50905060005b835181101561395d5761389684828151811061387a57fe5b60209081029190910101516009546001600160f01b0316613963565b8382815181106138a257fe5b602002602001018181525050601581815481106138bb57fe5b9060005260206000200160009054906101000a90046001600160a01b03166001600160a01b031663eb91d37e6040518163ffffffff1660e01b8152600401604080518083038186803b15801561391057600080fd5b505afa158015613924573d6000803e3d6000fd5b505050506040513d604081101561393a57600080fd5b5051825183908390811061394a57fe5b6020908102919091010152600101613862565b50909192565b60006001600160a01b03831661397b575060006110a1565b826001600160a01b031663caeb942b836040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b1580156139c157600080fd5b505af11580156139d5573d6000803e3d6000fd5b505050506040513d60208110156139eb57600080fd5b505190506110a1565b613a01878785858561452f565b600a8701546003870155600b8701546004870155600586018590556006808701859055870154613a39908581613a3357fe5b04614598565b86546001600160801b0319166001600160801b039190911617865560078781015490870154613a6a919081613a3357fe5b86546001600160801b03918216600160801b0291161786555050506001909201805460ff60f01b1916600160f01b179055505050565b60006001600160a01b038416613ab857506000611d35565b836001600160a01b031663e587497e84846040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050602060405180830381600087803b158015613b0f57600080fd5b505af1158015613b23573d6000803e3d6000fd5b505050506040513d6020811015613b3957600080fd5b5051949350505050565b60008260040154613b548484613755565b019392505050565b600d8201546000908190815b81811015613bfe576000818152600e870160205260408120600101546001600160a01b0316905b8651811015613bf457868181518110613ba457fe5b60200260200101516001600160a01b0316826001600160a01b03161415613bec576000838152600e890160205260409020546001600160801b03169590950194600194909401935b600101613b8f565b5050600101613b68565b50509250929050565b600d830154606090806001600160401b0381118015613c2557600080fd5b50604051908082528060200260200182016040528015613c4f578160200160208202803683370190505b5091506000613c5d856145e0565b90506000613c6a856145e0565b9050600080613c7a898585614625565b90508315613c8a57806127100391505b60005b85811015613d115760008315613cd357613cd08a8381518110613cac57fe5b602002602001015164e8d4a510000261271088028661418d9092919063ffffffff16565b90505b60008315613ced57613cea8a8481518110613cac57fe5b90505b808201898481518110613cfc57fe5b60209081029190910101525050600101613c8d565b5050505050509392505050565b6000806000888a1415613d38575088915086905085613feb565b613d40614c22565b88815260208101889052613d52614c5f565b60208082018d905260408083018d90528051448184015242818301528151808203830181526060909101909152805191012060005b60208301516040840151613db99190816001818303018681613da557fe5b0601876000015188602001518f8f8f61468c565b606087015260408601528084528a518b91908110613dd357fe5b602002602001015183606001818152505087836060015181518110613df457fe5b6020026020010151846080018181525050836060015184604001518560800151010190508e60021415613ecf5760028106600282040160a0850181905280820360c0860152604085015110801590613e4f57508b8460a00151115b15613e7457825160001901604084015260608401516080850151016020850152613eca565b8360c001518460600151118015613e8e57508a8460c00151115b15613eaf578251600101602084015260408401516080850151018452613eca565b50505160408201516060909201519094509092509050613feb565b613fe6565b8e60011415613f60576004810460a0850181905280820360c08601526040850151118015613f0057508b8460a00151115b15613f2557825160001901604084015260608401516080850151016020850152613eca565b8360c00151846060015110158015613e8e57508a8460c001511115613eaf578251600101602084015260408401516080850151018452613eca565b6004810460c08501819052810360a08501819052604085015110801590613f8a57508b8460a00151115b15613faf57825160001901604084015260608401516080850151016020850152613fe6565b8360c001518460600151118015613fc957508a8460c00151115b15613eaf5782516001016020840152604084015160808501510184525b613d87565b985098509895505050505050565b600083851415614031578183868151811061401057fe5b60200260200101518151811061402257fe5b60200260200101519050612aac565b60008284876001018151811061404357fe5b60200260200101518151811061405557fe5b602002602001015190506000808760020190505b8681116140ad578486828151811061407d57fe5b60200260200101518151811061408f57fe5b60200260200101519150828210156140a5578192505b600101614069565b50909695505050505050565b60008085888a03880282136140d45789879250925050614181565b600085878c815181106140e357fe5b6020026020010151815181106140f557fe5b602090810291909101015190508a89016000815b60008c8e830302136141755789818151811061412157fe5b602002602001015191508389838151811061413857fe5b6020026020010151141561416e5787828151811061415257fe5b60200260200101518503945061416983828c614855565b918b01915b8b01614109565b50508990039350909150505b97509795505050505050565b60008082116141d6576040805162461bcd60e51b815260206004820152601060248201526f4469766973696f6e206279207a65726f60801b604482015290519081900360640190fd5b836141e357506000611d35565b838302838582816141f057fe5b0414156142095782818161420057fe5b04915050611d35565b600083868161421457fe5b049050600084878161422257fe5b069050600085878161423057fe5b049050600086888161423e57fe5b06905061428c6142588861425286856144d6565b906148bd565b61428661426586866144d6565b61428661427289876144d6565b6142868d6142808c8b6144d6565b906144d6565b9061447c565b9998505050505050505050565b604080516020808201949094526001600160a01b03929092168282015280518083038201815260609092019052805191012060019081161490565b6000806142e18484613755565b9050428111158015612aac5750836004015481014210949350505050565b600080831561431357506000905080614355565b849150614321878785614924565b87549091506001600160801b0380821691600160801b90041681841115614346578193505b80831115614352578092505b50505b9550959350505050565b600086600d0154905060006143808787878b600601548c60070154896149e2565b63ffffffff838116608083019081526000858152600e8c0160209081526040918290208551815492870151938701516001600160801b03199093166001600160801b039091161767ffffffffffffffff60801b1916600160801b6001600160401b0394851602176001600160c01b0316600160c01b939092169290920217815560608401516001918201805493516001600160a01b03199094166001600160a01b039092169190911763ffffffff60a01b1916600160a01b93851693909302929092179091558401600d8b015560088a01549192506144629190889061447c16565b60088901555050600a909501805490950190945550505050565b600082820183811015611d35576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b6000826144e5575060006110a1565b828202828482816144f257fe5b0414611d355760405162461bcd60e51b8152600401808060200182810382526021815260200180614d506021913960400191505060405180910390fd5b825161454490600b8601906020860190614b60565b506145518584848461321e565b805161456791600c870191602090910190614c87565b506000614575868685614a3f565b60078601819055905061458886826135c0565b8560090181905550505050505050565b6000600160801b82106145dc5760405162461bcd60e51b8152600401808060200182810382526027815260200180614d066027913960400191505060405180910390fd5b5090565b600080805b835181101561461e576146148482815181106145fd57fe5b60200260200101518361447c90919063ffffffff16565b91506001016145e5565b5092915050565b60008161463457506000611d35565b826146425750612710611d35565b60006146568361271064e8d4a5100061418d565b90508460030154811061466f5750506009830154611d35565b6003850154600986015461468491839061418d565b915050611d35565b60008060008085878b8151811061469f57fe5b6020026020010151815181106146b157fe5b60200260200101519050600060026001600160401b03811180156146d457600080fd5b506040519080825280602002602001820160405280156146fe578160200160208202803683370190505b509050898160008151811061470f57fe5b602002602001018181525050888160018151811061472957fe5b60209081029190910101528c8c6147418d828c614855565b81805b828110156148055760008c828151811061475a57fe5b60200260200101519050868c828151811061477157fe5b602002602001015110156147c8578a818151811061478b57fe5b6020026020010151866000815181106147a057fe5b6020026020010181815101915081815250506147bd83838f614855565b6001909201916147fc565b8a81815181106147d457fe5b6020026020010151866001815181106147e957fe5b6020026020010181815101915081815250505b50600101614744565b5061481182828d614855565b808460008151811061481f57fe5b60200260200101518560018151811061483457fe5b60200260200101519750975097505050505050985098509895505050505050565b8183141561486257610c03565b80828151811061486e57fe5b602002602001015181848151811061488257fe5b602002602001015182858151811061489657fe5b602002602001018385815181106148a957fe5b602090810291909101019190915252505050565b6000808211614913576040805162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b81838161491c57fe5b049392505050565b600b83015460009081906001600160401b038111801561494357600080fd5b5060405190808252806020026020018201604052801561496d578160200160208202803683370190505b50905060005b600b8601548110156149cc576149ad86600b01828154811061499157fe5b6000918252602090912001546001600160a01b03168686613aa0565b8282815181106149b957fe5b6020908102919091010152600101614973565b506149d960048683614a3f565b95945050505050565b6149ea614cc2565b6001600160a01b0387166060820152614a038685614b2f565b6001600160401b03166020820152614a1b8584614b2f565b6001600160401b031660408201526001600160801b03909116815295945050505050565b600080805b600b850154811015614b265760006001600160a01b031685600b018281548110614a6a57fe5b6000918252602090912001546001600160a01b03161415614a8a57614b1e565b614b1b612710614b0b868481518110614a9f57fe5b60200260200101518960010160008a600b018781548110614abc57fe5b60009182526020808320909101546001600160a01b03168352820192909252604001902054600c8a01805487908110614af157fe5b906000526020600020015461418d9092919063ffffffff16565b81614b1257fe5b8491900461447c565b91505b600101614a44565b50949350505050565b6000811580614b3c575082155b15614b49575060006110a1565b614b598364e8d4a510008461418d565b90506110a1565b828054828255906000526020600020908101928215614bb5579160200282015b82811115614bb557825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190614b80565b506145dc929150614cf0565b6040518061018001604052806000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6040518060e00160405280600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6040518060800160405280600081526020016000815260200160008152602001600081525090565b828054828255906000526020600020908101928215614bb5579160200282015b82811115614bb5578251825591602001919060010190614ca7565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915290565b5b808211156145dc5760008155600101614cf156fe53616665436173743a2076616c756520646f65736e27742066697420696e203132382062697473507269636520616c72656164792072657665616c6564206f72206e6f742076616c6964536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77a26469706673582212206abbc057beb7e88e14da4471adff41c5166c6aa382754ead6b2806155764308e64736f6c6343000706003300000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000100000000000000000000000000000000000000300000000000000000000000002f0826ef6ad107cfc861152b32b52fd11bab9ed000000000000000000000000bfa12e4e1411b62eda8b035d71735667422a6a9e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005dc00000000000000000000000000000000000000000000000000000000000000c800000000000000000000000000000000000000000000000000000000000000034254430000000000000000000000000000000000000000000000000000000000
Deployed ByteCode
0x608060405234801561001057600080fd5b50600436106102275760003560e01c8063974d7a6b11610130578063d89601fd116100b8578063f025bf661161007c578063f025bf661461088f578063f670ebe314610897578063f72cab28146108bc578063f7dba1f5146108e8578063f937d6ad146109af57610227565b8063d89601fd146107a9578063e3749e0c146107b1578063e3b3a3b31461081c578063e536f39614610851578063eb91d37e1461086e57610227565b8063c5d8b9e7116100ff578063c5d8b9e714610715578063cc245ab514610741578063cd4b691414610749578063cf35bdd014610766578063d0d552dd1461078357610227565b8063974d7a6b146106cb5780639de6f927146106e85780639edbf00714610705578063a29a839f1461070d57610227565b80635303548b116101b35780636b52b242116101825780636b52b2421461054f5780637d1d6f1214610557578063826cc76b146105745780638357d08c1461059157806395d89b411461064e57610227565b80635303548b146104d2578063555989da1461050157806355f7b69b146105095780635c222bad1461054757610227565b806318931c35116101fa57806318931c351461033757806327bd2ad51461038f5780632f0a6f3c146103c1578063306ba253146103ea57806340462a2d1461040d57610227565b806302fb0c5e1461022c57806311a7aaaa14610248578063131fdee21461026c578063144e159114610311575b600080fd5b6102346109b7565b604080519115158252519081900360200190f35b6102506109c0565b604080516001600160a01b039092168252519081900360200190f35b61030f6004803603602081101561028257600080fd5b81019060208101813564010000000081111561029d57600080fd5b8201836020820111156102af57600080fd5b803590602001918460208302840111640100000000831117156102d157600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295506109e4945050505050565b005b610319610ad9565b60408051938452602084019290925282820152519081900360600190f35b61033f610ae7565b60408051602080825283518183015283519192839290830191858101910280838360005b8381101561037b578181015183820152602001610363565b505050509050019250505060405180910390f35b61030f600480360360608110156103a557600080fd5b506001600160a01b038135169060208101359060400135610bae565b61030f600480360360608110156103d757600080fd5b5080359060208101359060400135610c08565b61030f6004803603604081101561040057600080fd5b5080359060200135610d19565b6104326004803603604081101561042357600080fd5b50803590602001351515610ddd565b604051808060200180602001848152602001838103835286818151815260200191508051906020019060200280838360005b8381101561047c578181015183820152602001610464565b50505050905001838103825285818151815260200191508051906020019060200280838360005b838110156104bb5781810151838201526020016104a3565b505050509050019550505050505060405180910390f35b6104ef600480360360208110156104e857600080fd5b5035611094565b60408051918252519081900360200190f35b61030f6110a7565b61030f600480360360a081101561051f57600080fd5b506001600160a01b0381351690602081013590604081013590606081013590608001356110fd565b61025061115b565b6104ef6111db565b6104ef6004803603602081101561056d57600080fd5b50356111e0565b6102506004803603602081101561058a57600080fd5b50356111f5565b61059961121f565b604051808060200180602001878152602001868152602001858152602001848152602001838103835289818151815260200191508051906020019060200280838360005b838110156105f55781810151838201526020016105dd565b50505050905001838103825288818151815260200191508051906020019060200280838360005b8381101561063457818101518382015260200161061c565b505050509050019850505050505050505060405180910390f35b610656611588565b6040805160208082528351818301528351919283929083019185019080838360005b83811015610690578181015183820152602001610678565b50505050905090810190601f1680156106bd5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61030f600480360360208110156106e157600080fd5b5035611615565b61030f600480360360208110156106fe57600080fd5b503561167c565b6102506116df565b6104ef611703565b6104ef6004803603604081101561072b57600080fd5b50803590602001356001600160a01b031661170e565b6104ef611761565b6104ef6004803603602081101561075f57600080fd5b5035611785565b6102506004803603602081101561077c57600080fd5b503561179a565b61030f6004803603602081101561079957600080fd5b50356001600160a01b03166117aa565b6104ef6118af565b6107b9611915565b6040518088815260200187815260200186815260200185815260200184815260200183815260200180602001828103825283818151815260200191508051906020019060200280838360008381101561063457818101518382015260200161061c565b6108246119bc565b60408051958652602086019490945284840192909252606084015215156080830152519081900360a00190f35b61030f6004803603602081101561086757600080fd5b5035611a48565b610876611abe565b6040805192835260208301919091528051918290030190f35b6104ef611ac8565b61030f600480360360408110156108ad57600080fd5b50803590602001351515611aec565b6104ef600480360360408110156108d257600080fd5b506001600160a01b038135169060200135611cf3565b61030f600480360360e08110156108fe57600080fd5b81359160208101359160408201359160608101359160808201359160a08101359181019060e0810160c082013564010000000081111561093d57600080fd5b82018360208201111561094f57600080fd5b8035906020019184602083028401116401000000008311171561097157600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550611d3c945050505050565b610250611e72565b60005460ff1681565b7f000000000000000000000000bfa12e4e1411b62eda8b035d71735667422a6a9e81565b336001600160a01b037f000000000000000000000000bfa12e4e1411b62eda8b035d71735667422a6a9e1614610a1c57610a1c611e96565b6000815111610a2757fe5b600181511180610a5d5750306001600160a01b031681600081518110610a4957fe5b60200260200101516001600160a01b031614155b610a6357fe5b8051610a76906015906020840190614b60565b5080516001600160401b0381118015610a8e57600080fd5b50604051908082528060200260200182016040528015610ab8578160200160208202803683370190505b508051610acd91601491602090910190614b60565b50610ad6611efe565b50565b600654600754600854909192565b6014546060906001148015610afe57506015546001145b8015610b345750306001600160a01b03166015600081548110610b1d57fe5b6000918252602090912001546001600160a01b0316145b610b98576015805480602002602001604051908101604052809291908181526020018280548015610b8e57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610b70575b5050505050610ba8565b6040805160008152602081019091525b90505b90565b60005460ff16610bc057610bc06120da565b336001600160a01b037f00000000000000000000000010000000000000000000000000000000000000031614610bf857610bf8611e96565b610c03838383612144565b505050565b336001600160a01b037f000000000000000000000000bfa12e4e1411b62eda8b035d71735667422a6a9e1614610c4057610c40611e96565b600054604080518082019091526016815275119514d3c8185b1c9958591e481858dd1a5d985d195960521b60208201529060ff1615610cfd5760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015610cc2578181015183820152602001610caa565b50505050905090810190601f168015610cef5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b506006929092556007556008556000805460ff19166001179055565b336001600160a01b037f000000000000000000000000bfa12e4e1411b62eda8b035d71735667422a6a9e1614610d5157610d51611e96565b600054604080518082019091526016815275119514d3c8185b1c9958591e481858dd1a5d985d195960521b60208201529060ff1615610dd15760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610cc2578181015183820152602001610caa565b50600291909155600355565b6060806000336001600160a01b037f000000000000000000000000bfa12e4e1411b62eda8b035d71735667422a6a9e1614610e1a57610e1a611e96565b6000610e25866122cf565b60018101805460ff60f01b19169055600581015490915060009015610e5d57816005015461271083600801540281610e5957fe5b0490505b6001820154600160f81b900460ff1680610e7b575081600401548111155b15610eee576001820154600160f81b900460ff16610edb57600482015460408051838152602081019290925242828201525188917fbece8aa526cdc5e528cdaa56c1d03edc19da51e41845aa146f64a7071f74c65a919081900360600190a25b610ee78783600061240b565b505061108d565b6060806060610efc85612575565b919450925090506060610f0d614bc1565b610f17858561270a565b61012081015191935091507f00000000000000000000000000000000000000000000000000000000000005dc90610f73906004908f907f00000000000000000000000000000000000000000000000000000000000000c8612a28565b1115610f9157610f858c88600061240b565b5050505050505061108d565b60108701805460ff19166001179055610120810151600f88018190556002554260035560008b15610fdb57610fc888838587612ab4565b8251929d50909b50995015610fdb575060015b610fe78d838584612d70565b8c7ffe8865c1fe85bbf124b9e0f16cccfeeb6f330454fd79475a31261c8fa250bc3089600f0154838561014001518661016001518d60100160009054906101000a900460ff164260405180878152602001861515815260200185815260200184815260200183600581111561105857fe5b8152602001828152602001965050505050505060405180910390a2505050600190940180546001600160f81b03169055505050505b9250925092565b60006110a1600483612d76565b92915050565b60005460ff166110b9576110b96120da565b336001600160a01b037f000000000000000000000000bfa12e4e1411b62eda8b035d71735667422a6a9e16146110f1576110f1611e96565b6000805460ff19169055565b60005460ff1661110f5761110f6120da565b336001600160a01b037f0000000000000000000000001000000000000000000000000000000000000003161461114757611147611e96565b6111548585858585612da7565b5050505050565b601454600090600114801561117257506015546001145b80156111a85750306001600160a01b0316601560008154811061119157fe5b6000918252602090912001546001600160a01b0316145b6111b3576000610ba8565b60146000815481106111c157fe5b6000918252602090912001546001600160a01b0316905090565b600581565b60006111eb82613156565b600f015492915050565b6015818154811061120557600080fd5b6000918252602090912001546001600160a01b0316905081565b606080600080600080601480548060200260200160405190810160405280929190818152602001828054801561127e57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611260575b50506009548451949a506001600160f01b031694506000939250506001600160401b038211905080156112b057600080fd5b506040519080825280602002602001820160405280156112da578160200160208202803683370190505b509050600087516001600160401b03811180156112f657600080fd5b50604051908082528060200260200182016040528015611320578160200160208202803683370190505b50905060005b88518110156114af5760006001600160a01b031689828151811061134657fe5b60200260200101516001600160a01b031614156113645760006113e8565b88818151811061137057fe5b60200260200101516001600160a01b0316633e5aa26a856040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b1580156113bb57600080fd5b505afa1580156113cf573d6000803e3d6000fd5b505050506040513d60208110156113e557600080fd5b50515b8382815181106113f457fe5b6020026020010181815250506015818154811061140d57fe5b9060005260206000200160009054906101000a90046001600160a01b03166001600160a01b031663eb91d37e6040518163ffffffff1660e01b8152600401604080518083038186803b15801561146257600080fd5b505afa158015611476573d6000803e3d6000fd5b505050506040513d604081101561148c57600080fd5b5051825183908390811061149c57fe5b6020908102919091010152600101611326565b5060006114bf60048a858561321e565b90506114cd60048a836133c4565b97507f00000000000000000000000002f0826ef6ad107cfc861152b32b52fd11bab9ed6001600160a01b0316633e5aa26a856040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561153357600080fd5b505afa158015611547573d6000803e3d6000fd5b505050506040513d602081101561155d57600080fd5b5051965061156e60048a85846134f8565b955061157b6004876135c0565b9450505050909192939495565b60018054604080516020600284861615610100026000190190941693909304601f8101849004840282018401909252818152929183018282801561160d5780601f106115e25761010080835404028352916020019161160d565b820191906000526020600020905b8154815290600101906020018083116115f057829003601f168201915b505050505081565b336001600160a01b037f000000000000000000000000bfa12e4e1411b62eda8b035d71735667422a6a9e161461164d5761164d611e96565b6000611658826122cf565b6001808201805460ff60f01b191690559091506116789083908390613615565b5050565b336001600160a01b037f000000000000000000000000bfa12e4e1411b62eda8b035d71735667422a6a9e16146116b4576116b4611e96565b60006116bf826122cf565b6001808201805460ff60f01b19169055909150611678908390839061240b565b7f00000000000000000000000002f0826ef6ad107cfc861152b32b52fd11bab9ed81565b6000610ba842611094565b60008061171a84613156565b9050600061172882856136fd565b90508061173a576000925050506110a1565b600019016000908152600e90910160205260409020546001600160801b0316905092915050565b7f00000000000000000000000000000000000000000000000000000000000005dc81565b600061179082613156565b600a015492915050565b6014818154811061120557600080fd5b336001600160a01b037f000000000000000000000000bfa12e4e1411b62eda8b035d71735667422a6a9e16146117e2576117e2611e96565b60408051602081019091523081526117fe906015906001614b60565b5060408051602081019091526001600160a01b0382168152611824906014906001614b60565b50806001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b15801561185e57600080fd5b505afa158015611872573d6000803e3d6000fd5b505050506040513d602081101561188857600080fd5b50516001600160a01b03909116600090815260056020526040902060ff909116600a0a9055565b6000806118ba611703565b9050806118cb576000915050610bab565b600460007f00000000000000000000000000000000000000000000000000000000000000c86000198401816118fc57fe5b068152602001908152602001600020600a015491505090565b60008060008060008060606004600601546004600701546004600801546004600901546004600a01546004600b01546004600c018080548060200260200160405190810160405280929190818152602001828054801561199e57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611980575b50505050509050965096509650965096509650965090919293949596565b60008060008060006119cc611703565b94506119d9600486613755565b6008549094508401925060006004817f00000000000000000000000000000000000000000000000000000000000000c88881611a1157fe5b0681526020810191909152604001600020600101549596949593946001600160f01b03851694600160f81b900460ff169350915050565b336001600160a01b037f000000000000000000000000bfa12e4e1411b62eda8b035d71735667422a6a9e1614611a8057611a80611e96565b438110611a8c57600080fd5b600160f01b8110611a9c57600080fd5b600980546001600160f01b0319166001600160f01b0392909216919091179055565b6002546003549091565b7f00000000000000000000000000000000000000000000000000000000000000c881565b336001600160a01b037f000000000000000000000000bfa12e4e1411b62eda8b035d71735667422a6a9e1614611b2457611b24611e96565b6000611b2e611703565b905060006004817f00000000000000000000000000000000000000000000000000000000000000c88481611b5e57fe5b0681526020810191909152604001600090812060108101805460ff1916905560088101829055600a8101829055600d81019190915560095460018201805486158015600160f81b026001600160f81b036001600160f01b03199093166001600160f01b03909516949094179190911692909217905560028201849055909150611be8575050611678565b606080611bf361376d565b6009549194509250611ca3915084908890611c38907f00000000000000000000000002f0826ef6ad107cfc861152b32b52fd11bab9ed906001600160f01b0316613963565b6014805480602002602001604051908101604052809291908181526020018280548015611c8e57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611c70575b5060049796959493508a9250899150506139f4565b6013849055837fc0eaa359541c7c642d9947c9496507c134f3e4f8e1fd433313eb27dc48cb1fb7611cd5600483613755565b604080519182524260208301528051918290030190a2505050505050565b6000611d357f00000000000000000000000002f0826ef6ad107cfc861152b32b52fd11bab9ed84611d2385613156565b600101546001600160f01b0316613aa0565b9392505050565b336001600160a01b037f000000000000000000000000bfa12e4e1411b62eda8b035d71735667422a6a9e1614611d7457611d74611e96565b600a879055600b869055600c859055600d849055600e839055600f82905560105460005b81811015611df55760006004600d0160006004600c018481548110611db957fe5b6000918252602080832091909101546001600160a01b031683528201929092526040019020805460ff1916911515919091179055600101611d98565b5050805160005b81811015611e535760016004600d016000858481518110611e1957fe5b6020908102919091018101516001600160a01b03168252810191909152604001600020805460ff1916911515919091179055600101611dfc565b508151611e67906010906020850190614b60565b505050505050505050565b7f000000000000000000000000100000000000000000000000000000000000000381565b604080518082018252600d81526c1058d8d95cdcc819195b9a5959609a1b6020808301918252925162461bcd60e51b81526004810193845282516024820152825192939283926044909201919080838360008315610cc2578181015183820152602001610caa565b6015546001148015611f3a5750306001600160a01b03166015600081548110611f2357fe5b6000918252602090912001546001600160a01b0316145b15611f44576120d8565b60005b601554811015610ad657600060158281548110611f6057fe5b6000918252602091829020015460408051635c222bad60e01b815290516001600160a01b0390921692635c222bad92600480840193829003018186803b158015611fa957600080fd5b505afa158015611fbd573d6000803e3d6000fd5b505050506040513d6020811015611fd357600080fd5b5051601480549192509083908110611fe757fe5b6000918252602090912001546001600160a01b038281169116141561200c57506120d0565b806014838154811061201a57fe5b600091825260209091200180546001600160a01b0319166001600160a01b039283161790558116156120ce57806001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b15801561207f57600080fd5b505afa158015612093573d6000803e3d6000fd5b505050506040513d60208110156120a957600080fd5b50516001600160a01b038216600090815260056020526040902060ff909116600a0a90555b505b600101611f47565b565b604080518082018252600f81526e4654534f206e6f742061637469766560881b6020808301918252925162461bcd60e51b81526004810193845282516024820152825192939283926044909201919080838360008315610cc2578181015183820152602001610caa565b61214c611703565b82146040518060400160405280600e81526020016d15dc9bdb99c8195c1bd8da081a5960921b815250906121c15760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610cc2578181015183820152602001610caa565b5060008281526012602090815260408083206001600160a01b0387168452825291829020548251808401909352601983527f4475706c6963617465207375626d697420696e2065706f63680000000000000091830191909152156122665760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610cc2578181015183820152602001610caa565b5060008281526012602090815260408083206001600160a01b038716808552908352928190208490558051848152429281019290925280518593927f615c0184a2b16dbbcd09eae0bf239e28977aa4e6ff2204eda59c14016310bb6692908290030190a3505050565b60006122dc600483613b43565b4210156040518060400160405280602081526020017f45706f6368206e6f7420726561647920666f722066696e616c697a6174696f6e815250906123615760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610cc2578181015183820152602001610caa565b5061236b82613156565b90506000601082015460ff16600581111561238257fe5b146040518060400160405280601781526020017f45706f636820616c72656164792066696e616c697a6564000000000000000000815250906124055760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610cc2578181015183820152602001610caa565b50919050565b600080612475846004600c0180548060200260200160405190810160405280929190818152602001828054801561246b57602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161244d575b5050505050613b5c565b9092509050801561256a5780828161248957fe5b04600f8501558261249b57600261249e565b60045b60108501805460ff191660018360058111156124b657fe5b0217905550600f840154600255426003556124d085610ad6565b847ffe8865c1fe85bbf124b9e0f16cccfeeb6f330454fd79475a31261c8fa250bc3085600f015460008060008960100160009054906101000a900460ff164260405180878152602001861515815260200185815260200184815260200183600581111561253957fe5b8152602001828152602001965050505050505060405180910390a26001840180546001600160f81b03169055611154565b611154858585613615565b6060806060600084600d01549050806001600160401b038111801561259957600080fd5b506040519080825280602002602001820160405280156125c3578160200160208202803683370190505b509350806001600160401b03811180156125dc57600080fd5b50604051908082528060200260200182016040528015612606578160200160208202803683370190505b5091506000816001600160401b038111801561262157600080fd5b5060405190808252806020026020018201604052801561264b578160200160208202803683370190505b50905060005b828110156126f3576000818152600e880160205260409020805487516001600160801b039091169088908490811061268557fe5b602090810291909101015280548551600160801b9091046001600160401b0316908690849081106126b257fe5b602090810291909101015280548351600160c01b9091046001600160401b0316908490849081106126df57fe5b602090810291909101015250600101612651565b506126ff868483613c07565b935050509193909250565b6060612714614bc1565b8351806001600160401b038111801561272c57600080fd5b50604051908082528060200260200182016040528015612756578160200160208202803683370190505b50925060005b81811015612784578084828151811061277157fe5b602090810291909101015260010161275c565b5061279b6002600060018403600080888b8b613d1e565b6080850152606084015280835283518591859181106127b657fe5b6020026020010151815181106127c857fe5b602090810291909101015160a0830181905260808301516060840151845192010190612801578251600060c08501526020840152612855565b60048104836060015111612825578251606084015160c08501526020840152612855565b61284960016000600186600001510360008760a00151886080015101898c8c613d1e565b5060c085015260208401525b825160001983011415612876578251600061010085015260408401526128ce565b6004810483608001511161289b578251608084015161010085015260408401526128ce565b6128c160038460000151600101600185038660a001518760600151016000898c8c613d1e565b6101008601525060408401525b85848460000151815181106128df57fe5b6020026020010151815181106128f157fe5b6020908102919091010151610120840152600281048360a0015184606001510114801561291f575060028106155b1561294f576002612939846000015160018503878a613ff9565b846101200151018161294757fe5b046101208401525b612969836020015160006000198660c00151888b8b6140b9565b60c0850152602084015260408301516101008401516129949190600019850190600190888b8b6140b9565b6101008501526040840152602083015184518791869181106129b257fe5b6020026020010151815181106129c457fe5b60200260200101518361014001818152505085848460400151815181106129e757fe5b6020026020010151815181106129f957fe5b602090810291909101015161016084015261010083015160c08401519091030360e08301525090939092509050565b600083612a3757506000612aac565b6000858184600019880181612a4857fe5b068152602001908152602001600020600f0154905080841415612a6f576000915050612aac565b83612a825764e8d4a51000915050612aac565b600084821115612a955750838103612a9a565b508084035b612aa7816127108761418d565b925050505b949350505050565b600a840154602084015160609182916000919082905b88604001518111612b95576000888281518110612ae357fe5b602002602001015190506000888281518110612afb57fe5b60200260200101511115612b8b576000818152600e8c0160205260409020546101408b01516001600160801b0390911690811480612b4657508a6101600151816001600160801b0316145b8015612b7757506000828152600e8d016020526040902060010154612b759086906001600160a01b0316614299565b155b15612b83575050612b8d565b506001909201915b505b600101612aca565b50806001600160401b0381118015612bac57600080fd5b50604051908082528060200260200182016040528015612bd6578160200160208202803683370190505b509450806001600160401b0381118015612bef57600080fd5b50604051908082528060200260200182016040528015612c19578160200160208202803683370190505b5060208901519094506000905b89604001518111612d62576000898281518110612c3f57fe5b602002602001015190506000898281518110612c5757fe5b602002602001015190506000811115612d57576000828152600e8e0160205260409020546101408d01516001600160801b0390911690811480612ca757508c6101600151816001600160801b0316145b8015612cd857506000838152600e8f016020526040902060010154612cd69088906001600160a01b0316614299565b155b15612ce557505050612d5a565b6000838152600e8f0160205260409020600101548a516001600160a01b03909116908b9087908110612d1357fe5b60200260200101906001600160a01b031690816001600160a01b03168152505081898681518110612d4057fe5b602090810291909101015250958601956001909301925b50505b600101612c26565b505050509450945094915050565b50505050565b60008260020154821015612d8c575060006110a1565b82600301548360020154830381612d9f57fe5b0490506110a1565b60408051808201909152600e81526d0a0e4d2c6ca40e8dede40d0d2ced60931b6020820152600160801b8410612e1e5760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610cc2578181015183820152602001610caa565b50612e2a6004856142d4565b6040518060400160405280601881526020017f52657665616c20706572696f64206e6f7420616374697665000000000000000081525090612eac5760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610cc2578181015183820152602001610caa565b506040805160208082018690528183018590526001600160a01b0388166060808401829052845180850390910181526080840180865281519184019190912060008a81526012855286812093815292909352908490205460e08401909452602380825293909114929091614d2d9060a0013990612f6a5760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610cc2578181015183820152602001610caa565b5060006004817f00000000000000000000000000000000000000000000000000000000000000c88781612f9957fe5b0681526020810191909152604001600020600181015490915060ff600160f81b8204811691600160f01b8104909116906001600160f01b03168180612ffe5750828015612ffe57506001600160a01b03891660009081526011602052604090205460ff165b6040518060400160405280602081526020017f45706f6368206e6f7420696e697469616c697a656420666f722072657665616c815250906130805760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610cc2578181015183820152602001610caa565b50600080613091868c8988876142ff565b915091506130d4868c84848d8d8f60405160200180838152602001828152602001925050506040516020818303038152906040528051906020012060001c61435f565b60008a81526012602090815260408083206001600160a01b038f168085529083528184209390935580518c81529182018b905242828201526060820185905260808201849052518c92917f408bc8c9d02102257c33373c2df5771b03067bd8ea7ec60c35f314ec4ee99d05919081900360a00190a35050505050505050505050565b60006004817f00000000000000000000000000000000000000000000000000000000000000c8848161318457fe5b0681526020019081526020016000209050806002015482146040518060400160405280601881526020017f45706f63682064617461206e6f7420617661696c61626c650000000000000000815250906124055760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610cc2578181015183820152602001610caa565b82516060906000816001600160401b038111801561323b57600080fd5b50604051908082528060200260200182016040528015613265578160200160208202803683370190505b5090506000805b8381101561334e5760006001600160a01b031688828151811061328b57fe5b60200260200101516001600160a01b031614156132a757613346565b600061331e8783815181106132b857fe5b60200260200101518b60010160008c86815181106132d257fe5b60200260200101516001600160a01b03166001600160a01b03168152602001908152602001600020548a858151811061330757fe5b602002602001015161418d9092919063ffffffff16565b90508084838151811061332d57fe5b6020908102919091010152613342838261447c565b9250505b60010161326c565b5080156133b95760005b838110156133b75761339861338b61271088848151811061337557fe5b60200260200101516144d690919063ffffffff16565b8385848151811061330757fe5b8382815181106133a457fe5b6020908102919091010152600101613358565b505b509695505050505050565b8151606090806001600160401b03811180156133df57600080fd5b50604051908082528060200260200182016040528015613409578160200160208202803683370190505b50915060005b818110156134ef5760006001600160a01b031685828151811061342e57fe5b60200260200101516001600160a01b0316146134cc57600086600101600087848151811061345857fe5b60200260200101516001600160a01b03166001600160a01b031681526020019081526020016000205490506134ae61271064e8d4a510008161349657fe5b04670de0b6b3a7640000028287858151811061330757fe5b8483815181106134ba57fe5b602002602001018181525050506134e7565b60008382815181106134da57fe5b6020026020010181815250505b60010161340f565b50509392505050565b600080805b85518110156135b65760006001600160a01b031686828151811061351d57fe5b60200260200101516001600160a01b0316146135ae57600061271061359687848151811061354757fe5b60200260200101518a60010160008b878151811061356157fe5b60200260200101516001600160a01b03166001600160a01b031681526020019081526020016000205488868151811061330757fe5b8161359d57fe5b0490506135aa838261447c565b9250505b6001016134fd565b5095945050505050565b60008083600801548310156135d757506000611d35565b836009015483106135eb5750611388611d35565b600884015460098501546101f49190819003908503611194028161360b57fe5b0401949350505050565b821561362857600254600f830155613630565b6000600f8301555b8061363c57600361363f565b60055b60108301805460ff1916600183600581111561365757fe5b021790555061366583610ad6565b827ffe8865c1fe85bbf124b9e0f16cccfeeb6f330454fd79475a31261c8fa250bc3083600f015460008060008760100160009054906101000a900460ff16426040518087815260200186151581526020018581526020018481526020018360058111156136ce57fe5b8152602001828152602001965050505050505060405180910390a25060010180546001600160f81b0316905550565b600d820154600090815b8181101561374a576000818152600e860160205260409020600101546001600160a01b03858116911614156137425760010191506110a19050565b600101613707565b506000949350505050565b60038201546002830154600183019091020192915050565b606080606061377a611efe565b60148054806020026020016040519081016040528092919081815260200182805480156137d057602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116137b2575b5050505050925082516001600160401b03811180156137ee57600080fd5b50604051908082528060200260200182016040528015613818578160200160208202803683370190505b50915082516001600160401b038111801561383257600080fd5b5060405190808252806020026020018201604052801561385c578160200160208202803683370190505b50905060005b835181101561395d5761389684828151811061387a57fe5b60209081029190910101516009546001600160f01b0316613963565b8382815181106138a257fe5b602002602001018181525050601581815481106138bb57fe5b9060005260206000200160009054906101000a90046001600160a01b03166001600160a01b031663eb91d37e6040518163ffffffff1660e01b8152600401604080518083038186803b15801561391057600080fd5b505afa158015613924573d6000803e3d6000fd5b505050506040513d604081101561393a57600080fd5b5051825183908390811061394a57fe5b6020908102919091010152600101613862565b50909192565b60006001600160a01b03831661397b575060006110a1565b826001600160a01b031663caeb942b836040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b1580156139c157600080fd5b505af11580156139d5573d6000803e3d6000fd5b505050506040513d60208110156139eb57600080fd5b505190506110a1565b613a01878785858561452f565b600a8701546003870155600b8701546004870155600586018590556006808701859055870154613a39908581613a3357fe5b04614598565b86546001600160801b0319166001600160801b039190911617865560078781015490870154613a6a919081613a3357fe5b86546001600160801b03918216600160801b0291161786555050506001909201805460ff60f01b1916600160f01b179055505050565b60006001600160a01b038416613ab857506000611d35565b836001600160a01b031663e587497e84846040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050602060405180830381600087803b158015613b0f57600080fd5b505af1158015613b23573d6000803e3d6000fd5b505050506040513d6020811015613b3957600080fd5b5051949350505050565b60008260040154613b548484613755565b019392505050565b600d8201546000908190815b81811015613bfe576000818152600e870160205260408120600101546001600160a01b0316905b8651811015613bf457868181518110613ba457fe5b60200260200101516001600160a01b0316826001600160a01b03161415613bec576000838152600e890160205260409020546001600160801b03169590950194600194909401935b600101613b8f565b5050600101613b68565b50509250929050565b600d830154606090806001600160401b0381118015613c2557600080fd5b50604051908082528060200260200182016040528015613c4f578160200160208202803683370190505b5091506000613c5d856145e0565b90506000613c6a856145e0565b9050600080613c7a898585614625565b90508315613c8a57806127100391505b60005b85811015613d115760008315613cd357613cd08a8381518110613cac57fe5b602002602001015164e8d4a510000261271088028661418d9092919063ffffffff16565b90505b60008315613ced57613cea8a8481518110613cac57fe5b90505b808201898481518110613cfc57fe5b60209081029190910101525050600101613c8d565b5050505050509392505050565b6000806000888a1415613d38575088915086905085613feb565b613d40614c22565b88815260208101889052613d52614c5f565b60208082018d905260408083018d90528051448184015242818301528151808203830181526060909101909152805191012060005b60208301516040840151613db99190816001818303018681613da557fe5b0601876000015188602001518f8f8f61468c565b606087015260408601528084528a518b91908110613dd357fe5b602002602001015183606001818152505087836060015181518110613df457fe5b6020026020010151846080018181525050836060015184604001518560800151010190508e60021415613ecf5760028106600282040160a0850181905280820360c0860152604085015110801590613e4f57508b8460a00151115b15613e7457825160001901604084015260608401516080850151016020850152613eca565b8360c001518460600151118015613e8e57508a8460c00151115b15613eaf578251600101602084015260408401516080850151018452613eca565b50505160408201516060909201519094509092509050613feb565b613fe6565b8e60011415613f60576004810460a0850181905280820360c08601526040850151118015613f0057508b8460a00151115b15613f2557825160001901604084015260608401516080850151016020850152613eca565b8360c00151846060015110158015613e8e57508a8460c001511115613eaf578251600101602084015260408401516080850151018452613eca565b6004810460c08501819052810360a08501819052604085015110801590613f8a57508b8460a00151115b15613faf57825160001901604084015260608401516080850151016020850152613fe6565b8360c001518460600151118015613fc957508a8460c00151115b15613eaf5782516001016020840152604084015160808501510184525b613d87565b985098509895505050505050565b600083851415614031578183868151811061401057fe5b60200260200101518151811061402257fe5b60200260200101519050612aac565b60008284876001018151811061404357fe5b60200260200101518151811061405557fe5b602002602001015190506000808760020190505b8681116140ad578486828151811061407d57fe5b60200260200101518151811061408f57fe5b60200260200101519150828210156140a5578192505b600101614069565b50909695505050505050565b60008085888a03880282136140d45789879250925050614181565b600085878c815181106140e357fe5b6020026020010151815181106140f557fe5b602090810291909101015190508a89016000815b60008c8e830302136141755789818151811061412157fe5b602002602001015191508389838151811061413857fe5b6020026020010151141561416e5787828151811061415257fe5b60200260200101518503945061416983828c614855565b918b01915b8b01614109565b50508990039350909150505b97509795505050505050565b60008082116141d6576040805162461bcd60e51b815260206004820152601060248201526f4469766973696f6e206279207a65726f60801b604482015290519081900360640190fd5b836141e357506000611d35565b838302838582816141f057fe5b0414156142095782818161420057fe5b04915050611d35565b600083868161421457fe5b049050600084878161422257fe5b069050600085878161423057fe5b049050600086888161423e57fe5b06905061428c6142588861425286856144d6565b906148bd565b61428661426586866144d6565b61428661427289876144d6565b6142868d6142808c8b6144d6565b906144d6565b9061447c565b9998505050505050505050565b604080516020808201949094526001600160a01b03929092168282015280518083038201815260609092019052805191012060019081161490565b6000806142e18484613755565b9050428111158015612aac5750836004015481014210949350505050565b600080831561431357506000905080614355565b849150614321878785614924565b87549091506001600160801b0380821691600160801b90041681841115614346578193505b80831115614352578092505b50505b9550959350505050565b600086600d0154905060006143808787878b600601548c60070154896149e2565b63ffffffff838116608083019081526000858152600e8c0160209081526040918290208551815492870151938701516001600160801b03199093166001600160801b039091161767ffffffffffffffff60801b1916600160801b6001600160401b0394851602176001600160c01b0316600160c01b939092169290920217815560608401516001918201805493516001600160a01b03199094166001600160a01b039092169190911763ffffffff60a01b1916600160a01b93851693909302929092179091558401600d8b015560088a01549192506144629190889061447c16565b60088901555050600a909501805490950190945550505050565b600082820183811015611d35576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b6000826144e5575060006110a1565b828202828482816144f257fe5b0414611d355760405162461bcd60e51b8152600401808060200182810382526021815260200180614d506021913960400191505060405180910390fd5b825161454490600b8601906020860190614b60565b506145518584848461321e565b805161456791600c870191602090910190614c87565b506000614575868685614a3f565b60078601819055905061458886826135c0565b8560090181905550505050505050565b6000600160801b82106145dc5760405162461bcd60e51b8152600401808060200182810382526027815260200180614d066027913960400191505060405180910390fd5b5090565b600080805b835181101561461e576146148482815181106145fd57fe5b60200260200101518361447c90919063ffffffff16565b91506001016145e5565b5092915050565b60008161463457506000611d35565b826146425750612710611d35565b60006146568361271064e8d4a5100061418d565b90508460030154811061466f5750506009830154611d35565b6003850154600986015461468491839061418d565b915050611d35565b60008060008085878b8151811061469f57fe5b6020026020010151815181106146b157fe5b60200260200101519050600060026001600160401b03811180156146d457600080fd5b506040519080825280602002602001820160405280156146fe578160200160208202803683370190505b509050898160008151811061470f57fe5b602002602001018181525050888160018151811061472957fe5b60209081029190910101528c8c6147418d828c614855565b81805b828110156148055760008c828151811061475a57fe5b60200260200101519050868c828151811061477157fe5b602002602001015110156147c8578a818151811061478b57fe5b6020026020010151866000815181106147a057fe5b6020026020010181815101915081815250506147bd83838f614855565b6001909201916147fc565b8a81815181106147d457fe5b6020026020010151866001815181106147e957fe5b6020026020010181815101915081815250505b50600101614744565b5061481182828d614855565b808460008151811061481f57fe5b60200260200101518560018151811061483457fe5b60200260200101519750975097505050505050985098509895505050505050565b8183141561486257610c03565b80828151811061486e57fe5b602002602001015181848151811061488257fe5b602002602001015182858151811061489657fe5b602002602001018385815181106148a957fe5b602090810291909101019190915252505050565b6000808211614913576040805162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b81838161491c57fe5b049392505050565b600b83015460009081906001600160401b038111801561494357600080fd5b5060405190808252806020026020018201604052801561496d578160200160208202803683370190505b50905060005b600b8601548110156149cc576149ad86600b01828154811061499157fe5b6000918252602090912001546001600160a01b03168686613aa0565b8282815181106149b957fe5b6020908102919091010152600101614973565b506149d960048683614a3f565b95945050505050565b6149ea614cc2565b6001600160a01b0387166060820152614a038685614b2f565b6001600160401b03166020820152614a1b8584614b2f565b6001600160401b031660408201526001600160801b03909116815295945050505050565b600080805b600b850154811015614b265760006001600160a01b031685600b018281548110614a6a57fe5b6000918252602090912001546001600160a01b03161415614a8a57614b1e565b614b1b612710614b0b868481518110614a9f57fe5b60200260200101518960010160008a600b018781548110614abc57fe5b60009182526020808320909101546001600160a01b03168352820192909252604001902054600c8a01805487908110614af157fe5b906000526020600020015461418d9092919063ffffffff16565b81614b1257fe5b8491900461447c565b91505b600101614a44565b50949350505050565b6000811580614b3c575082155b15614b49575060006110a1565b614b598364e8d4a510008461418d565b90506110a1565b828054828255906000526020600020908101928215614bb5579160200282015b82811115614bb557825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190614b80565b506145dc929150614cf0565b6040518061018001604052806000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6040518060e00160405280600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6040518060800160405280600081526020016000815260200160008152602001600081525090565b828054828255906000526020600020908101928215614bb5579160200282015b82811115614bb5578251825591602001919060010190614ca7565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915290565b5b808211156145dc5760008155600101614cf156fe53616665436173743a2076616c756520646f65736e27742066697420696e203132382062697473507269636520616c72656164792072657665616c6564206f72206e6f742076616c6964536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77a26469706673582212206abbc057beb7e88e14da4471adff41c5166c6aa382754ead6b2806155764308e64736f6c63430007060033