// SPDX-License-Identifier: MIT
pragma solidity 0.7.6;
import "../interface/IIVoterWhitelister.sol";
import "../../addressUpdater/implementation/AddressUpdatable.sol";
import "../../genesis/interface/IIPriceSubmitter.sol";
import "../../governance/implementation/Governed.sol";
import "../../token/interface/IIVPToken.sol";
import "../../userInterfaces/IFtsoRegistry.sol";
import "../../utils/implementation/SafePct.sol";
import "@openzeppelin/contracts/math/SafeMath.sol";
contract VoterWhitelister is IIVoterWhitelister, Governed, AddressUpdatable {
using SafeMath for uint256;
using SafePct for uint256;
uint256 internal constant TERA = 10 ** 12; // 10^12
uint256 internal constant BIPS100 = 10 ** 4; // 100 * 100%
uint256 public override defaultMaxVotersForFtso;
/**
* Maximum number of voters in a single ftso whitelist.
* Adjustable separately for each ftsoIndex.
*/
mapping (uint256 => uint256) public override maxVotersForFtso;
// mapping: ftsoIndex => array of whitelisted voters for this ftso
mapping (uint256 => address[]) internal whitelist;
IIPriceSubmitter public immutable priceSubmitter;
IFtsoRegistry public ftsoRegistry;
address public ftsoManager;
modifier onlyFtsoManager {
require(msg.sender == ftsoManager, "only ftso manager");
_;
}
constructor(
address _governance,
address _addressUpdater,
IIPriceSubmitter _priceSubmitter,
uint256 _defaultMaxVotersForFtso
)
Governed(_governance) AddressUpdatable(_addressUpdater)
{
priceSubmitter = _priceSubmitter;
defaultMaxVotersForFtso = _defaultMaxVotersForFtso;
}
/**
* Request to whitelist `_voter` account to all active ftsos.
* May be called by any address.
* It returns an array of supported ftso indices and success flag per index.
*/
function requestFullVoterWhitelisting(
address _voter
)
external override
returns (
uint256[] memory _supportedIndices,
bool[] memory _success
)
{
if (_isTrustedAddress(_voter)) {
revert("trusted address");
}
_supportedIndices = ftsoRegistry.getSupportedIndices();
uint256 len = _supportedIndices.length;
_success = new bool[](len);
for (uint256 i = 0; i < len; i++) {
_success[i] = _requestWhitelistingVoter(_voter, _supportedIndices[i]);
}
}
/**
* Request to whitelist `_voter` account to ftso at `_ftsoIndex`. Will revert if vote power too low.
* May be called by any address.
*/
function requestWhitelistingVoter(address _voter, uint256 _ftsoIndex) external override {
if (_isTrustedAddress(_voter)) {
revert("trusted address");
}
bool success = _requestWhitelistingVoter(_voter, _ftsoIndex);
require(success, "vote power too low");
}
/**
* Set the maximum number of voters in the whitelist for FTSO at index `_ftsoIndex`.
* Calling this function might remove several voters with the least votepower from the whitelist.
*/
function setMaxVotersForFtso(uint256 _ftsoIndex, uint256 _newMaxVoters) external override onlyGovernance {
maxVotersForFtso[_ftsoIndex] = _newMaxVoters;
// need to remove any?
address[] storage addressesForFtso = whitelist[_ftsoIndex];
if (_newMaxVoters >= addressesForFtso.length) {
return;
}
// remove voters with minimum vote power
IIFtso ftso = ftsoRegistry.getFtso(_ftsoIndex);
uint256[] memory votePowers = _getVotePowerWeights(ftso, addressesForFtso);
uint256 length = votePowers.length;
uint256 toRemove = length - _newMaxVoters;
address[] memory removedVoters = new address[](toRemove);
for (uint256 n = 0; n < toRemove; n++) {
uint256 minIndex = _findMinimum(votePowers, length);
removedVoters[n] = addressesForFtso[minIndex];
if (minIndex < length - 1) {
addressesForFtso[minIndex] = addressesForFtso[length - 1];
votePowers[minIndex] = votePowers[length - 1];
}
addressesForFtso.pop();
--length;
}
_votersRemovedFromWhitelist(removedVoters, _ftsoIndex);
}
/**
* Set the maximum number of voters in the whitelist for a new FTSO.
*/
function setDefaultMaxVotersForFtso(uint256 _defaultMaxVotersForFtso) external override onlyGovernance {
defaultMaxVotersForFtso = _defaultMaxVotersForFtso;
}
/**
* Create whitelist with default size for ftso.
*/
function addFtso(uint256 _ftsoIndex) external override onlyFtsoManager {
require(maxVotersForFtso[_ftsoIndex] == 0, "whitelist already exist");
maxVotersForFtso[_ftsoIndex] = defaultMaxVotersForFtso;
}
/**
* Clear whitelist for ftso at `_ftsoIndex`.
*/
function removeFtso(uint256 _ftsoIndex) external override onlyFtsoManager {
_votersRemovedFromWhitelist(whitelist[_ftsoIndex], _ftsoIndex);
delete whitelist[_ftsoIndex];
delete maxVotersForFtso[_ftsoIndex];
}
/**
* Remove `_trustedAddress` from whitelist for ftso at `_ftsoIndex`.
*/
function removeTrustedAddressFromWhitelist(address _trustedAddress, uint256 _ftsoIndex) external override {
if (!_isTrustedAddress(_trustedAddress)) {
revert("not trusted address");
}
address[] storage addressesForFtso = whitelist[_ftsoIndex];
uint256 length = addressesForFtso.length;
// find index of _trustedAddress
uint256 index = 0;
for ( ; index < length; index++) {
if (addressesForFtso[index] == _trustedAddress) {
break;
}
}
require(index < length, "trusted address not whitelisted");
// kick the index out and replace it with the last one
address[] memory removedVoters = new address[](1);
removedVoters[0] = addressesForFtso[index];
addressesForFtso[index] = addressesForFtso[length - 1];
addressesForFtso.pop();
_votersRemovedFromWhitelist(removedVoters, _ftsoIndex);
}
/**
* Get whitelisted price providers for ftso with `_symbol`
*/
function getFtsoWhitelistedPriceProvidersBySymbol(
string memory _symbol
)
external view override
returns (
address[] memory
)
{
uint256 ftsoIndex = ftsoRegistry.getFtsoIndex(_symbol);
return getFtsoWhitelistedPriceProviders(ftsoIndex);
}
/**
* Get whitelisted price providers for ftso at `_ftsoIndex`
*/
function getFtsoWhitelistedPriceProviders(uint256 _ftsoIndex) public view override returns (address[] memory) {
uint256 maxVoters = maxVotersForFtso[_ftsoIndex];
require(maxVoters > 0, "FTSO index not supported");
return whitelist[_ftsoIndex];
}
/**
* @notice Implementation of the AddressUpdatable abstract method.
*/
function _updateContractAddresses(
bytes32[] memory _contractNameHashes,
address[] memory _contractAddresses
)
internal override
{
ftsoRegistry = IFtsoRegistry(_getContractAddress(_contractNameHashes, _contractAddresses, "FtsoRegistry"));
ftsoManager = _getContractAddress(_contractNameHashes, _contractAddresses, "FtsoManager");
}
/**
* Request to whitelist `_voter` account to ftso at `_ftsoIndex` - implementation.
*/
function _requestWhitelistingVoter(address _voter, uint256 _ftsoIndex) internal returns(bool) {
uint256 maxVoters = maxVotersForFtso[_ftsoIndex];
require(maxVoters > 0, "FTSO index not supported");
address[] storage addressesForFtso = whitelist[_ftsoIndex];
uint256 length = addressesForFtso.length;
// copy to memory and check if it contains _voter
address[] memory addresses = new address[](length + 1);
for (uint256 i = 0; i < length; i++) {
address addr = addressesForFtso[i];
if (addr == _voter) {
// _voter is already whitelisted, return
return true;
}
addresses[i] = addr;
}
addresses[length] = _voter;
// can we just add a new one?
if (length < maxVoters) {
addressesForFtso.push(_voter);
_voterWhitelisted(_voter, _ftsoIndex);
return true;
}
// find a candidate to kick out
uint256 minIndex = _minVotePowerIndex(addresses, _ftsoIndex);
if (minIndex == length) {
// the new _voter has the minimum vote power, do nothing
return false;
}
// kick the minIndex out and replace it with _voter
address[] memory removedVoters = new address[](1);
removedVoters[0] = addresses[minIndex];
addressesForFtso[minIndex] = _voter;
_votersRemovedFromWhitelist(removedVoters, _ftsoIndex);
_voterWhitelisted(_voter, _ftsoIndex);
return true;
}
/**
* Find index of the element with minimum vote power weight.
* In case of a tie, returns later index.
*/
function _minVotePowerIndex(address[] memory _addresses, uint256 _ftsoIndex) internal returns (uint256) {
IIFtso ftso = ftsoRegistry.getFtso(_ftsoIndex);
uint256[] memory votePowers = _getVotePowerWeights(ftso, _addresses);
return _findMinimum(votePowers, votePowers.length);
}
/**
* Calculate vote power weights like FTSO.
* Unlike FTSO, it calls VPToken vote power in a batch to limit gas consumption.
* Another difference with FTSO is that voter turnout is ignored (it makes
* no sense for whitelist, since it has to be initialized before any voting occurs).
* Apart from turnout, the results should be equal as for FTSO.
*/
function _getVotePowerWeights(IIFtso ftso, address[] memory _addresses) internal
returns (uint256[] memory _votePowers)
{
// get parameters
IIVPToken[] memory assets;
uint256[] memory assetMultipliers;
uint256 totalVotePowerNat;
uint256 totalVotePowerAsset;
uint256 assetWeightRatio;
uint256 votePowerBlock;
(assets, assetMultipliers, totalVotePowerNat, totalVotePowerAsset, assetWeightRatio, votePowerBlock)
= ftso.getVoteWeightingParameters();
// nat vote powers
uint256[] memory wNatVP =
_getNativeVotePowerWeights(ftso.wNat(), totalVotePowerNat, _addresses, votePowerBlock);
// asset vote powers
uint256[] memory combinedAssetVP =
_getAssetVotePowerWeights(assets, assetMultipliers, totalVotePowerAsset, _addresses, votePowerBlock);
// combine asset and wNat
return _computeWeightedSum(wNatVP, combinedAssetVP, assetWeightRatio);
}
/**
* Calculate native vote power weights like FTSO.
*/
function _getNativeVotePowerWeights(
IIVPToken _wNat,
uint256 _totalVotePowerNat,
address[] memory _addresses,
uint256 _blockNumber
)
internal
returns (uint256[] memory _wNatVP)
{
_wNatVP = _getVotePowers(_wNat, _addresses, _blockNumber);
if (_totalVotePowerNat == 0) {
return _wNatVP; // if total is 0, all values must be 0, no division needed
}
for (uint256 i = 0; i < _addresses.length; i++) {
_wNatVP[i] = _wNatVP[i].mulDiv(TERA, _totalVotePowerNat);
}
}
/**
* Calculate asset vote power weights like FTSO.
*/
function _getAssetVotePowerWeights(
IIVPToken[] memory _assets,
uint256[] memory _assetMultipliers,
uint256 _totalVotePowerAsset,
address[] memory _addresses,
uint256 _blockNumber
)
internal
returns (uint256[] memory _combinedAssetVP)
{
_combinedAssetVP = new uint256[](_addresses.length);
for (uint256 i = 0; i < _addresses.length; i++) {
_combinedAssetVP[i] = 0;
}
if (_totalVotePowerAsset == 0) {
return _combinedAssetVP;
}
uint256 divisor = _totalVotePowerAsset.mul(1e18);
for (uint256 n = 0; n < _assets.length; n++) {
IIVPToken asset = _assets[n];
if (address(asset) != address(0)) {
uint256[] memory assetVP = _getVotePowers(asset, _addresses, _blockNumber);
// add
for (uint256 i = 0; i < _addresses.length; i++) {
uint256 weightedVP = assetVP[i].mulDiv(_assetMultipliers[n], divisor);
_combinedAssetVP[i] = _combinedAssetVP[i].add(weightedVP);
}
}
}
}
/**
* Get vote powers from VPToken in a batch.
* This is needed to avoid gas consumption of many cros-contract calls.
*/
function _getVotePowers(
IIVPToken _token,
address[] memory _addresses,
uint256 _blockNumber
)
internal
returns (uint256[] memory)
{
// warm up cache for new voter (in this way everyone pays cache storing price for himself)
_token.votePowerOfAtCached(_addresses[_addresses.length - 1], _blockNumber);
// get all vote powers in a batch
return _token.batchVotePowerOfAt(_addresses, _blockNumber);
}
/**
* Checks if _voter is trusted address
*/
function _isTrustedAddress(address _voter) internal view returns(bool) {
address[] memory trustedAddresses = priceSubmitter.getTrustedAddresses();
for (uint256 i = 0; i < trustedAddresses.length; i++) {
if (trustedAddresses[i] == _voter) {
return true;
}
}
return false;
}
/**
* Update a voter whitelisting and emit an event.
*/
function _voterWhitelisted(address _voter, uint256 _ftsoIndex) private {
emit VoterWhitelisted(_voter, _ftsoIndex);
priceSubmitter.voterWhitelisted(_voter, _ftsoIndex);
}
/**
* Update when a voter is removed from the whitelist. And emit an event.
*/
function _votersRemovedFromWhitelist(address[] memory _removedVoters, uint256 _ftsoIndex) private {
for (uint256 i = 0; i < _removedVoters.length; i++) {
emit VoterRemovedFromWhitelist(_removedVoters[i], _ftsoIndex);
}
priceSubmitter.votersRemovedFromWhitelist(_removedVoters, _ftsoIndex);
}
/**
* 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;
}
/**
* Calculate weighted sum of two arrays (like in FTSO):
* result[i] = (100% - _assetWeightRatio) * _weightsNat[i] + _assetWeightRatio * _weightsAsset[i]
*/
function _computeWeightedSum(
uint256[] memory _weightsNat,
uint256[] memory _weightsAsset,
uint256 _assetWeightRatio
)
private pure
returns (uint256[] memory _weights)
{
uint256 weightAssetSum = _arraySum(_weightsAsset);
uint256 weightNatSum = _arraySum(_weightsNat);
uint256 weightAssetShare = weightAssetSum > 0 ? _assetWeightRatio : 0;
uint256 weightNatShare = weightNatSum > 0 ? BIPS100 - weightAssetShare : 0;
_weights = new uint256[](_weightsNat.length);
for (uint256 i = 0; i < _weightsNat.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;
}
}
/**
* Find minimum index of an uint256 array.
* In case of a tie, returns later index.
*/
function _findMinimum(uint256[] memory _votePowers, uint256 _length) private pure returns (uint256) {
uint256 minIndex = 0;
uint256 minVP = _votePowers[0];
for (uint256 i = 0; i < _length; i++) {
uint256 vp = _votePowers[i];
// using `<=` ensures that later index is used if there is a tie
// this is useful because in requestWhitelistingVoter, the new voter is last
// also, when removing, it is cheaper to remove last element
if (vp <= minVP) {
minIndex = i;
minVP = vp;
}
}
return minIndex;
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.7.6;
import "../interface/IIAddressUpdatable.sol";
abstract contract AddressUpdatable is IIAddressUpdatable {
// https://docs.soliditylang.org/en/v0.8.7/contracts.html#constant-and-immutable-state-variables
// No storage slot is allocated
bytes32 internal constant ADDRESS_STORAGE_POSITION =
keccak256("flare.diamond.AddressUpdatable.ADDRESS_STORAGE_POSITION");
modifier onlyAddressUpdater() {
require (msg.sender == getAddressUpdater(), "only address updater");
_;
}
constructor(address _addressUpdater) {
setAddressUpdaterValue(_addressUpdater);
}
function getAddressUpdater() public view returns (address _addressUpdater) {
// Only direct constants are allowed in inline assembly, so we assign it here
bytes32 position = ADDRESS_STORAGE_POSITION;
// solhint-disable-next-line no-inline-assembly
assembly {
_addressUpdater := sload(position)
}
}
/**
* @notice external method called from AddressUpdater only
*/
function updateContractAddresses(
bytes32[] memory _contractNameHashes,
address[] memory _contractAddresses
)
external override
onlyAddressUpdater
{
// update addressUpdater address
setAddressUpdaterValue(_getContractAddress(_contractNameHashes, _contractAddresses, "AddressUpdater"));
// update all other addresses
_updateContractAddresses(_contractNameHashes, _contractAddresses);
}
/**
* @notice virtual method that a contract extending AddressUpdatable must implement
*/
function _updateContractAddresses(
bytes32[] memory _contractNameHashes,
address[] memory _contractAddresses
) internal virtual;
/**
* @notice helper method to get contract address
* @dev it reverts if contract name does not exist
*/
function _getContractAddress(
bytes32[] memory _nameHashes,
address[] memory _addresses,
string memory _nameToFind
)
internal pure
returns(address)
{
bytes32 nameHash = keccak256(abi.encode(_nameToFind));
address a = address(0);
for (uint256 i = 0; i < _nameHashes.length; i++) {
if (nameHash == _nameHashes[i]) {
a = _addresses[i];
break;
}
}
require(a != address(0), "address zero");
return a;
}
function setAddressUpdaterValue(address _addressUpdater) internal {
// Only direct constants are allowed in inline assembly, so we assign it here
bytes32 position = ADDRESS_STORAGE_POSITION;
// solhint-disable-next-line no-inline-assembly
assembly {
sstore(position, _addressUpdater)
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.6 <0.9;
interface IIAddressUpdatable {
/**
* @notice Updates contract addresses - should be called only from AddressUpdater contract
* @param _contractNameHashes list of keccak256(abi.encode(...)) contract names
* @param _contractAddresses list of contract addresses corresponding to the contract names
*/
function updateContractAddresses(
bytes32[] memory _contractNameHashes,
address[] memory _contractAddresses
) external;
}
./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/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);
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.6 <0.9;
import "../../userInterfaces/IPriceSubmitter.sol";
interface IIPriceSubmitter is IPriceSubmitter {
/**
* Set trusted addresses that are always allowed to submit and reveal.
* Only ftso manager can call this method.
*/
function setTrustedAddresses(address[] memory _trustedAddresses) external;
/**
* Called from whitelister when new voter has been whitelisted.
*/
function voterWhitelisted(address _voter, uint256 _ftsoIndex) external;
/**
* Called from whitelister when one or more voters have been removed.
*/
function votersRemovedFromWhitelist(address[] memory _voters, uint256 _ftsoIndex) external;
/**
* Returns a list of trusted addresses that are always allowed to submit and reveal.
*/
function getTrustedAddresses() external view returns (address[] memory);
}
// SPDX-License-Identifier: MIT
pragma solidity 0.7.6;
import "../../userInterfaces/IGovernanceSettings.sol";
/**
* @title Governed Base
* @notice This abstract base class defines behaviors for a governed contract.
* @dev This class is abstract so that specific behaviors can be defined for the constructor.
* Contracts should not be left ungoverned, but not all contract will have a constructor
* (for example those pre-defined in genesis).
**/
abstract contract GovernedBase {
struct TimelockedCall {
uint256 allowedAfterTimestamp;
bytes encodedCall;
}
// solhint-disable-next-line const-name-snakecase
IGovernanceSettings public constant governanceSettings =
IGovernanceSettings(0x1000000000000000000000000000000000000007);
address private initialGovernance;
bool private initialised;
bool public productionMode;
bool private executing;
mapping(bytes4 => TimelockedCall) public timelockedCalls;
event GovernanceCallTimelocked(bytes4 selector, uint256 allowedAfterTimestamp, bytes encodedCall);
event TimelockedGovernanceCallExecuted(bytes4 selector, uint256 timestamp);
event TimelockedGovernanceCallCanceled(bytes4 selector, uint256 timestamp);
event GovernanceInitialised(address initialGovernance);
event GovernedProductionModeEntered(address governanceSettings);
modifier onlyGovernance {
if (executing || !productionMode) {
_beforeExecute();
_;
} else {
_recordTimelockedCall(msg.data);
}
}
modifier onlyImmediateGovernance () {
_checkOnlyGovernance();
_;
}
constructor(address _initialGovernance) {
if (_initialGovernance != address(0)) {
initialise(_initialGovernance);
}
}
/**
* @notice Execute the timelocked governance calls once the timelock period expires.
* @dev Only executor can call this method.
* @param _selector The method selector (only one timelocked call per method is stored).
*/
function executeGovernanceCall(bytes4 _selector) external {
require(governanceSettings.isExecutor(msg.sender), "only executor");
TimelockedCall storage call = timelockedCalls[_selector];
require(call.allowedAfterTimestamp != 0, "timelock: invalid selector");
require(block.timestamp >= call.allowedAfterTimestamp, "timelock: not allowed yet");
bytes memory encodedCall = call.encodedCall;
delete timelockedCalls[_selector];
executing = true;
//solhint-disable-next-line avoid-low-level-calls
(bool success,) = address(this).call(encodedCall);
executing = false;
emit TimelockedGovernanceCallExecuted(_selector, block.timestamp);
_passReturnOrRevert(success);
}
/**
* Cancel a timelocked governance call before it has been executed.
* @dev Only governance can call this method.
* @param _selector The method selector.
*/
function cancelGovernanceCall(bytes4 _selector) external onlyImmediateGovernance {
require(timelockedCalls[_selector].allowedAfterTimestamp != 0, "timelock: invalid selector");
emit TimelockedGovernanceCallCanceled(_selector, block.timestamp);
delete timelockedCalls[_selector];
}
/**
* Enter the production mode after all the initial governance settings have been set.
* This enables timelocks and the governance is afterwards obtained by calling
* governanceSettings.getGovernanceAddress().
*/
function switchToProductionMode() external {
_checkOnlyGovernance();
require(!productionMode, "already in production mode");
initialGovernance = address(0);
productionMode = true;
emit GovernedProductionModeEntered(address(governanceSettings));
}
/**
* @notice Initialize the governance address if not first initialized.
*/
function initialise(address _initialGovernance) public virtual {
require(initialised == false, "initialised != false");
initialised = true;
initialGovernance = _initialGovernance;
emit GovernanceInitialised(_initialGovernance);
}
/**
* Returns the current effective governance address.
*/
function governance() public view returns (address) {
return productionMode ? governanceSettings.getGovernanceAddress() : initialGovernance;
}
function _beforeExecute() private {
if (executing) {
// can only be run from executeGovernanceCall(), where we check that only executor can call
// make sure nothing else gets executed, even in case of reentrancy
assert(msg.sender == address(this));
executing = false;
} else {
// must be called with: productionMode=false
// must check governance in this case
_checkOnlyGovernance();
}
}
function _recordTimelockedCall(bytes calldata _data) private {
_checkOnlyGovernance();
bytes4 selector;
//solhint-disable-next-line no-inline-assembly
assembly {
selector := calldataload(_data.offset)
}
uint256 timelock = governanceSettings.getTimelock();
uint256 allowedAt = block.timestamp + timelock;
timelockedCalls[selector] = TimelockedCall({
allowedAfterTimestamp: allowedAt,
encodedCall: _data
});
emit GovernanceCallTimelocked(selector, allowedAt, _data);
}
function _checkOnlyGovernance() private view {
require(msg.sender == governance(), "only governance");
}
function _passReturnOrRevert(bool _success) private pure {
// pass exact return or revert data - needs to be done in assembly
//solhint-disable-next-line no-inline-assembly
assembly {
let size := returndatasize()
let ptr := mload(0x40)
mstore(0x40, add(ptr, size))
returndatacopy(ptr, 0, size)
if _success {
return(ptr, size)
}
revert(ptr, size)
}
}
}
./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);
}
// 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/IFtsoRegistry.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.6 <0.9;
pragma abicoder v2;
import "../ftso/interface/IIFtso.sol";
import "../genesis/interface/IFtsoRegistryGenesis.sol";
interface IFtsoRegistry is IFtsoRegistryGenesis {
function getFtso(uint256 _ftsoIndex) external view returns(IIFtso _activeFtsoAddress);
function getFtsoBySymbol(string memory _symbol) external view returns(IIFtso _activeFtsoAddress);
function getSupportedIndices() external view returns(uint256[] memory _supportedIndices);
function getSupportedSymbols() external view returns(string[] memory _supportedSymbols);
function getSupportedFtsos() external view returns(IIFtso[] memory _ftsos);
function getFtsoIndex(string memory _symbol) external view returns (uint256 _assetIndex);
function getFtsoSymbol(uint256 _ftsoIndex) external view returns (string memory _symbol);
function getCurrentPrice(uint256 _ftsoIndex) external view returns(uint256 _price, uint256 _timestamp);
function getCurrentPrice(string memory _symbol) external view returns(uint256 _price, uint256 _timestamp);
function getSupportedIndicesAndFtsos() external view
returns(uint256[] memory _supportedIndices, IIFtso[] memory _ftsos);
function getSupportedSymbolsAndFtsos() external view
returns(string[] memory _supportedSymbols, IIFtso[] memory _ftsos);
function getSupportedIndicesAndSymbols() external view
returns(uint256[] memory _supportedIndices, string[] memory _supportedSymbols);
function getSupportedIndicesSymbolsAndFtsos() external view
returns(uint256[] memory _supportedIndices, string[] memory _supportedSymbols, IIFtso[] memory _ftsos);
}
// SPDX-License-Identifier: MIT
pragma solidity 0.7.6;
/**
* A special contract that holds Flare governance address.
* This contract enables updating governance address and timelock only by hard forking the network,
* meaning only by updating validator code.
*/
interface IGovernanceSettings {
/**
* Get the governance account address.
* The governance address can only be changed by a hardfork.
*/
function getGovernanceAddress() external view returns (address);
/**
* Get the time in seconds that must pass between a governance call and execution.
* The timelock value can only be changed by a hardfork.
*/
function getTimelock() external view returns (uint256);
/**
* Get the addresses of the accounts that are allowed to execute the timelocked governance calls
* once the timelock period expires.
* Executors can be changed without a hardfork, via a normal governance call.
*/
function getExecutors() external view returns (address[] memory);
/**
* Check whether an address is one of the executors.
*/
function isExecutor(address _address) external view returns (bool);
}
// 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/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/userInterfaces/IVoterWhitelister.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.6 <0.9;
interface IVoterWhitelister {
/**
* Raised when an account is removed from the voter whitelist.
*/
event VoterWhitelisted(address voter, uint256 ftsoIndex);
/**
* Raised when an account is removed from the voter whitelist.
*/
event VoterRemovedFromWhitelist(address voter, uint256 ftsoIndex);
/**
* Request to whitelist `_voter` account to ftso at `_ftsoIndex`. Will revert if vote power too low.
* May be called by any address.
*/
function requestWhitelistingVoter(address _voter, uint256 _ftsoIndex) external;
/**
* Request to whitelist `_voter` account to all active ftsos.
* May be called by any address.
* It returns an array of supported ftso indices and success flag per index.
*/
function requestFullVoterWhitelisting(
address _voter
)
external
returns (
uint256[] memory _supportedIndices,
bool[] memory _success
);
/**
* Maximum number of voters in the whitelist for a new FTSO.
*/
function defaultMaxVotersForFtso() external view returns (uint256);
/**
* Maximum number of voters in the whitelist for FTSO at index `_ftsoIndex`.
*/
function maxVotersForFtso(uint256 _ftsoIndex) external view returns (uint256);
/**
* Get whitelisted price providers for ftso with `_symbol`
*/
function getFtsoWhitelistedPriceProvidersBySymbol(string memory _symbol) external view returns (address[] memory);
/**
* Get whitelisted price providers for ftso at `_ftsoIndex`
*/
function getFtsoWhitelistedPriceProviders(uint256 _ftsoIndex) external view returns (address[] memory);
}
./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));
}
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.6 <0.9;
import "../../userInterfaces/IVoterWhitelister.sol";
interface IIVoterWhitelister is IVoterWhitelister {
/**
* Set the maximum number of voters in the whitelist for FTSO at index `_ftsoIndex`.
* Possibly removes several voters with the least votepower from the whitelist.
* Only governance can call this method.
*/
function setMaxVotersForFtso(uint256 _ftsoIndex, uint256 _newMaxVoters) external;
/**
* Set the maximum number of voters in the whitelist for a new FTSO.
* Only governance can call this method.
*/
function setDefaultMaxVotersForFtso(uint256 _defaultMaxVotersForFtso) external;
/**
* Create whitelist with default size for ftso.
* Only ftso manager can call this method.
*/
function addFtso(uint256 _ftsoIndex) external;
/**
* Clear whitelist for ftso at `_ftsoIndex`.
* Only ftso manager can call this method.
*/
function removeFtso(uint256 _ftsoIndex) external;
/**
* Remove `_trustedAddress` from whitelist for ftso at `_ftsoIndex`.
*/
function removeTrustedAddressFromWhitelist(address _trustedAddress, uint256 _ftsoIndex) external;
}
@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);
}