- Contract name:
- Ftso
- Optimization enabled
- true
- Compiler version
- v0.7.6+commit.7338295f
- Optimization runs
- 200
- Verified at
- 2022-07-13T21:26:41.556461Z
Constructor Arguments
0000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000000500000000000000000000000010000000000000000000000000000000000000030000000000000000000000001d80c49bbbcd1c0911346656b529df9e5c2f783d000000000000000000000000ab7c7da1ff2b25d0867908ddfa52827570e028940000000000000000000000000000000000000000000000000000000062cf1e4600000000000000000000000000000000000000000000000000000000000000b4000000000000000000000000000000000000000000000000000000000000005a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005dc00000000000000000000000000000000000000000000000000000000000000c800000000000000000000000000000000000000000000000000000000000000034c54430000000000000000000000000000000000000000000000000000000000
Arg [0] (string) : LTC
Arg [1] (uint256) : 5
Arg [2] (address) : 0x1000000000000000000000000000000000000003
Arg [3] (address) : 0x1d80c49bbbcd1c0911346656b529df9e5c2f783d
Arg [4] (address) : 0xab7c7da1ff2b25d0867908ddfa52827570e02894
Arg [5] (uint256) : 1657740870
Arg [6] (uint256) : 180
Arg [7] (uint256) : 90
Arg [8] (uint128) : 0
Arg [9] (uint256) : 1500
Arg [10] (uint256) : 200
./contracts/ftso/implementation/Ftso.sol
// SPDX-License-Identifier: MIT pragma solidity 0.7.6; import "../../token/interface/IIVPToken.sol"; import "../interface/IIFtso.sol"; import "../lib/FtsoEpoch.sol"; import "../lib/FtsoVote.sol"; import "../lib/FtsoMedian.sol"; import "../../userInterfaces/IPriceSubmitter.sol"; /** * @title A contract implementing Flare Time Series Oracle */ contract Ftso is IIFtso { using FtsoEpoch for FtsoEpoch.State; using SafeCast for uint256; // 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_EPOCH_FINALIZATION_FAILURE = "Epoch not ready for finalization"; string internal constant ERR_EPOCH_ALREADY_FINALIZED = "Epoch already finalized"; 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_INVALID_PRICE_EPOCH_PARAMETERS = "Invalid price epoch parameters"; // 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 // 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 // solhint-disable-next-line var-name-mixedcase uint256 public immutable ASSET_PRICE_USD_DECIMALS; uint128 internal assetPriceUSD; // current asset USD price uint128 internal assetPriceTimestamp; // time when price was updated uint128 internal assetTrustedProvidersPriceUSD; // current asset USD price from trusted providers uint128 internal assetTrustedProvidersPriceTimestamp; // time when price from trusted providers was updated PriceFinalizationType internal assetPriceFinalizationType; // price finalization type (uint8) uint240 internal lastPriceEpochFinalizationTimestamp; // last price epoch finalization timestamp PriceFinalizationType internal lastPriceEpochFinalizationType; // last price epoch finalization type (uint8) FtsoEpoch.State internal epochs; // epoch storage // immutable settings uint256 private immutable firstEpochStartTs; // start timestamp of the first epoch instance uint256 private immutable submitPeriodSeconds; // duration of price submission for an epoch instance uint256 private immutable revealPeriodSeconds; // duration of price reveal for an epoch instance // external contracts IIVPToken public immutable override wNat; // wrapped native token address immutable public override 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 != ftsoManager) { revertNoAccess(); } _; } modifier onlyPriceSubmitter { if (msg.sender != address(priceSubmitter)) { revertNoAccess(); } _; } constructor( string memory _symbol, uint256 _decimals, IPriceSubmitter _priceSubmitter, IIVPToken _wNat, address _ftsoManager, uint256 _firstEpochStartTs, uint256 _submitPeriodSeconds, uint256 _revealPeriodSeconds, uint128 _initialPriceUSD, uint256 _priceDeviationThresholdBIPS, uint256 _cyclicBufferSize ) { symbol = _symbol; ASSET_PRICE_USD_DECIMALS = _decimals; priceSubmitter = _priceSubmitter; wNat = _wNat; ftsoManager = _ftsoManager; firstEpochStartTs = _firstEpochStartTs; submitPeriodSeconds = _submitPeriodSeconds; revealPeriodSeconds = _revealPeriodSeconds; assetPriceUSD = _initialPriceUSD; assetPriceTimestamp = uint128(block.timestamp); // no overflow priceDeviationThresholdBIPS = _priceDeviationThresholdBIPS; priceEpochCyclicBufferSize = _cyclicBufferSize; } /** * @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 * @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 _voterWNatVP ) external override whenActive onlyPriceSubmitter { _revealPrice(_voter, _epochId, _price, _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); } _medianFinalizePriceEpoch(_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._computeWeighted(price, weight); // check price deviation if (epochs._getPriceDeviation(_epochId, data.finalMedianPrice, priceEpochCyclicBufferSize) > priceDeviationThresholdBIPS) { // revert to median price calculation _medianFinalizePriceEpoch(_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 = uint128(data.finalMedianPrice); // no overflow assetPriceTimestamp = uint128(block.timestamp); // no overflow assetPriceFinalizationType = PriceFinalizationType.WEIGHTED_MEDIAN; lastPriceEpochFinalizationTimestamp = uint240(block.timestamp); // no overflow lastPriceEpochFinalizationType = PriceFinalizationType.WEIGHTED_MEDIAN; // update trusted providers price if (epoch.trustedVotes.length > 0) { assetTrustedProvidersPriceUSD = uint128(FtsoMedian._computeSimple(epoch.trustedVotes)); // no overflow assetTrustedProvidersPriceTimestamp = uint128(block.timestamp); // no overflow } // return reward data if requested bool rewardedFtso = false; if (_returnRewardData) { uint256 random = _getRandom(_epochId); (_eligibleAddresses, _natWeights, _natWeightsSum) = _readRewardData(epoch, data, random, 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 median price from trusted addresses * @param _epochId Id of the epoch to finalize * @dev Used as a fallback method if epoch finalization is failing */ function fallbackFinalizePriceEpoch(uint256 _epochId) external override onlyFtsoManager { FtsoEpoch.Instance storage epoch = _getEpochForFinalization(_epochId); epoch.initializedForReveal = false; // set back to false for next usage _medianFinalizePriceEpoch(_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 _firstEpochStartTs Timestamp of the first epoch as seconds from unix epoch * @param _submitPeriodSeconds Duration of epoch submission period in seconds * @param _revealPeriodSeconds Duration of epoch reveal period in seconds */ function activateFtso( uint256 _firstEpochStartTs, uint256 _submitPeriodSeconds, uint256 _revealPeriodSeconds ) external override onlyFtsoManager { require(!active, ERR_ALREADY_ACTIVATED); require(firstEpochStartTs == _firstEpochStartTs, ERR_INVALID_PRICE_EPOCH_PARAMETERS); require(submitPeriodSeconds == _submitPeriodSeconds, ERR_INVALID_PRICE_EPOCH_PARAMETERS); require(revealPeriodSeconds == _revealPeriodSeconds, ERR_INVALID_PRICE_EPOCH_PARAMETERS); 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.toUint128(); assetPriceTimestamp = _initialPriceTimestamp.toUint128(); } /** * @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.nextVoteIndex = 0; epoch.votePowerBlock = epochs.votePowerBlock; epoch.fallbackMode = _fallbackMode; epoch.epochId = epochId; delete epoch.trustedVotes; if (_fallbackMode) { return; } uint256[] memory assetVotePowers; uint256[] memory assetPrices; (, assetVotePowers, assetPrices) = _getAssetData(); epochs._initializeInstanceForReveal( epoch, _circulatingSupplyNat, _getVotePowerAt(wNat, epochs.votePowerBlock), assets, assetVotePowers, assetPrices ); emit PriceEpochInitializedOnFtso(epochId, _getEpochSubmitEndTime(epochId), block.timestamp); } /** * @notice Returns current epoch data * @return _firstEpochStartTs First epoch start timestamp * @return _submitPeriodSeconds Submit period in seconds * @return _revealPeriodSeconds Reveal period in seconds */ function getPriceEpochConfiguration() external view override returns ( uint256 _firstEpochStartTs, uint256 _submitPeriodSeconds, uint256 _revealPeriodSeconds ) { return ( firstEpochStartTs, submitPeriodSeconds, revealPeriodSeconds ); } /** * @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 current asset price calculated from trusted providers * @return _price Price in USD multiplied by ASSET_PRICE_USD_DECIMALS * @return _timestamp Time when price was updated for the last time */ function getCurrentPriceFromTrustedProviders() external view override returns ( uint256 _price, uint256 _timestamp ) { return (assetTrustedProvidersPriceUSD, assetTrustedProvidersPriceTimestamp); } /** * @notice Returns current asset price details * @return _price Price in USD multiplied by ASSET_PRICE_USD_DECIMALS * @return _priceTimestamp Time when price was updated for the last time * @return _priceFinalizationType Finalization type when price was updated for the last time * @return _lastPriceEpochFinalizationTimestamp Time when last price epoch was finalized * @return _lastPriceEpochFinalizationType Finalization type of last finalized price epoch */ function getCurrentPriceDetails() external view override returns ( uint256 _price, uint256 _priceTimestamp, PriceFinalizationType _priceFinalizationType, uint256 _lastPriceEpochFinalizationTimestamp, PriceFinalizationType _lastPriceEpochFinalizationType ) { return ( assetPriceUSD, assetPriceTimestamp, assetPriceFinalizationType, lastPriceEpochFinalizationTimestamp, lastPriceEpochFinalizationType ); } /** * @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; } return _getRandom(currentEpochId - 1); } /** * @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 _getRandom(_epochId); } /** * @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 = _getEpochSubmitEndTime(_epochId); _epochRevealEndTime = _epochSubmitEndTime + revealPeriodSeconds; //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 virtual 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 _getEpochId(_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 * @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 _voterWNatVP ) internal { require(_price < 2**128, ERR_PRICE_TOO_HIGH); require(_isEpochRevealInProcess(_epochId), ERR_PRICE_REVEAL_FAILURE); // 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 ); epochs._addVote( epoch, _voter, votePowerNat, votePowerAsset, _price ); // inform about price reveal result emit PriceRevealed(_voter, _epochId, _price, 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 median 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 median of prices from trusted addresses or force finalize if no votes submitted */ function _medianFinalizePriceEpoch( uint256 _epochId, FtsoEpoch.Instance storage _epoch, bool _exception ) internal { if (_epoch.trustedVotes.length > 0) { // finalizationType = PriceFinalizationType.TRUSTED_ADDRESSES _epoch.price = FtsoMedian._computeSimple(_epoch.trustedVotes); _epoch.finalizationType = _exception ? PriceFinalizationType.TRUSTED_ADDRESSES_EXCEPTION : PriceFinalizationType.TRUSTED_ADDRESSES; // update price assetPriceUSD = uint128(_epoch.price); // no overflow assetPriceTimestamp = uint128(block.timestamp); // no overflow assetPriceFinalizationType = _epoch.finalizationType; lastPriceEpochFinalizationTimestamp = uint240(block.timestamp); // no overflow lastPriceEpochFinalizationType = _epoch.finalizationType; // update trusted providers price assetTrustedProvidersPriceUSD = uint128(_epoch.price); // no overflow assetTrustedProvidersPriceTimestamp = uint128(block.timestamp); // no overflow _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; lastPriceEpochFinalizationTimestamp = uint240(block.timestamp); // no overflow lastPriceEpochFinalizationType = _epoch.finalizationType; _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 Returns random for given epoch id */ function _getRandom(uint256 _epochId) internal view virtual returns (uint256) { return uint256(keccak256(abi.encode(priceSubmitter.getRandom(_epochId), address(this)))); } /** * @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 Extracts reward data for epoch * @param _epoch The epoch instance to read data from * @param _data Median computation data * @param _random Random number * @param _index Array of vote indices * @param _weightNat Array of native token weights */ function _readRewardData( FtsoEpoch.Instance storage _epoch, FtsoMedian.Data memory _data, uint256 _random, uint256[] memory _index, uint256[] memory _weightNat ) internal view returns ( address[] memory _eligibleAddresses, uint256[] memory _natWeights, uint256 _natWeightsSum ) { 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 >= _getEpochRevealEndTime(_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 Returns the id of the epoch opened for price submission at the given timestamp * @param _timestamp Timestamp as seconds since unix epoch * @return Epoch id * @dev Should never revert */ function _getEpochId(uint256 _timestamp) internal view returns (uint256) { if (_timestamp < firstEpochStartTs) { return 0; } else { return (_timestamp - firstEpochStartTs) / submitPeriodSeconds; } } /** * @notice Returns start time of price submission for an epoch instance * @param _epochId Id of epoch instance * @return Timestamp as seconds since unix epoch */ function _getEpochSubmitStartTime(uint256 _epochId) internal view returns (uint256) { return firstEpochStartTs + _epochId * submitPeriodSeconds; } /** * @notice Returns end time of price submission for an epoch instance = reveal start time * @param _epochId Id of epoch instance * @return Timestamp as seconds since unix epoch * @dev half-closed interval - end time not included */ function _getEpochSubmitEndTime(uint256 _epochId) internal view returns (uint256) { return firstEpochStartTs + (_epochId + 1) * submitPeriodSeconds; } /** * @notice Returns end time of price reveal for an epoch instance * @param _epochId Id of epoch instance * @return Timestamp as seconds since unix epoch * @dev half-closed interval - end time not included */ function _getEpochRevealEndTime(uint256 _epochId) internal view returns (uint256) { return _getEpochSubmitEndTime(_epochId) + revealPeriodSeconds; } /** * @notice Determines if the epoch with the given id is currently in the reveal process * @param _epochId Id of epoch * @return True if epoch reveal is in process and false otherwise */ function _isEpochRevealInProcess(uint256 _epochId) internal view returns (bool) { uint256 revealStartTime = _getEpochSubmitEndTime(_epochId); return revealStartTime <= block.timestamp && block.timestamp < revealStartTime + revealPeriodSeconds; } /** * @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); } }
./contracts/userInterfaces/IVPContractEvents.sol
// SPDX-License-Identifier: MIT pragma solidity >=0.7.6 <0.9; 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); }
./contracts/userInterfaces/IVPToken.sol
// SPDX-License-Identifier: MIT pragma solidity >=0.7.6 <0.9; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {IGovernanceVotePower} from "./IGovernanceVotePower.sol"; import {IVPContractEvents} from "./IVPContractEvents.sol"; 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 Undelegate all percentage delegations from teh sender and then delegate corresponding * `_bips` percentage of voting power from the sender to each member of `_delegatees`. * @param _delegatees The addresses of the new recipients. * @param _bips The percentages of voting power to be delegated expressed in basis points (1/100 of one percent). * Total of all `_bips` values must be at most 10000. **/ function batchDelegate(address[] memory _delegatees, uint256[] memory _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 vote power of `_owner` at block `_blockNumber`, ignoring revocation information (and cache). * @param _owner The address to get voting power. * @param _blockNumber The block number at which to fetch. * @return Vote power of `_owner` at `_blockNumber`. Result doesn't change if vote power is revoked. */ function votePowerOfAtIgnoringRevocation(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); }
./contracts/ftso/interface/IIFtso.sol
// SPDX-License-Identifier: MIT pragma solidity >=0.7.6 <0.9; import "../../genesis/interface/IFtsoGenesis.sol"; import "../../userInterfaces/IFtso.sol"; import "../../token/interface/IIVPToken.sol"; 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 fallbackFinalizePriceEpoch(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 _submitPeriodSeconds, uint256 _revealPeriodSeconds ) 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 ftso manager address */ function ftsoManager() external view returns (address); /** * @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); /** * @notice Returns current asset price calculated from trusted providers * @return _price Price in USD multiplied by ASSET_PRICE_USD_DECIMALS * @return _timestamp Time when price was updated for the last time */ function getCurrentPriceFromTrustedProviders() external view returns (uint256 _price, uint256 _timestamp); }
./contracts/ftso/lib/FtsoEpoch.sol
// SPDX-License-Identifier: MIT pragma solidity 0.7.6; import "../../token/interface/IIVPToken.sol"; import "../../userInterfaces/IFtso.sol"; import "@openzeppelin/contracts/math/SafeMath.sol"; import "../../utils/implementation/SafePct.sol"; import "@openzeppelin/contracts/utils/SafeCast.sol"; import "./FtsoVote.sol"; /** * @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 // 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; 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[] trustedVotes; // array of all votes from trusted providers 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 _state Epoch state * @param _instance Epoch instance * @param _voter Voter address * @param _votePowerNat Vote power for native token * @param _votePowerAsset Vote power for asset * @param _price Price in USD submitted in a vote */ function _addVote( State storage _state, Instance storage _instance, address _voter, uint256 _votePowerNat, uint256 _votePowerAsset, uint256 _price ) 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); if (_state.trustedAddressesMapping[_voter]) { _instance.trustedVotes.push(_price); } } /** * @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 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; } }
./contracts/ftso/lib/FtsoMedian.sol
// SPDX-License-Identifier: MIT 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 _computeWeighted( 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; } /** * @notice Computes the simple median price (using insertion sort) - sorts original array * @param _prices positional array of prices to be sorted * @return _finalMedianPrice median price */ function _computeSimple( uint256[] memory _prices ) internal pure returns ( uint256 _finalMedianPrice ) { uint256 length = _prices.length; assert(length > 0); for (uint256 i = 1; i < length; i++) { // price to sort next uint256 currentPrice = _prices[i]; // shift bigger prices right uint256 j = i; while (j > 0 && _prices[j - 1] > currentPrice) { _prices[j] = _prices[j - 1]; j--; // no underflow } // insert _prices[j] = currentPrice; } uint256 middleIndex = length / 2; if (length % 2 == 1) { return _prices[middleIndex]; } else { // if median is "in the middle", take the average price of the two consecutive prices return (_prices[middleIndex - 1] + _prices[middleIndex]) / 2; } } }
./contracts/ftso/lib/FtsoVote.sol
// SPDX-License-Identifier: MIT pragma solidity 0.7.6; import "../../utils/implementation/SafePct.sol"; /** * @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)); } } }
./contracts/genesis/interface/IFtsoGenesis.sol
// SPDX-License-Identifier: MIT pragma solidity >=0.7.6 <0.9; interface IFtsoGenesis { /** * @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 * @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 _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); }
./contracts/genesis/interface/IFtsoManagerGenesis.sol
// SPDX-License-Identifier: MIT pragma solidity >=0.7.6 <0.9; interface IFtsoManagerGenesis { function getCurrentPriceEpochId() external view returns (uint256 _priceEpochId); }
./contracts/genesis/interface/IFtsoRegistryGenesis.sol
// SPDX-License-Identifier: MIT pragma solidity >=0.7.6 <0.9; import "./IFtsoGenesis.sol"; interface IFtsoRegistryGenesis { function getFtsos(uint256[] memory _indices) external view returns(IFtsoGenesis[] memory _ftsos); }
./contracts/token/interface/IICleanable.sol
// SPDX-License-Identifier: MIT pragma solidity >=0.7.6 <0.9; 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; /** * Get the current cleanup block number. */ function cleanupBlockNumber() external view returns (uint256); }
./contracts/token/interface/IIGovernanceVotePower.sol
// SPDX-License-Identifier: MIT pragma solidity >=0.7.6 <0.9; import "../../userInterfaces/IVPToken.sol"; import "../../userInterfaces/IGovernanceVotePower.sol"; interface IIGovernanceVotePower is IGovernanceVotePower { /** * Event triggered when an delegator's balance changes. * * Note: the event is always emitted from `GovernanceVotePower`. */ event DelegateVotesChanged( address indexed delegate, uint256 previousBalance, uint256 newBalance ); /** * Event triggered when an account delegates to another account. * * Note: the event is always emitted from `GovernanceVotePower`. */ event DelegateChanged( address indexed delegator, address indexed fromDelegate, address indexed toDelegate ); /** * 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); function getCleanupBlockNumber() external view returns(uint256); }
./contracts/token/interface/IIVPContract.sol
// SPDX-License-Identifier: MIT pragma solidity >=0.7.6 <0.9; import "../../userInterfaces/IVPToken.sol"; import "../../userInterfaces/IVPContractEvents.sol"; import "./IICleanable.sol"; 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); /** * @notice Get the vote power of `_who` at block `_blockNumber`, ignoring revocation information (and cache). * @param _who The address to get voting power. * @param _blockNumber The block number at which to fetch. * @return Vote power of `_who` at `_blockNumber`. Result doesn't change if vote power is revoked. */ function votePowerOfAtIgnoringRevocation(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); }
./contracts/token/interface/IIVPToken.sol
// SPDX-License-Identifier: MIT pragma solidity >=0.7.6 <0.9; import "../../userInterfaces/IVPToken.sol"; import "../../userInterfaces/IGovernanceVotePower.sol"; import "./IIVPContract.sol"; import "./IIGovernanceVotePower.sol"; import "./IICleanable.sol"; interface IIVPToken is IVPToken, IICleanable { /** * Set the contract that is allowed to set cleanupBlockNumber. * Usually this will be an instance of CleanupBlockNumberManager. */ function setCleanupBlockNumberManager(address _cleanupBlockNumberManager) external; /** * 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); }
./contracts/userInterfaces/IFtso.sol
// SPDX-License-Identifier: MIT pragma solidity >=0.7.6 <0.9; interface IFtso { enum PriceFinalizationType { // initial state NOT_FINALIZED, // median calculation used to find price WEIGHTED_MEDIAN, // low turnout - price calculated from median of trusted addresses TRUSTED_ADDRESSES, // low turnout + no votes from trusted addresses - price copied from previous epoch PREVIOUS_PRICE_COPIED, // price calculated from median of trusted addresses - triggered due to an exception TRUSTED_ADDRESSES_EXCEPTION, // previous price copied - triggered due to an exception PREVIOUS_PRICE_COPIED_EXCEPTION } event PriceRevealed( address indexed voter, uint256 indexed epochId, uint256 price, 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 _firstEpochStartTs First epoch start timestamp * @return _submitPeriodSeconds Submit period in seconds * @return _revealPeriodSeconds Reveal period in seconds */ function getPriceEpochConfiguration() external view returns ( uint256 _firstEpochStartTs, uint256 _submitPeriodSeconds, uint256 _revealPeriodSeconds ); /** * @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 asset price details * @return _price Price in USD multiplied by ASSET_PRICE_USD_DECIMALS * @return _priceTimestamp Time when price was updated for the last time * @return _priceFinalizationType Finalization type when price was updated for the last time * @return _lastPriceEpochFinalizationTimestamp Time when last price epoch was finalized * @return _lastPriceEpochFinalizationType Finalization type of last finalized price epoch */ function getCurrentPriceDetails() external view returns ( uint256 _price, uint256 _priceTimestamp, PriceFinalizationType _priceFinalizationType, uint256 _lastPriceEpochFinalizationTimestamp, PriceFinalizationType _lastPriceEpochFinalizationType ); /** * @notice Returns current random number */ function getCurrentRandom() external view returns (uint256); }
./contracts/userInterfaces/IGovernanceVotePower.sol
// SPDX-License-Identifier: MIT pragma solidity >=0.7.6 <0.9; 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 of `msg.sender``. **/ 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 _votePower Governance vote power of `_who` at `_blockNumber`. */ function votePowerOfAt(address _who, uint256 _blockNumber) external view returns(uint256); /** * @notice Get the vote power of `account` at the current block. * @param account The address to get voting power. * @return Vote power of `account` at the current block number. */ function getVotes(address account) external view returns (uint256); /** * @notice Get the delegate's address of `_who` at block `_blockNumber` * @param _who The address to get delegate's address. * @param _blockNumber The block number at which to fetch. * @return Delegate's address of `_who` at `_blockNumber`. */ function getDelegateOfAt(address _who, uint256 _blockNumber) external view returns (address); /** * @notice Get the delegate's address of `_who` at the current block. * @param _who The address to get delegate's address. * @return Delegate's address of `_who` at the current block number. */ function getDelegateOfAtNow(address _who) external view returns (address); }
./contracts/userInterfaces/IPriceSubmitter.sol
// SPDX-License-Identifier: MIT pragma solidity >=0.7.6 <0.9; import "../genesis/interface/IFtsoGenesis.sol"; import "../genesis/interface/IFtsoRegistryGenesis.sol"; import "../genesis/interface/IFtsoManagerGenesis.sol"; interface IPriceSubmitter { /** * Event emitted when hash was submitted through PriceSubmitter. * @param submitter the address of the sender * @param epochId current price epoch id * @param hash the submitted hash * @param timestamp current block timestamp */ event HashSubmitted( address indexed submitter, uint256 indexed epochId, bytes32 hash, 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 random, uint256 timestamp ); /** * @notice Submits hash for current epoch * @param _epochId Target epoch id to which hash is submitted * @param _hash Hash of ftso indices, prices, random number and voter address * @notice Emits HashSubmitted event */ function submitHash( uint256 _epochId, bytes32 _hash ) 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 increasing ftso indices * @param _prices List of submitted prices in USD * @param _random Submitted random number * @notice The hash of ftso indices, prices, random number and voter address must be equal to the submitted hash * @notice Emits PricesRevealed event */ function revealPrices( uint256 _epochId, uint256[] memory _ftsoIndices, uint256[] memory _prices, uint256 _random ) 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 (IFtsoManagerGenesis); /** * @notice Returns current random number */ function getCurrentRandom() 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); }
./contracts/utils/implementation/SafePct.sol
// SPDX-License-Identifier: MIT pragma solidity 0.7.6; import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol"; /** * @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)); } }
@openzeppelin/contracts/math/SafeMath.sol
// SPDX-License-Identifier: MIT 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; } }
@openzeppelin/contracts/token/ERC20/IERC20.sol
// 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); }
@openzeppelin/contracts/utils/SafeCast.sol
// SPDX-License-Identifier: MIT 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); } }
Contract ABI
[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"string","name":"_symbol","internalType":"string"},{"type":"uint256","name":"_decimals","internalType":"uint256"},{"type":"address","name":"_priceSubmitter","internalType":"contract IPriceSubmitter"},{"type":"address","name":"_wNat","internalType":"contract IIVPToken"},{"type":"address","name":"_ftsoManager","internalType":"address"},{"type":"uint256","name":"_firstEpochStartTs","internalType":"uint256"},{"type":"uint256","name":"_submitPeriodSeconds","internalType":"uint256"},{"type":"uint256","name":"_revealPeriodSeconds","internalType":"uint256"},{"type":"uint128","name":"_initialPriceUSD","internalType":"uint128"},{"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":"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":"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":"_firstEpochStartTs","internalType":"uint256"},{"type":"uint256","name":"_submitPeriodSeconds","internalType":"uint256"},{"type":"uint256","name":"_revealPeriodSeconds","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":"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":[],"name":"fallbackFinalizePriceEpoch","inputs":[{"type":"uint256","name":"_epochId","internalType":"uint256"}]},{"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":"address"}],"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":"_price","internalType":"uint256"},{"type":"uint256","name":"_priceTimestamp","internalType":"uint256"},{"type":"uint8","name":"_priceFinalizationType","internalType":"enum IFtso.PriceFinalizationType"},{"type":"uint256","name":"_lastPriceEpochFinalizationTimestamp","internalType":"uint256"},{"type":"uint8","name":"_lastPriceEpochFinalizationType","internalType":"enum IFtso.PriceFinalizationType"}],"name":"getCurrentPriceDetails","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"_price","internalType":"uint256"},{"type":"uint256","name":"_timestamp","internalType":"uint256"}],"name":"getCurrentPriceFromTrustedProviders","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":"_firstEpochStartTs","internalType":"uint256"},{"type":"uint256","name":"_submitPeriodSeconds","internalType":"uint256"},{"type":"uint256","name":"_revealPeriodSeconds","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":"_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":"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"}]}]
Deployed ByteCode
0x608060405234801561001057600080fd5b50600436106102325760003560e01c80639edbf00711610130578063d89601fd116100b8578063f025bf661161007c578063f025bf66146108ba578063f670ebe3146108c2578063f72cab28146108e7578063f7dba1f514610913578063f937d6ad146109da57610232565b8063d89601fd146107ed578063e3749e0c146107f5578063e3b3a3b314610860578063e536f39614610895578063eb91d37e146108b257610232565b8063c5d8b9e7116100ff578063c5d8b9e714610759578063cc245ab514610785578063cd4b69141461078d578063cf35bdd0146107aa578063d0d552dd146107c757610232565b80639edbf007146106f0578063a29a839f146106f8578063af52df0814610700578063c1f6c36e1461072157610232565b80634afd5102116101be5780637d1d6f12116101825780637d1d6f121461055f578063826cc76b1461057c5780638357d08c1461059957806395d89b4114610656578063974d7a6b146106d357610232565b80634afd5102146104fb5780635303548b14610518578063555989da146105475780635c222bad1461054f5780636b52b2421461055757610232565b8063144e159111610205578063144e15911461036c57806318931c35146103925780632f0a6f3c146103ea578063306ba2531461041357806340462a2d1461043657610232565b806302fb0c5e14610237578063040d73b81461025357806311a7aaaa146102a3578063131fdee2146102c7575b600080fd5b61023f6109e2565b604080519115158252519081900360200190f35b61025b6109eb565b6040518086815260200185815260200184600581111561027757fe5b815260200183815260200182600581111561028e57fe5b81526020019550505050505060405180910390f35b6102ab610a2a565b604080516001600160a01b039092168252519081900360200190f35b61036a600480360360208110156102dd57600080fd5b8101906020810181356401000000008111156102f857600080fd5b82018360208201111561030a57600080fd5b8035906020019184602083028401116401000000008311171561032c57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550610a4e945050505050565b005b610374610b43565b60408051938452602084019290925282820152519081900360600190f35b61039a610bab565b60408051602080825283518183015283519192839290830191858101910280838360005b838110156103d65781810151838201526020016103be565b505050509050019250505060405180910390f35b61036a6004803603606081101561040057600080fd5b5080359060208101359060400135610c72565b61036a6004803603604081101561042957600080fd5b5080359060200135610f63565b61045b6004803603604081101561044c57600080fd5b5080359060200135151561106f565b604051808060200180602001848152602001838103835286818151815260200191508051906020019060200280838360005b838110156104a557818101518382015260200161048d565b50505050905001838103825285818151815260200191508051906020019060200280838360005b838110156104e45781810151838201526020016104cc565b505050509050019550505050505060405180910390f35b61036a6004803603602081101561051157600080fd5b5035611431565b6105356004803603602081101561052e57600080fd5b5035611498565b60408051918252519081900360200190f35b61036a6114ab565b6102ab611501565b610535611581565b6105356004803603602081101561057557600080fd5b50356115a5565b6102ab6004803603602081101561059257600080fd5b50356115ba565b6105a16115e4565b604051808060200180602001878152602001868152602001858152602001848152602001838103835289818151815260200191508051906020019060200280838360005b838110156105fd5781810151838201526020016105e5565b50505050905001838103825288818151815260200191508051906020019060200280838360005b8381101561063c578181015183820152602001610624565b505050509050019850505050505050505060405180910390f35b61065e61194d565b6040805160208082528351818301528351919283929083019185019080838360005b83811015610698578181015183820152602001610680565b50505050905090810190601f1680156106c55780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61036a600480360360208110156106e957600080fd5b50356119da565b6102ab611a3d565b610535611a61565b610708611a6c565b6040805192835260208301919091528051918290030190f35b61036a6004803603608081101561073757600080fd5b506001600160a01b038135169060208101359060408101359060600135611a87565b6105356004803603604081101561076f57600080fd5b50803590602001356001600160a01b0316611ae3565b610535611b37565b610535600480360360208110156107a357600080fd5b5035611b5b565b6102ab600480360360208110156107c057600080fd5b5035611b66565b61036a600480360360208110156107dd57600080fd5b50356001600160a01b0316611b76565b610535611c7b565b6107fd611ca9565b6040518088815260200187815260200186815260200185815260200184815260200183815260200180602001828103825283818151815260200191508051906020019060200280838360008381101561063c578181015183820152602001610624565b610868611d4f565b60408051958652602086019490945284840192909252606084015215156080830152519081900360a00190f35b61036a600480360360208110156108ab57600080fd5b5035611df6565b610708611e6c565b610535611e87565b61036a600480360360408110156108d857600080fd5b50803590602001351515611eab565b610535600480360360408110156108fd57600080fd5b506001600160a01b0381351690602001356120ab565b61036a600480360360e081101561092957600080fd5b81359160208101359160408201359160608101359160808201359160a08101359181019060e0810160c082013564010000000081111561096857600080fd5b82018360208201111561097a57600080fd5b8035906020019184602083028401116401000000008311171561099c57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295506120f4945050505050565b6102ab61222a565b60005460ff1681565b6002546004546001600160801b0380831693600160801b909304169160ff8083169261010081046001600160f01b031692600160f81b90910490911690565b7f000000000000000000000000ab7c7da1ff2b25d0867908ddfa52827570e0289481565b336001600160a01b037f000000000000000000000000ab7c7da1ff2b25d0867908ddfa52827570e028941614610a8657610a8661224e565b6000815111610a9157fe5b600181511180610ac75750306001600160a01b031681600081518110610ab357fe5b60200260200101516001600160a01b031614155b610acd57fe5b8051610ae0906011906020840190614fa1565b5080516001600160401b0381118015610af857600080fd5b50604051908082528060200260200182016040528015610b22578160200160208202803683370190505b508051610b3791601091602090910190614fa1565b50610b406122b6565b50565b7f0000000000000000000000000000000000000000000000000000000062cf1e467f00000000000000000000000000000000000000000000000000000000000000b47f000000000000000000000000000000000000000000000000000000000000005a909192565b6010546060906001148015610bc257506011546001145b8015610bf85750306001600160a01b03166011600081548110610be157fe5b6000918252602090912001546001600160a01b0316145b610c5c576011805480602002602001604051908101604052809291908181526020018280548015610c5257602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610c34575b5050505050610c6c565b6040805160008152602081019091525b90505b90565b336001600160a01b037f000000000000000000000000ab7c7da1ff2b25d0867908ddfa52827570e028941614610caa57610caa61224e565b600054604080518082019091526016815275119514d3c8185b1c9958591e481858dd1a5d985d195960521b60208201529060ff1615610d675760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015610d2c578181015183820152602001610d14565b50505050905090810190601f168015610d595780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060408051808201909152601e81527f496e76616c69642070726963652065706f636820706172616d6574657273000060208201527f0000000000000000000000000000000000000000000000000000000062cf1e468414610e0a5760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610d2c578181015183820152602001610d14565b5060408051808201909152601e81527f496e76616c69642070726963652065706f636820706172616d6574657273000060208201527f00000000000000000000000000000000000000000000000000000000000000b48314610ead5760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610d2c578181015183820152602001610d14565b5060408051808201909152601e81527f496e76616c69642070726963652065706f636820706172616d6574657273000060208201527f000000000000000000000000000000000000000000000000000000000000005a8214610f505760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610d2c578181015183820152602001610d14565b50506000805460ff191660011790555050565b336001600160a01b037f000000000000000000000000ab7c7da1ff2b25d0867908ddfa52827570e028941614610f9b57610f9b61224e565b600054604080518082019091526016815275119514d3c8185b1c9958591e481858dd1a5d985d195960521b60208201529060ff161561101b5760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610d2c578181015183820152602001610d14565b5061102582612492565b600280546001600160801b0319166001600160801b039290921691909117905561104e81612492565b600280546001600160801b03928316600160801b0292169190911790555050565b6060806000336001600160a01b037f000000000000000000000000ab7c7da1ff2b25d0867908ddfa52827570e0289416146110ac576110ac61224e565b60006110b7866124da565b60018101805460ff60f01b191690556005810154909150600090156110ef578160050154612710836008015402816110eb57fe5b0490505b6001820154600160f81b900460ff168061110d575081600401548111155b15611180576001820154600160f81b900460ff1661116d57600482015460408051838152602081019290925242828201525188917fbece8aa526cdc5e528cdaa56c1d03edc19da51e41845aa146f64a7071f74c65a919081900360600190a25b61117987836000612614565b505061142a565b606080606061118e85612835565b91945092509050606061119f615002565b6111a985856129ca565b61012081015191935091507f00000000000000000000000000000000000000000000000000000000000005dc90611205906005908f907f00000000000000000000000000000000000000000000000000000000000000c8612ce8565b1115611223576112178c886000612614565b5050505050505061142a565b60108701805460ff199081166001908117909255610120830151600f8a01819055600280546001600160801b0319166001600160801b03928316178216600160801b429384160217905560048054600160f81b6001600160f81b036001600160f01b0390941661010002610100600160f81b031992909516909517169290921716919091179055600e870154156113405761130f87600e0180548060200260200160405190810160405280929190818152602001828054801561130557602002820191906000526020600020905b8154815260200190600101908083116112f1575b5050505050612d74565b60038054426001600160801b03908116600160801b029381166001600160801b031990921691909117169190911790555b60008b156113785760006113538e612e98565b90506113628984838789612f58565b8251929e50909c509a501561137657600191505b505b6113848d838584611add565b8c7ffe8865c1fe85bbf124b9e0f16cccfeeb6f330454fd79475a31261c8fa250bc3089600f0154838561014001518661016001518d60100160009054906101000a900460ff16426040518087815260200186151581526020018581526020018481526020018360058111156113f557fe5b8152602001828152602001965050505050505060405180910390a2505050600190940180546001600160f81b03169055505050505b9250925092565b336001600160a01b037f000000000000000000000000ab7c7da1ff2b25d0867908ddfa52827570e0289416146114695761146961224e565b6000611474826124da565b6001808201805460ff60f01b191690559091506114949083908390612614565b5050565b60006114a38261320e565b90505b919050565b60005460ff166114bd576114bd613293565b336001600160a01b037f000000000000000000000000ab7c7da1ff2b25d0867908ddfa52827570e0289416146114f5576114f561224e565b6000805460ff19169055565b601054600090600114801561151857506011546001145b801561154e5750306001600160a01b0316601160008154811061153757fe5b6000918252602090912001546001600160a01b0316145b611559576000610c6c565b601060008154811061156757fe5b6000918252602090912001546001600160a01b0316905090565b7f000000000000000000000000000000000000000000000000000000000000000581565b60006115b0826132fd565b600f015492915050565b601181815481106115ca57600080fd5b6000918252602090912001546001600160a01b0316905081565b606080600080600080601080548060200260200160405190810160405280929190818152602001828054801561164357602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611625575b50506007548451949a506001600160f01b031694506000939250506001600160401b0382119050801561167557600080fd5b5060405190808252806020026020018201604052801561169f578160200160208202803683370190505b509050600087516001600160401b03811180156116bb57600080fd5b506040519080825280602002602001820160405280156116e5578160200160208202803683370190505b50905060005b88518110156118745760006001600160a01b031689828151811061170b57fe5b60200260200101516001600160a01b031614156117295760006117ad565b88818151811061173557fe5b60200260200101516001600160a01b0316633e5aa26a856040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561178057600080fd5b505afa158015611794573d6000803e3d6000fd5b505050506040513d60208110156117aa57600080fd5b50515b8382815181106117b957fe5b602002602001018181525050601181815481106117d257fe5b9060005260206000200160009054906101000a90046001600160a01b03166001600160a01b031663eb91d37e6040518163ffffffff1660e01b8152600401604080518083038186803b15801561182757600080fd5b505afa15801561183b573d6000803e3d6000fd5b505050506040513d604081101561185157600080fd5b5051825183908390811061186157fe5b60209081029190910101526001016116eb565b50600061188460058a85856133c5565b905061189260058a8361356b565b97507f0000000000000000000000001d80c49bbbcd1c0911346656b529df9e5c2f783d6001600160a01b0316633e5aa26a856040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b1580156118f857600080fd5b505afa15801561190c573d6000803e3d6000fd5b505050506040513d602081101561192257600080fd5b5051965061193360058a858461369f565b9550611940600587613767565b9450505050909192939495565b60018054604080516020600284861615610100026000190190941693909304601f810184900484028201840190925281815292918301828280156119d25780601f106119a7576101008083540402835291602001916119d2565b820191906000526020600020905b8154815290600101906020018083116119b557829003601f168201915b505050505081565b336001600160a01b037f000000000000000000000000ab7c7da1ff2b25d0867908ddfa52827570e028941614611a1257611a1261224e565b6000611a1d826124da565b6001808201805460ff60f01b1916905590915061149490839083906137bc565b7f0000000000000000000000001d80c49bbbcd1c0911346656b529df9e5c2f783d81565b6000610c6c4261320e565b6003546001600160801b0380821691600160801b9004169091565b60005460ff16611a9957611a99613293565b336001600160a01b037f00000000000000000000000010000000000000000000000000000000000000031614611ad157611ad161224e565b611add848484846138f7565b50505050565b600080611aef846132fd565b90506000611afd8285613b94565b905080611b0f57600092505050611b31565b600019016000908152600d90910160205260409020546001600160801b031690505b92915050565b7f00000000000000000000000000000000000000000000000000000000000005dc81565b60006114a382612e98565b601081815481106115ca57600080fd5b336001600160a01b037f000000000000000000000000ab7c7da1ff2b25d0867908ddfa52827570e028941614611bae57611bae61224e565b6040805160208101909152308152611bca906011906001614fa1565b5060408051602081019091526001600160a01b0382168152611bf0906010906001614fa1565b50806001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b158015611c2a57600080fd5b505afa158015611c3e573d6000803e3d6000fd5b505050506040513d6020811015611c5457600080fd5b50516001600160a01b03909116600090815260066020526040902060ff909116600a0a9055565b600080611c86611a61565b905080611c97576000915050610c6f565b611ca360018203612e98565b91505090565b60008060008060008060606005600301546005600401546005800154600560060154600560070154600560080154600560090180805480602002602001604051908101604052809291908181526020018280548015611d3157602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611d13575b50505050509050965096509650965096509650965090919293949596565b6000806000806000611d5f611a61565b9450611d6a85613bec565b93507f000000000000000000000000000000000000000000000000000000000000005a8401925060006005817f00000000000000000000000000000000000000000000000000000000000000c88881611dbf57fe5b0681526020810191909152604001600020600101549596949593946001600160f01b03851694600160f81b900460ff169350915050565b336001600160a01b037f000000000000000000000000ab7c7da1ff2b25d0867908ddfa52827570e028941614611e2e57611e2e61224e565b438110611e3a57600080fd5b600160f01b8110611e4a57600080fd5b600780546001600160f01b0319166001600160f01b0392909216919091179055565b6002546001600160801b0380821691600160801b9004169091565b7f00000000000000000000000000000000000000000000000000000000000000c881565b336001600160a01b037f000000000000000000000000ab7c7da1ff2b25d0867908ddfa52827570e028941614611ee357611ee361224e565b6000611eed611a61565b905060006005817f00000000000000000000000000000000000000000000000000000000000000c88481611f1d57fe5b0681526020810191909152604001600090812060108101805460ff1916905560088101829055600c81018290556007546001820180546001600160f01b039092166001600160f01b0319909216919091176001600160f81b0316600160f81b87151502179055600281018490559150611f9a90600e830190615063565b8215611fa7575050611494565b606080611fb2613c39565b6007549194509250612062915084908890611ff7907f0000000000000000000000001d80c49bbbcd1c0911346656b529df9e5c2f783d906001600160f01b0316613e2f565b601080548060200260200160405190810160405280929190818152602001828054801561204d57602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161202f575b5060059796959493508a925089915050613ec0565b837fc0eaa359541c7c642d9947c9496507c134f3e4f8e1fd433313eb27dc48cb1fb761208d86613bec565b604080519182524260208301528051918290030190a2505050505050565b60006120ed7f0000000000000000000000001d80c49bbbcd1c0911346656b529df9e5c2f783d846120db856132fd565b600101546001600160f01b0316613f6f565b9392505050565b336001600160a01b037f000000000000000000000000ab7c7da1ff2b25d0867908ddfa52827570e02894161461212c5761212c61224e565b60088790556009869055600a859055600b849055600c839055600d829055600e5460005b818110156121ad5760006005600a0160006005600901848154811061217157fe5b6000918252602080832091909101546001600160a01b031683528201929092526040019020805460ff1916911515919091179055600101612150565b5050805160005b8181101561220b5760016005600a0160008584815181106121d157fe5b6020908102919091018101516001600160a01b03168252810191909152604001600020805460ff19169115159190911790556001016121b4565b50815161221f90600e906020850190614fa1565b505050505050505050565b7f000000000000000000000000100000000000000000000000000000000000000381565b604080518082018252600d81526c1058d8d95cdcc819195b9a5959609a1b6020808301918252925162461bcd60e51b81526004810193845282516024820152825192939283926044909201919080838360008315610d2c578181015183820152602001610d14565b60115460011480156122f25750306001600160a01b031660116000815481106122db57fe5b6000918252602090912001546001600160a01b0316145b156122fc57612490565b60005b601154811015610b405760006011828154811061231857fe5b6000918252602091829020015460408051635c222bad60e01b815290516001600160a01b0390921692635c222bad92600480840193829003018186803b15801561236157600080fd5b505afa158015612375573d6000803e3d6000fd5b505050506040513d602081101561238b57600080fd5b505160108054919250908390811061239f57fe5b6000918252602090912001546001600160a01b03828116911614156123c45750612488565b80601083815481106123d257fe5b600091825260209091200180546001600160a01b0319166001600160a01b0392831617905581161561248657806001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b15801561243757600080fd5b505afa15801561244b573d6000803e3d6000fd5b505050506040513d602081101561246157600080fd5b50516001600160a01b038216600090815260066020526040902060ff909116600a0a90555b505b6001016122ff565b565b6000600160801b82106124d65760405162461bcd60e51b81526004018080602001828103825260278152602001806151656027913960400191505060405180910390fd5b5090565b60006124e582614012565b4210156040518060400160405280602081526020017f45706f6368206e6f7420726561647920666f722066696e616c697a6174696f6e8152509061256a5760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610d2c578181015183820152602001610d14565b50612574826132fd565b90506000601082015460ff16600581111561258b57fe5b146040518060400160405280601781526020017f45706f636820616c72656164792066696e616c697a65640000000000000000008152509061260e5760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610d2c578181015183820152602001610d14565b50919050565b600e820154156128255761267782600e0180548060200260200160405190810160405280929190818152602001828054801561130557602002820191906000526020600020908154815260200190600101908083116112f1575050505050612d74565b600f8301558061268857600261268b565b60045b60108301805460ff191660018360058111156126a357fe5b0217905550600f82015460028054426001600160801b03908116600160801b029381166001600160801b0319909216919091171691909117905560108201546004805460ff9092169160ff191660018360058111156126fe57fe5b021790555060048054610100600160f81b031916610100426001600160f01b03160217808255601084015460ff1691906001600160f81b0316600160f81b83600581111561274857fe5b0217905550600f82015460038054426001600160801b03908116600160801b029381166001600160801b0319909216919091171691909117905561278b83610b40565b827ffe8865c1fe85bbf124b9e0f16cccfeeb6f330454fd79475a31261c8fa250bc3083600f015460008060008760100160009054906101000a900460ff16426040518087815260200186151581526020018581526020018481526020018360058111156127f457fe5b8152602001828152602001965050505050505060405180910390a26001820180546001600160f81b03169055612830565b6128308383836137bc565b505050565b6060806060600084600c01549050806001600160401b038111801561285957600080fd5b50604051908082528060200260200182016040528015612883578160200160208202803683370190505b509350806001600160401b038111801561289c57600080fd5b506040519080825280602002602001820160405280156128c6578160200160208202803683370190505b5091506000816001600160401b03811180156128e157600080fd5b5060405190808252806020026020018201604052801561290b578160200160208202803683370190505b50905060005b828110156129b3576000818152600d880160205260409020805487516001600160801b039091169088908490811061294557fe5b602090810291909101015280548551600160801b9091046001600160401b03169086908490811061297257fe5b602090810291909101015280548351600160c01b9091046001600160401b03169084908490811061299f57fe5b602090810291909101015250600101612911565b506129bf868483614045565b935050509193909250565b60606129d4615002565b8351806001600160401b03811180156129ec57600080fd5b50604051908082528060200260200182016040528015612a16578160200160208202803683370190505b50925060005b81811015612a445780848281518110612a3157fe5b6020908102919091010152600101612a1c565b50612a5b6002600060018403600080888b8b61415c565b608085015260608401528083528351859185918110612a7657fe5b602002602001015181518110612a8857fe5b602090810291909101015160a0830181905260808301516060840151845192010190612ac1578251600060c08501526020840152612b15565b60048104836060015111612ae5578251606084015160c08501526020840152612b15565b612b0960016000600186600001510360008760a00151886080015101898c8c61415c565b5060c085015260208401525b825160001983011415612b3657825160006101008501526040840152612b8e565b60048104836080015111612b5b57825160808401516101008501526040840152612b8e565b612b8160038460000151600101600185038660a001518760600151016000898c8c61415c565b6101008601525060408401525b8584846000015181518110612b9f57fe5b602002602001015181518110612bb157fe5b6020908102919091010151610120840152600281048360a00151846060015101148015612bdf575060028106155b15612c0f576002612bf9846000015160018503878a614437565b8461012001510181612c0757fe5b046101208401525b612c29836020015160006000198660c00151888b8b6144f7565b60c085015260208401526040830151610100840151612c549190600019850190600190888b8b6144f7565b610100850152604084015260208301518451879186918110612c7257fe5b602002602001015181518110612c8457fe5b6020026020010151836101400181815250508584846040015181518110612ca757fe5b602002602001015181518110612cb957fe5b602090810291909101015161016084015261010083015160c08401519091030360e08301525090939092509050565b600083612cf757506000612d6c565b6000858184600019880181612d0857fe5b068152602001908152602001600020600f0154905080841415612d2f576000915050612d6c565b83612d425764e8d4a51000915050612d6c565b600084821115612d555750838103612d5a565b508084035b612d6781612710876145cb565b925050505b949350505050565b805160009080612d8057fe5b60015b81811015612e2c576000848281518110612d9957fe5b6020026020010151905060008290505b600081118015612dce575081866001830381518110612dc457fe5b6020026020010151115b15612e0a57856001820381518110612de257fe5b6020026020010151868281518110612df657fe5b602090810291909101015260001901612da9565b81868281518110612e1757fe5b60209081029190910101525050600101612d83565b506002810460018083161415612e5957838181518110612e4857fe5b6020026020010151925050506114a6565b6002848281518110612e6757fe5b6020026020010151856001840381518110612e7e57fe5b60200260200101510181612e8e57fe5b04925050506114a6565b60007f00000000000000000000000010000000000000000000000000000000000000036001600160a01b031663cd4b6914836040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015612efe57600080fd5b505afa158015612f12573d6000803e3d6000fd5b505050506040513d6020811015612f2857600080fd5b50516040805160208082019390935230818301528151808203830181526060909101909152805191012092915050565b6020840151606090819060009081905b88604001518111613033576000878281518110612f8157fe5b602002602001015190506000878281518110612f9957fe5b60200260200101511115613029576000818152600d8c0160205260409020546101408b01516001600160801b0390911690811480612fe457508a6101600151816001600160801b0316145b801561301557506000828152600d8d016020526040902060010154613013908b906001600160a01b03166146d7565b155b1561302157505061302b565b506001909201915b505b600101612f68565b50806001600160401b038111801561304a57600080fd5b50604051908082528060200260200182016040528015613074578160200160208202803683370190505b509350806001600160401b038111801561308d57600080fd5b506040519080825280602002602001820160405280156130b7578160200160208202803683370190505b5060208901519093506000905b896040015181116132005760008882815181106130dd57fe5b6020026020010151905060008882815181106130f557fe5b6020026020010151905060008111156131f5576000828152600d8e0160205260409020546101408d01516001600160801b039091169081148061314557508c6101600151816001600160801b0316145b801561317657506000838152600d8f016020526040902060010154613174908d906001600160a01b03166146d7565b155b15613183575050506131f8565b6000838152600d8f01602052604090206001015489516001600160a01b03909116908a90879081106131b157fe5b60200260200101906001600160a01b031690816001600160a01b031681525050818886815181106131de57fe5b602090810291909101015250948501946001909301925b50505b6001016130c4565b505050955095509592505050565b60007f0000000000000000000000000000000000000000000000000000000062cf1e46821015613240575060006114a6565b7f00000000000000000000000000000000000000000000000000000000000000b47f0000000000000000000000000000000000000000000000000000000062cf1e4683038161328b57fe5b0490506114a6565b604080518082018252600f81526e4654534f206e6f742061637469766560881b6020808301918252925162461bcd60e51b81526004810193845282516024820152825192939283926044909201919080838360008315610d2c578181015183820152602001610d14565b60006005817f00000000000000000000000000000000000000000000000000000000000000c8848161332b57fe5b0681526020019081526020016000209050806002015482146040518060400160405280601881526020017f45706f63682064617461206e6f7420617661696c61626c6500000000000000008152509061260e5760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610d2c578181015183820152602001610d14565b82516060906000816001600160401b03811180156133e257600080fd5b5060405190808252806020026020018201604052801561340c578160200160208202803683370190505b5090506000805b838110156134f55760006001600160a01b031688828151811061343257fe5b60200260200101516001600160a01b0316141561344e576134ed565b60006134c587838151811061345f57fe5b60200260200101518b60010160008c868151811061347957fe5b60200260200101516001600160a01b03166001600160a01b03168152602001908152602001600020548a85815181106134ae57fe5b60200260200101516145cb9092919063ffffffff16565b9050808483815181106134d457fe5b60209081029190910101526134e98382614712565b9250505b600101613413565b5080156135605760005b8381101561355e5761353f61353261271088848151811061351c57fe5b602002602001015161476c90919063ffffffff16565b838584815181106134ae57fe5b83828151811061354b57fe5b60209081029190910101526001016134ff565b505b509695505050505050565b8151606090806001600160401b038111801561358657600080fd5b506040519080825280602002602001820160405280156135b0578160200160208202803683370190505b50915060005b818110156136965760006001600160a01b03168582815181106135d557fe5b60200260200101516001600160a01b0316146136735760008660010160008784815181106135ff57fe5b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002054905061365561271064e8d4a510008161363d57fe5b04670de0b6b3a764000002828785815181106134ae57fe5b84838151811061366157fe5b6020026020010181815250505061368e565b600083828151811061368157fe5b6020026020010181815250505b6001016135b6565b50509392505050565b600080805b855181101561375d5760006001600160a01b03168682815181106136c457fe5b60200260200101516001600160a01b03161461375557600061271061373d8784815181106136ee57fe5b60200260200101518a60010160008b878151811061370857fe5b60200260200101516001600160a01b03166001600160a01b03168152602001908152602001600020548886815181106134ae57fe5b8161374457fe5b0490506137518382614712565b9250505b6001016136a4565b5095945050505050565b600080836005015483101561377e575060006120ed565b8360060154831061379257506113886120ed565b600584015460068501546101f4919081900390850361119402816137b257fe5b0401949350505050565b82156137d8576002546001600160801b0316600f8301556137e0565b6000600f8301555b806137ec5760036137ef565b60055b60108301805460ff1916600183600581111561380757fe5b021790555060048054610100600160f81b031916610100426001600160f01b03160217808255601084015460ff1691906001600160f81b0316600160f81b83600581111561385157fe5b021790555061385f83610b40565b827ffe8865c1fe85bbf124b9e0f16cccfeeb6f330454fd79475a31261c8fa250bc3083600f015460008060008760100160009054906101000a900460ff16426040518087815260200186151581526020018581526020018481526020018360058111156138c857fe5b8152602001828152602001965050505050505060405180910390a25060010180546001600160f81b0316905550565b60408051808201909152600e81526d0a0e4d2c6ca40e8dede40d0d2ced60931b6020820152600160801b831061396e5760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610d2c578181015183820152602001610d14565b50613978836147c5565b6040518060400160405280601881526020017f52657665616c20706572696f64206e6f74206163746976650000000000000000815250906139fa5760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610d2c578181015183820152602001610d14565b5060006005817f00000000000000000000000000000000000000000000000000000000000000c88681613a2957fe5b0681526020810191909152604001600020600181015490915060ff600160f81b8204811691600160f01b8104909116906001600160f01b03168180613a8e5750828015613a8e57506001600160a01b0388166000908152600f602052604090205460ff165b6040518060400160405280602081526020017f45706f6368206e6f7420696e697469616c697a656420666f722072657665616c81525090613b105760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610d2c578181015183820152602001610d14565b50600080613b21868b898887614808565b9092509050613b356005878c85858d614868565b604080518981524260208201528082018490526060810183905290518a916001600160a01b038d16917fc1b1d37612887c207efe8cb44f4069b7f10c45edaf6e8405648a94e02b6e9ec79181900360800190a350505050505050505050565b600c820154600090815b81811015613be1576000818152600d860160205260409020600101546001600160a01b0385811691161415613bd9576001019150611b319050565b600101613b9e565b506000949350505050565b7f0000000000000000000000000000000000000000000000000000000062cf1e46600182017f00000000000000000000000000000000000000000000000000000000000000b40201919050565b6060806060613c466122b6565b6010805480602002602001604051908101604052809291908181526020018280548015613c9c57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311613c7e575b5050505050925082516001600160401b0381118015613cba57600080fd5b50604051908082528060200260200182016040528015613ce4578160200160208202803683370190505b50915082516001600160401b0381118015613cfe57600080fd5b50604051908082528060200260200182016040528015613d28578160200160208202803683370190505b50905060005b8351811015613e2957613d62848281518110613d4657fe5b60209081029190910101516007546001600160f01b0316613e2f565b838281518110613d6e57fe5b60200260200101818152505060118181548110613d8757fe5b9060005260206000200160009054906101000a90046001600160a01b03166001600160a01b031663eb91d37e6040518163ffffffff1660e01b8152600401604080518083038186803b158015613ddc57600080fd5b505afa158015613df0573d6000803e3d6000fd5b505050506040513d6040811015613e0657600080fd5b50518251839083908110613e1657fe5b6020908102919091010152600101613d2e565b50909192565b60006001600160a01b038316613e4757506000611b31565b826001600160a01b031663caeb942b836040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b158015613e8d57600080fd5b505af1158015613ea1573d6000803e3d6000fd5b505050506040513d6020811015613eb757600080fd5b50519050611b31565b613ecd87878585856149b8565b6007870154600380880191909155600888015460048801556005870186905560068701859055870154613f08908581613f0257fe5b04612492565b86546001600160801b0319166001600160801b039190911617865560048701546007870154613f39919081613f0257fe5b86546001600160801b03918216600160801b0291161786555050506001909201805460ff60f01b1916600160f01b179055505050565b60006001600160a01b038416613f87575060006120ed565b836001600160a01b031663e587497e84846040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050602060405180830381600087803b158015613fde57600080fd5b505af1158015613ff2573d6000803e3d6000fd5b505050506040513d602081101561400857600080fd5b5051949350505050565b60007f000000000000000000000000000000000000000000000000000000000000005a61403e83613bec565b0192915050565b600c830154606090806001600160401b038111801561406357600080fd5b5060405190808252806020026020018201604052801561408d578160200160208202803683370190505b509150600061409b85614a21565b905060006140a885614a21565b90506000806140b8898585614a66565b905083156140c857806127100391505b60005b8581101561414f57600083156141115761410e8a83815181106140ea57fe5b602002602001015164e8d4a51000026127108802866145cb9092919063ffffffff16565b90505b6000831561412b576141288a84815181106140ea57fe5b90505b80820189848151811061413a57fe5b602090810291909101015250506001016140cb565b5050505050509392505050565b6000806000888a1415614176575088915086905085614429565b61417e615081565b888152602081018890526141906150be565b60208082018d905260408083018d90528051448184015242818301528151808203830181526060909101909152805191012060005b602083015160408401516141f791908160018183030186816141e357fe5b0601876000015188602001518f8f8f614acd565b606087015260408601528084528a518b9190811061421157fe5b60200260200101518360600181815250508783606001518151811061423257fe5b6020026020010151846080018181525050836060015184604001518560800151010190508e6002141561430d5760028106600282040160a0850181905280820360c086015260408501511080159061428d57508b8460a00151115b156142b257825160001901604084015260608401516080850151016020850152614308565b8360c0015184606001511180156142cc57508a8460c00151115b156142ed578251600101602084015260408401516080850151018452614308565b50505160408201516060909201519094509092509050614429565b614424565b8e6001141561439e576004810460a0850181905280820360c0860152604085015111801561433e57508b8460a00151115b1561436357825160001901604084015260608401516080850151016020850152614308565b8360c001518460600151101580156142cc57508a8460c0015111156142ed578251600101602084015260408401516080850151018452614308565b6004810460c08501819052810360a085018190526040850151108015906143c857508b8460a00151115b156143ed57825160001901604084015260608401516080850151016020850152614424565b8360c00151846060015111801561440757508a8460c00151115b156142ed5782516001016020840152604084015160808501510184525b6141c5565b985098509895505050505050565b60008385141561446f578183868151811061444e57fe5b60200260200101518151811061446057fe5b60200260200101519050612d6c565b60008284876001018151811061448157fe5b60200260200101518151811061449357fe5b602002602001015190506000808760020190505b8681116144eb57848682815181106144bb57fe5b6020026020010151815181106144cd57fe5b60200260200101519150828210156144e3578192505b6001016144a7565b50909695505050505050565b60008085888a038802821361451257898792509250506145bf565b600085878c8151811061452157fe5b60200260200101518151811061453357fe5b602090810291909101015190508a89016000815b60008c8e830302136145b35789818151811061455f57fe5b602002602001015191508389838151811061457657fe5b602002602001015114156145ac5787828151811061459057fe5b6020026020010151850394506145a783828c614c96565b918b01915b8b01614547565b50508990039350909150505b97509795505050505050565b6000808211614614576040805162461bcd60e51b815260206004820152601060248201526f4469766973696f6e206279207a65726f60801b604482015290519081900360640190fd5b83614621575060006120ed565b8383028385828161462e57fe5b0414156146475782818161463e57fe5b049150506120ed565b600083868161465257fe5b049050600084878161466057fe5b069050600085878161466e57fe5b049050600086888161467c57fe5b0690506146ca61469688614690868561476c565b90614cfe565b6146c46146a3868661476c565b6146c46146b0898761476c565b6146c48d6146be8c8b61476c565b9061476c565b90614712565b9998505050505050505050565b604080516020808201949094526001600160a01b03929092168282015280518083038201815260609092019052805191012060019081161490565b6000828201838110156120ed576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b60008261477b57506000611b31565b8282028284828161478857fe5b04146120ed5760405162461bcd60e51b815260040180806020018281038252602181526020018061518c6021913960400191505060405180910390fd5b6000806147d183613bec565b90504281111580156120ed57507f000000000000000000000000000000000000000000000000000000000000005a01421092915050565b600080831561481c5750600090508061485e565b84915061482a878785614d65565b87549091506001600160801b0380821691600160801b9004168184111561484f578193505b8083111561485b578092505b50505b9550959350505050565b600085600c0154905060006148898686868a600601548b6007015488614e23565b63ffffffff838116608083019081526000858152600d8b0160209081526040918290208551815492870151938701516001600160801b03199093166001600160801b039091161767ffffffffffffffff60801b1916600160801b6001600160401b0394851602176001600160c01b0316600160c01b939092169290920217815560608401516001918201805493516001600160a01b03199094166001600160a01b039092169190911763ffffffff60a01b1916600160a01b93851693909302929092179091558401600c8a0155600889015491925061496b9190879061471216565b60088801556001600160a01b0386166000908152600a8901602052604090205460ff16156149ae57600e8701805460018101825560009182526020909120018390555b5050505050505050565b82516149cd90600a8601906020860190614fa1565b506149da858484846133c5565b80516149f091600b8701916020909101906150e6565b5060006149fe868685614e80565b600786018190559050614a118682613767565b8560090181905550505050505050565b600080805b8351811015614a5f57614a55848281518110614a3e57fe5b60200260200101518361471290919063ffffffff16565b9150600101614a26565b5092915050565b600081614a75575060006120ed565b82614a8357506127106120ed565b6000614a978361271064e8d4a510006145cb565b905084600301548110614ab057505060098301546120ed565b60038501546009860154614ac59183906145cb565b9150506120ed565b60008060008085878b81518110614ae057fe5b602002602001015181518110614af257fe5b60200260200101519050600060026001600160401b0381118015614b1557600080fd5b50604051908082528060200260200182016040528015614b3f578160200160208202803683370190505b5090508981600081518110614b5057fe5b6020026020010181815250508881600181518110614b6a57fe5b60209081029190910101528c8c614b828d828c614c96565b81805b82811015614c465760008c8281518110614b9b57fe5b60200260200101519050868c8281518110614bb257fe5b60200260200101511015614c09578a8181518110614bcc57fe5b602002602001015186600081518110614be157fe5b602002602001018181510191508181525050614bfe83838f614c96565b600190920191614c3d565b8a8181518110614c1557fe5b602002602001015186600181518110614c2a57fe5b6020026020010181815101915081815250505b50600101614b85565b50614c5282828d614c96565b8084600081518110614c6057fe5b602002602001015185600181518110614c7557fe5b60200260200101519750975097505050505050985098509895505050505050565b81831415614ca357612830565b808281518110614caf57fe5b6020026020010151818481518110614cc357fe5b6020026020010151828581518110614cd757fe5b60200260200101838581518110614cea57fe5b602090810291909101019190915252505050565b6000808211614d54576040805162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b818381614d5d57fe5b049392505050565b600a83015460009081906001600160401b0381118015614d8457600080fd5b50604051908082528060200260200182016040528015614dae578160200160208202803683370190505b50905060005b600a860154811015614e0d57614dee86600a018281548110614dd257fe5b6000918252602090912001546001600160a01b03168686613f6f565b828281518110614dfa57fe5b6020908102919091010152600101614db4565b50614e1a60058683614e80565b95945050505050565b614e2b615121565b6001600160a01b0387166060820152614e448685614f70565b6001600160401b03166020820152614e5c8584614f70565b6001600160401b031660408201526001600160801b03909116815295945050505050565b600080805b600a850154811015614f675760006001600160a01b031685600a018281548110614eab57fe5b6000918252602090912001546001600160a01b03161415614ecb57614f5f565b614f5c612710614f4c868481518110614ee057fe5b60200260200101518960010160008a600a018781548110614efd57fe5b60009182526020808320909101546001600160a01b03168352820192909252604001902054600b8a01805487908110614f3257fe5b90600052602060002001546145cb9092919063ffffffff16565b81614f5357fe5b84919004614712565b91505b600101614e85565b50949350505050565b6000811580614f7d575082155b15614f8a57506000611b31565b614f9a8364e8d4a51000846145cb565b9050611b31565b828054828255906000526020600020908101928215614ff6579160200282015b82811115614ff657825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190614fc1565b506124d692915061514f565b6040518061018001604052806000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b5080546000825590600052602060002090810190610b40919061514f565b6040518060e00160405280600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6040518060800160405280600081526020016000815260200160008152602001600081525090565b828054828255906000526020600020908101928215614ff6579160200282015b82811115614ff6578251825591602001919060010190615106565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915290565b5b808211156124d6576000815560010161515056fe53616665436173743a2076616c756520646f65736e27742066697420696e203132382062697473536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77a2646970667358221220a0feb28364f05773bf3bdc810cf6412e41f6fe7ef2c4a4adf535c3c83494e52564736f6c63430007060033