Created
January 8, 2020 17:38
-
-
Save thodges-gh/7976963e6fb813bd42f74155e556245f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma solidity 0.5.14; | |
/** | |
* @dev Wrappers over Solidity's arithmetic operations with added overflow | |
* checks. | |
* | |
* Arithmetic operations in Solidity wrap on overflow. This can easily result | |
* in bugs, because programmers usually assume that an overflow raises an | |
* error, which is the standard behavior in high level programming languages. | |
* `SafeMath` restores this intuition by reverting the transaction when an | |
* operation overflows. | |
* | |
* Using this library instead of the unchecked operations eliminates an entire | |
* class of bugs, so it's recommended to use it always. | |
*/ | |
library SafeMath { | |
/** | |
* @dev Returns the addition of two unsigned integers, reverting on | |
* overflow. | |
* | |
* Counterpart to Solidity's `+` operator. | |
* | |
* Requirements: | |
* - Addition cannot overflow. | |
*/ | |
function add(uint256 a, uint256 b) internal pure returns (uint256) { | |
uint256 c = a + b; | |
require(c >= a, "SafeMath: addition overflow"); | |
return c; | |
} | |
/** | |
* @dev Returns the subtraction of two unsigned integers, reverting on | |
* overflow (when the result is negative). | |
* | |
* Counterpart to Solidity's `-` operator. | |
* | |
* Requirements: | |
* - Subtraction cannot overflow. | |
*/ | |
function sub(uint256 a, uint256 b) internal pure returns (uint256) { | |
require(b <= a, "SafeMath: subtraction overflow"); | |
uint256 c = a - b; | |
return c; | |
} | |
/** | |
* @dev Returns the multiplication of two unsigned integers, reverting on | |
* overflow. | |
* | |
* Counterpart to Solidity's `*` operator. | |
* | |
* Requirements: | |
* - Multiplication cannot overflow. | |
*/ | |
function mul(uint256 a, uint256 b) internal pure returns (uint256) { | |
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the | |
// benefit is lost if 'b' is also tested. | |
// See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 | |
if (a == 0) { | |
return 0; | |
} | |
uint256 c = a * b; | |
require(c / a == b, "SafeMath: multiplication overflow"); | |
return c; | |
} | |
/** | |
* @dev Returns the integer division of two unsigned integers. Reverts on | |
* division by zero. The result is rounded towards zero. | |
* | |
* Counterpart to Solidity's `/` operator. Note: this function uses a | |
* `revert` opcode (which leaves remaining gas untouched) while Solidity | |
* uses an invalid opcode to revert (consuming all remaining gas). | |
* | |
* Requirements: | |
* - The divisor cannot be zero. | |
*/ | |
function div(uint256 a, uint256 b) internal pure returns (uint256) { | |
// Solidity only automatically asserts when dividing by 0 | |
require(b > 0, "SafeMath: division by zero"); | |
uint256 c = a / b; | |
// assert(a == b * c + a % b); // There is no case in which this doesn't hold | |
return c; | |
} | |
/** | |
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), | |
* Reverts when dividing by zero. | |
* | |
* Counterpart to Solidity's `%` operator. This function uses a `revert` | |
* opcode (which leaves remaining gas untouched) while Solidity uses an | |
* invalid opcode to revert (consuming all remaining gas). | |
* | |
* Requirements: | |
* - The divisor cannot be zero. | |
*/ | |
function mod(uint256 a, uint256 b) internal pure returns (uint256) { | |
require(b != 0, "SafeMath: modulo by zero"); | |
return a % b; | |
} | |
} | |
library SignedSafeMath { | |
/** | |
* @dev Adds two int256s and makes sure the result doesn't overflow. Signed | |
* integers aren't supported by the SafeMath library, thus this method | |
* @param _a The first number to be added | |
* @param _a The second number to be added | |
*/ | |
function add(int256 _a, int256 _b) | |
internal | |
pure | |
returns (int256) | |
{ | |
// solium-disable-next-line zeppelin/no-arithmetic-operations | |
int256 c = _a + _b; | |
require((_b >= 0 && c >= _a) || (_b < 0 && c < _a), "SignedSafeMath: addition overflow"); | |
return c; | |
} | |
} | |
library Median { | |
using SafeMath for uint256; | |
using SignedSafeMath for int256; | |
/** | |
* @dev Returns the sorted middle, or the average of the two middle indexed | |
* items if the array has an even number of elements | |
* @param _list The list of elements to compare | |
*/ | |
function calculate(int256[] memory _list) | |
internal | |
pure | |
returns (int256) | |
{ | |
uint256 answerLength = _list.length; | |
uint256 middleIndex = answerLength.div(2); | |
if (answerLength % 2 == 0) { | |
int256 median1 = quickselect(copy(_list), middleIndex); | |
int256 median2 = quickselect(_list, middleIndex.add(1)); // quickselect is 1 indexed | |
int256 remainder = (median1 % 2 + median2 % 2) / 2; | |
return (median1 / 2).add(median2 / 2).add(remainder); // signed integers are not supported by SafeMath | |
} else { | |
return quickselect(_list, middleIndex.add(1)); // quickselect is 1 indexed | |
} | |
} | |
/** | |
* @dev Returns the kth value of the ordered array | |
* See: http://www.cs.yale.edu/homes/aspnes/pinewiki/QuickSelect.html | |
* @param _a The list of elements to pull from | |
* @param _k The index, 1 based, of the elements you want to pull from when ordered | |
*/ | |
function quickselect(int256[] memory _a, uint256 _k) | |
private | |
pure | |
returns (int256) | |
{ | |
int256[] memory a = _a; | |
uint256 k = _k; | |
uint256 aLen = a.length; | |
int256[] memory a1 = new int256[](aLen); | |
int256[] memory a2 = new int256[](aLen); | |
uint256 a1Len; | |
uint256 a2Len; | |
int256 pivot; | |
uint256 i; | |
while (true) { | |
pivot = a[aLen.div(2)]; | |
a1Len = 0; | |
a2Len = 0; | |
for (i = 0; i < aLen; i++) { | |
if (a[i] < pivot) { | |
a1[a1Len] = a[i]; | |
a1Len++; | |
} else if (a[i] > pivot) { | |
a2[a2Len] = a[i]; | |
a2Len++; | |
} | |
} | |
if (k <= a1Len) { | |
aLen = a1Len; | |
(a, a1) = swap(a, a1); | |
} else if (k > (aLen.sub(a2Len))) { | |
k = k.sub(aLen.sub(a2Len)); | |
aLen = a2Len; | |
(a, a2) = swap(a, a2); | |
} else { | |
return pivot; | |
} | |
} | |
} | |
/** | |
* @dev Swaps the pointers to two uint256 arrays in memory | |
* @param _a The pointer to the first in memory array | |
* @param _b The pointer to the second in memory array | |
*/ | |
function swap(int256[] memory _a, int256[] memory _b) | |
private | |
pure | |
returns(int256[] memory, int256[] memory) | |
{ | |
return (_b, _a); | |
} | |
/** | |
* @dev Makes an in memory copy of the array passed in | |
* @param _list The pointer to the array to be copied | |
*/ | |
function copy(int256[] memory _list) | |
private | |
pure | |
returns(int256[] memory) | |
{ | |
int256[] memory list2 = new int256[](_list.length); | |
for (uint256 i = 0; i < _list.length; i++) { | |
list2[i] = _list[i]; | |
} | |
return list2; | |
} | |
} | |
/** | |
* @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. | |
* | |
* This library is a version of Open Zeppelin's SafeMath, modified to support | |
* unsigned 128 bit integers. | |
*/ | |
library SafeMath128 { | |
/** | |
* @dev Returns the addition of two unsigned integers, reverting on | |
* overflow. | |
* | |
* Counterpart to Solidity's `+` operator. | |
* | |
* Requirements: | |
* - Addition cannot overflow. | |
*/ | |
function add(uint128 a, uint128 b) internal pure returns (uint128) { | |
uint128 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(uint128 a, uint128 b) internal pure returns (uint128) { | |
require(b <= a, "SafeMath: subtraction overflow"); | |
uint128 c = a - b; | |
return c; | |
} | |
/** | |
* @dev Returns the multiplication of two unsigned integers, reverting on | |
* overflow. | |
* | |
* Counterpart to Solidity's `*` operator. | |
* | |
* Requirements: | |
* - Multiplication cannot overflow. | |
*/ | |
function mul(uint128 a, uint128 b) internal pure returns (uint128) { | |
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the | |
// benefit is lost if 'b' is also tested. | |
// See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 | |
if (a == 0) { | |
return 0; | |
} | |
uint128 c = a * b; | |
require(c / a == b, "SafeMath: multiplication overflow"); | |
return c; | |
} | |
/** | |
* @dev Returns the integer division of two unsigned integers. Reverts on | |
* division by zero. The result is rounded towards zero. | |
* | |
* Counterpart to Solidity's `/` operator. Note: this function uses a | |
* `revert` opcode (which leaves remaining gas untouched) while Solidity | |
* uses an invalid opcode to revert (consuming all remaining gas). | |
* | |
* Requirements: | |
* - The divisor cannot be zero. | |
*/ | |
function div(uint128 a, uint128 b) internal pure returns (uint128) { | |
// Solidity only automatically asserts when dividing by 0 | |
require(b > 0, "SafeMath: division by zero"); | |
uint128 c = a / b; | |
// assert(a == b * c + a % b); // There is no case in which this doesn't hold | |
return c; | |
} | |
/** | |
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), | |
* Reverts when dividing by zero. | |
* | |
* Counterpart to Solidity's `%` operator. This function uses a `revert` | |
* opcode (which leaves remaining gas untouched) while Solidity uses an | |
* invalid opcode to revert (consuming all remaining gas). | |
* | |
* Requirements: | |
* - The divisor cannot be zero. | |
*/ | |
function mod(uint128 a, uint128 b) internal pure returns (uint128) { | |
require(b != 0, "SafeMath: modulo by zero"); | |
return a % b; | |
} | |
} | |
/** | |
* @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. | |
* | |
* This library is a version of Open Zeppelin's SafeMath, modified to support | |
* unsigned 64 bit integers. | |
*/ | |
library SafeMath64 { | |
/** | |
* @dev Returns the addition of two unsigned integers, reverting on | |
* overflow. | |
* | |
* Counterpart to Solidity's `+` operator. | |
* | |
* Requirements: | |
* - Addition cannot overflow. | |
*/ | |
function add(uint64 a, uint64 b) internal pure returns (uint64) { | |
uint64 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(uint64 a, uint64 b) internal pure returns (uint64) { | |
require(b <= a, "SafeMath: subtraction overflow"); | |
uint64 c = a - b; | |
return c; | |
} | |
/** | |
* @dev Returns the multiplication of two unsigned integers, reverting on | |
* overflow. | |
* | |
* Counterpart to Solidity's `*` operator. | |
* | |
* Requirements: | |
* - Multiplication cannot overflow. | |
*/ | |
function mul(uint64 a, uint64 b) internal pure returns (uint64) { | |
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the | |
// benefit is lost if 'b' is also tested. | |
// See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 | |
if (a == 0) { | |
return 0; | |
} | |
uint64 c = a * b; | |
require(c / a == b, "SafeMath: multiplication overflow"); | |
return c; | |
} | |
/** | |
* @dev Returns the integer division of two unsigned integers. Reverts on | |
* division by zero. The result is rounded towards zero. | |
* | |
* Counterpart to Solidity's `/` operator. Note: this function uses a | |
* `revert` opcode (which leaves remaining gas untouched) while Solidity | |
* uses an invalid opcode to revert (consuming all remaining gas). | |
* | |
* Requirements: | |
* - The divisor cannot be zero. | |
*/ | |
function div(uint64 a, uint64 b) internal pure returns (uint64) { | |
// Solidity only automatically asserts when dividing by 0 | |
require(b > 0, "SafeMath: division by zero"); | |
uint64 c = a / b; | |
// assert(a == b * c + a % b); // There is no case in which this doesn't hold | |
return c; | |
} | |
/** | |
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), | |
* Reverts when dividing by zero. | |
* | |
* Counterpart to Solidity's `%` operator. This function uses a `revert` | |
* opcode (which leaves remaining gas untouched) while Solidity uses an | |
* invalid opcode to revert (consuming all remaining gas). | |
* | |
* Requirements: | |
* - The divisor cannot be zero. | |
*/ | |
function mod(uint64 a, uint64 b) internal pure returns (uint64) { | |
require(b != 0, "SafeMath: modulo by zero"); | |
return a % b; | |
} | |
} | |
/** | |
* @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. | |
* | |
* This library is a version of Open Zeppelin's SafeMath, modified to support | |
* unsigned 32 bit integers. | |
*/ | |
library SafeMath32 { | |
/** | |
* @dev Returns the addition of two unsigned integers, reverting on | |
* overflow. | |
* | |
* Counterpart to Solidity's `+` operator. | |
* | |
* Requirements: | |
* - Addition cannot overflow. | |
*/ | |
function add(uint32 a, uint32 b) internal pure returns (uint32) { | |
uint32 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(uint32 a, uint32 b) internal pure returns (uint32) { | |
require(b <= a, "SafeMath: subtraction overflow"); | |
uint32 c = a - b; | |
return c; | |
} | |
/** | |
* @dev Returns the multiplication of two unsigned integers, reverting on | |
* overflow. | |
* | |
* Counterpart to Solidity's `*` operator. | |
* | |
* Requirements: | |
* - Multiplication cannot overflow. | |
*/ | |
function mul(uint32 a, uint32 b) internal pure returns (uint32) { | |
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the | |
// benefit is lost if 'b' is also tested. | |
// See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 | |
if (a == 0) { | |
return 0; | |
} | |
uint32 c = a * b; | |
require(c / a == b, "SafeMath: multiplication overflow"); | |
return c; | |
} | |
/** | |
* @dev Returns the integer division of two unsigned integers. Reverts on | |
* division by zero. The result is rounded towards zero. | |
* | |
* Counterpart to Solidity's `/` operator. Note: this function uses a | |
* `revert` opcode (which leaves remaining gas untouched) while Solidity | |
* uses an invalid opcode to revert (consuming all remaining gas). | |
* | |
* Requirements: | |
* - The divisor cannot be zero. | |
*/ | |
function div(uint32 a, uint32 b) internal pure returns (uint32) { | |
// Solidity only automatically asserts when dividing by 0 | |
require(b > 0, "SafeMath: division by zero"); | |
uint32 c = a / b; | |
// assert(a == b * c + a % b); // There is no case in which this doesn't hold | |
return c; | |
} | |
/** | |
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), | |
* Reverts when dividing by zero. | |
* | |
* Counterpart to Solidity's `%` operator. This function uses a `revert` | |
* opcode (which leaves remaining gas untouched) while Solidity uses an | |
* invalid opcode to revert (consuming all remaining gas). | |
* | |
* Requirements: | |
* - The divisor cannot be zero. | |
*/ | |
function mod(uint32 a, uint32 b) internal pure returns (uint32) { | |
require(b != 0, "SafeMath: modulo by zero"); | |
return a % b; | |
} | |
} | |
interface LinkTokenInterface { | |
function allowance(address owner, address spender) external returns (uint256 remaining); | |
function approve(address spender, uint256 value) external returns (bool success); | |
function balanceOf(address owner) external returns (uint256 balance); | |
function decimals() external returns (uint8 decimalPlaces); | |
function decreaseApproval(address spender, uint256 addedValue) external returns (bool success); | |
function increaseApproval(address spender, uint256 subtractedValue) external; | |
function name() external returns (string memory tokenName); | |
function symbol() external returns (string memory tokenSymbol); | |
function totalSupply() external returns (uint256 totalTokensIssued); | |
function transfer(address to, uint256 value) external returns (bool success); | |
function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool success); | |
function transferFrom(address from, address to, uint256 value) external returns (bool success); | |
} | |
interface WithdrawalInterface { | |
/** | |
* @notice transfer LINK held by the contract belonging to msg.sender to | |
* another address | |
* @param recipient is the address to send the LINK to | |
* @param amount is the amount of LINK to send | |
*/ | |
function withdraw(address recipient, uint256 amount) external; | |
/** | |
* @notice query the available amount of LINK to withdraw by msg.sender | |
*/ | |
function withdrawable() external view returns (uint256); | |
} | |
interface AggregatorInterface { | |
function latestAnswer() external view returns (int256); | |
function latestTimestamp() external view returns (uint256); | |
function latestRound() external view returns (uint256); | |
function getAnswer(uint256 roundId) external view returns (int256); | |
function getTimestamp(uint256 roundId) external view returns (uint256); | |
event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 timestamp); | |
event NewRound(uint256 indexed roundId, address indexed startedBy); | |
} | |
/** | |
* @title The Owned contract | |
* @notice A contract with helpers for basic contract ownership. | |
*/ | |
contract Owned { | |
address public owner; | |
address private pendingOwner; | |
event OwnershipTransferRequested( | |
address indexed from, | |
address indexed to | |
); | |
event OwnershipTransfered( | |
address indexed from, | |
address indexed to | |
); | |
constructor() public { | |
owner = msg.sender; | |
} | |
/** | |
* @dev Allows an owner to begin transferring ownership to a new address, | |
* pending. | |
*/ | |
function transferOwnership(address _to) | |
external | |
onlyOwner() | |
{ | |
pendingOwner = _to; | |
emit OwnershipTransferRequested(owner, _to); | |
} | |
/** | |
* @dev Allows an ownership transfer to be completed by the recipient. | |
*/ | |
function acceptOwnership() | |
external | |
{ | |
require(msg.sender == pendingOwner, "Must be proposed owner"); | |
address oldOwner = owner; | |
owner = msg.sender; | |
pendingOwner = address(0); | |
emit OwnershipTransfered(oldOwner, msg.sender); | |
} | |
/** | |
* @dev Reverts if called by anyone other than the contract owner. | |
*/ | |
modifier onlyOwner() { | |
require(msg.sender == owner, "Only callable by owner"); | |
_; | |
} | |
} | |
/** | |
* @title The Prepaid Aggregator contract | |
* @notice Node handles aggregating data pushed in from off-chain, and unlocks | |
* payment for oracles as they report. Oracles' submissions are gathered in | |
* rounds, with each round aggregating the submissions for each oracle into a | |
* single answer. The latest aggregated answer is exposed as well as historical | |
* answers and their updated at timestamp. | |
*/ | |
contract PrepaidAggregator is AggregatorInterface, Owned, WithdrawalInterface { | |
using SafeMath for uint256; | |
using SafeMath128 for uint128; | |
using SafeMath64 for uint64; | |
using SafeMath32 for uint32; | |
struct Round { | |
int256 answer; | |
uint64 startedAt; | |
uint64 updatedAt; | |
uint32 answeredInRound; | |
RoundDetails details; | |
} | |
struct RoundDetails { | |
int256[] answers; | |
uint32 maxAnswers; | |
uint32 minAnswers; | |
uint32 timeout; | |
uint128 paymentAmount; | |
} | |
struct OracleStatus { | |
uint128 withdrawable; | |
uint32 startingRound; | |
uint32 endingRound; | |
uint32 lastReportedRound; | |
uint32 lastStartedRound; | |
int256 latestAnswer; | |
} | |
uint128 public allocatedFunds; | |
uint128 public availableFunds; | |
// Round related params | |
uint128 public paymentAmount; | |
uint32 public oracleCount; | |
uint32 public maxAnswerCount; | |
uint32 public minAnswerCount; | |
uint32 public restartDelay; | |
uint32 public timeout; | |
uint8 public decimals; | |
bytes32 public description; | |
uint32 private reportingRoundId; | |
uint32 private latestRoundId; | |
LinkTokenInterface private LINK; | |
mapping(address => OracleStatus) private oracles; | |
mapping(uint32 => Round) private rounds; | |
event AvailableFundsUpdated(uint256 indexed amount); | |
event RoundDetailsUpdated( | |
uint128 indexed paymentAmount, | |
uint32 indexed minAnswerCount, | |
uint32 indexed maxAnswerCount, | |
uint32 restartDelay, | |
uint32 timeout // measured in seconds | |
); | |
event OracleAdded(address indexed oracle); | |
event OracleRemoved(address indexed oracle); | |
uint32 constant private ROUND_MAX = 2**32-1; | |
/** | |
* @notice Deploy with the address of the LINK token and initial payment amount | |
* @dev Sets the LinkToken address and amount of LINK paid | |
* @param _link The address of the LINK token | |
* @param _paymentAmount The amount paid of LINK paid to each oracle per response | |
* @param _timeout is the number of seconds after the previous round that are | |
* allowed to lapse before allowing an oracle to skip an unfinished round | |
*/ | |
constructor( | |
address _link, | |
uint128 _paymentAmount, | |
uint32 _timeout, | |
uint8 _decimals, | |
bytes32 _description | |
) public { | |
LINK = LinkTokenInterface(_link); | |
paymentAmount = _paymentAmount; | |
timeout = _timeout; | |
decimals = _decimals; | |
description = _description; | |
} | |
/** | |
* @notice called by oracles when they have witnessed a need to update | |
* @param _round is the ID of the round this answer pertains to | |
* @param _answer is the updated data that the oracle is submitting | |
*/ | |
function updateAnswer(uint256 _round, int256 _answer) | |
external | |
onlyValidRoundId(uint32(_round)) | |
onlyValidOracleRound(uint32(_round)) | |
{ | |
startNewRound(uint32(_round)); | |
recordSubmission(_answer, uint32(_round)); | |
updateRoundAnswer(uint32(_round)); | |
payOracle(uint32(_round)); | |
deleteRound(uint32(_round)); | |
} | |
/** | |
* @notice called by the owner to add a new Oracle and update the round | |
* related parameters | |
* @param _oracle is the address of the new Oracle being added | |
* @param _minAnswers is the new minimum answer count for each round | |
* @param _maxAnswers is the new maximum answer count for each round | |
* @param _restartDelay is the number of rounds an Oracle has to wait before | |
* they can initiate a round | |
*/ | |
function addOracle( | |
address _oracle, | |
uint32 _minAnswers, | |
uint32 _maxAnswers, | |
uint32 _restartDelay | |
) | |
external | |
onlyOwner() | |
onlyUnenabledAddress(_oracle) | |
{ | |
require(oracleCount < 42, "cannot add more than 42 oracles"); | |
oracles[_oracle].startingRound = getStartingRound(_oracle); | |
oracles[_oracle].endingRound = ROUND_MAX; | |
oracleCount += 1; | |
emit OracleAdded(_oracle); | |
updateFutureRounds(paymentAmount, _minAnswers, _maxAnswers, _restartDelay, timeout); | |
} | |
/** | |
* @notice called by the owner to remove an Oracle and update the round | |
* related parameters | |
* @param _oracle is the address of the Oracle being removed | |
* @param _minAnswers is the new minimum answer count for each round | |
* @param _maxAnswers is the new maximum answer count for each round | |
* @param _restartDelay is the number of rounds an Oracle has to wait before | |
* they can initiate a round | |
*/ | |
function removeOracle( | |
address _oracle, | |
uint32 _minAnswers, | |
uint32 _maxAnswers, | |
uint32 _restartDelay | |
) | |
external | |
onlyOwner() | |
onlyEnabledAddress(_oracle) | |
{ | |
oracleCount -= 1; | |
oracles[_oracle].endingRound = reportingRoundId; | |
emit OracleRemoved(_oracle); | |
updateFutureRounds(paymentAmount, _minAnswers, _maxAnswers, _restartDelay, timeout); | |
} | |
/** | |
* @notice update the round and payment related parameters for subsequent | |
* rounds | |
* @param _newPaymentAmount is the payment amount for subsequent rounds | |
* @param _minAnswers is the new minimum answer count for each round | |
* @param _maxAnswers is the new maximum answer count for each round | |
* @param _restartDelay is the number of rounds an Oracle has to wait before | |
* they can initiate a round | |
*/ | |
function updateFutureRounds( | |
uint128 _newPaymentAmount, | |
uint32 _minAnswers, | |
uint32 _maxAnswers, | |
uint32 _restartDelay, | |
uint32 _timeout | |
) | |
public | |
onlyOwner() | |
onlyValidRange(_minAnswers, _maxAnswers, _restartDelay) | |
{ | |
paymentAmount = _newPaymentAmount; | |
minAnswerCount = _minAnswers; | |
maxAnswerCount = _maxAnswers; | |
restartDelay = _restartDelay; | |
timeout = _timeout; | |
emit RoundDetailsUpdated( | |
paymentAmount, | |
_minAnswers, | |
_maxAnswers, | |
_restartDelay, | |
_timeout | |
); | |
} | |
/** | |
* @notice recalculate the amount of LINK available for payouts | |
*/ | |
function updateAvailableFunds() | |
public | |
{ | |
uint128 pastAvailableFunds = availableFunds; | |
uint256 available = LINK.balanceOf(address(this)).sub(allocatedFunds); | |
availableFunds = uint128(available); | |
if (pastAvailableFunds != available) { | |
emit AvailableFundsUpdated(available); | |
} | |
} | |
/** | |
* @notice query the available amount of LINK for an oracle to withdraw | |
*/ | |
function withdrawable() | |
external | |
view | |
returns (uint256) | |
{ | |
return oracles[msg.sender].withdrawable; | |
} | |
/** | |
* @notice get the most recently reported answer | |
*/ | |
function latestAnswer() | |
external | |
view | |
returns (int256) | |
{ | |
return rounds[latestRoundId].answer; | |
} | |
/** | |
* @notice get the most recent updated at timestamp | |
*/ | |
function latestTimestamp() | |
external | |
view | |
returns (uint256) | |
{ | |
return rounds[latestRoundId].updatedAt; | |
} | |
/** | |
* @notice get the ID of the last updated round | |
*/ | |
function latestRound() | |
external | |
view | |
returns (uint256) | |
{ | |
return latestRoundId; | |
} | |
/** | |
* @notice get the ID of the round most recently reported on | |
*/ | |
function reportingRound() | |
external | |
view | |
returns (uint256) | |
{ | |
return reportingRoundId; | |
} | |
/** | |
* @notice get past rounds answers | |
* @param _roundId the round number to retrieve the answer for | |
*/ | |
function getAnswer(uint256 _roundId) | |
external | |
view | |
returns (int256) | |
{ | |
return rounds[uint32(_roundId)].answer; | |
} | |
/** | |
* @notice get timestamp when an answer was last updated | |
* @param _roundId the round number to retrieve the updated timestamp for | |
*/ | |
function getTimestamp(uint256 _roundId) | |
external | |
view | |
returns (uint256) | |
{ | |
return rounds[uint32(_roundId)].updatedAt; | |
} | |
/** | |
* @notice get the timed out status of a given round | |
* @param _roundId the round number to retrieve the timed out status for | |
*/ | |
function getTimedOutStatus(uint256 _roundId) | |
external | |
view | |
returns (bool) | |
{ | |
uint32 roundId = uint32(_roundId); | |
uint32 answeredIn = rounds[roundId].answeredInRound; | |
return answeredIn > 0 && answeredIn != roundId; | |
} | |
/** | |
* @notice get the round ID that an answer was originally reported in | |
* @param _roundId the round number to retrieve the answer for | |
*/ | |
function getOriginatingRoundOfAnswer(uint256 _roundId) | |
external | |
view | |
returns (uint256) | |
{ | |
return rounds[uint32(_roundId)].answeredInRound; | |
} | |
/** | |
* @notice transfers the oracle's LINK to another address | |
* @param _recipient is the address to send the LINK to | |
* @param _amount is the amount of LINK to send | |
*/ | |
function withdraw(address _recipient, uint256 _amount) | |
external | |
{ | |
uint128 amount = uint128(_amount); | |
uint128 available = oracles[msg.sender].withdrawable; | |
require(available >= amount, "Insufficient balance"); | |
oracles[msg.sender].withdrawable = available.sub(amount); | |
allocatedFunds = allocatedFunds.sub(amount); | |
assert(LINK.transfer(_recipient, uint256(amount))); | |
} | |
/** | |
* @notice transfers the owner's LINK to another address | |
* @param _recipient is the address to send the LINK to | |
* @param _amount is the amount of LINK to send | |
*/ | |
function withdrawFunds(address _recipient, uint256 _amount) | |
external | |
onlyOwner() | |
{ | |
require(availableFunds >= _amount, "Insufficient funds"); | |
require(LINK.transfer(_recipient, _amount), "LINK transfer failed"); | |
updateAvailableFunds(); | |
} | |
/** | |
* @notice get the latest submission for any oracle | |
* @param _oracle is the address to lookup the latest submission for | |
*/ | |
function latestSubmission(address _oracle) | |
external | |
view | |
returns (int256, uint256) | |
{ | |
return (oracles[_oracle].latestAnswer, oracles[_oracle].lastReportedRound); | |
} | |
/** | |
* @notice called through LINK's transferAndCall to update available funds | |
* in the same transaction as the funds were transfered to the aggregator | |
*/ | |
function onTokenTransfer(address, uint256, bytes memory) public { | |
updateAvailableFunds(); | |
} | |
/** | |
* Private | |
*/ | |
function startNewRound(uint32 _id) | |
private | |
ifNewRound(_id) | |
ifDelayed(_id) | |
{ | |
updateTimedOutRoundInfo(_id.sub(1)); | |
reportingRoundId = _id; | |
rounds[_id].details.maxAnswers = maxAnswerCount; | |
rounds[_id].details.minAnswers = minAnswerCount; | |
rounds[_id].details.paymentAmount = paymentAmount; | |
rounds[_id].details.timeout = timeout; | |
rounds[_id].startedAt = uint64(block.timestamp); | |
oracles[msg.sender].lastStartedRound = _id; | |
emit NewRound(_id, msg.sender); | |
} | |
function updateTimedOutRoundInfo(uint32 _id) | |
private | |
ifTimedOut(_id) | |
onlyWithPreviousAnswer(_id) | |
{ | |
uint32 prevId = _id.sub(1); | |
rounds[_id].answer = rounds[prevId].answer; | |
rounds[_id].answeredInRound = rounds[prevId].answeredInRound; | |
rounds[_id].updatedAt = uint64(block.timestamp); | |
delete rounds[_id].details; | |
} | |
function updateRoundAnswer(uint32 _id) | |
private | |
ifMinAnswersReceived(_id) | |
{ | |
int256 newAnswer = Median.calculate(rounds[_id].details.answers); | |
rounds[_id].answer = newAnswer; | |
rounds[_id].updatedAt = uint64(block.timestamp); | |
rounds[_id].answeredInRound = _id; | |
latestRoundId = _id; | |
emit AnswerUpdated(newAnswer, _id, now); | |
} | |
function payOracle(uint32 _id) | |
private | |
{ | |
uint128 payment = rounds[_id].details.paymentAmount; | |
uint128 available = availableFunds.sub(payment); | |
availableFunds = available; | |
allocatedFunds = allocatedFunds.add(payment); | |
oracles[msg.sender].withdrawable = oracles[msg.sender].withdrawable.add(payment); | |
emit AvailableFundsUpdated(available); | |
} | |
function recordSubmission(int256 _answer, uint32 _id) | |
private | |
onlyWhenAcceptingAnswers(_id) | |
{ | |
rounds[_id].details.answers.push(_answer); | |
oracles[msg.sender].lastReportedRound = _id; | |
oracles[msg.sender].latestAnswer = _answer; | |
} | |
function deleteRound(uint32 _id) | |
private | |
ifMaxAnswersReceived(_id) | |
{ | |
delete rounds[_id].details; | |
} | |
function timedOut(uint32 _id) | |
private | |
returns (bool) | |
{ | |
uint64 startedAt = rounds[_id].startedAt; | |
uint32 roundTimeout = rounds[_id].details.timeout; | |
return startedAt > 0 && roundTimeout > 0 && startedAt.add(roundTimeout) < block.timestamp; | |
} | |
function finished(uint32 _id) | |
private | |
returns (bool) | |
{ | |
return rounds[_id].updatedAt > 0; | |
} | |
function getStartingRound(address _oracle) | |
private | |
returns (uint32) | |
{ | |
uint32 currentRound = reportingRoundId; | |
if (currentRound != 0 && currentRound == oracles[_oracle].endingRound) { | |
return currentRound; | |
} | |
return currentRound.add(1); | |
} | |
/** | |
* Modifiers | |
*/ | |
modifier onlyValidOracleRound(uint32 _id) { | |
uint32 startingRound = oracles[msg.sender].startingRound; | |
require(startingRound != 0, "Only updatable by whitelisted oracles"); | |
require(startingRound <= _id, "New oracles cannot participate in in-progress rounds"); | |
require(oracles[msg.sender].endingRound >= _id, "Oracle has been removed from whitelist"); | |
require(oracles[msg.sender].lastReportedRound < _id, "Cannot update round reports"); | |
_; | |
} | |
modifier ifMinAnswersReceived(uint32 _id) { | |
if (rounds[_id].details.answers.length >= rounds[_id].details.minAnswers) { | |
_; | |
} | |
} | |
modifier ifMaxAnswersReceived(uint32 _id) { | |
if (rounds[_id].details.answers.length == rounds[_id].details.maxAnswers) { | |
_; | |
} | |
} | |
modifier onlyWhenAcceptingAnswers(uint32 _id) { | |
require(rounds[_id].details.maxAnswers != 0, "Round not currently eligible for reporting"); | |
_; | |
} | |
modifier ifNewRound(uint32 _id) { | |
if (_id == reportingRoundId.add(1)) { | |
_; | |
} | |
} | |
modifier ifDelayed(uint32 _id) { | |
uint256 lastStarted = oracles[msg.sender].lastStartedRound; | |
if (_id > lastStarted + restartDelay || lastStarted == 0) { | |
_; | |
} | |
} | |
modifier onlyValidRoundId(uint32 _id) { | |
require(_id == reportingRoundId || _id == reportingRoundId.add(1), "Must report on current round"); | |
require(_id == 1 || finished(_id.sub(1)) || timedOut(_id.sub(1)), "Not eligible to bump round"); | |
_; | |
} | |
modifier onlyValidRange(uint32 _min, uint32 _max, uint32 _restartDelay) { | |
uint32 oracleNum = oracleCount; // Save on storage reads | |
require(oracleNum >= _max, "Cannot have the answer max higher oracle count"); | |
require(_max >= _min, "Cannot have the answer minimum higher the max"); | |
require(oracleNum == 0 || oracleNum > _restartDelay, "Restart delay must be less than oracle count"); | |
_; | |
} | |
modifier onlyUnenabledAddress(address _oracle) { | |
require(oracles[_oracle].endingRound != ROUND_MAX, "Address is already recorded as an oracle"); | |
_; | |
} | |
modifier onlyEnabledAddress(address _oracle) { | |
require(oracles[_oracle].endingRound == ROUND_MAX, "Address is not a whitelisted oracle"); | |
_; | |
} | |
modifier ifTimedOut(uint32 _id) { | |
if (timedOut(_id)) { | |
_; | |
} | |
} | |
modifier onlyWithPreviousAnswer(uint32 _id) { | |
require(rounds[_id.sub(1)].updatedAt != 0, "Must have a previous answer to pull from"); | |
_; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment