Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
- Contract name:
- InflationAllocation
- Optimization enabled
- true
- Compiler version
- v0.7.6+commit.7338295f
- Optimization runs
- 200
- Verified at
- 2022-07-13T21:12:01.501960Z
Constructor Arguments
0000000000000000000000004598a6c05910ab914f0cbaaca1911cd337d10d29000000000000000000000000baf89d873d198ff78e72d2745b01cba3c6e5be6b0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000001900000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000002bc00000000000000000000000000000000000000000000000000000000000002bc00000000000000000000000000000000000000000000000000000000000002bc00000000000000000000000000000000000000000000000000000000000002bc00000000000000000000000000000000000000000000000000000000000002bc00000000000000000000000000000000000000000000000000000000000002bc00000000000000000000000000000000000000000000000000000000000002bc00000000000000000000000000000000000000000000000000000000000002bc00000000000000000000000000000000000000000000000000000000000002bc00000000000000000000000000000000000000000000000000000000000002bc00000000000000000000000000000000000000000000000000000000000002bc00000000000000000000000000000000000000000000000000000000000002bc00000000000000000000000000000000000000000000000000000000000001f4
Arg [0] (address) : 0x4598a6c05910ab914f0cbaaca1911cd337d10d29
Arg [1] (address) : 0xbaf89d873d198ff78e72d2745b01cba3c6e5be6b
Arg [2] (uint256[]) : [1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 500]
./contracts/inflation/implementation/InflationAllocation.sol
// SPDX-License-Identifier: MIT pragma solidity 0.7.6; pragma abicoder v2; import "../../governance/implementation/Governed.sol"; import "../../addressUpdater/implementation/AddressUpdatable.sol"; import "./Inflation.sol"; import "../interface/IIInflationAllocation.sol"; /** * @title Inflation allocation contract * @notice This contract implements Inflation settings agreed upon by Flare Foundation governance. **/ contract InflationAllocation is IIInflationAllocation, Governed, AddressUpdatable { struct InflationReceiver { IIInflationReceiver receiverContract; uint32 percentageBips; // limited to BIPS100 } // constants string internal constant ERR_LENGTH_MISMATCH = "length mismatch"; string internal constant ERR_HIGH_SHARING_PERCENTAGE = "high sharing percentage"; string internal constant ERR_SUM_SHARING_PERCENTAGE = "sum sharing percentage not 100%"; string internal constant ERR_IS_ZERO = "address is 0"; string internal constant ANNUAL_INFLATION_OUT_OF_BOUNDS = "annual inflation out of bounds"; string internal constant ERR_ANNUAL_INFLATION_SCHEDULE_EMPTY = "annual inflation schedule empty"; string internal constant ERR_TOO_MANY = "too many"; string internal constant ERR_ONLY_INFLATION = "only inflation"; uint256 internal constant BIPS100 = 1e4; // 100% in basis points uint256 internal constant MAX_SCHEDULE_COUNT = 25; uint256 internal constant MAX_INFLATION_RECEIVERS = 10; uint256 internal constant MAX_INFLATION_PERCENTAGE_BIPS = BIPS100 / 10; // 10% in basis points InflationReceiver[] public inflationReceivers; Inflation public inflation; uint256 public lastAnnualInflationPercentageBips; uint256[] public annualInflationPercentagesBips; event InflationSet(address oldAddress, address newAddress); event AnnualInflationPercentageYielded(uint256 percentageBips); event AnnualInflationPercentageScheduleSet(uint256[] annualInflationPercentagesBips); event InflationSharingPercentagesSet( IIInflationReceiver[] inflationReceivers, uint256[] percentagePerReceiverBips ); modifier notZero(address _address) { require(_address != address(0), ERR_IS_ZERO); _; } modifier onlyInflation { require(msg.sender == address(inflation), ERR_ONLY_INFLATION); _; } /** * @dev _inflation contract need not be set here, but must be set at the point that * annual inflation percentages are to be retrieved from the schedule. */ constructor( address _governance, address _addressUpdater, uint256[] memory _annualInflationScheduleBips ) Governed(_governance) AddressUpdatable(_addressUpdater) { require(_annualInflationScheduleBips.length > 0, ERR_ANNUAL_INFLATION_SCHEDULE_EMPTY); // validity is checked in _setAnnualInflationSchedule lastAnnualInflationPercentageBips = _annualInflationScheduleBips[0]; _setAnnualInflationSchedule(_annualInflationScheduleBips); } /** * @notice Set the sharing percentages between inflation receiver contracts. Percentages must sum * to 100%. * @param _inflationReceivers An array of contracts to receive inflation rewards for distribution. * @param _percentagePerReceiverBips An array of sharing percentages in bips. */ function setSharingPercentages( IIInflationReceiver[] memory _inflationReceivers, uint256[] memory _percentagePerReceiverBips ) external onlyGovernance { _setSharingPercentages(_inflationReceivers, _percentagePerReceiverBips); } /** * @notice Set the annual inflation percentage schedule. This schedule is meant to be set for recognition * a per-annum basis. * @param _annualInflationScheduleBips An array of inflation percentages in bips. * @dev The schedule must be a decaying schedule. Once the schedule has been used up, the last percentage * yielded will be the percentage that will continue to be yielded. */ function setAnnualInflation(uint256[] memory _annualInflationScheduleBips) external onlyGovernance { // Clear the existing schedule uint256 lenExistingSchedule = annualInflationPercentagesBips.length; for (uint256 i = 0; i < lenExistingSchedule; i++) { annualInflationPercentagesBips.pop(); } // Set new schedule _setAnnualInflationSchedule(_annualInflationScheduleBips); emit AnnualInflationPercentageScheduleSet(_annualInflationScheduleBips); } /** * @notice Get the next annual inflation percentage from the schedule and pop it off the schedule. * If there are no percentages remaining within the schedule, yield the last percentage known. * @return The annual inflation percentage. * @dev Note that it is up to the caller to call this function at the appropriate annum interval. */ function getAnnualPercentageBips() external override notZero(address(inflation)) onlyInflation returns(uint256) { // If there is not a schedule of percentages, return the last one given (or set). if (annualInflationPercentagesBips.length > 0) { // Since there is a schedule, get the next percentage. lastAnnualInflationPercentageBips = annualInflationPercentagesBips[0]; // Iterate over the schedule, shifting each down an index uint256 len = annualInflationPercentagesBips.length; if (len > 1) { for (uint256 i = 0; i < len - 1; i++) { annualInflationPercentagesBips[i] = annualInflationPercentagesBips[i+1]; } } annualInflationPercentagesBips.pop(); } emit AnnualInflationPercentageYielded(lastAnnualInflationPercentageBips); return lastAnnualInflationPercentageBips; } /** * @notice Get the inflation receiver contracts and the current sharing percentages. * @return _sharingPercentages An array of SharingPercentage. */ function getSharingPercentages() external view override returns(SharingPercentage[] memory _sharingPercentages) { uint256 len = inflationReceivers.length; _sharingPercentages = new SharingPercentage[](len); for (uint i = 0; i < len; i++) { _sharingPercentages[i].percentBips = inflationReceivers[i].percentageBips; _sharingPercentages[i].inflationReceiver = inflationReceivers[i].receiverContract; } } /** * @notice Set the sharing percentages between inflation receiver contracts. Percentages must sum * to 100%. * @param _inflationReceivers An array of contracts to receive inflation rewards for distribution. * @param _percentagePerReceiverBips An array of sharing percentages in bips. */ function _setSharingPercentages( IIInflationReceiver[] memory _inflationReceivers, uint256[] memory _percentagePerReceiverBips ) internal { require(_inflationReceivers.length == _percentagePerReceiverBips.length, ERR_LENGTH_MISMATCH); require (_inflationReceivers.length <= MAX_INFLATION_RECEIVERS, ERR_TOO_MANY); uint256 sumSharingPercentage; uint256 len = inflationReceivers.length; for (uint256 i = 0; i < len; i++) { inflationReceivers.pop(); } for (uint256 i = 0; i < _inflationReceivers.length; i++) { require (_percentagePerReceiverBips[i] <= BIPS100, ERR_HIGH_SHARING_PERCENTAGE); require (_inflationReceivers[i] != IIInflationReceiver(0), ERR_IS_ZERO); sumSharingPercentage += _percentagePerReceiverBips[i]; inflationReceivers.push( InflationReceiver({ receiverContract: _inflationReceivers[i], percentageBips: uint32(_percentagePerReceiverBips[i]) })); } require (sumSharingPercentage == BIPS100, ERR_SUM_SHARING_PERCENTAGE); emit InflationSharingPercentagesSet(_inflationReceivers, _percentagePerReceiverBips); } /** * @notice Implementation of the AddressUpdatable abstract method - updates Inflation * and inflation receivers contracts. */ function _updateContractAddresses( bytes32[] memory _contractNameHashes, address[] memory _contractAddresses ) internal override { Inflation _inflation = Inflation(_getContractAddress(_contractNameHashes, _contractAddresses, "Inflation")); emit InflationSet(address(inflation), address(_inflation)); inflation = _inflation; uint256 len = inflationReceivers.length; if (len == 0) { return; } IIInflationReceiver[] memory receivers = new IIInflationReceiver[](len); uint256[] memory percentages = new uint256[](len); for (uint256 i = 0; i < len; i++) { InflationReceiver memory inflationReceiver = inflationReceivers[i]; receivers[i] = IIInflationReceiver( _getContractAddress(_contractNameHashes, _contractAddresses, inflationReceiver.receiverContract.getContractName())); percentages[i] = inflationReceiver.percentageBips; } _setSharingPercentages(receivers, percentages); } /** * @notice Set the annual inflation percentage schedule. This schedule is meant to be set for recognition * a per-annum basis. * @param _annualInflationScheduleBips An array of inflation percentages in bips. * @dev The schedule must be a decaying schedule. Once the schedule has been used up, the last percentage * yielded will be the percentage that will continue to be yielded. */ function _setAnnualInflationSchedule(uint256[] memory _annualInflationScheduleBips) internal { require(_annualInflationScheduleBips.length <= MAX_SCHEDULE_COUNT, ERR_TOO_MANY); uint256 len = _annualInflationScheduleBips.length; uint256 lastOne = lastAnnualInflationPercentageBips; for (uint256 i = 0; i < len; i++) { // Validate the schedule...percentages must be the same or decay, and cannot be greater than last given. require( _annualInflationScheduleBips[i] <= lastOne && _annualInflationScheduleBips[i] > 0 && _annualInflationScheduleBips[i] <= MAX_INFLATION_PERCENTAGE_BIPS, ANNUAL_INFLATION_OUT_OF_BOUNDS); lastOne = _annualInflationScheduleBips[i]; // Push in the new schedule annualInflationPercentagesBips.push(_annualInflationScheduleBips[i]); } } }
./contracts/addressUpdater/implementation/AddressUpdatable.sol
// 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) } } }
./contracts/addressUpdater/interface/IIAddressUpdatable.sol
// 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/genesis/implementation/FlareDaemon.sol
// SPDX-License-Identifier: MIT // WARNING, WARNING, WARNING // If you modify this contract, you need to re-install the binary into the validator // genesis file for the chain you wish to run. See ./docs/CompilingContracts.md for more information. // You have been warned. That is all. pragma solidity 0.7.6; pragma abicoder v2; import "../../governance/implementation/GovernedAtGenesis.sol"; import "../../addressUpdater/implementation/AddressUpdatable.sol"; import "../interface/IInflationGenesis.sol"; import "../interface/IFlareDaemonize.sol"; import "@openzeppelin/contracts/math/SafeMath.sol"; import "../../utils/implementation/SafePct.sol"; /** * @title Flare Daemon contract * @notice This contract exists to coordinate regular daemon-like polling of contracts * that are registered to receive said polling. The trigger method is called by the * validator right at the end of block state transition. */ contract FlareDaemon is GovernedAtGenesis, AddressUpdatable { using SafeMath for uint256; using SafePct for uint256; //==================================================================== // Data Structures //==================================================================== struct DaemonizedError { uint192 lastErrorBlock; uint64 numErrors; address fromContract; uint64 errorTypeIndex; string errorMessage; } struct LastErrorData { uint192 totalDaemonizedErrors; uint64 lastErrorTypeIndex; } struct Registration { IFlareDaemonize daemonizedContract; uint256 gasLimit; } string internal constant ERR_ALREADY_SET = "already set"; string internal constant ERR_OUT_OF_BALANCE = "out of balance"; string internal constant ERR_NOT_INFLATION = "not inflation"; string internal constant ERR_TOO_MANY = "too many"; string internal constant ERR_TOO_BIG = "too big"; string internal constant ERR_TOO_OFTEN = "too often"; string internal constant ERR_INFLATION_ZERO = "inflation zero"; string internal constant ERR_BLOCK_NUMBER_SMALL = "block.number small"; string internal constant INDEX_TOO_HIGH = "start index high"; string internal constant UPDATE_GAP_TOO_SHORT = "time gap too short"; string internal constant MAX_MINT_TOO_HIGH = "max mint too high"; string internal constant MAX_MINT_IS_ZERO = "max mint is zero"; string internal constant ERR_DUPLICATE_ADDRESS = "dup address"; string internal constant ERR_ADDRESS_ZERO = "address zero"; string internal constant ERR_OUT_OF_GAS = "out of gas"; string internal constant ERR_INFLATION_MINT_RECEIVE_FAIL = "unknown error. receiveMinting"; uint256 internal constant MAX_DAEMONIZE_CONTRACTS = 10; // Initial max mint request - 60 million native token uint256 internal constant MAX_MINTING_REQUEST_DEFAULT = 60000000 ether; // How often can inflation request minting from the validator - 23 hours constant uint256 internal constant MAX_MINTING_FREQUENCY_SEC = 23 hours; // How often can the maximal mint request amount be updated uint256 internal constant MAX_MINTING_REQUEST_FREQUENCY_SEC = 24 hours; // By how much can the maximum be increased (as a percentage of the previous maximum) uint256 internal constant MAX_MINTING_REQUEST_INCREASE_PERCENT = 110; // upper estimate of gas needed after error occurs in call to daemonizedContract.daemonize() uint256 internal constant MIN_GAS_LEFT_AFTER_DAEMONIZE = 300000; // lower estimate for gas needed for daemonize() call in trigger uint256 internal constant MIN_GAS_FOR_DAEMONIZE_CALL = 5000; IInflationGenesis public inflation; uint256 public systemLastTriggeredAt; uint256 public totalMintingRequestedWei; uint256 public totalMintingReceivedWei; uint256 public totalMintingWithdrawnWei; uint256 public totalSelfDestructReceivedWei; uint256 public maxMintingRequestWei; uint256 public lastMintRequestTs; uint256 public lastUpdateMaxMintRequestTs; LastErrorData public errorData; uint256 public blockHoldoff; uint256 private lastBalance; uint256 private expectedMintRequest; bool private initialized; // track deamonized contracts IFlareDaemonize[] internal daemonizeContracts; mapping (IFlareDaemonize => uint256) internal gasLimits; mapping (IFlareDaemonize => uint256) internal blockHoldoffsRemaining; // track daemonize errors mapping(bytes32 => DaemonizedError) internal daemonizedErrors; bytes32 [] internal daemonizeErrorHashes; event ContractDaemonized(address theContract, uint256 gasConsumed); event ContractDaemonizeErrored(address theContract, uint256 atBlock, string theMessage, uint256 gasConsumed); event ContractHeldOff(address theContract, uint256 blockHoldoffsRemaining); event ContractsSkippedOutOfGas(uint256 numberOfSkippedConstracts); event MintingRequestReceived(uint256 amountWei); event MintingRequestTriggered(uint256 amountWei); event MintingReceived(uint256 amountWei); event MintingWithdrawn(uint256 amountWei); event RegistrationUpdated(IFlareDaemonize theContract, bool add); event SelfDestructReceived(uint256 amountWei); event InflationSet(IInflationGenesis theNewContract, IInflationGenesis theOldContract); /** * @dev As there is not a constructor, this modifier exists to make sure the inflation * contract is set for methods that require it. */ modifier inflationSet { // Don't revert...just report. if (address(inflation) == address(0)) { addDaemonizeError(address(this), ERR_INFLATION_ZERO, 0); } _; } /** * @dev Access control to protect methods to allow only minters to call select methods * (like transferring balance out). */ modifier onlyInflation (address _inflation) { require (address(inflation) == _inflation, ERR_NOT_INFLATION); _; } /** * @dev Access control to protect trigger() method. * Please note that the sender address is the same as deployed FlareDaemon address in this case. */ modifier onlySystemTrigger { require (msg.sender == 0x1000000000000000000000000000000000000002); _; } //==================================================================== // Constructor for pre-compiled code //==================================================================== /** * @dev This constructor should contain no code as this contract is pre-loaded into the genesis block. * The super constructor is called for testing convenience. */ constructor() GovernedAtGenesis(address(0)) AddressUpdatable(address(0)) { /* empty block */ } //==================================================================== // Functions //==================================================================== /** * @notice Register contracts to be polled by the daemon process. * @param _registrations An array of Registration structures of IFlareDaemonize contracts to daemonize * and gas limits for each contract. * @dev A gas limit of zero will set no limit for the contract but the validator has an overall * limit for the trigger() method. * @dev If any registrations already exist, they will be unregistered. * @dev Contracts will be daemonized in the order in which presented via the _registrations array. */ function registerToDaemonize(Registration[] memory _registrations) external onlyGovernance { _registerToDaemonize(_registrations); } /** * @notice Queue up a minting request to send to the validator at next trigger. * @param _amountWei The amount to mint. */ function requestMinting(uint256 _amountWei) external onlyInflation(msg.sender) { require(_amountWei <= maxMintingRequestWei, ERR_TOO_BIG); require(_getNextMintRequestAllowedTs() < block.timestamp, ERR_TOO_OFTEN); if (_amountWei > 0) { lastMintRequestTs = block.timestamp; totalMintingRequestedWei = totalMintingRequestedWei.add(_amountWei); emit MintingRequestReceived(_amountWei); } } /** * @notice Set number of blocks that must elapse before a daemonized contract exceeding gas limit can have * its daemonize() method called again. * @param _blockHoldoff The number of blocks to holdoff. */ function setBlockHoldoff(uint256 _blockHoldoff) external onlyGovernance { blockHoldoff = _blockHoldoff; } /** * @notice Set limit on how much can be minted per request. * @param _maxMintingRequestWei The request maximum in wei. * @notice this number can't be udated too often */ function setMaxMintingRequest(uint256 _maxMintingRequestWei) external onlyGovernance { // make sure increase amount is reasonable require( _maxMintingRequestWei <= (maxMintingRequestWei.mulDiv(MAX_MINTING_REQUEST_INCREASE_PERCENT,100)), MAX_MINT_TOO_HIGH ); require(_maxMintingRequestWei > 0, MAX_MINT_IS_ZERO); // make sure enough time since last update require( block.timestamp > lastUpdateMaxMintRequestTs + MAX_MINTING_REQUEST_FREQUENCY_SEC, UPDATE_GAP_TOO_SHORT ); maxMintingRequestWei = _maxMintingRequestWei; lastUpdateMaxMintRequestTs = block.timestamp; } /** * @notice Sets the address udpater contract. * @param _addressUpdater The address updater contract. */ function setAddressUpdater(address _addressUpdater) external onlyGovernance { require(getAddressUpdater() == address(0), ERR_ALREADY_SET); setAddressUpdaterValue(_addressUpdater); } /** * @notice The meat of this contract. Poll all registered contracts, calling the daemonize() method of each, * in the order in which registered. * @return _toMintWei Return the amount to mint back to the validator. The asked for balance will show * up in the next block (it is actually added right before this block's state transition, * but well after this method call will see it.) * @dev This method watches for balances being added to this contract and handles appropriately - legit * mint requests as made via requestMinting, and also self-destruct sending to this contract, should * it happen for some reason. */ //slither-disable-next-line reentrancy-eth // method protected by reentrancy guard (see comment below) function trigger() external virtual inflationSet onlySystemTrigger returns (uint256 _toMintWei) { return triggerInternal(); } function getDaemonizedContractsData() external view returns( IFlareDaemonize[] memory _daemonizeContracts, uint256[] memory _gasLimits, uint256[] memory _blockHoldoffsRemaining ) { uint256 len = daemonizeContracts.length; _daemonizeContracts = new IFlareDaemonize[](len); _gasLimits = new uint256[](len); _blockHoldoffsRemaining = new uint256[](len); for (uint256 i; i < len; i++) { IFlareDaemonize daemonizeContract = daemonizeContracts[i]; _daemonizeContracts[i] = daemonizeContract; _gasLimits[i] = gasLimits[daemonizeContract]; _blockHoldoffsRemaining[i] = blockHoldoffsRemaining[daemonizeContract]; } } function getNextMintRequestAllowedTs() external view returns(uint256) { return _getNextMintRequestAllowedTs(); } function showLastDaemonizedError () external view returns( uint256[] memory _lastErrorBlock, uint256[] memory _numErrors, string[] memory _errorString, address[] memory _erroringContract, uint256 _totalDaemonizedErrors ) { return showDaemonizedErrors(errorData.lastErrorTypeIndex, 1); } /** * @notice Set the governance address to a hard-coded known address. * @dev This should be done at contract deployment time. * @return The governance address. */ function initialiseFixedAddress() public override returns(address) { if (!initialized) { initialized = true; address governanceAddress = super.initialiseFixedAddress(); return governanceAddress; } else { return governance(); } } function showDaemonizedErrors (uint startIndex, uint numErrorTypesToShow) public view returns( uint256[] memory _lastErrorBlock, uint256[] memory _numErrors, string[] memory _errorString, address[] memory _erroringContract, uint256 _totalDaemonizedErrors ) { require(startIndex < daemonizeErrorHashes.length, INDEX_TOO_HIGH); uint256 numReportElements = daemonizeErrorHashes.length >= startIndex + numErrorTypesToShow ? numErrorTypesToShow : daemonizeErrorHashes.length - startIndex; _lastErrorBlock = new uint256[] (numReportElements); _numErrors = new uint256[] (numReportElements); _errorString = new string[] (numReportElements); _erroringContract = new address[] (numReportElements); // we have error data error type. // error type is hash(error_string, source contract) // per error type we report how many times it happened. // what was last block it happened. // what is the error string. // what is the erroring contract for (uint i = 0; i < numReportElements; i++) { bytes32 hash = daemonizeErrorHashes[startIndex + i]; _lastErrorBlock[i] = daemonizedErrors[hash].lastErrorBlock; _numErrors[i] = daemonizedErrors[hash].numErrors; _errorString[i] = daemonizedErrors[hash].errorMessage; _erroringContract[i] = daemonizedErrors[hash].fromContract; } _totalDaemonizedErrors = errorData.totalDaemonizedErrors; } /** * @notice Implementation of the AddressUpdatable abstract method - updates Inflation and daemonized contracts. * @dev It also sets `maxMintingRequestWei` if it was not set before. */ function _updateContractAddresses( bytes32[] memory _contractNameHashes, address[] memory _contractAddresses ) internal override { IInflationGenesis _inflation = IInflationGenesis( _getContractAddress(_contractNameHashes, _contractAddresses, "Inflation")); emit InflationSet(_inflation, inflation); inflation = _inflation; if (maxMintingRequestWei == 0) { maxMintingRequestWei = MAX_MINTING_REQUEST_DEFAULT; } uint256 len = daemonizeContracts.length; if (len == 0) { return; } Registration[] memory registrations = new Registration[](len); for (uint256 i = 0; i < len; i++) { IFlareDaemonize daemonizeContract = daemonizeContracts[i]; registrations[i].daemonizedContract = IFlareDaemonize( _getContractAddress(_contractNameHashes, _contractAddresses, daemonizeContract.getContractName())); registrations[i].gasLimit = gasLimits[daemonizeContract]; } _registerToDaemonize(registrations); } /** * @notice Implementation of the trigger() method. The external wrapper has extra guard for msg.sender. */ //slither-disable-next-line reentrancy-eth // method protected by reentrancy guard (see comment below) function triggerInternal() internal returns (uint256 _toMintWei) { // only one trigger() call per block allowed // this also serves as reentrancy guard, since any re-entry will happen in the same block if(block.number == systemLastTriggeredAt) return 0; systemLastTriggeredAt = block.number; uint256 currentBalance = address(this).balance; // Did the validator or a self-destructor conjure some native token? if (currentBalance > lastBalance) { uint256 balanceExpected = lastBalance.add(expectedMintRequest); // Did we get what was last asked for? if (currentBalance == balanceExpected) { // Yes, so assume it all came from the validator. uint256 minted = expectedMintRequest; totalMintingReceivedWei = totalMintingReceivedWei.add(minted); emit MintingReceived(minted); //slither-disable-next-line arbitrary-send // only sent to inflation, set by governance try inflation.receiveMinting{ value: minted }() { totalMintingWithdrawnWei = totalMintingWithdrawnWei.add(minted); emit MintingWithdrawn(minted); } catch Error(string memory message) { addDaemonizeError(address(this), message, 0); } catch { addDaemonizeError(address(this), ERR_INFLATION_MINT_RECEIVE_FAIL, 0); } } else if (currentBalance < balanceExpected) { // No, and if less, there are two possibilities: 1) the validator did not // send us what we asked (not possible unless a bug), or 2) an attacker // sent us something in between a request and a mint. Assume 2. uint256 selfDestructReceived = currentBalance.sub(lastBalance); totalSelfDestructReceivedWei = totalSelfDestructReceivedWei.add(selfDestructReceived); emit SelfDestructReceived(selfDestructReceived); } else { // No, so assume we got a minting request (perhaps zero...does not matter) // and some self-destruct proceeds (unlikely but can happen). totalMintingReceivedWei = totalMintingReceivedWei.add(expectedMintRequest); uint256 selfDestructReceived = currentBalance.sub(lastBalance).sub(expectedMintRequest); totalSelfDestructReceivedWei = totalSelfDestructReceivedWei.add(selfDestructReceived); emit MintingReceived(expectedMintRequest); emit SelfDestructReceived(selfDestructReceived); //slither-disable-next-line arbitrary-send // only sent to inflation, set by governance try inflation.receiveMinting{ value: expectedMintRequest }() { totalMintingWithdrawnWei = totalMintingWithdrawnWei.add(expectedMintRequest); emit MintingWithdrawn(expectedMintRequest); } catch Error(string memory message) { addDaemonizeError(address(this), message, 0); } catch { addDaemonizeError(address(this), ERR_INFLATION_MINT_RECEIVE_FAIL, 0); } } } uint256 len = daemonizeContracts.length; // Perform trigger operations here for (uint256 i = 0; i < len; i++) { IFlareDaemonize daemonizedContract = daemonizeContracts[i]; uint256 blockHoldoffRemainingForContract = blockHoldoffsRemaining[daemonizedContract]; if (blockHoldoffRemainingForContract > 0) { blockHoldoffsRemaining[daemonizedContract] = blockHoldoffRemainingForContract - 1; emit ContractHeldOff(address(daemonizedContract), blockHoldoffRemainingForContract); } else { // Figure out what gas to limit call by uint256 gasLimit = gasLimits[daemonizedContract]; uint256 startGas = gasleft(); // End loop if there isn't enough gas left for any daemonize call if (startGas < MIN_GAS_LEFT_AFTER_DAEMONIZE + MIN_GAS_FOR_DAEMONIZE_CALL) { emit ContractsSkippedOutOfGas(len - i); break; } // Calculate the gas limit for the next call uint256 useGas = startGas - MIN_GAS_LEFT_AFTER_DAEMONIZE; if (gasLimit > 0 && gasLimit < useGas) { useGas = gasLimit; } // Run daemonize for the contract, consume errors, and record try daemonizedContract.daemonize{gas: useGas}() { emit ContractDaemonized(address(daemonizedContract), (startGas - gasleft())); // Catch all requires with messages } catch Error(string memory message) { addDaemonizeError(address(daemonizedContract), message, (startGas - gasleft())); daemonizedContract.switchToFallbackMode(); // Catch everything else...out of gas, div by zero, asserts, etc. } catch { uint256 endGas = gasleft(); // Interpret out of gas errors if (gasLimit > 0 && startGas.sub(endGas) >= gasLimit) { addDaemonizeError(address(daemonizedContract), ERR_OUT_OF_GAS, (startGas - endGas)); // When daemonize() fails with out-of-gas, try to fix it in two steps: // 1) try to switch contract to fallback mode // (to allow the contract's daemonize() to recover in fallback mode in next block) // 2) if constract is already in fallback mode or fallback mode is not supported // (switchToFallbackMode() returns false), start the holdoff for this contract bool switchedToFallback = daemonizedContract.switchToFallbackMode(); if (!switchedToFallback) { blockHoldoffsRemaining[daemonizedContract] = blockHoldoff; } } else { // Don't know error cause...just log it as unknown addDaemonizeError(address(daemonizedContract), "unknown", (startGas - endGas)); daemonizedContract.switchToFallbackMode(); } } } } // Get any requested minting and return to validator _toMintWei = getPendingMintRequest(); if (_toMintWei > 0) { expectedMintRequest = _toMintWei; emit MintingRequestTriggered(_toMintWei); } else { expectedMintRequest = 0; } // Update balance lastBalance = address(this).balance; // We should be in balance - don't revert, just report... uint256 contractBalanceExpected = getExpectedBalance(); if (contractBalanceExpected != address(this).balance) { addDaemonizeError(address(this), ERR_OUT_OF_BALANCE, 0); } } function addDaemonizeError(address daemonizedContract, string memory message, uint256 gasConsumed) internal { bytes32 errorStringHash = keccak256(abi.encode(daemonizedContract, message)); DaemonizedError storage daemonizedError = daemonizedErrors[errorStringHash]; if (daemonizedError.numErrors == 0) { // first time we recieve this error string. daemonizeErrorHashes.push(errorStringHash); daemonizedError.fromContract = daemonizedContract; // limit message length to fit in fixed number of storage words (to make gas usage predictable) daemonizedError.errorMessage = truncateString(message, 64); daemonizedError.errorTypeIndex = uint64(daemonizeErrorHashes.length - 1); } daemonizedError.numErrors += 1; daemonizedError.lastErrorBlock = uint192(block.number); emit ContractDaemonizeErrored(daemonizedContract, block.number, message, gasConsumed); errorData.totalDaemonizedErrors += 1; errorData.lastErrorTypeIndex = daemonizedError.errorTypeIndex; } /** * @notice Register contracts to be polled by the daemon process. * @param _registrations An array of Registration structures of IFlareDaemonize contracts to daemonize * and gas limits for each contract. * @dev A gas limit of zero will set no limit for the contract but the validator has an overall * limit for the trigger() method. * @dev If any registrations already exist, they will be unregistered. * @dev Contracts will be daemonized in the order in which presented via the _registrations array. */ function _registerToDaemonize(Registration[] memory _registrations) internal { // Make sure there are not too many contracts to register. uint256 registrationsLength = _registrations.length; require(registrationsLength <= MAX_DAEMONIZE_CONTRACTS, ERR_TOO_MANY); // Unregister everything first _unregisterAll(); // Loop over all contracts to register for (uint256 registrationIndex = 0; registrationIndex < registrationsLength; registrationIndex++) { // Address cannot be zero require(address(_registrations[registrationIndex].daemonizedContract) != address(0), ERR_ADDRESS_ZERO); uint256 daemonizeContractsLength = daemonizeContracts.length; // Make sure no dups...yes, inefficient. Registration should not be done often. for (uint256 i = 0; i < daemonizeContractsLength; i++) { require(_registrations[registrationIndex].daemonizedContract != daemonizeContracts[i], ERR_DUPLICATE_ADDRESS); // already registered } // Store off the registered contract to daemonize, in the order presented. daemonizeContracts.push(_registrations[registrationIndex].daemonizedContract); // Record the gas limit for the contract. gasLimits[_registrations[registrationIndex].daemonizedContract] = _registrations[registrationIndex].gasLimit; // Clear any blocks being held off for the given contract, if any. Contracts may be re-presented // if only order is being modified, for example. blockHoldoffsRemaining[_registrations[registrationIndex].daemonizedContract] = 0; emit RegistrationUpdated (_registrations[registrationIndex].daemonizedContract, true); } } /** * @notice Unregister all contracts from being polled by the daemon process. */ function _unregisterAll() private { uint256 len = daemonizeContracts.length; for (uint256 i = 0; i < len; i++) { IFlareDaemonize daemonizedContract = daemonizeContracts[daemonizeContracts.length - 1]; daemonizeContracts.pop(); emit RegistrationUpdated (daemonizedContract, false); } } /** * @notice Net totals to obtain the expected balance of the contract. */ function getExpectedBalance() private view returns(uint256 _balanceExpectedWei) { _balanceExpectedWei = totalMintingReceivedWei. sub(totalMintingWithdrawnWei). add(totalSelfDestructReceivedWei); } /** * @notice Net total received from total requested. */ function getPendingMintRequest() private view returns(uint256 _mintRequestPendingWei) { _mintRequestPendingWei = totalMintingRequestedWei.sub(totalMintingReceivedWei); } function _getNextMintRequestAllowedTs() internal view returns (uint256) { return (lastMintRequestTs + MAX_MINTING_FREQUENCY_SEC); } function truncateString(string memory _str, uint256 _maxlength) private pure returns (string memory) { bytes memory strbytes = bytes(_str); if (strbytes.length <= _maxlength) { return _str; } bytes memory result = new bytes(_maxlength); for (uint256 i = 0; i < _maxlength; i++) { result[i] = strbytes[i]; } return string(result); } }
./contracts/genesis/interface/IFlareDaemonize.sol
// SPDX-License-Identifier: MIT pragma solidity >=0.7.6 <0.9; /// Any contracts that want to recieve a trigger from Flare daemon should /// implement IFlareDaemonize interface IFlareDaemonize { /// Implement this function for recieving a trigger from FlareDaemon. function daemonize() external returns (bool); /// This function will be called after an error is caught in daemonize(). /// It will switch the contract to a simpler fallback mode, which hopefully works when full mode doesn't. /// Not every contract needs to support fallback mode (FtsoManager does), so this method may be empty. /// Switching back to normal mode is left to the contract (typically a governed method call). /// This function may be called due to low-gas error, so it shouldn't use more than ~30.000 gas. /// @return true if switched to fallback mode, false if already in fallback mode or if falback not supported function switchToFallbackMode() external returns (bool); /// Implement this function for updating daemonized contracts through AddressUpdater. function getContractName() external view returns (string memory); }
./contracts/genesis/interface/IInflationGenesis.sol
// SPDX-License-Identifier: MIT pragma solidity >=0.7.6 <0.9; interface IInflationGenesis { /** * @notice Receive newly minted native tokens from the FlareDaemon. * @dev Assume that the amount received will be >= last topup requested across all services. * If there is not enough balance sent to cover the topup request, expect library method will revert. * Also assume that any balance received greater than the topup request calculated * came from self-destructor sending a balance to this contract. */ function receiveMinting() external payable; }
./contracts/governance/implementation/Governed.sol
// SPDX-License-Identifier: MIT pragma solidity 0.7.6; import { GovernedBase } from "./GovernedBase.sol"; /** * @title Governed * @dev For deployed, governed contracts, enforce a non-zero address at create time. **/ contract Governed is GovernedBase { constructor(address _governance) GovernedBase(_governance) { require(_governance != address(0), "_governance zero"); } }
./contracts/governance/implementation/GovernedAtGenesis.sol
// SPDX-License-Identifier: MIT pragma solidity 0.7.6; import "./GovernedBase.sol"; /** * @title Governed At Genesis * @dev This contract enforces a fixed governance address when the constructor * is not executed on a contract (for instance when directly loaded to the genesis block). * This is required to fix governance on a contract when the network starts, at such point * where theoretically no accounts yet exist, and leaving it ungoverned could result in a race * to claim governance by an unauthorized address. **/ contract GovernedAtGenesis is GovernedBase { constructor(address _governance) GovernedBase(_governance) { } /** * @notice Set governance to a fixed address when constructor is not called. **/ function initialiseFixedAddress() public virtual returns (address) { address governanceAddress = address(0xfffEc6C83c8BF5c3F4AE0cCF8c45CE20E4560BD7); super.initialise(governanceAddress); return governanceAddress; } /** * @notice Disallow initialise to be called * @param _governance The governance address for initial claiming **/ // solhint-disable-next-line no-unused-vars function initialise(address _governance) public override pure { assert(false); } }
./contracts/governance/implementation/GovernedBase.sol
// 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/inflation/implementation/Inflation.sol
// SPDX-License-Identifier: MIT pragma solidity 0.7.6; pragma abicoder v2; import "../../genesis/implementation/FlareDaemon.sol"; import "../../genesis/interface/IFlareDaemonize.sol"; import "../../genesis/interface/IInflationGenesis.sol"; import "../../utils/implementation/GovernedAndFlareDaemonized.sol"; import "../../addressUpdater/implementation/AddressUpdatable.sol"; import "../lib/InflationAnnum.sol"; import "../lib/InflationAnnums.sol"; import "../interface/IIInflationAllocation.sol"; import "../lib/RewardService.sol"; import "../interface/IISupply.sol"; import "@openzeppelin/contracts/math/SafeMath.sol"; import "../../utils/implementation/SafePct.sol"; /** * @title Inflation * @notice A contract to manage the process of recognizing, authorizing, minting, and funding * native tokens for Flare services that are rewardable by inflation. * @dev Please see docs/specs/Inflation.md to better understand this terminology. **/ contract Inflation is IInflationGenesis, GovernedAndFlareDaemonized, IFlareDaemonize, AddressUpdatable { using InflationAnnums for InflationAnnums.InflationAnnumsState; using SafeMath for uint256; using SafePct for uint256; // Composable contracts IIInflationAllocation public inflationAllocation; IISupply public supply; // The annums InflationAnnums.InflationAnnumsState private inflationAnnums; // Inflation annum data // Instance vars uint256 public lastAuthorizationTs; // The last time inflation was authorized mapping(IIInflationReceiver => TopupConfiguration) internal topupConfigurations; // A topup configuration for a contract // receiving inflation. uint256 public totalSelfDestructReceivedWei; uint256 immutable public rewardEpochStartTs; // Do not start inflation annums before this uint256 public rewardEpochStartedTs; // When the first reward epoch was started // Constants string internal constant ERR_IS_ZERO = "address is 0"; string internal constant ERR_OUT_OF_BALANCE = "out of balance"; string internal constant ERR_TOPUP_LOW = "topup low"; string internal constant ERR_GET_ANNUAL_PERCENT = "unknown error. getAnnualPercentageBips"; string internal constant ERR_SUPPLY_UPDATE = "unknown error. updateAuthorizedInflationAndCirculatingSupply"; string internal constant ERR_REQUEST_MINT = "unknown error. requestMinting"; uint256 internal constant BIPS100 = 1e4; // 100% in basis points uint256 internal constant DEFAULT_TOPUP_FACTOR_X100 = 120; // DO NOT UPDATE - this affects supply contract, which is expected to be updated once a day uint256 internal constant AUTHORIZE_TIME_FRAME_SEC = 1 days; event InflationAuthorized(uint256 amountWei); event MintingReceived(uint256 amountWei, uint256 selfDestructAmountWei); event TopupRequested(uint256 amountWei); event InflationAllocationSet(IIInflationAllocation inflationAllocation); event RewardServiceTopupComputed(IIInflationReceiver inflationReceiver, uint256 amountWei); event RewardServiceDailyAuthorizedInflationComputed(IIInflationReceiver inflationReceiver, uint256 amountWei); event RewardServiceTopupRequestReceived(IIInflationReceiver inflationReceiver, uint256 amountWei); event SupplySet(IISupply oldSupply, IISupply newSupply); event TopupConfigurationSet(TopupConfiguration topupConfiguration); event NewAnnumInitialized( uint256 startTimeStamp, uint256 endTimeStamp, uint256 inflatableSupplyWei, uint256 recognizedInflationWei, uint256 totalAuthorizedInflationWei, uint256 totalInflationTopupRequestedWei, uint256 totalInflationTopupReceivedWei, uint256 totalInflationTopupWithdrawnWei ); /** * @dev This modifier ensures that this contract's balance matches the expected balance. */ modifier mustBalance { _; require (getExpectedBalance() == address(this).balance, ERR_OUT_OF_BALANCE); } modifier notZero(address _address) { require(_address != address(0), ERR_IS_ZERO); _; } constructor ( address _governance, FlareDaemon _flareDaemon, address _addressUpdater, uint256 _rewardEpochStartTs ) GovernedAndFlareDaemonized(_governance, _flareDaemon) AddressUpdatable(_addressUpdater) { rewardEpochStartTs = _rewardEpochStartTs; } /** * @notice Get a tuple of totals across inflation annums. * @return _totalAuthorizedInflationWei Total inflation authorized to be mintable * @return _totalInflationTopupRequestedWei Total inflation requested to be topped up for rewarding * @return _totalInflationTopupReceivedWei Total inflation received for funding reward services * @return _totalInflationTopupWithdrawnWei Total inflation used for funding reward services * @return _totalRecognizedInflationWei Total inflation recognized for rewarding * @return _totalSelfDestructReceivedWei Total balance received as a self-destruct recipient */ function getTotals() external view returns ( uint256 _totalAuthorizedInflationWei, uint256 _totalInflationTopupRequestedWei, uint256 _totalInflationTopupReceivedWei, uint256 _totalInflationTopupWithdrawnWei, uint256 _totalRecognizedInflationWei, uint256 _totalSelfDestructReceivedWei ) { _totalAuthorizedInflationWei = inflationAnnums.totalAuthorizedInflationWei; _totalInflationTopupRequestedWei = inflationAnnums.totalInflationTopupRequestedWei; _totalInflationTopupReceivedWei = inflationAnnums.totalInflationTopupReceivedWei; _totalInflationTopupWithdrawnWei = inflationAnnums.totalInflationTopupWithdrawnWei; _totalRecognizedInflationWei = inflationAnnums.totalRecognizedInflationWei; _totalSelfDestructReceivedWei = totalSelfDestructReceivedWei; } /** * @notice Given an index, return the annum at that index. * @param _index The index of the annum to fetch. * @return The inflation annum state. * @dev Expect library to revert if index not found. */ function getAnnum(uint256 _index) external view returns(InflationAnnum.InflationAnnumState memory) { return inflationAnnums.getAnnum(_index); } /** * @notice Return the current annum. * @return The inflation annum state of the current annum. * @dev Expect library to revert if there is no current annum. */ function getCurrentAnnum() external view returns(InflationAnnum.InflationAnnumState memory) { return inflationAnnums.getCurrentAnnum(); } /** * @notice Receive newly minted native tokens from the FlareDaemon. * @dev Assume that the amount received will be >= last topup requested across all services. * If there is not enough balance sent to cover the topup request, expect library method will revert. * Also assume that any balance received greater than the topup request calculated * came from self-destructor sending a balance to this contract. */ function receiveMinting() external override payable onlyFlareDaemon mustBalance { uint256 amountPostedWei = inflationAnnums.receiveTopupRequest(); // Assume that if we received (or already have) more than we posted, // it must be amounts sent from a contract self-destruct // recipient in this block. uint256 prevBalance = getExpectedBalance(); uint256 selfDestructProceeds = address(this).balance.sub(prevBalance); if (selfDestructProceeds > 0) { totalSelfDestructReceivedWei = totalSelfDestructReceivedWei.add(selfDestructProceeds); } emit MintingReceived(amountPostedWei, selfDestructProceeds); } /** * @notice Set the topup configuration for a reward service. * @param _inflationReceiver The reward service to receive the inflation funds for distribution. * @param _topupType The type to signal how the topup amounts are to be calculated. * FACTOROFDAILYAUTHORIZED = Use a factor of last daily authorized to set a * target balance for a reward service to maintain as a reserve for claiming. * ALLAUTHORIZED = Mint enough native tokens to topup reward service contract to hold * all authorized but unrequested rewards. * @param _topupFactorX100 If _topupType == FACTOROFDAILYAUTHORIZED, then this factor (times 100) * is multipled by last daily authorized inflation to obtain the * maximum balance that a reward service can hold at any given time. If it holds less, * then this max amount is used to compute the mint request topup required to * bring the reward service contract native token balance up to that amount. * @dev Topup factor, if _topupType == FACTOROFDAILYAUTHORIZED, must be greater than 100. */ function setTopupConfiguration( IIInflationReceiver _inflationReceiver, TopupType _topupType, uint256 _topupFactorX100 ) external notZero(address(_inflationReceiver)) onlyGovernance { if (_topupType == TopupType.FACTOROFDAILYAUTHORIZED) { require(_topupFactorX100 > 100, ERR_TOPUP_LOW); } TopupConfiguration storage topupConfiguration = topupConfigurations[_inflationReceiver]; topupConfiguration.topupType = _topupType; topupConfiguration.topupFactorX100 = _topupFactorX100; topupConfiguration.configured = true; emit TopupConfigurationSet(topupConfiguration); } /** * @notice Given an inflation receiver, get the topup configuration. * @param _inflationReceiver The reward service. * @return _topupConfiguration The configurartion of how the topup requests are calculated for a given * reward service. */ function getTopupConfiguration( IIInflationReceiver _inflationReceiver ) external notZero(address(_inflationReceiver)) returns(TopupConfiguration memory _topupConfiguration) { TopupConfiguration storage topupConfiguration = topupConfigurations[_inflationReceiver]; if (!topupConfiguration.configured) { topupConfiguration.topupType = TopupType.FACTOROFDAILYAUTHORIZED; topupConfiguration.topupFactorX100 = DEFAULT_TOPUP_FACTOR_X100; topupConfiguration.configured = true; } _topupConfiguration.topupType = topupConfiguration.topupType; _topupConfiguration.topupFactorX100 = topupConfiguration.topupFactorX100; _topupConfiguration.configured = topupConfiguration.configured; } /** * @notice Pulsed by the FlareDaemon to trigger timing-based events for the inflation process. * @dev There are two events: * 1) an annual event to recognize inflation for a new annum * 2) a daily event to: * a) authorize mintable inflation for rewarding * b) request minting of enough native tokens to topup reward services for claiming reserves */ function daemonize() external virtual override notZero(address(supply)) onlyFlareDaemon returns(bool) { // If inflation rewarding not started yet, blow off processing until it does. if (block.timestamp < rewardEpochStartTs) { return true; } // If inflation rewarding started and we have not updated when it started, do so now. if (rewardEpochStartedTs == 0) { rewardEpochStartedTs = block.timestamp; } // Is it time to recognize an initial inflation annum? if (inflationAnnums.getCount() == 0) { _initNewAnnum(block.timestamp); } else { uint256 currentAnnumEndTimeStamp = inflationAnnums.getCurrentAnnum().endTimeStamp; // Is it time to recognize a new inflation annum? if (block.timestamp > currentAnnumEndTimeStamp) { _initNewAnnum(currentAnnumEndTimeStamp.add(1)); } } // Is it time to authorize new inflation? Do it daily. if (lastAuthorizationTs.add(AUTHORIZE_TIME_FRAME_SEC) <= block.timestamp) { // Update time we last authorized. lastAuthorizationTs = block.timestamp; // Authorize inflation for current sharing percentges. uint256 amountAuthorizedWei = inflationAnnums.authorizeDailyInflation( block.timestamp, inflationAllocation.getSharingPercentages() ); emit InflationAuthorized(amountAuthorizedWei); // Call supply contract to keep inflatable balance and circulating supply updated. try supply.updateAuthorizedInflationAndCirculatingSupply(amountAuthorizedWei) { } catch Error(string memory message) { revert(message); } catch { revert(ERR_SUPPLY_UPDATE); } // Time to compute topup amount for inflation receivers. uint256 topupRequestWei = inflationAnnums.computeTopupRequest(this); emit TopupRequested(topupRequestWei); // Send mint request to the daemon. try flareDaemon.requestMinting(topupRequestWei) { } catch Error(string memory message) { revert(message); } catch { revert(ERR_REQUEST_MINT); } } return true; } function switchToFallbackMode() external view override onlyFlareDaemon returns (bool) { // do nothing - there is no fallback mode in Inflation return false; } /** * @notice Implement this function for updating daemonized contracts through AddressUpdater. */ function getContractName() external pure override returns (string memory) { return "Inflation"; } /** * @notice Returns next expected inflation topup time stamp which is also inflation authorization time. * The returned time from this API is actually the time of the block in which the topup is requested. * The Actual topup will take place in the next block. * Expected diff is up to a few seconds (max is less then a minute). */ function getNextExpectedTopupTs () external view returns (uint256 _nextTopupTs) { _nextTopupTs = lastAuthorizationTs.add(AUTHORIZE_TIME_FRAME_SEC); } /** * @notice Implementation of the AddressUpdatable abstract method - updates supply and inflation allocation. * @notice Set a reference to a provider of sharing percentages by inflation receiver. * @dev Assume that sharing percentages sum to 100% if at least one exists, but * if no sharing percentages are defined, then no inflation will be authorized. * @notice Set a reference to a provider of the annual inflation percentage. * @dev Assume that referencing contract has reasonablness limitations on percentages. */ function _updateContractAddresses( bytes32[] memory _contractNameHashes, address[] memory _contractAddresses ) internal override { IISupply _supply = IISupply(_getContractAddress(_contractNameHashes, _contractAddresses, "Supply")); emit SupplySet(supply, _supply); supply = _supply; inflationAllocation = IIInflationAllocation( _getContractAddress(_contractNameHashes, _contractAddresses, "InflationAllocation")); emit InflationAllocationSet(inflationAllocation); } function _initNewAnnum(uint256 startTs) internal { supply.updateCirculatingSupply(); uint256 inflatableSupply = supply.getInflatableBalance(); try inflationAllocation.getAnnualPercentageBips() returns(uint256 annualPercentBips) { inflationAnnums.initializeNewAnnum(startTs, inflatableSupply, annualPercentBips); } catch Error(string memory message) { revert(message); } catch { revert(ERR_GET_ANNUAL_PERCENT); } InflationAnnum.InflationAnnumState memory inflationAnnum = inflationAnnums.getCurrentAnnum(); emit NewAnnumInitialized( inflationAnnum.startTimeStamp, inflationAnnum.endTimeStamp, inflatableSupply, inflationAnnum.recognizedInflationWei, inflationAnnum.rewardServices.totalAuthorizedInflationWei, inflationAnnum.rewardServices.totalInflationTopupRequestedWei, inflationAnnum.rewardServices.totalInflationTopupReceivedWei, inflationAnnum.rewardServices.totalInflationTopupWithdrawnWei ); } /** * @notice Compute the expected balance of this contract. * @param _balanceExpectedWei The computed balance expected. */ function getExpectedBalance() private view returns(uint256 _balanceExpectedWei) { return inflationAnnums.totalInflationTopupReceivedWei .sub(inflationAnnums.totalInflationTopupWithdrawnWei) .add(totalSelfDestructReceivedWei); } }
./contracts/inflation/interface/IIInflationAllocation.sol
// SPDX-License-Identifier: MIT pragma solidity >=0.7.6 <0.9; pragma abicoder v2; import "./IIInflationPercentageProvider.sol"; import "./IIInflationSharingPercentageProvider.sol"; interface IIInflationAllocation is IIInflationPercentageProvider, IIInflationSharingPercentageProvider { }
./contracts/inflation/interface/IIInflationPercentageProvider.sol
// SPDX-License-Identifier: MIT pragma solidity >=0.7.6 <0.9; interface IIInflationPercentageProvider { /** * Return the annual inflation rate in bips. */ function getAnnualPercentageBips() external returns(uint256); }
./contracts/inflation/interface/IIInflationReceiver.sol
// SPDX-License-Identifier: MIT pragma solidity >=0.7.6 <0.9; interface IIInflationReceiver { /** * Notify the receiver that it is entitled to receive `_toAuthorizeWei` inflation amount. * @param _toAuthorizeWei the amount of inflation that can be awarded in the coming day */ function setDailyAuthorizedInflation(uint256 _toAuthorizeWei) external; /** * Receive native tokens from inflation. */ function receiveInflation() external payable; /** * Inflation receivers have a reference to the Inflation contract. */ function getInflationAddress() external returns(address); /** * Implement this function for updating inflation receiver contracts through AddressUpdater. */ function getContractName() external view returns (string memory); }
./contracts/inflation/interface/IIInflationSharingPercentageProvider.sol
// SPDX-License-Identifier: MIT pragma solidity >=0.7.6 <0.9; pragma abicoder v2; import "./IIInflationReceiver.sol"; struct SharingPercentage { IIInflationReceiver inflationReceiver; uint256 percentBips; } interface IIInflationSharingPercentageProvider { /** * Return the shared percentage per inflation receiver. * @dev Assumption is that implementer edited that percents sum to 100 pct and * that receiver addresses are valid. */ function getSharingPercentages() external returns(SharingPercentage[] memory); }
./contracts/inflation/interface/IISupply.sol
// SPDX-License-Identifier: MIT pragma solidity >=0.7.6 <0.9; interface IISupply { /** * @notice Updates circulating supply * @dev Also updates the burn address amount */ function updateCirculatingSupply() external; /** * @notice Updates authorized inflation and circulating supply - emits event if error * @param _inflationAuthorizedWei Authorized inflation * @dev Also updates the burn address amount */ function updateAuthorizedInflationAndCirculatingSupply(uint256 _inflationAuthorizedWei) external; /** * @notice Get approximate circulating supply for given block number from cache - only past block * @param _blockNumber Block number * @return _circulatingSupplyWei Return approximate circulating supply for last known block <= _blockNumber */ function getCirculatingSupplyAtCached(uint256 _blockNumber) external returns(uint256 _circulatingSupplyWei); /** * @notice Get approximate circulating supply for given block number * @param _blockNumber Block number * @return _circulatingSupplyWei Return approximate circulating supply for last known block <= _blockNumber */ function getCirculatingSupplyAt(uint256 _blockNumber) external view returns(uint256 _circulatingSupplyWei); /** * @notice Get total inflatable balance (initial genesis amount + total authorized inflation) * @return _inflatableBalanceWei Return inflatable balance */ function getInflatableBalance() external view returns(uint256 _inflatableBalanceWei); }
./contracts/inflation/lib/InflationAnnum.sol
// SPDX-License-Identifier: MIT pragma solidity 0.7.6; pragma abicoder v2; import "../../utils/implementation/DateTimeLibrary.sol"; import "./RewardServices.sol"; import "@openzeppelin/contracts/math/Math.sol"; import "@openzeppelin/contracts/utils/SafeCast.sol"; import "@openzeppelin/contracts/math/SafeMath.sol"; import "../../utils/implementation/SafePct.sol"; /** * @title Inflation Annum library * @notice A library to manage an inflation annum. **/ library InflationAnnum { using BokkyPooBahsDateTimeLibrary for uint256; using InflationAnnum for InflationAnnum.InflationAnnumState; using SafeCast for uint256; using SafeMath for uint256; using SafePct for uint256; /** * @dev `InflationAnnumState` is state structure used by this library to manage * an inflation annum. */ struct InflationAnnumState { uint256 recognizedInflationWei; uint256 startTimeStamp; uint256 endTimeStamp; RewardServices.RewardServicesState rewardServices; } uint256 internal constant BIPS100 = 1e4; // 100% in basis points uint256 internal constant MAX_ANNUAL_INFLATION = 5000000000 ether; /** * @notice Helper function to compute recognized inflation. * @param _inflatableBalance The balance used to recognize inflation. * @param _annualInflationPercentageBips The annual percentage used to recognize inflation. * @return The computed recognized inflation. */ function _computeRecognizedInflationWei( uint256 _inflatableBalance, uint256 _annualInflationPercentageBips ) internal pure returns(uint256) { return Math.min( _inflatableBalance.mulDiv(_annualInflationPercentageBips, 12 * BIPS100), MAX_ANNUAL_INFLATION.div(12) ); // monthly inflation } /** * @notice Helper function to compute the number of days remaining in an annum. * @param _atTimeStamp Compute the number of days for the annum at this time stamp. * @return The number of days computed. * @dev If _atTimeStamp is after the end of the annum, 0 days will be returned. */ function _computeDaysRemainingInAnnum( InflationAnnumState storage _self, uint256 _atTimeStamp ) internal view returns(uint256) { uint256 endTimeStamp = _self.endTimeStamp; if (_atTimeStamp > endTimeStamp) { return 0; } else { return _atTimeStamp.diffDays(endTimeStamp); } } /** * @notice Given a start time stamp, compute the end time stamp for an annum. * @param _startTimeStamp The start time stamp for an annum. * @return The end time stamp for the annum. */ function _getAnnumEndsTs(uint256 _startTimeStamp) internal pure returns (uint256) { // This should cover passing through Feb 29 return _startTimeStamp.addDays(30).subSeconds(1); } /** * @notice Compute the number of periods remaining within an annum. * @param _atTimeStamp Compute periods remaining at this time stamp. * @return The number of periods remaining. * @dev The number of periods must include the current day. */ function getPeriodsRemaining( InflationAnnumState storage _self, uint256 _atTimeStamp ) internal view returns(uint256) { assert(_atTimeStamp <= _self.endTimeStamp); // Add 1 to the periods remaining because the difference between days does not count the current day. return _computeDaysRemainingInAnnum(_self, _atTimeStamp).add(1); } /** * @notice Initialize a new annum data structure. * @param _startTimeStamp The start time stamp of the new annum. * @param _inflatableBalanceWei The inflatable balance used to calculate recognized inflation for the new annum. * @param _annualInflationPercentageBips The annual inflation percentage in bips to calc recognized inflation. * @dev A newly created InflationAnnumState is expected to exist. */ function initialize( InflationAnnumState storage _self, uint256 _startTimeStamp, uint256 _inflatableBalanceWei, uint256 _annualInflationPercentageBips ) internal { _self.startTimeStamp = _startTimeStamp; _self.recognizedInflationWei = _computeRecognizedInflationWei( _inflatableBalanceWei, _annualInflationPercentageBips); _self.endTimeStamp = _getAnnumEndsTs(_startTimeStamp); } }
./contracts/inflation/lib/InflationAnnums.sol
// SPDX-License-Identifier: MIT pragma solidity 0.7.6; pragma abicoder v2; import "../implementation/Inflation.sol"; import "./InflationAnnum.sol"; import "./RewardServices.sol"; import "@openzeppelin/contracts/math/SafeMath.sol"; import "../../utils/implementation/SafePct.sol"; import "../interface/IIInflationSharingPercentageProvider.sol"; /** * @title Inflation Annums library * @notice A library to manage a collection of inflation annum and associated totals. * @dev Operations such as authorizing daily inflation are dispatched from this collection * library because the result of the authorization is added to the total authorized across * all annums, which is a concern of this library and not the concern of a given annum, nor the caller. **/ library InflationAnnums { using InflationAnnum for InflationAnnum.InflationAnnumState; using RewardServices for RewardServices.RewardServicesState; using SafeMath for uint256; using SafePct for uint256; /** * @dev `InflationAnnumsState` is state structure used by this library to manage * a collection of inflation annums and associated totals. */ struct InflationAnnumsState { // Collection of annums InflationAnnum.InflationAnnumState[] inflationAnnums; uint256 currentAnnum; // Balances uint256 totalRecognizedInflationWei; uint256 totalAuthorizedInflationWei; uint256 totalInflationTopupRequestedWei; uint256 totalInflationTopupReceivedWei; uint256 totalInflationTopupWithdrawnWei; } string internal constant ERR_NO_ANNUM = "no annum"; string internal constant ERR_TOO_EARLY = "too early"; /** * @notice Dispatch inflation authorization to be performed across all reward services according to their * sharing percentage for the current annum, and then maintain sum total of inflation * authorized across all annums. * @param _atTimeStamp The timestamp at which the number of daily periods remaining in the current * annum will be calculated. * @param _sharingPercentages An array of the sharing percentages by inflation receiver used to * allocate authorized inflation. * @return _amountAuthorizedWei The amount of inflation authorized for this authorization cycle. * @dev Invariant: total inflation authorized cannot be greater than total inflation recognized. */ function authorizeDailyInflation( InflationAnnumsState storage _self, uint256 _atTimeStamp, SharingPercentage[] memory _sharingPercentages ) internal returns(uint256 _amountAuthorizedWei) { // Get the current annum InflationAnnum.InflationAnnumState storage currentAnnum = getCurrentAnnum(_self); // Authorize daily inflation for the current annum, across reward services, given // sharing percentages. _amountAuthorizedWei = currentAnnum.rewardServices.authorizeDailyInflation( _self.totalRecognizedInflationWei, _self.totalAuthorizedInflationWei, currentAnnum.getPeriodsRemaining(_atTimeStamp), _sharingPercentages); // Accumulate total authorized inflation across all annums _self.totalAuthorizedInflationWei = _self.totalAuthorizedInflationWei.add(_amountAuthorizedWei); // Make sure that total authorized never exceeds total recognized assert(_self.totalAuthorizedInflationWei <= _self.totalRecognizedInflationWei); } /** * @notice Dispatch topup request calculations across reward services and sum up total mint request made * to fund topup of reward services. * @param _inflation The Inflation contract containing the topup confguration of each reward service. * @return _topupRequestWei The amount of native token requested to be minted across reward services for * this cycle. * @dev Invariant: total inflation topup requested cannot exceed total inflation authorized */ function computeTopupRequest( InflationAnnumsState storage _self, Inflation _inflation ) internal returns(uint256 _topupRequestWei) { // Get the current annum InflationAnnum.InflationAnnumState storage currentAnnum = getCurrentAnnum(_self); // Compute the topup _topupRequestWei = currentAnnum.rewardServices.computeTopupRequest(_inflation); // Sum the topup request total across annums _self.totalInflationTopupRequestedWei = _self.totalInflationTopupRequestedWei.add(_topupRequestWei); // Make sure that total topup requested can never exceed inflation authorized assert(_self.totalInflationTopupRequestedWei <= _self.totalAuthorizedInflationWei); } /** * @notice Receive minted native tokens (and fund) to satisfy reward services topup requests. * @return _amountPostedWei The native tokens posted (funded) to reward service contracts. * @dev Invariants: * 1) Native tokens topup received cannot exceed native tokens topup requested * 2) Native tokens topup withdrawn for funding cannot exceed native tokens topup received */ function receiveTopupRequest( InflationAnnumsState storage _self ) internal returns(uint256 _amountPostedWei) { // Get the current annum InflationAnnum.InflationAnnumState storage currentAnnum = getCurrentAnnum(_self); // Receive minting of topup request. Post to received and withdrawn buckets for each reward service. _amountPostedWei = currentAnnum.rewardServices.receiveTopupRequest(); // Post the amount of native tokens received into the Inflation contract _self.totalInflationTopupReceivedWei = _self.totalInflationTopupReceivedWei.add(_amountPostedWei); // Received should never be more than requested assert(_self.totalInflationTopupReceivedWei <= _self.totalInflationTopupRequestedWei); // Post amount withdrawn and transferred to reward service contracts _self.totalInflationTopupWithdrawnWei = _self.totalInflationTopupWithdrawnWei.add(_amountPostedWei); // Withdrawn should never be more than received assert(_self.totalInflationTopupWithdrawnWei <= _self.totalInflationTopupReceivedWei); } /** * @notice Get the number of inflation annums. * @return The count. */ function getCount(InflationAnnumsState storage _self) internal view returns(uint256) { return _self.inflationAnnums.length; } /** * @notice Given an index, return a given inflation annum data. * @param _index The index of the annum to fetch. * @return _inflationAnnum Returns InflationAnnum.InflationAnnumState found at _index. * @dev Will revert if index not found. */ function getAnnum( InflationAnnumsState storage _self, uint256 _index ) internal view returns (InflationAnnum.InflationAnnumState storage _inflationAnnum) { require(_index < getCount(_self), ERR_NO_ANNUM); _inflationAnnum = _self.inflationAnnums[_index]; } /** * @notice Return inflation annum data for the current annum. * @return _inflationAnnum Returns InflationAnnum.InflationAnnumState for the current annum. * @dev Will revert if no current annum. */ function getCurrentAnnum( InflationAnnumsState storage _self ) internal view returns (InflationAnnum.InflationAnnumState storage _inflationAnnum) { require(getCount(_self) > 0, ERR_NO_ANNUM); _inflationAnnum = _self.inflationAnnums[_self.currentAnnum]; } /** * @notice Initialize a new annum, add it to the annum collection, maintian running total * of recognized inflation resulting from new annum, and set current annum pointer. * @param _startTimeStamp The timestamp to start the annum. * @param _inflatableBalance The balance to use when recognizing inflation for the annum. * @param _annualInflationPercentageBips The inflation percentage in bips to use when recognizing inflation. */ function initializeNewAnnum( InflationAnnumsState storage _self, uint256 _startTimeStamp, uint256 _inflatableBalance, uint256 _annualInflationPercentageBips ) internal { // Start time cannot be before last annum ends if (getCount(_self) > 0) { require(_startTimeStamp > getCurrentAnnum(_self).endTimeStamp, ERR_TOO_EARLY); } // Create an empty annum InflationAnnum.InflationAnnumState storage inflationAnnum = _self.inflationAnnums.push(); // Initialize it with newly passed in annum info inflationAnnum.initialize(_startTimeStamp, _inflatableBalance, _annualInflationPercentageBips); // Accumulate total recognized inflation across annums _self.totalRecognizedInflationWei = _self.totalRecognizedInflationWei.add(inflationAnnum.recognizedInflationWei); // Reposition index pointing to current annum if (_self.inflationAnnums.length > 1) { _self.currentAnnum = _self.currentAnnum.add(1); } } }
./contracts/inflation/lib/RewardService.sol
// SPDX-License-Identifier: MIT pragma solidity 0.7.6; pragma abicoder v2; import "../interface/IIInflationReceiver.sol"; import "@openzeppelin/contracts/math/SafeMath.sol"; import "../../utils/implementation/SafePct.sol"; enum TopupType{ FACTOROFDAILYAUTHORIZED, ALLAUTHORIZED } /** * @notice A struct that defines how mint request topups will be computed for a reward service. * @param topupType The type to signal how the topup amounts are to be calculated. * FACTOROFDAILYAUTHORIZED = Use a factor of last daily authorized to set a * target balance for a reward service to maintain as a reserve for claiming. * ALLAUTHORIZED = Mint enough native tokens to topup reward service contract to hold * all authorized but unrequested rewards. * @param topupFactorX100 If _topupType == FACTOROFDAILYAUTHORIZED, then this factor (times 100) * is multipled by last daily authorized inflation to obtain the * maximum balance that a reward service can hold at any given time. If it holds less, * then this max amount is used to compute the mint request topup required to * bring the reward service contract native token balance up to that amount. */ struct TopupConfiguration { TopupType topupType; // Topup algo type uint256 topupFactorX100; // Topup factor, times 100, if applicable for type bool configured; // Flag to indicate whether initially configured } /** * @title Reward Service library * @notice A library representing a reward service. A reward service consists of a reward contract and * associated inflation-related totals. When a topup configuration is applied, a reward service can * also make minting requests to topup native tokens within a reward contract. * @dev A reward service exists within the context of a given inflation annum. **/ library RewardService { using SafeMath for uint256; using SafePct for uint256; /** * @dev `RewardServiceState` is state structure used by this library to manage * an a reward service tracking authorize inflation. */ struct RewardServiceState { IIInflationReceiver inflationReceiver; // The target rewarding contract uint256 authorizedInflationWei; // Total authorized inflation for this reward service uint256 lastDailyAuthorizedInflationWei; // Last daily authorized inflation amount uint256 inflationTopupRequestedWei; // Total inflation topup requested to be minted uint256 inflationTopupReceivedWei; // Total inflation minting received uint256 inflationTopupWithdrawnWei; // Total inflation minting sent to rewarding service contract } event RewardServiceTopupComputed(IIInflationReceiver inflationReceiver, uint256 amountWei); /** * @notice Maintain authorized inflation total for service. * @param _amountWei Amount to add. */ function addAuthorizedInflation(RewardServiceState storage _self, uint256 _amountWei) internal { _self.authorizedInflationWei = _self.authorizedInflationWei.add(_amountWei); _self.lastDailyAuthorizedInflationWei = _amountWei; } /** * @notice Maintain topup native tokens received total for service. * @param _amountWei Amount to add. */ function addTopupReceived(RewardServiceState storage _self, uint256 _amountWei) internal { _self.inflationTopupReceivedWei = _self.inflationTopupReceivedWei.add(_amountWei); } /** * @notice Maintain topup native tokens withdrawn (funded) total for service. * @param _amountWei Amount to add. */ function addTopupWithdrawn(RewardServiceState storage _self, uint256 _amountWei) internal { _self.inflationTopupWithdrawnWei = _self.inflationTopupWithdrawnWei.add(_amountWei); } /** * @notice Given a topup configuration, compute the topup request for the reward contract associated * to the service. * @param _topupConfiguration The topup configuration defining the algo used to compute the topup amount. * @return _topupRequestWei The topup request amount computed. */ function computeTopupRequest( RewardServiceState storage _self, TopupConfiguration memory _topupConfiguration ) internal returns (uint256 _topupRequestWei) { // Get the balance of the inflation receiver uint256 inflationReceiverBalanceWei = address(_self.inflationReceiver).balance; if (_topupConfiguration.topupType == TopupType.FACTOROFDAILYAUTHORIZED) { // Compute a topup request based purely on the given factor, the last daily authorization, and // the balance that is sitting in the reward service contract. uint256 requestedBalanceWei = _self.lastDailyAuthorizedInflationWei .mulDiv(_topupConfiguration.topupFactorX100, 100); uint256 rawTopupRequestWei = 0; // If current balance is less then requested, request some more. if (requestedBalanceWei > inflationReceiverBalanceWei) { rawTopupRequestWei = requestedBalanceWei.sub(inflationReceiverBalanceWei); } // Compute what is already pending to be topped up uint256 topupPendingWei = getPendingTopup(_self); // If what is pending to topup is greater than the raw request, request no more. if (topupPendingWei > rawTopupRequestWei) { _topupRequestWei = 0; } else { // Back out any request that is already pending _topupRequestWei = rawTopupRequestWei.sub(topupPendingWei); } // And finally, in any case, topup requested cannot be more than the net of // authorized, pending, and received uint256 maxTopupRequestWei = _self.authorizedInflationWei .sub(topupPendingWei) .sub(_self.inflationTopupReceivedWei); if (_topupRequestWei > maxTopupRequestWei) { _topupRequestWei = maxTopupRequestWei; } } else if (_topupConfiguration.topupType == TopupType.ALLAUTHORIZED) { _topupRequestWei = _self.authorizedInflationWei .sub(_self.inflationTopupRequestedWei); } else { // This code is unreachable since TopupType currently has only 2 constructors _topupRequestWei = 0; assert(false); } _self.inflationTopupRequestedWei = _self.inflationTopupRequestedWei.add(_topupRequestWei); emit RewardServiceTopupComputed(_self.inflationReceiver, _topupRequestWei); } /** * @notice Compute a pending topup request. * @return _pendingTopupWei The amount pending to be minted. */ function getPendingTopup( RewardServiceState storage _self ) internal view returns(uint256 _pendingTopupWei) { return _self.inflationTopupRequestedWei.sub(_self.inflationTopupReceivedWei); } /** * @notice Initial a new reward service. * @dev Assume service is already instantiated. */ function initialize( RewardServiceState storage _self, IIInflationReceiver _inflationReceiver ) internal { _self.inflationReceiver = _inflationReceiver; _self.authorizedInflationWei = 0; _self.lastDailyAuthorizedInflationWei = 0; _self.inflationTopupRequestedWei = 0; _self.inflationTopupReceivedWei = 0; _self.inflationTopupWithdrawnWei = 0; } }
./contracts/inflation/lib/RewardServices.sol
// SPDX-License-Identifier: MIT pragma solidity 0.7.6; pragma abicoder v2; import "../../utils/implementation/DateTimeLibrary.sol"; import "../implementation/Inflation.sol"; import "../interface/IIInflationReceiver.sol"; import "./RewardService.sol"; import "@openzeppelin/contracts/math/SafeMath.sol"; import "../../utils/implementation/SafePct.sol"; import "../interface/IIInflationSharingPercentageProvider.sol"; import "./RewardService.sol"; /** * @title Reward Services library * @notice A library to manage a collection of reward services, their associated totals, and to perform operations * that impact or involve the collection, such as calculating topup amounts across services. * @dev There are two concepts that are helpful to understand. A sharing percentage associates an inflation receiver * with a sharing percentage used to calculate percentage of authorized inflation a given reward contract * is entitled to receive for distributing rewards. A reward service is associtated to a topup configuration, which * dictates how much native token will be minted and sent for claiming reserves, and it stores totals for a given * inflation * receiver, for a given annum. **/ library RewardServices { using BokkyPooBahsDateTimeLibrary for uint256; using RewardService for RewardService.RewardServiceState; using SafeMath for uint256; using SafePct for uint256; /** * @dev `RewardServicesState` is state structure used by this library to manage * a collection of reward services and associated totals. */ struct RewardServicesState { // Collection of annums RewardService.RewardServiceState[] rewardServices; // Balances uint256 totalAuthorizedInflationWei; uint256 totalInflationTopupRequestedWei; uint256 totalInflationTopupReceivedWei; uint256 totalInflationTopupWithdrawnWei; } uint256 internal constant BIPS100 = 1e4; // 100% in basis points event RewardServiceDailyAuthorizedInflationComputed(IIInflationReceiver inflationReceiver, uint256 amountWei); event RewardServiceTopupRequestReceived(IIInflationReceiver inflationReceiver, uint256 amountWei); /** * @notice For all sharing percentages, compute authorized daily inflation for current cycle * and then allocate it across associated inflation receivers according to their sharing percentages, * updating reward service totals along the way. Finally, * set the daily authorized inflation for the given inflation receiver. * @param _totalRecognizedInflationWei The total recognized inflation across all annums. * @param _totalAuthorizedInflationWei The total authorized inflation across all annums. * @param _periodsRemaining The number of periods remaining in the current annum. * @param _sharingPercentages An array of inflation sharing percentages. * @return _amountAuthorizedWei The inflation authorized for this cycle. * @dev This method requires totals across all annums so as to continually calculate * the amount remaining to be authorized regardless of timing slippage between annums should it * occur. */ function authorizeDailyInflation( RewardServicesState storage _self, uint256 _totalRecognizedInflationWei, uint256 _totalAuthorizedInflationWei, uint256 _periodsRemaining, SharingPercentage[] memory _sharingPercentages ) internal returns(uint256 _amountAuthorizedWei) { // If there are no sharing percentages, then there is nothing to authorize. if (_sharingPercentages.length == 0) { _amountAuthorizedWei = 0; return _amountAuthorizedWei; } // Compute amount to allocate uint256 amountToAuthorizeRemaingWei = _totalRecognizedInflationWei .sub(_totalAuthorizedInflationWei) .div(_periodsRemaining); // Set up return value with amount authorized _amountAuthorizedWei = amountToAuthorizeRemaingWei; // Accumulate authorized total...note that this total is for a given annum, for a given service _self.totalAuthorizedInflationWei = _self.totalAuthorizedInflationWei.add(amountToAuthorizeRemaingWei); // Start with total bips in denominator uint256 divisorRemaining = BIPS100; // Loop over sharing percentages for (uint256 i; i < _sharingPercentages.length; i++) { // Compute the amount to authorize for a given service uint256 toAuthorizeWei = amountToAuthorizeRemaingWei.mulDiv( _sharingPercentages[i].percentBips, divisorRemaining ); // Reduce the numerator by amount just computed amountToAuthorizeRemaingWei = amountToAuthorizeRemaingWei.sub(toAuthorizeWei); // Reduce the divisor by the bips just allocated divisorRemaining = divisorRemaining.sub(_sharingPercentages[i].percentBips); // Try to find a matching reward service for the given sharing percentage. // New sharing percentages can be added at any time. And if one gets removed, // we don't remove that reward service for a given annum, since its total still // remains applicable. ( bool found, uint256 rewardServiceIndex ) = findRewardService(_self, _sharingPercentages[i].inflationReceiver); if (found) { // Get the existing reward service RewardService.RewardServiceState storage rewardService = _self.rewardServices[rewardServiceIndex]; // Accumulate the amount authorized for the service rewardService.addAuthorizedInflation(toAuthorizeWei); } else { // Initialize a new reward service RewardService.RewardServiceState storage rewardService = _self.rewardServices.push(); rewardService.initialize(_sharingPercentages[i].inflationReceiver); // Accumulate the amount authorized for the service rewardService.addAuthorizedInflation(toAuthorizeWei); } // Signal the inflation receiver of the reward service (the actual rewarding contract) // with amount just authorized. _sharingPercentages[i].inflationReceiver.setDailyAuthorizedInflation(toAuthorizeWei); emit RewardServiceDailyAuthorizedInflationComputed( _sharingPercentages[i].inflationReceiver, toAuthorizeWei); } } /** * @notice Given topup configurations as maintained by an instantiated Inflation contract, compute * the topup minting requests needed to topup reward contracts with native token reserves to satisfy claim * requests. * @param _inflation The Inflation contract holding the topup configurations. * @return _topupRequestWei The topup request to mint native tokens across reward services for this cycle. */ function computeTopupRequest( RewardServicesState storage _self, Inflation _inflation ) internal returns (uint256 _topupRequestWei) { for (uint256 i; i < _self.rewardServices.length; i++) { TopupConfiguration memory topupConfiguration = _inflation.getTopupConfiguration(_self.rewardServices[i].inflationReceiver); _topupRequestWei = _topupRequestWei.add(_self.rewardServices[i].computeTopupRequest(topupConfiguration)); } _self.totalInflationTopupRequestedWei = _self.totalInflationTopupRequestedWei.add(_topupRequestWei); // Make sure topup requested never exceeds the amount authorized assert(_self.totalInflationTopupRequestedWei <= _self.totalAuthorizedInflationWei); } /** * @notice Given an inflation receiver, return the index of the associated reward service. * @param _inflationReceiver The inflation receiver. * @return _found True if the reward service was found. * @return _index The index on the rewardServices array of the found service. Index is undefined * if the reward service was not found. */ function findRewardService( RewardServicesState storage _self, IIInflationReceiver _inflationReceiver ) internal view returns(bool _found, uint256 _index) { // The number of these is expected to be low. _found = false; for (uint256 i; i < _self.rewardServices.length; i++) { if (address(_self.rewardServices[i].inflationReceiver) == address(_inflationReceiver)) { _index = i; _found = true; break; } } } /** * @notice Receive a topup request of minted native tokens and disburse amongst requestors. * @return _amountPostedWei The total amount of native tokens funded. * @dev Assume value is siting in Inflation contract waiting to be posted and transmitted. * This function is atomic, so if for some reason not enough native tokens got minted, this * function will fail until all topup requests can be satisfied. */ function receiveTopupRequest( RewardServicesState storage _self ) internal returns(uint256 _amountPostedWei) { // Spin through all reward services for (uint256 i; i < _self.rewardServices.length; i++) { // Get the pending topup for the service uint256 pendingTopupWei = _self.rewardServices[i].getPendingTopup(); // Accumulate topup received _self.rewardServices[i].addTopupReceived(pendingTopupWei); _self.totalInflationTopupReceivedWei = _self.totalInflationTopupReceivedWei.add(pendingTopupWei); // Transfer topup to rewarding service contract _self.rewardServices[i].inflationReceiver.receiveInflation{ value: pendingTopupWei }(); // Accumulate topup withdrawn _self.rewardServices[i].addTopupWithdrawn(pendingTopupWei); _self.totalInflationTopupWithdrawnWei = _self.totalInflationTopupWithdrawnWei.add(pendingTopupWei); // Accumulate amount posted _amountPostedWei = _amountPostedWei.add(pendingTopupWei); emit RewardServiceTopupRequestReceived(_self.rewardServices[i].inflationReceiver, pendingTopupWei); } } }
./contracts/userInterfaces/IGovernanceSettings.sol
// 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); }
./contracts/utils/implementation/DateTimeLibrary.sol
// SPDX-License-Identifier: MIT pragma solidity 0.7.6; // ---------------------------------------------------------------------------- // BokkyPooBah's DateTime Library v1.01 // // A gas-efficient Solidity date and time library // // https://github.com/bokkypoobah/BokkyPooBahsDateTimeLibrary // // Tested date range 1970/01/01 to 2345/12/31 // // Conventions: // Unit | Range | Notes // :-------- |:-------------:|:----- // timestamp | >= 0 | Unix timestamp, number of seconds since 1970/01/01 00:00:00 UTC // year | 1970 ... 2345 | // month | 1 ... 12 | // day | 1 ... 31 | // hour | 0 ... 23 | // minute | 0 ... 59 | // second | 0 ... 59 | // dayOfWeek | 1 ... 7 | 1 = Monday, ..., 7 = Sunday // // // Enjoy. (c) BokkyPooBah / Bok Consulting Pty Ltd 2018-2019. The MIT Licence. // ---------------------------------------------------------------------------- library BokkyPooBahsDateTimeLibrary { uint public constant SECONDS_PER_DAY = 24 * 60 * 60; uint public constant SECONDS_PER_HOUR = 60 * 60; uint public constant SECONDS_PER_MINUTE = 60; int public constant OFFSET19700101 = 2440588; uint public constant DOW_MON = 1; uint public constant DOW_TUE = 2; uint public constant DOW_WED = 3; uint public constant DOW_THU = 4; uint public constant DOW_FRI = 5; uint public constant DOW_SAT = 6; uint public constant DOW_SUN = 7; // ------------------------------------------------------------------------ // Calculate the number of days from 1970/01/01 to year/month/day using // the date conversion algorithm from // http://aa.usno.navy.mil/faq/docs/JD_Formula.php // and subtracting the offset 2440588 so that 1970/01/01 is day 0 // // days = day // - 32075 // + 1461 * (year + 4800 + (month - 14) / 12) / 4 // + 367 * (month - 2 - (month - 14) / 12 * 12) / 12 // - 3 * ((year + 4900 + (month - 14) / 12) / 100) / 4 // - offset // ------------------------------------------------------------------------ function _daysFromDate(uint year, uint month, uint day) internal pure returns (uint _days) { require(year >= 1970); int _year = int(year); int _month = int(month); int _day = int(day); int __days = _day - 32075 + 1461 * (_year + 4800 + (_month - 14) / 12) / 4 + 367 * (_month - 2 - (_month - 14) / 12 * 12) / 12 - 3 * ((_year + 4900 + (_month - 14) / 12) / 100) / 4 - OFFSET19700101; _days = uint(__days); } // ------------------------------------------------------------------------ // Calculate year/month/day from the number of days since 1970/01/01 using // the date conversion algorithm from // http://aa.usno.navy.mil/faq/docs/JD_Formula.php // and adding the offset 2440588 so that 1970/01/01 is day 0 // // int tempL = days + 68569 + offset // int tempN = 4 * tempL / 146097 // tempL = tempL - (146097 * tempN + 3) / 4 // year = 4000 * (tempL + 1) / 1461001 // tempL = tempL - 1461 * year / 4 + 31 // month = 80 * tempL / 2447 // dd = tempL - 2447 * month / 80 // tempL = month / 11 // month = month + 2 - 12 * tempL // year = 100 * (tempN - 49) + year + tempL // ------------------------------------------------------------------------ //solhint-disable max-line-length function _daysToDate(uint _days) internal pure returns (uint year, uint month, uint day) { int __days = int(_days); /* solhint-disable var-name-mixedcase */ int L = __days + 68569 + OFFSET19700101; int N = 4 * L / 146097; /* solhint-enable var-name-mixedcase */ L = L - (146097 * N + 3) / 4; int _year = 4000 * (L + 1) / 1461001; L = L - 1461 * _year / 4 + 31; int _month = 80 * L / 2447; int _day = L - 2447 * _month / 80; L = _month / 11; _month = _month + 2 - 12 * L; _year = 100 * (N - 49) + _year + L; year = uint(_year); month = uint(_month); day = uint(_day); } function timestampFromDate(uint year, uint month, uint day) internal pure returns (uint timestamp) { timestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY; } function timestampFromDateTime( uint year, uint month, uint day, uint hour, uint minute, uint second) internal pure returns (uint timestamp) { timestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY + hour * SECONDS_PER_HOUR + minute * SECONDS_PER_MINUTE + second; } function timestampToDate(uint timestamp) internal pure returns (uint year, uint month, uint day) { (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY); } function timestampToDateTime(uint timestamp) internal pure returns ( uint year, uint month, uint day, uint hour, uint minute, uint second) { (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY); uint secs = timestamp % SECONDS_PER_DAY; hour = secs / SECONDS_PER_HOUR; secs = secs % SECONDS_PER_HOUR; minute = secs / SECONDS_PER_MINUTE; second = secs % SECONDS_PER_MINUTE; } function isValidDate(uint year, uint month, uint day) internal pure returns (bool valid) { if (year >= 1970 && month > 0 && month <= 12) { uint daysInMonth = _getDaysInMonth(year, month); if (day > 0 && day <= daysInMonth) { valid = true; } } } function isValidDateTime( uint year, uint month, uint day, uint hour, uint minute, uint second) internal pure returns (bool valid) { if (isValidDate(year, month, day)) { if (hour < 24 && minute < 60 && second < 60) { valid = true; } } } function isLeapYear(uint timestamp) internal pure returns (bool leapYear) { (uint year,,) = _daysToDate(timestamp / SECONDS_PER_DAY); leapYear = _isLeapYear(year); } function _isLeapYear(uint year) internal pure returns (bool leapYear) { leapYear = ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0); } function isWeekDay(uint timestamp) internal pure returns (bool weekDay) { weekDay = getDayOfWeek(timestamp) <= DOW_FRI; } function isWeekEnd(uint timestamp) internal pure returns (bool weekEnd) { weekEnd = getDayOfWeek(timestamp) >= DOW_SAT; } function getDaysInMonth(uint timestamp) internal pure returns (uint daysInMonth) { (uint year, uint month,) = _daysToDate(timestamp / SECONDS_PER_DAY); daysInMonth = _getDaysInMonth(year, month); } function _getDaysInMonth(uint year, uint month) internal pure returns (uint daysInMonth) { if (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12) { daysInMonth = 31; } else if (month != 2) { daysInMonth = 30; } else { daysInMonth = _isLeapYear(year) ? 29 : 28; } } // 1 = Monday, 7 = Sunday function getDayOfWeek(uint timestamp) internal pure returns (uint dayOfWeek) { uint _days = timestamp / SECONDS_PER_DAY; dayOfWeek = (_days + 3) % 7 + 1; } function getYear(uint timestamp) internal pure returns (uint year) { (year,,) = _daysToDate(timestamp / SECONDS_PER_DAY); } function getMonth(uint timestamp) internal pure returns (uint month) { (,month,) = _daysToDate(timestamp / SECONDS_PER_DAY); } function getDay(uint timestamp) internal pure returns (uint day) { (,,day) = _daysToDate(timestamp / SECONDS_PER_DAY); } function getHour(uint timestamp) internal pure returns (uint hour) { uint secs = timestamp % SECONDS_PER_DAY; hour = secs / SECONDS_PER_HOUR; } function getMinute(uint timestamp) internal pure returns (uint minute) { uint secs = timestamp % SECONDS_PER_HOUR; minute = secs / SECONDS_PER_MINUTE; } function getSecond(uint timestamp) internal pure returns (uint second) { second = timestamp % SECONDS_PER_MINUTE; } function addYears(uint timestamp, uint _years) internal pure returns (uint newTimestamp) { (uint year, uint month, uint day) = _daysToDate(timestamp / SECONDS_PER_DAY); year += _years; uint daysInMonth = _getDaysInMonth(year, month); // When adding a year to feb 29th if (day > daysInMonth) { day = daysInMonth; } newTimestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY + timestamp % SECONDS_PER_DAY; require(newTimestamp >= timestamp); } function addMonths(uint timestamp, uint _months) internal pure returns (uint newTimestamp) { (uint year, uint month, uint day) = _daysToDate(timestamp / SECONDS_PER_DAY); month += _months; year += (month - 1) / 12; month = (month - 1) % 12 + 1; uint daysInMonth = _getDaysInMonth(year, month); if (day > daysInMonth) { day = daysInMonth; } newTimestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY + timestamp % SECONDS_PER_DAY; require(newTimestamp >= timestamp); } function addDays(uint timestamp, uint _days) internal pure returns (uint newTimestamp) { newTimestamp = timestamp + _days * SECONDS_PER_DAY; require(newTimestamp >= timestamp); } function addHours(uint timestamp, uint _hours) internal pure returns (uint newTimestamp) { newTimestamp = timestamp + _hours * SECONDS_PER_HOUR; require(newTimestamp >= timestamp); } function addMinutes(uint timestamp, uint _minutes) internal pure returns (uint newTimestamp) { newTimestamp = timestamp + _minutes * SECONDS_PER_MINUTE; require(newTimestamp >= timestamp); } function addSeconds(uint timestamp, uint _seconds) internal pure returns (uint newTimestamp) { newTimestamp = timestamp + _seconds; require(newTimestamp >= timestamp); } /** * @dev removed since it can be a cause of errors * adding and removing a year may not end up on the same point in time */ // function subYears(uint timestamp, uint _years) internal pure returns (uint newTimestamp) { // (uint year, uint month, uint day) = _daysToDate(timestamp / SECONDS_PER_DAY); // year -= _years; // uint daysInMonth = _getDaysInMonth(year, month); // if (day > daysInMonth) { // day = daysInMonth; // } // newTimestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY + timestamp % SECONDS_PER_DAY; // require(newTimestamp <= timestamp); // } /** * @dev removed since it can be a cause of errors * adding and removing a month may not end up on the same point in time * Intendet functionality: * 31.5 + 1 month => 30.6 * 30.6 - 1 month => 30.5 * this may cause problems */ // function subMonths(uint timestamp, uint _months) internal pure returns (uint newTimestamp) { // (uint year, uint month, uint day) = _daysToDate(timestamp / SECONDS_PER_DAY); // uint yearMonth = year * 12 + (month - 1) - _months; // year = yearMonth / 12; // month = yearMonth % 12 + 1; // uint daysInMonth = _getDaysInMonth(year, month); // if (day > daysInMonth) { // day = daysInMonth; // } // newTimestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY + timestamp % SECONDS_PER_DAY; // require(newTimestamp <= timestamp); // } function subDays(uint timestamp, uint _days) internal pure returns (uint newTimestamp) { newTimestamp = timestamp - _days * SECONDS_PER_DAY; require(newTimestamp <= timestamp); } function subHours(uint timestamp, uint _hours) internal pure returns (uint newTimestamp) { newTimestamp = timestamp - _hours * SECONDS_PER_HOUR; require(newTimestamp <= timestamp); } function subMinutes(uint timestamp, uint _minutes) internal pure returns (uint newTimestamp) { newTimestamp = timestamp - _minutes * SECONDS_PER_MINUTE; require(newTimestamp <= timestamp); } function subSeconds(uint timestamp, uint _seconds) internal pure returns (uint newTimestamp) { newTimestamp = timestamp - _seconds; require(newTimestamp <= timestamp); } function diffYears(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _years) { require(fromTimestamp <= toTimestamp); (uint fromYear,,) = _daysToDate(fromTimestamp / SECONDS_PER_DAY); (uint toYear,,) = _daysToDate(toTimestamp / SECONDS_PER_DAY); _years = toYear - fromYear; } function diffMonths(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _months) { require(fromTimestamp <= toTimestamp); (uint fromYear, uint fromMonth,) = _daysToDate(fromTimestamp / SECONDS_PER_DAY); (uint toYear, uint toMonth,) = _daysToDate(toTimestamp / SECONDS_PER_DAY); _months = toYear * 12 + toMonth - fromYear * 12 - fromMonth; } function diffDays(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _days) { require(fromTimestamp <= toTimestamp); _days = (toTimestamp - fromTimestamp) / SECONDS_PER_DAY; } function diffHours(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _hours) { require(fromTimestamp <= toTimestamp); _hours = (toTimestamp - fromTimestamp) / SECONDS_PER_HOUR; } function diffMinutes(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _minutes) { require(fromTimestamp <= toTimestamp); _minutes = (toTimestamp - fromTimestamp) / SECONDS_PER_MINUTE; } function diffSeconds(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _seconds) { require(fromTimestamp <= toTimestamp); _seconds = toTimestamp - fromTimestamp; } function getDaysInYear(uint timestamp) internal pure returns (uint daysInYear) { return isLeapYear(timestamp) ? 366 : 365; } }
./contracts/utils/implementation/GovernedAndFlareDaemonized.sol
// SPDX-License-Identifier: MIT pragma solidity 0.7.6; import { FlareDaemon } from "../../genesis/implementation/FlareDaemon.sol"; import { Governed } from "../../governance/implementation/Governed.sol"; contract GovernedAndFlareDaemonized is Governed { FlareDaemon public immutable flareDaemon; modifier onlyFlareDaemon () { require (msg.sender == address(flareDaemon), "only flare daemon"); _; } constructor(address _governance, FlareDaemon _flareDaemon) Governed(_governance) { require(address(_flareDaemon) != address(0), "flare daemon zero"); flareDaemon = _flareDaemon; } }
./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/Math.sol
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a >= b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow, so we distribute return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2); } }
@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/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":"address","name":"_governance","internalType":"address"},{"type":"address","name":"_addressUpdater","internalType":"address"},{"type":"uint256[]","name":"_annualInflationScheduleBips","internalType":"uint256[]"}]},{"type":"event","name":"AnnualInflationPercentageScheduleSet","inputs":[{"type":"uint256[]","name":"annualInflationPercentagesBips","internalType":"uint256[]","indexed":false}],"anonymous":false},{"type":"event","name":"AnnualInflationPercentageYielded","inputs":[{"type":"uint256","name":"percentageBips","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"GovernanceCallTimelocked","inputs":[{"type":"bytes4","name":"selector","internalType":"bytes4","indexed":false},{"type":"uint256","name":"allowedAfterTimestamp","internalType":"uint256","indexed":false},{"type":"bytes","name":"encodedCall","internalType":"bytes","indexed":false}],"anonymous":false},{"type":"event","name":"GovernanceInitialised","inputs":[{"type":"address","name":"initialGovernance","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"GovernedProductionModeEntered","inputs":[{"type":"address","name":"governanceSettings","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"InflationSet","inputs":[{"type":"address","name":"oldAddress","internalType":"address","indexed":false},{"type":"address","name":"newAddress","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"InflationSharingPercentagesSet","inputs":[{"type":"address[]","name":"inflationReceivers","internalType":"contract IIInflationReceiver[]","indexed":false},{"type":"uint256[]","name":"percentagePerReceiverBips","internalType":"uint256[]","indexed":false}],"anonymous":false},{"type":"event","name":"TimelockedGovernanceCallCanceled","inputs":[{"type":"bytes4","name":"selector","internalType":"bytes4","indexed":false},{"type":"uint256","name":"timestamp","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"TimelockedGovernanceCallExecuted","inputs":[{"type":"bytes4","name":"selector","internalType":"bytes4","indexed":false},{"type":"uint256","name":"timestamp","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"annualInflationPercentagesBips","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"cancelGovernanceCall","inputs":[{"type":"bytes4","name":"_selector","internalType":"bytes4"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"executeGovernanceCall","inputs":[{"type":"bytes4","name":"_selector","internalType":"bytes4"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"_addressUpdater","internalType":"address"}],"name":"getAddressUpdater","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getAnnualPercentageBips","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple[]","name":"_sharingPercentages","internalType":"struct SharingPercentage[]","components":[{"type":"address","name":"inflationReceiver","internalType":"contract IIInflationReceiver"},{"type":"uint256","name":"percentBips","internalType":"uint256"}]}],"name":"getSharingPercentages","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"governance","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IGovernanceSettings"}],"name":"governanceSettings","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract Inflation"}],"name":"inflation","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"receiverContract","internalType":"contract IIInflationReceiver"},{"type":"uint32","name":"percentageBips","internalType":"uint32"}],"name":"inflationReceivers","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"initialise","inputs":[{"type":"address","name":"_initialGovernance","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"lastAnnualInflationPercentageBips","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"productionMode","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setAnnualInflation","inputs":[{"type":"uint256[]","name":"_annualInflationScheduleBips","internalType":"uint256[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setSharingPercentages","inputs":[{"type":"address[]","name":"_inflationReceivers","internalType":"contract IIInflationReceiver[]"},{"type":"uint256[]","name":"_percentagePerReceiverBips","internalType":"uint256[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"switchToProductionMode","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"allowedAfterTimestamp","internalType":"uint256"},{"type":"bytes","name":"encodedCall","internalType":"bytes"}],"name":"timelockedCalls","inputs":[{"type":"bytes4","name":"","internalType":"bytes4"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updateContractAddresses","inputs":[{"type":"bytes32[]","name":"_contractNameHashes","internalType":"bytes32[]"},{"type":"address[]","name":"_contractAddresses","internalType":"address[]"}]}]
Contract Creation Code
0x60806040523480156200001157600080fd5b5060405162002469380380620024698339810160408190526200003491620003b2565b8183806001600160a01b038116156200005257620000528162000145565b506001600160a01b038116620000a2576040805162461bcd60e51b815260206004820152601060248201526f5f676f7665726e616e6365207a65726f60801b604482015290519081900360640190fd5b50620000ae8162000208565b5060008151116040518060400160405280601f81526020017f616e6e75616c20696e666c6174696f6e207363686564756c6520656d7074790081525090620001145760405162461bcd60e51b81526004016200010b91906200048f565b60405180910390fd5b50806000815181106200012357fe5b60209081029190910101516004556200013c816200022c565b505050620004e5565b600054600160a01b900460ff1615620001a5576040805162461bcd60e51b815260206004820152601460248201527f696e697469616c6973656420213d2066616c7365000000000000000000000000604482015290519081900360640190fd5b60008054600160a01b60ff60a01b19909116176001600160a01b0319166001600160a01b03831690811790915560408051918252517f9789733827840833afc031fb2ef9ab6894271f77bad2085687cf4ae5c7bee4db916020908290030190a150565b7f714f205b2abd25bef1d06a1af944e38c113fe6160375c4e1d6d5cf28848e771955565b60198151111560405180604001604052806008815260200167746f6f206d616e7960c01b81525090620002745760405162461bcd60e51b81526004016200010b91906200048f565b50805160045460005b828110156200038f57818482815181106200029457fe5b602002602001015111158015620002bf57506000848281518110620002b557fe5b6020026020010151115b8015620002e55750600a61271004848281518110620002da57fe5b602002602001015111155b6040518060400160405280601e81526020017f616e6e75616c20696e666c6174696f6e206f7574206f6620626f756e64730000815250906200033c5760405162461bcd60e51b81526004016200010b91906200048f565b508381815181106200034a57fe5b6020026020010151915060058482815181106200036357fe5b60209081029190910181015182546001818101855560009485529290932090920191909155016200027d565b50505050565b80516001600160a01b0381168114620003ad57600080fd5b919050565b600080600060608486031215620003c7578283fd5b620003d28462000395565b92506020620003e381860162000395565b60408601519093506001600160401b038082111562000400578384fd5b818701915087601f83011262000414578384fd5b8151818111156200042157fe5b838102604051858282010181811085821117156200043b57fe5b604052828152858101935084860182860187018c10156200045a578788fd5b8795505b838610156200047e5780518552600195909501949386019386016200045e565b508096505050505050509250925092565b6000602080835283518082850152825b81811015620004bd578581018301518582016040015282016200049f565b81811115620004cf5783604083870101525b50601f01601f1916929092016040019392505050565b611f7480620004f56000396000f3fe608060405234801561001057600080fd5b50600436106101165760003560e01c806398194029116100a2578063be0522e011610071578063be0522e014610227578063c853b8d41461022f578063e17f212e14610244578063ebee6fa414610259578063f5a983831461026157610116565b806398194029146101e65780639d6a890f146101ee578063b00c0b7614610201578063b2d9e8061461021457610116565b806362354e03116100e957806362354e031461017757806367fc40291461017f578063728a5bcb1461019257806374e6310e146101b257806390ac884b146101d357610116565b80631d019e341461011b5780635267a15d146101455780635aa6e6751461015a5780635ff2707914610162575b600080fd5b61012e610129366004611ce5565b610269565b60405161013c929190611e67565b60405180910390f35b61014d6102a1565b60405161013c9190611d63565b61014d6102c6565b610175610170366004611c33565b61035b565b005b61014d6106b0565b61017561018d366004611c33565b6106bb565b6101a56101a0366004611ce5565b6107a3565b60405161013c9190611e99565b6101c56101c0366004611c33565b6107c4565b60405161013c929190611ea2565b6101756101e1366004611bf8565b61086a565b6101a5610927565b6101756101fc366004611a68565b610ab7565b61017561020f366004611a8b565b610b70565b610175610222366004611b42565b610c1b565b61014d610c64565b610237610c73565b60405161013c9190611df1565b61024c610d7d565b60405161013c9190611e5c565b6101a5610d8d565b610175610d93565b6002818154811061027957600080fd5b6000918252602090912001546001600160a01b0381169150600160a01b900463ffffffff1682565b7f714f205b2abd25bef1d06a1af944e38c113fe6160375c4e1d6d5cf28848e77195490565b60008054600160a81b900460ff166102e9576000546001600160a01b0316610356565b60076001609c1b016001600160a01b031663732524946040518163ffffffff1660e01b815260040160206040518083038186803b15801561032957600080fd5b505afa15801561033d573d6000803e3d6000fd5b505050506040513d602081101561035357600080fd5b50515b905090565b60408051630debfda360e41b8152336004820152905160076001609c1b019163debfda30916024808301926020929190829003018186803b15801561039f57600080fd5b505afa1580156103b3573d6000803e3d6000fd5b505050506040513d60208110156103c957600080fd5b505161040c576040805162461bcd60e51b815260206004820152600d60248201526c37b7363c9032bc32b1baba37b960991b604482015290519081900360640190fd5b6001600160e01b0319811660009081526001602052604090208054610478576040805162461bcd60e51b815260206004820152601a60248201527f74696d656c6f636b3a20696e76616c69642073656c6563746f72000000000000604482015290519081900360640190fd5b80544210156104ce576040805162461bcd60e51b815260206004820152601960248201527f74696d656c6f636b3a206e6f7420616c6c6f7765642079657400000000000000604482015290519081900360640190fd5b6000816001018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105685780601f1061053d57610100808354040283529160200191610568565b820191906000526020600020905b81548152906001019060200180831161054b57829003601f168201915b5050506001600160e01b031986166000908152600160208190526040822082815594955090925061059c91508301826118a5565b50506000805460ff60b01b1916600160b01b178155604051825130918491819060208401908083835b602083106105e45780518252601f1990920191602091820191016105c5565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114610646576040519150601f19603f3d011682016040523d82523d6000602084013e61064b565b606091505b50506000805460ff60b01b19169055604080516001600160e01b03198716815242602082015281519293507fa7326b57fc9cfe267aaea5e7f0b01757154d265620a0585819416ee9ddd2c438929081900390910190a16106aa81610e4d565b50505050565b60076001609c1b0181565b6106c3610e6a565b6001600160e01b0319811660009081526001602052604090205461072e576040805162461bcd60e51b815260206004820152601a60248201527f74696d656c6f636b3a20696e76616c69642073656c6563746f72000000000000604482015290519081900360640190fd5b604080516001600160e01b03198316815242602082015281517f7735b2391c38a81419c513e30ca578db7158eadd7101511b23e221c654d19cf8929181900390910190a16001600160e01b0319811660009081526001602081905260408220828155919061079e908301826118a5565b505050565b600581815481106107b357600080fd5b600091825260209091200154905081565b600160208181526000928352604092839020805481840180548651600296821615610100026000190190911695909504601f810185900485028601850190965285855290949193929091908301828280156108605780601f1061083557610100808354040283529160200191610860565b820191906000526020600020905b81548152906001019060200180831161084357829003601f168201915b5050505050905082565b600054600160b01b900460ff168061088c5750600054600160a81b900460ff16155b1561091957610899610ecb565b60055460005b818110156108d25760058054806108b257fe5b60008281526020812082016000199081019190915501905560010161089f565b506108dc82610f00565b7fe3ad326cdb10e1201bcea3d96ccf2f5437ed71942548e5ba0aab68c9bd296fa48260405161090b9190611e49565b60405180910390a150610924565b610924600036611054565b50565b60035460408051808201909152600c81526b06164647265737320697320360a41b60208201526000916001600160a01b031690816109815760405162461bcd60e51b81526004016109789190611e86565b60405180910390fd5b5060035460408051808201909152600e81526d37b7363c9034b7333630ba34b7b760911b6020820152906001600160a01b031633146109d35760405162461bcd60e51b81526004016109789190611e86565b5060055415610a745760056000815481106109ea57fe5b6000918252602090912001546004556005546001811115610a515760005b60018203811015610a4f5760058160010181548110610a2357fe5b906000526020600020015460058281548110610a3b57fe5b600091825260209091200155600101610a08565b505b6005805480610a5c57fe5b60019003818190600052602060002001600090559055505b7fc46384622db26f8a6509e51f7cbe387b3fa773448f19fc9f070fcdc984cffb8e600454604051610aa59190611e99565b60405180910390a160045491505b5090565b600054600160a01b900460ff1615610b0d576040805162461bcd60e51b8152602060048201526014602482015273696e697469616c6973656420213d2066616c736560601b604482015290519081900360640190fd5b60008054600160a01b60ff60a01b19909116176001600160a01b0319166001600160a01b03831690811790915560408051918252517f9789733827840833afc031fb2ef9ab6894271f77bad2085687cf4ae5c7bee4db916020908290030190a150565b610b786102a1565b6001600160a01b0316336001600160a01b031614610bd4576040805162461bcd60e51b815260206004820152601460248201527337b7363c9030b2323932b9b9903ab83230ba32b960611b604482015290519081900360640190fd5b610c0d610c0883836040518060400160405280600e81526020016d20b2323932b9b9aab83230ba32b960911b8152506111d7565b611304565b610c178282611328565b5050565b600054600160b01b900460ff1680610c3d5750600054600160a81b900460ff16155b15610c5957610c4a610ecb565b610c54828261158f565b610c17565b610c17600036611054565b6003546001600160a01b031681565b6002546060908067ffffffffffffffff81118015610c9057600080fd5b50604051908082528060200260200182016040528015610cca57816020015b610cb76118e9565b815260200190600190039081610caf5790505b50915060005b81811015610d785760028181548110610ce557fe5b9060005260206000200160000160149054906101000a900463ffffffff1663ffffffff16838281518110610d1557fe5b6020026020010151602001818152505060028181548110610d3257fe5b60009182526020909120015483516001600160a01b0390911690849083908110610d5857fe5b60209081029190910101516001600160a01b039091169052600101610cd0565b505090565b600054600160a81b900460ff1681565b60045481565b610d9b610e6a565b600054600160a81b900460ff1615610dfa576040805162461bcd60e51b815260206004820152601a60248201527f616c726561647920696e2070726f64756374696f6e206d6f6465000000000000604482015290519081900360640190fd5b60008054600161ff0160a01b031916600160a81b1790556040805160076001609c1b01815290517f83af113638b5422f9e977cebc0aaf0eaf2188eb9a8baae7f9d46c42b33a1560c9181900360200190a1565b3d604051818101604052816000823e8215610e66578181f35b8181fd5b610e726102c6565b6001600160a01b0316336001600160a01b031614610ec9576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b565b600054600160b01b900460ff1615610ef857333014610ee657fe5b6000805460ff60b01b19169055610ec9565b610ec9610e6a565b60198151111560405180604001604052806008815260200167746f6f206d616e7960c01b81525090610f455760405162461bcd60e51b81526004016109789190611e86565b50805160045460005b828110156106aa5781848281518110610f6357fe5b602002602001015111158015610f8c57506000848281518110610f8257fe5b6020026020010151115b8015610fb05750600a61271004848281518110610fa557fe5b602002602001015111155b6040518060400160405280601e81526020017f616e6e75616c20696e666c6174696f6e206f7574206f6620626f756e64730000815250906110045760405162461bcd60e51b81526004016109789190611e86565b5083818151811061101157fe5b60200260200101519150600584828151811061102957fe5b6020908102919091018101518254600181810185556000948552929093209092019190915501610f4e565b61105c610e6a565b600082359050600060076001609c1b016001600160a01b0316636221a54b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156110a457600080fd5b505afa1580156110b8573d6000803e3d6000fd5b505050506040513d60208110156110ce57600080fd5b505160408051808201825242830180825282516020601f89018190048102820181019094528781529394509290918281019190889088908190840183828082843760009201829052509390945250506001600160e01b0319861681526001602081815260409092208451815584830151805191945061115293928501920190611900565b509050507fed948300a3694aa01d4a6b258bfd664350193d770c0b51f8387277f6d83ea3b68382878760405180856001600160e01b0319168152602001848152602001806020018281038252848482818152602001925080828437600083820152604051601f909101601f191690920182900397509095505050505050a15050505050565b600080826040516020018080602001828103825283818151815260200191508051906020019080838360005b8381101561121b578181015183820152602001611203565b50505050905090810190601f1680156112485780820380516001836020036101000a031916815260200191505b50925050506040516020818303038152906040528051906020012090506000805b86518110156112b05786818151811061127e57fe5b60200260200101518314156112a85785818151811061129957fe5b602002602001015191506112b0565b600101611269565b506001600160a01b0381166112fb576040805162461bcd60e51b815260206004820152600c60248201526b61646472657373207a65726f60a01b604482015290519081900360640190fd5b95945050505050565b7f714f205b2abd25bef1d06a1af944e38c113fe6160375c4e1d6d5cf28848e771955565b600061135683836040518060400160405280600981526020016824b7333630ba34b7b760b91b8152506111d7565b6003546040519192507f4bdd1012a7d55ed9afad8675a125e1b68c7c15f712c0f3d5cddac69c3b97980591611396916001600160a01b0316908490611d77565b60405180910390a1600380546001600160a01b0319166001600160a01b038316179055600254806113c8575050610c17565b60008167ffffffffffffffff811180156113e157600080fd5b5060405190808252806020026020018201604052801561140b578160200160208202803683370190505b50905060008267ffffffffffffffff8111801561142757600080fd5b50604051908082528060200260200182016040528015611451578160200160208202803683370190505b50905060005b8381101561157c5760006002828154811061146e57fe5b6000918252602080832060408051808201825293909101546001600160a01b038116808552600160a01b90910463ffffffff16928401929092528051637afadd3960e11b81529051929450611525938c938c939263f5f5ba72926004808301939192829003018186803b1580156114e457600080fd5b505afa1580156114f8573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526115209190810190611c5b565b6111d7565b84838151811061153157fe5b60200260200101906001600160a01b031690816001600160a01b031681525050806020015163ffffffff1683838151811061156857fe5b602090810291909101015250600101611457565b50611587828261158f565b505050505050565b80518251146040518060400160405280600f81526020016e0d8cadccee8d040dad2e6dac2e8c6d608b1b815250906115da5760405162461bcd60e51b81526004016109789190611e86565b50600a8251111560405180604001604052806008815260200167746f6f206d616e7960c01b815250906116205760405162461bcd60e51b81526004016109789190611e86565b50600254600090815b8181101561166657600280548061163c57fe5b600082815260209020810160001990810180546001600160c01b0319169055019055600101611629565b5060005b845181101561180e5761271084828151811061168257fe5b602002602001015111156040518060400160405280601781526020017f686967682073686172696e672070657263656e74616765000000000000000000815250906116e05760405162461bcd60e51b81526004016109789190611e86565b5060006001600160a01b03168582815181106116f857fe5b60200260200101516001600160a01b031614156040518060400160405280600c81526020016b06164647265737320697320360a41b8152509061174e5760405162461bcd60e51b81526004016109789190611e86565b5083818151811061175b57fe5b6020026020010151830192506002604051806040016040528087848151811061178057fe5b60200260200101516001600160a01b031681526020018684815181106117a257fe5b60209081029190910181015163ffffffff90811690925283546001808201865560009586529482902084519101805494909201516001600160a01b03199094166001600160a01b039091161763ffffffff60a01b1916600160a01b93909216929092021790550161166a565b5060408051808201909152601f81527f73756d2073686172696e672070657263656e74616765206e6f74203130302500602082015261271083146118655760405162461bcd60e51b81526004016109789190611e86565b507f5851f041454bd72003e19484c59ec9c1bd9c6f8df07679fd1e94412fb5880b448484604051611897929190611d91565b60405180910390a150505050565b50805460018160011615610100020316600290046000825580601f106118cb5750610924565b601f0160209004906000526020600020908101906109249190611984565b604080518082019091526000808252602082015290565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282611936576000855561197c565b82601f1061194f57805160ff191683800117855561197c565b8280016001018555821561197c579182015b8281111561197c578251825591602001919060010190611961565b50610ab39291505b5b80821115610ab35760008155600101611985565b600082601f8301126119a9578081fd5b813560206119be6119b983611edf565b611ebb565b82815281810190858301838502870184018810156119da578586fd5b855b85811015611a015781356119ef81611f29565b845292840192908401906001016119dc565b5090979650505050505050565b600082601f830112611a1e578081fd5b81356020611a2e6119b983611edf565b8281528181019085830183850287018401881015611a4a578586fd5b855b85811015611a0157813584529284019290840190600101611a4c565b600060208284031215611a79578081fd5b8135611a8481611f29565b9392505050565b60008060408385031215611a9d578081fd5b823567ffffffffffffffff80821115611ab4578283fd5b818501915085601f830112611ac7578283fd5b81356020611ad76119b983611edf565b82815281810190858301838502870184018b1015611af3578788fd5b8796505b84871015611b15578035835260019690960195918301918301611af7565b5096505086013592505080821115611b2b578283fd5b50611b3885828601611999565b9150509250929050565b60008060408385031215611b54578182fd5b823567ffffffffffffffff80821115611b6b578384fd5b818501915085601f830112611b7e578384fd5b81356020611b8e6119b983611edf565b82815281810190858301838502870184018b1015611baa578889fd5b8896505b84871015611bd5578035611bc181611f29565b835260019690960195918301918301611bae565b5096505086013592505080821115611beb578283fd5b50611b3885828601611a0e565b600060208284031215611c09578081fd5b813567ffffffffffffffff811115611c1f578182fd5b611c2b84828501611a0e565b949350505050565b600060208284031215611c44578081fd5b81356001600160e01b031981168114611a84578182fd5b600060208284031215611c6c578081fd5b815167ffffffffffffffff80821115611c83578283fd5b818401915084601f830112611c96578283fd5b815181811115611ca257fe5b611cb5601f8201601f1916602001611ebb565b9150808252856020828501011115611ccb578384fd5b611cdc816020840160208601611efd565b50949350505050565b600060208284031215611cf6578081fd5b5035919050565b6000815180845260208085019450808401835b83811015611d2c57815187529582019590820190600101611d10565b509495945050505050565b60008151808452611d4f816020860160208601611efd565b601f01601f19169290920160200192915050565b6001600160a01b0391909116815260200190565b6001600160a01b0392831681529116602082015260400190565b604080825283519082018190526000906020906060840190828701845b82811015611dd35781516001600160a01b031684529284019290840190600101611dae565b50505083810382850152611de78186611cfd565b9695505050505050565b602080825282518282018190526000919060409081850190868401855b82811015611e3c57815180516001600160a01b03168552860151868501529284019290850190600101611e0e565b5091979650505050505050565b600060208252611a846020830184611cfd565b901515815260200190565b6001600160a01b0392909216825263ffffffff16602082015260400190565b600060208252611a846020830184611d37565b90815260200190565b600083825260406020830152611c2b6040830184611d37565b60405181810167ffffffffffffffff81118282101715611ed757fe5b604052919050565b600067ffffffffffffffff821115611ef357fe5b5060209081020190565b60005b83811015611f18578181015183820152602001611f00565b838111156106aa5750506000910152565b6001600160a01b038116811461092457600080fdfea26469706673582212206e015ce89268710b69a880ab622154a8dff9421536a54fa1c807b6138e028cc964736f6c634300070600330000000000000000000000004598a6c05910ab914f0cbaaca1911cd337d10d29000000000000000000000000baf89d873d198ff78e72d2745b01cba3c6e5be6b0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000001900000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000002bc00000000000000000000000000000000000000000000000000000000000002bc00000000000000000000000000000000000000000000000000000000000002bc00000000000000000000000000000000000000000000000000000000000002bc00000000000000000000000000000000000000000000000000000000000002bc00000000000000000000000000000000000000000000000000000000000002bc00000000000000000000000000000000000000000000000000000000000002bc00000000000000000000000000000000000000000000000000000000000002bc00000000000000000000000000000000000000000000000000000000000002bc00000000000000000000000000000000000000000000000000000000000002bc00000000000000000000000000000000000000000000000000000000000002bc00000000000000000000000000000000000000000000000000000000000002bc00000000000000000000000000000000000000000000000000000000000001f4
Deployed ByteCode
0x608060405234801561001057600080fd5b50600436106101165760003560e01c806398194029116100a2578063be0522e011610071578063be0522e014610227578063c853b8d41461022f578063e17f212e14610244578063ebee6fa414610259578063f5a983831461026157610116565b806398194029146101e65780639d6a890f146101ee578063b00c0b7614610201578063b2d9e8061461021457610116565b806362354e03116100e957806362354e031461017757806367fc40291461017f578063728a5bcb1461019257806374e6310e146101b257806390ac884b146101d357610116565b80631d019e341461011b5780635267a15d146101455780635aa6e6751461015a5780635ff2707914610162575b600080fd5b61012e610129366004611ce5565b610269565b60405161013c929190611e67565b60405180910390f35b61014d6102a1565b60405161013c9190611d63565b61014d6102c6565b610175610170366004611c33565b61035b565b005b61014d6106b0565b61017561018d366004611c33565b6106bb565b6101a56101a0366004611ce5565b6107a3565b60405161013c9190611e99565b6101c56101c0366004611c33565b6107c4565b60405161013c929190611ea2565b6101756101e1366004611bf8565b61086a565b6101a5610927565b6101756101fc366004611a68565b610ab7565b61017561020f366004611a8b565b610b70565b610175610222366004611b42565b610c1b565b61014d610c64565b610237610c73565b60405161013c9190611df1565b61024c610d7d565b60405161013c9190611e5c565b6101a5610d8d565b610175610d93565b6002818154811061027957600080fd5b6000918252602090912001546001600160a01b0381169150600160a01b900463ffffffff1682565b7f714f205b2abd25bef1d06a1af944e38c113fe6160375c4e1d6d5cf28848e77195490565b60008054600160a81b900460ff166102e9576000546001600160a01b0316610356565b60076001609c1b016001600160a01b031663732524946040518163ffffffff1660e01b815260040160206040518083038186803b15801561032957600080fd5b505afa15801561033d573d6000803e3d6000fd5b505050506040513d602081101561035357600080fd5b50515b905090565b60408051630debfda360e41b8152336004820152905160076001609c1b019163debfda30916024808301926020929190829003018186803b15801561039f57600080fd5b505afa1580156103b3573d6000803e3d6000fd5b505050506040513d60208110156103c957600080fd5b505161040c576040805162461bcd60e51b815260206004820152600d60248201526c37b7363c9032bc32b1baba37b960991b604482015290519081900360640190fd5b6001600160e01b0319811660009081526001602052604090208054610478576040805162461bcd60e51b815260206004820152601a60248201527f74696d656c6f636b3a20696e76616c69642073656c6563746f72000000000000604482015290519081900360640190fd5b80544210156104ce576040805162461bcd60e51b815260206004820152601960248201527f74696d656c6f636b3a206e6f7420616c6c6f7765642079657400000000000000604482015290519081900360640190fd5b6000816001018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105685780601f1061053d57610100808354040283529160200191610568565b820191906000526020600020905b81548152906001019060200180831161054b57829003601f168201915b5050506001600160e01b031986166000908152600160208190526040822082815594955090925061059c91508301826118a5565b50506000805460ff60b01b1916600160b01b178155604051825130918491819060208401908083835b602083106105e45780518252601f1990920191602091820191016105c5565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114610646576040519150601f19603f3d011682016040523d82523d6000602084013e61064b565b606091505b50506000805460ff60b01b19169055604080516001600160e01b03198716815242602082015281519293507fa7326b57fc9cfe267aaea5e7f0b01757154d265620a0585819416ee9ddd2c438929081900390910190a16106aa81610e4d565b50505050565b60076001609c1b0181565b6106c3610e6a565b6001600160e01b0319811660009081526001602052604090205461072e576040805162461bcd60e51b815260206004820152601a60248201527f74696d656c6f636b3a20696e76616c69642073656c6563746f72000000000000604482015290519081900360640190fd5b604080516001600160e01b03198316815242602082015281517f7735b2391c38a81419c513e30ca578db7158eadd7101511b23e221c654d19cf8929181900390910190a16001600160e01b0319811660009081526001602081905260408220828155919061079e908301826118a5565b505050565b600581815481106107b357600080fd5b600091825260209091200154905081565b600160208181526000928352604092839020805481840180548651600296821615610100026000190190911695909504601f810185900485028601850190965285855290949193929091908301828280156108605780601f1061083557610100808354040283529160200191610860565b820191906000526020600020905b81548152906001019060200180831161084357829003601f168201915b5050505050905082565b600054600160b01b900460ff168061088c5750600054600160a81b900460ff16155b1561091957610899610ecb565b60055460005b818110156108d25760058054806108b257fe5b60008281526020812082016000199081019190915501905560010161089f565b506108dc82610f00565b7fe3ad326cdb10e1201bcea3d96ccf2f5437ed71942548e5ba0aab68c9bd296fa48260405161090b9190611e49565b60405180910390a150610924565b610924600036611054565b50565b60035460408051808201909152600c81526b06164647265737320697320360a41b60208201526000916001600160a01b031690816109815760405162461bcd60e51b81526004016109789190611e86565b60405180910390fd5b5060035460408051808201909152600e81526d37b7363c9034b7333630ba34b7b760911b6020820152906001600160a01b031633146109d35760405162461bcd60e51b81526004016109789190611e86565b5060055415610a745760056000815481106109ea57fe5b6000918252602090912001546004556005546001811115610a515760005b60018203811015610a4f5760058160010181548110610a2357fe5b906000526020600020015460058281548110610a3b57fe5b600091825260209091200155600101610a08565b505b6005805480610a5c57fe5b60019003818190600052602060002001600090559055505b7fc46384622db26f8a6509e51f7cbe387b3fa773448f19fc9f070fcdc984cffb8e600454604051610aa59190611e99565b60405180910390a160045491505b5090565b600054600160a01b900460ff1615610b0d576040805162461bcd60e51b8152602060048201526014602482015273696e697469616c6973656420213d2066616c736560601b604482015290519081900360640190fd5b60008054600160a01b60ff60a01b19909116176001600160a01b0319166001600160a01b03831690811790915560408051918252517f9789733827840833afc031fb2ef9ab6894271f77bad2085687cf4ae5c7bee4db916020908290030190a150565b610b786102a1565b6001600160a01b0316336001600160a01b031614610bd4576040805162461bcd60e51b815260206004820152601460248201527337b7363c9030b2323932b9b9903ab83230ba32b960611b604482015290519081900360640190fd5b610c0d610c0883836040518060400160405280600e81526020016d20b2323932b9b9aab83230ba32b960911b8152506111d7565b611304565b610c178282611328565b5050565b600054600160b01b900460ff1680610c3d5750600054600160a81b900460ff16155b15610c5957610c4a610ecb565b610c54828261158f565b610c17565b610c17600036611054565b6003546001600160a01b031681565b6002546060908067ffffffffffffffff81118015610c9057600080fd5b50604051908082528060200260200182016040528015610cca57816020015b610cb76118e9565b815260200190600190039081610caf5790505b50915060005b81811015610d785760028181548110610ce557fe5b9060005260206000200160000160149054906101000a900463ffffffff1663ffffffff16838281518110610d1557fe5b6020026020010151602001818152505060028181548110610d3257fe5b60009182526020909120015483516001600160a01b0390911690849083908110610d5857fe5b60209081029190910101516001600160a01b039091169052600101610cd0565b505090565b600054600160a81b900460ff1681565b60045481565b610d9b610e6a565b600054600160a81b900460ff1615610dfa576040805162461bcd60e51b815260206004820152601a60248201527f616c726561647920696e2070726f64756374696f6e206d6f6465000000000000604482015290519081900360640190fd5b60008054600161ff0160a01b031916600160a81b1790556040805160076001609c1b01815290517f83af113638b5422f9e977cebc0aaf0eaf2188eb9a8baae7f9d46c42b33a1560c9181900360200190a1565b3d604051818101604052816000823e8215610e66578181f35b8181fd5b610e726102c6565b6001600160a01b0316336001600160a01b031614610ec9576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b565b600054600160b01b900460ff1615610ef857333014610ee657fe5b6000805460ff60b01b19169055610ec9565b610ec9610e6a565b60198151111560405180604001604052806008815260200167746f6f206d616e7960c01b81525090610f455760405162461bcd60e51b81526004016109789190611e86565b50805160045460005b828110156106aa5781848281518110610f6357fe5b602002602001015111158015610f8c57506000848281518110610f8257fe5b6020026020010151115b8015610fb05750600a61271004848281518110610fa557fe5b602002602001015111155b6040518060400160405280601e81526020017f616e6e75616c20696e666c6174696f6e206f7574206f6620626f756e64730000815250906110045760405162461bcd60e51b81526004016109789190611e86565b5083818151811061101157fe5b60200260200101519150600584828151811061102957fe5b6020908102919091018101518254600181810185556000948552929093209092019190915501610f4e565b61105c610e6a565b600082359050600060076001609c1b016001600160a01b0316636221a54b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156110a457600080fd5b505afa1580156110b8573d6000803e3d6000fd5b505050506040513d60208110156110ce57600080fd5b505160408051808201825242830180825282516020601f89018190048102820181019094528781529394509290918281019190889088908190840183828082843760009201829052509390945250506001600160e01b0319861681526001602081815260409092208451815584830151805191945061115293928501920190611900565b509050507fed948300a3694aa01d4a6b258bfd664350193d770c0b51f8387277f6d83ea3b68382878760405180856001600160e01b0319168152602001848152602001806020018281038252848482818152602001925080828437600083820152604051601f909101601f191690920182900397509095505050505050a15050505050565b600080826040516020018080602001828103825283818151815260200191508051906020019080838360005b8381101561121b578181015183820152602001611203565b50505050905090810190601f1680156112485780820380516001836020036101000a031916815260200191505b50925050506040516020818303038152906040528051906020012090506000805b86518110156112b05786818151811061127e57fe5b60200260200101518314156112a85785818151811061129957fe5b602002602001015191506112b0565b600101611269565b506001600160a01b0381166112fb576040805162461bcd60e51b815260206004820152600c60248201526b61646472657373207a65726f60a01b604482015290519081900360640190fd5b95945050505050565b7f714f205b2abd25bef1d06a1af944e38c113fe6160375c4e1d6d5cf28848e771955565b600061135683836040518060400160405280600981526020016824b7333630ba34b7b760b91b8152506111d7565b6003546040519192507f4bdd1012a7d55ed9afad8675a125e1b68c7c15f712c0f3d5cddac69c3b97980591611396916001600160a01b0316908490611d77565b60405180910390a1600380546001600160a01b0319166001600160a01b038316179055600254806113c8575050610c17565b60008167ffffffffffffffff811180156113e157600080fd5b5060405190808252806020026020018201604052801561140b578160200160208202803683370190505b50905060008267ffffffffffffffff8111801561142757600080fd5b50604051908082528060200260200182016040528015611451578160200160208202803683370190505b50905060005b8381101561157c5760006002828154811061146e57fe5b6000918252602080832060408051808201825293909101546001600160a01b038116808552600160a01b90910463ffffffff16928401929092528051637afadd3960e11b81529051929450611525938c938c939263f5f5ba72926004808301939192829003018186803b1580156114e457600080fd5b505afa1580156114f8573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526115209190810190611c5b565b6111d7565b84838151811061153157fe5b60200260200101906001600160a01b031690816001600160a01b031681525050806020015163ffffffff1683838151811061156857fe5b602090810291909101015250600101611457565b50611587828261158f565b505050505050565b80518251146040518060400160405280600f81526020016e0d8cadccee8d040dad2e6dac2e8c6d608b1b815250906115da5760405162461bcd60e51b81526004016109789190611e86565b50600a8251111560405180604001604052806008815260200167746f6f206d616e7960c01b815250906116205760405162461bcd60e51b81526004016109789190611e86565b50600254600090815b8181101561166657600280548061163c57fe5b600082815260209020810160001990810180546001600160c01b0319169055019055600101611629565b5060005b845181101561180e5761271084828151811061168257fe5b602002602001015111156040518060400160405280601781526020017f686967682073686172696e672070657263656e74616765000000000000000000815250906116e05760405162461bcd60e51b81526004016109789190611e86565b5060006001600160a01b03168582815181106116f857fe5b60200260200101516001600160a01b031614156040518060400160405280600c81526020016b06164647265737320697320360a41b8152509061174e5760405162461bcd60e51b81526004016109789190611e86565b5083818151811061175b57fe5b6020026020010151830192506002604051806040016040528087848151811061178057fe5b60200260200101516001600160a01b031681526020018684815181106117a257fe5b60209081029190910181015163ffffffff90811690925283546001808201865560009586529482902084519101805494909201516001600160a01b03199094166001600160a01b039091161763ffffffff60a01b1916600160a01b93909216929092021790550161166a565b5060408051808201909152601f81527f73756d2073686172696e672070657263656e74616765206e6f74203130302500602082015261271083146118655760405162461bcd60e51b81526004016109789190611e86565b507f5851f041454bd72003e19484c59ec9c1bd9c6f8df07679fd1e94412fb5880b448484604051611897929190611d91565b60405180910390a150505050565b50805460018160011615610100020316600290046000825580601f106118cb5750610924565b601f0160209004906000526020600020908101906109249190611984565b604080518082019091526000808252602082015290565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282611936576000855561197c565b82601f1061194f57805160ff191683800117855561197c565b8280016001018555821561197c579182015b8281111561197c578251825591602001919060010190611961565b50610ab39291505b5b80821115610ab35760008155600101611985565b600082601f8301126119a9578081fd5b813560206119be6119b983611edf565b611ebb565b82815281810190858301838502870184018810156119da578586fd5b855b85811015611a015781356119ef81611f29565b845292840192908401906001016119dc565b5090979650505050505050565b600082601f830112611a1e578081fd5b81356020611a2e6119b983611edf565b8281528181019085830183850287018401881015611a4a578586fd5b855b85811015611a0157813584529284019290840190600101611a4c565b600060208284031215611a79578081fd5b8135611a8481611f29565b9392505050565b60008060408385031215611a9d578081fd5b823567ffffffffffffffff80821115611ab4578283fd5b818501915085601f830112611ac7578283fd5b81356020611ad76119b983611edf565b82815281810190858301838502870184018b1015611af3578788fd5b8796505b84871015611b15578035835260019690960195918301918301611af7565b5096505086013592505080821115611b2b578283fd5b50611b3885828601611999565b9150509250929050565b60008060408385031215611b54578182fd5b823567ffffffffffffffff80821115611b6b578384fd5b818501915085601f830112611b7e578384fd5b81356020611b8e6119b983611edf565b82815281810190858301838502870184018b1015611baa578889fd5b8896505b84871015611bd5578035611bc181611f29565b835260019690960195918301918301611bae565b5096505086013592505080821115611beb578283fd5b50611b3885828601611a0e565b600060208284031215611c09578081fd5b813567ffffffffffffffff811115611c1f578182fd5b611c2b84828501611a0e565b949350505050565b600060208284031215611c44578081fd5b81356001600160e01b031981168114611a84578182fd5b600060208284031215611c6c578081fd5b815167ffffffffffffffff80821115611c83578283fd5b818401915084601f830112611c96578283fd5b815181811115611ca257fe5b611cb5601f8201601f1916602001611ebb565b9150808252856020828501011115611ccb578384fd5b611cdc816020840160208601611efd565b50949350505050565b600060208284031215611cf6578081fd5b5035919050565b6000815180845260208085019450808401835b83811015611d2c57815187529582019590820190600101611d10565b509495945050505050565b60008151808452611d4f816020860160208601611efd565b601f01601f19169290920160200192915050565b6001600160a01b0391909116815260200190565b6001600160a01b0392831681529116602082015260400190565b604080825283519082018190526000906020906060840190828701845b82811015611dd35781516001600160a01b031684529284019290840190600101611dae565b50505083810382850152611de78186611cfd565b9695505050505050565b602080825282518282018190526000919060409081850190868401855b82811015611e3c57815180516001600160a01b03168552860151868501529284019290850190600101611e0e565b5091979650505050505050565b600060208252611a846020830184611cfd565b901515815260200190565b6001600160a01b0392909216825263ffffffff16602082015260400190565b600060208252611a846020830184611d37565b90815260200190565b600083825260406020830152611c2b6040830184611d37565b60405181810167ffffffffffffffff81118282101715611ed757fe5b604052919050565b600067ffffffffffffffff821115611ef357fe5b5060209081020190565b60005b83811015611f18578181015183820152602001611f00565b838111156106aa5750506000910152565b6001600160a01b038116811461092457600080fdfea26469706673582212206e015ce89268710b69a880ab622154a8dff9421536a54fa1c807b6138e028cc964736f6c63430007060033