Transaction Hash:
Block:
12020461 at Mar-12-2021 12:23:47 AM +UTC
Transaction Fee:
0.039325436232943057 ETH
$73.92
Gas Used:
83,179 Gas / 472.780824883 Gwei
Emitted Events:
9 |
Aggregator.ChainlinkFulfilled( id=C3CAB98CC2136D06D2C8AD2F85313AE51A90006CD3B484195000EB109BB7FE06 )
|
10 |
Aggregator.ResponseReceived( response=28721000000, answerId=1359, sender=[Receiver] Oracle )
|
11 |
Aggregator.AnswerUpdated( current=28698095000, roundId=1359, timestamp=1615508627 )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x0821f21F...525495D06 | (Chainlink: BNB - USD Aggregator) | ||||
0x3A8B67C0...Ee51BDe3F | |||||
0x829BD824...93333A830
Miner
| (F2Pool Old) | 4,869.481087603900856264 Eth | 4,869.520413040133799321 Eth | 0.039325436232943057 | |
0xa932fb7c...C5744F4FC |
20.288543511171884492 Eth
Nonce: 128500
|
20.249218074938941435 Eth
Nonce: 128501
| 0.039325436232943057 |
Execution Trace
Oracle.fulfillOracleRequest( _requestId=C3CAB98CC2136D06D2C8AD2F85313AE51A90006CD3B484195000EB109BB7FE06, _payment=750000000000000000, _callbackAddress=0x0821f21F21C325AE39557CA83B6B4df525495D06, _callbackFunctionId=System.Byte[], _expiration=1615508496, _data=00000000000000000000000000000000000000000000000000000006AFE7AE40 ) => ( True )
-
Aggregator.chainlinkCallback( _clRequestId=C3CAB98CC2136D06D2C8AD2F85313AE51A90006CD3B484195000EB109BB7FE06, _response=28721000000 )
fulfillOracleRequest[Oracle (ln:348)]
add[Oracle (ln:370)]
gasleft[Oracle (ln:372)]
call[Oracle (ln:376)]
File 1 of 2: Oracle
File 2 of 2: Aggregator
/* * ___ ___ ___ ___ * `MM `MMb dMM' `MM * MM MMM. ,PMM MM * ____MM M`Mb d'MM ___ MM __ ____ ___ __ ____ _____ ___ __ __ * 6MMMMMM M YM. ,P MM 6MMMMb MM d' 6MMMMb `MM 6MM 6MMMMb\ 6MMMMMb `MM 6MM 6MMbMMM * 6M' `MM M `Mb d' MM 8M' `Mb MM d' 6M' `Mb MM69 " MM' ` 6M' `Mb MM69 " 6M'`Mb * MM MM M YM.P MM ,oMM MM d' MM MM MM' YM. MM MM MM' MM MM * MM MM M `Mb' MM ,6MM9'MM MMdM. MMMMMMMM MM YMMMMb MM MM MM YM.,M9 * MM MM M YP MM MM' MM MMPYM. MM MM `Mb MM MM MM YMM9 * YM. ,MM M `' MM MM. ,MM MM YM. YM d9 MM L ,MM 68b YM. ,M9 MM (M * YMMMMMM__M_ _MM_`YMMM9'Yb_MM_ YM._YMMMM9 _MM_ MYMMMM9 Y89 YMMMMM9 _MM_ YMMMMb. * 6M Yb * YM. d9 * YMMMM9 */ pragma solidity 0.4.24; /** * @title Ownable * @dev The Ownable contract has an owner address, and provides basic authorization control * functions, this simplifies the implementation of "user permissions". */ contract Ownable { address public owner; event OwnershipRenounced(address indexed previousOwner); event OwnershipTransferred( address indexed previousOwner, address indexed newOwner ); /** * @dev The Ownable constructor sets the original `owner` of the contract to the sender * account. */ constructor() public { owner = msg.sender; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(msg.sender == owner); _; } /** * @dev Allows the current owner to relinquish control of the contract. * @notice Renouncing to ownership will leave the contract without an owner. * It will not be possible to call the functions with the `onlyOwner` * modifier anymore. */ function renounceOwnership() public onlyOwner { emit OwnershipRenounced(owner); owner = address(0); } /** * @dev Allows the current owner to transfer control of the contract to a newOwner. * @param _newOwner The address to transfer ownership to. */ function transferOwnership(address _newOwner) public onlyOwner { _transferOwnership(_newOwner); } /** * @dev Transfers control of the contract to a newOwner. * @param _newOwner The address to transfer ownership to. */ function _transferOwnership(address _newOwner) internal { require(_newOwner != address(0)); emit OwnershipTransferred(owner, _newOwner); owner = _newOwner; } } // File: openzeppelin-solidity/contracts/math/SafeMath.sol pragma solidity ^0.4.24; /** * @title SafeMath * @dev Math operations with safety checks that throw on error */ library SafeMath { /** * @dev Multiplies two numbers, throws on overflow. */ function mul(uint256 _a, uint256 _b) internal pure returns (uint256 c) { // Gas optimization: this is cheaper than asserting '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; } c = _a * _b; assert(c / _a == _b); return c; } /** * @dev Integer division of two numbers, truncating the quotient. */ function div(uint256 _a, uint256 _b) internal pure returns (uint256) { // assert(_b > 0); // Solidity automatically throws when dividing by 0 // uint256 c = _a / _b; // assert(_a == _b * c + _a % _b); // There is no case in which this doesn't hold return _a / _b; } /** * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend). */ function sub(uint256 _a, uint256 _b) internal pure returns (uint256) { assert(_b <= _a); return _a - _b; } /** * @dev Adds two numbers, throws on overflow. */ function add(uint256 _a, uint256 _b) internal pure returns (uint256 c) { c = _a + _b; assert(c >= _a); return c; } } // File: contracts/interfaces/ChainlinkRequestInterface.sol pragma solidity 0.4.24; interface ChainlinkRequestInterface { function oracleRequest( address sender, uint256 payment, bytes32 id, address callbackAddress, bytes4 callbackFunctionId, uint256 nonce, uint256 version, bytes data ) external; function cancelOracleRequest( bytes32 requestId, uint256 payment, bytes4 callbackFunctionId, uint256 expiration ) external; } // File: contracts/interfaces/OracleInterface.sol pragma solidity 0.4.24; interface OracleInterface { function fulfillOracleRequest( bytes32 requestId, uint256 payment, address callbackAddress, bytes4 callbackFunctionId, uint256 expiration, bytes32 data ) external returns (bool); function getAuthorizationStatus(address node) external view returns (bool); function setFulfillmentPermission(address node, bool allowed) external; function withdraw(address recipient, uint256 amount) external; function withdrawable() external view returns (uint256); } // File: contracts/interfaces/LinkTokenInterface.sol pragma solidity 0.4.24; interface LinkTokenInterface { function allowance(address owner, address spender) external returns (bool success); 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 tokenName); function symbol() external returns (string tokenSymbol); function totalSupply() external returns (uint256 totalTokensIssued); function transfer(address to, uint256 value) external returns (bool success); function transferAndCall(address to, uint256 value, bytes data) external returns (bool success); function transferFrom(address from, address to, uint256 value) external returns (bool success); } // File: contracts/Oracle.sol pragma solidity 0.4.24; /** * @title The Chainlink Oracle contract * @notice Node operators can deploy this contract to fulfill requests sent to them */ contract Oracle is ChainlinkRequestInterface, OracleInterface, Ownable { using SafeMath for uint256; uint256 constant public EXPIRY_TIME = 5 minutes; uint256 constant private MINIMUM_CONSUMER_GAS_LIMIT = 400000; // We initialize fields to 1 instead of 0 so that the first invocation // does not cost more gas. uint256 constant private ONE_FOR_CONSISTENT_GAS_COST = 1; uint256 constant private SELECTOR_LENGTH = 4; uint256 constant private EXPECTED_REQUEST_WORDS = 2; // solium-disable-next-line zeppelin/no-arithmetic-operations uint256 constant private MINIMUM_REQUEST_LENGTH = SELECTOR_LENGTH + (32 * EXPECTED_REQUEST_WORDS); LinkTokenInterface internal LinkToken; mapping(bytes32 => bytes32) private commitments; mapping(address => bool) private authorizedNodes; uint256 private withdrawableTokens = ONE_FOR_CONSISTENT_GAS_COST; event OracleRequest( bytes32 indexed specId, address requester, bytes32 requestId, uint256 payment, address callbackAddr, bytes4 callbackFunctionId, uint256 cancelExpiration, uint256 dataVersion, bytes data ); event CancelOracleRequest( bytes32 indexed requestId ); /** * @notice Deploy with the address of the LINK token * @dev Sets the LinkToken address for the imported LinkTokenInterface * @param _link The address of the LINK token */ constructor(address _link) Ownable() public { LinkToken = LinkTokenInterface(_link); } /** * @notice Called when LINK is sent to the contract via `transferAndCall` * @dev The data payload's first 2 words will be overwritten by the `_sender` and `_amount` * values to ensure correctness. Calls oracleRequest. * @param _sender Address of the sender * @param _amount Amount of LINK sent (specified in wei) * @param _data Payload of the transaction */ function onTokenTransfer( address _sender, uint256 _amount, bytes _data ) public onlyLINK validRequestLength(_data) permittedFunctionsForLINK(_data) { assembly { // solium-disable-next-line security/no-low-level-calls mstore(add(_data, 36), _sender) // ensure correct sender is passed // solium-disable-next-line security/no-low-level-calls mstore(add(_data, 68), _amount) // ensure correct amount is passed } // solium-disable-next-line security/no-low-level-calls require(address(this).delegatecall(_data), "Unable to create request"); // calls oracleRequest } /** * @notice Creates the Chainlink request * @dev Stores the hash of the params as the on-chain commitment for the request. * Emits OracleRequest event for the Chainlink node to detect. * @param _sender The sender of the request * @param _payment The amount of payment given (specified in wei) * @param _specId The Job Specification ID * @param _callbackAddress The callback address for the response * @param _callbackFunctionId The callback function ID for the response * @param _nonce The nonce sent by the requester * @param _dataVersion The specified data version * @param _data The CBOR payload of the request */ function oracleRequest( address _sender, uint256 _payment, bytes32 _specId, address _callbackAddress, bytes4 _callbackFunctionId, uint256 _nonce, uint256 _dataVersion, bytes _data ) external onlyLINK checkCallbackAddress(_callbackAddress) { bytes32 requestId = keccak256(abi.encodePacked(_sender, _nonce)); require(commitments[requestId] == 0, "Must use a unique ID"); uint256 expiration = now.add(EXPIRY_TIME); commitments[requestId] = keccak256( abi.encodePacked( _payment, _callbackAddress, _callbackFunctionId, expiration ) ); emit OracleRequest( _specId, _sender, requestId, _payment, _callbackAddress, _callbackFunctionId, expiration, _dataVersion, _data); } /** * @notice Called by the Chainlink node to fulfill requests * @dev Given params must hash back to the commitment stored from `oracleRequest`. * Will call the callback address' callback function without bubbling up error * checking in a `require` so that the node can get paid. * @param _requestId The fulfillment request ID that must match the requester's * @param _payment The payment amount that will be released for the oracle (specified in wei) * @param _callbackAddress The callback address to call for fulfillment * @param _callbackFunctionId The callback function ID to use for fulfillment * @param _expiration The expiration that the node should respond by before the requester can cancel * @param _data The data to return to the consuming contract * @return Status if the external call was successful */ function fulfillOracleRequest( bytes32 _requestId, uint256 _payment, address _callbackAddress, bytes4 _callbackFunctionId, uint256 _expiration, bytes32 _data ) external onlyAuthorizedNode isValidRequest(_requestId) returns (bool) { bytes32 paramsHash = keccak256( abi.encodePacked( _payment, _callbackAddress, _callbackFunctionId, _expiration ) ); require(commitments[_requestId] == paramsHash, "Params do not match request ID"); withdrawableTokens = withdrawableTokens.add(_payment); delete commitments[_requestId]; require(gasleft() >= MINIMUM_CONSUMER_GAS_LIMIT, "Must provide consumer enough gas"); // All updates to the oracle's fulfillment should come before calling the // callback(addr+functionId) as it is untrusted. // See: https://solidity.readthedocs.io/en/develop/security-considerations.html#use-the-checks-effects-interactions-pattern return _callbackAddress.call(_callbackFunctionId, _requestId, _data); // solium-disable-line security/no-low-level-calls } /** * @notice Use this to check if a node is authorized for fulfilling requests * @param _node The address of the Chainlink node * @return The authorization status of the node */ function getAuthorizationStatus(address _node) external view returns (bool) { return authorizedNodes[_node]; } /** * @notice Sets the fulfillment permission for a given node. Use `true` to allow, `false` to disallow. * @param _node The address of the Chainlink node * @param _allowed Bool value to determine if the node can fulfill requests */ function setFulfillmentPermission(address _node, bool _allowed) external onlyOwner { authorizedNodes[_node] = _allowed; } /** * @notice Allows the node operator to withdraw earned LINK to a given address * @dev The owner of the contract can be another wallet and does not have to be a Chainlink node * @param _recipient The address to send the LINK token to * @param _amount The amount to send (specified in wei) */ function withdraw(address _recipient, uint256 _amount) external onlyOwner hasAvailableFunds(_amount) { withdrawableTokens = withdrawableTokens.sub(_amount); assert(LinkToken.transfer(_recipient, _amount)); } /** * @notice Displays the amount of LINK that is available for the node operator to withdraw * @dev We use `ONE_FOR_CONSISTENT_GAS_COST` in place of 0 in storage * @return The amount of withdrawable LINK on the contract */ function withdrawable() external view onlyOwner returns (uint256) { return withdrawableTokens.sub(ONE_FOR_CONSISTENT_GAS_COST); } /** * @notice Allows requesters to cancel requests sent to this oracle contract. Will transfer the LINK * sent for the request back to the requester's address. * @dev Given params must hash to a commitment stored on the contract in order for the request to be valid * Emits CancelOracleRequest event. * @param _requestId The request ID * @param _payment The amount of payment given (specified in wei) * @param _callbackFunc The requester's specified callback address * @param _expiration The time of the expiration for the request */ function cancelOracleRequest( bytes32 _requestId, uint256 _payment, bytes4 _callbackFunc, uint256 _expiration ) external { bytes32 paramsHash = keccak256( abi.encodePacked( _payment, msg.sender, _callbackFunc, _expiration) ); require(paramsHash == commitments[_requestId], "Params do not match request ID"); require(_expiration <= now, "Request is not expired"); delete commitments[_requestId]; emit CancelOracleRequest(_requestId); assert(LinkToken.transfer(msg.sender, _payment)); } // MODIFIERS /** * @dev Reverts if amount requested is greater than withdrawable balance * @param _amount The given amount to compare to `withdrawableTokens` */ modifier hasAvailableFunds(uint256 _amount) { require(withdrawableTokens >= _amount.add(ONE_FOR_CONSISTENT_GAS_COST), "Amount requested is greater than withdrawable balance"); _; } /** * @dev Reverts if request ID does not exist * @param _requestId The given request ID to check in stored `commitments` */ modifier isValidRequest(bytes32 _requestId) { require(commitments[_requestId] != 0, "Must have a valid requestId"); _; } /** * @dev Reverts if `msg.sender` is not authorized to fulfill requests */ modifier onlyAuthorizedNode() { require(authorizedNodes[msg.sender] || msg.sender == owner, "Not an authorized node to fulfill requests"); _; } /** * @dev Reverts if not sent from the LINK token */ modifier onlyLINK() { require(msg.sender == address(LinkToken), "Must use LINK token"); _; } /** * @dev Reverts if the given data does not begin with the `oracleRequest` function selector * @param _data The data payload of the request */ modifier permittedFunctionsForLINK(bytes _data) { bytes4 funcSelector; assembly { // solium-disable-next-line security/no-low-level-calls funcSelector := mload(add(_data, 32)) } require(funcSelector == this.oracleRequest.selector, "Must use whitelisted functions"); _; } /** * @dev Reverts if the callback address is the LINK token * @param _to The callback address */ modifier checkCallbackAddress(address _to) { require(_to != address(LinkToken), "Cannot callback to LINK"); _; } /** * @dev Reverts if the given payload is less than needed to create a request * @param _data The request payload */ modifier validRequestLength(bytes _data) { require(_data.length >= MINIMUM_REQUEST_LENGTH, "Invalid request length"); _; } }
File 2 of 2: Aggregator
pragma solidity 0.4.24; /** * @dev A library for working with mutable byte buffers in Solidity. * * Byte buffers are mutable and expandable, and provide a variety of primitives * for writing to them. At any time you can fetch a bytes object containing the * current contents of the buffer. The bytes object should not be stored between * operations, as it may change due to resizing of the buffer. */ library Buffer { /** * @dev Represents a mutable buffer. Buffers have a current value (buf) and * a capacity. The capacity may be longer than the current value, in * which case it can be extended without the need to allocate more memory. */ struct buffer { bytes buf; uint capacity; } /** * @dev Initializes a buffer with an initial capacity. * @param buf The buffer to initialize. * @param capacity The number of bytes of space to allocate the buffer. * @return The buffer, for chaining. */ function init(buffer memory buf, uint capacity) internal pure returns(buffer memory) { if (capacity % 32 != 0) { capacity += 32 - (capacity % 32); } // Allocate space for the buffer data buf.capacity = capacity; assembly { let ptr := mload(0x40) mstore(buf, ptr) mstore(ptr, 0) mstore(0x40, add(32, add(ptr, capacity))) } return buf; } /** * @dev Initializes a new buffer from an existing bytes object. * Changes to the buffer may mutate the original value. * @param b The bytes object to initialize the buffer with. * @return A new buffer. */ function fromBytes(bytes memory b) internal pure returns(buffer memory) { buffer memory buf; buf.buf = b; buf.capacity = b.length; return buf; } function resize(buffer memory buf, uint capacity) private pure { bytes memory oldbuf = buf.buf; init(buf, capacity); append(buf, oldbuf); } function max(uint a, uint b) private pure returns(uint) { if (a > b) { return a; } return b; } /** * @dev Sets buffer length to 0. * @param buf The buffer to truncate. * @return The original buffer, for chaining.. */ function truncate(buffer memory buf) internal pure returns (buffer memory) { assembly { let bufptr := mload(buf) mstore(bufptr, 0) } return buf; } /** * @dev Writes a byte string to a buffer. Resizes if doing so would exceed * the capacity of the buffer. * @param buf The buffer to append to. * @param off The start offset to write to. * @param data The data to append. * @param len The number of bytes to copy. * @return The original buffer, for chaining. */ function write(buffer memory buf, uint off, bytes memory data, uint len) internal pure returns(buffer memory) { require(len <= data.length); if (off + len > buf.capacity) { resize(buf, max(buf.capacity, len + off) * 2); } uint dest; uint src; assembly { // Memory address of the buffer data let bufptr := mload(buf) // Length of existing buffer data let buflen := mload(bufptr) // Start address = buffer address + offset + sizeof(buffer length) dest := add(add(bufptr, 32), off) // Update buffer length if we're extending it if gt(add(len, off), buflen) { mstore(bufptr, add(len, off)) } src := add(data, 32) } // Copy word-length chunks while possible for (; len >= 32; len -= 32) { assembly { mstore(dest, mload(src)) } dest += 32; src += 32; } // Copy remaining bytes uint mask = 256 ** (32 - len) - 1; assembly { let srcpart := and(mload(src), not(mask)) let destpart := and(mload(dest), mask) mstore(dest, or(destpart, srcpart)) } return buf; } /** * @dev Appends a byte string to a buffer. Resizes if doing so would exceed * the capacity of the buffer. * @param buf The buffer to append to. * @param data The data to append. * @param len The number of bytes to copy. * @return The original buffer, for chaining. */ function append(buffer memory buf, bytes memory data, uint len) internal pure returns (buffer memory) { return write(buf, buf.buf.length, data, len); } /** * @dev Appends a byte string to a buffer. Resizes if doing so would exceed * the capacity of the buffer. * @param buf The buffer to append to. * @param data The data to append. * @return The original buffer, for chaining. */ function append(buffer memory buf, bytes memory data) internal pure returns (buffer memory) { return write(buf, buf.buf.length, data, data.length); } /** * @dev Writes a byte to the buffer. Resizes if doing so would exceed the * capacity of the buffer. * @param buf The buffer to append to. * @param off The offset to write the byte at. * @param data The data to append. * @return The original buffer, for chaining. */ function writeUint8(buffer memory buf, uint off, uint8 data) internal pure returns(buffer memory) { if (off >= buf.capacity) { resize(buf, buf.capacity * 2); } assembly { // Memory address of the buffer data let bufptr := mload(buf) // Length of existing buffer data let buflen := mload(bufptr) // Address = buffer address + sizeof(buffer length) + off let dest := add(add(bufptr, off), 32) mstore8(dest, data) // Update buffer length if we extended it if eq(off, buflen) { mstore(bufptr, add(buflen, 1)) } } return buf; } /** * @dev Appends a byte to the buffer. Resizes if doing so would exceed the * capacity of the buffer. * @param buf The buffer to append to. * @param data The data to append. * @return The original buffer, for chaining. */ function appendUint8(buffer memory buf, uint8 data) internal pure returns(buffer memory) { return writeUint8(buf, buf.buf.length, data); } /** * @dev Writes up to 32 bytes to the buffer. Resizes if doing so would * exceed the capacity of the buffer. * @param buf The buffer to append to. * @param off The offset to write at. * @param data The data to append. * @param len The number of bytes to write (left-aligned). * @return The original buffer, for chaining. */ function write(buffer memory buf, uint off, bytes32 data, uint len) private pure returns(buffer memory) { if (len + off > buf.capacity) { resize(buf, (len + off) * 2); } uint mask = 256 ** len - 1; // Right-align data data = data >> (8 * (32 - len)); assembly { // Memory address of the buffer data let bufptr := mload(buf) // Address = buffer address + sizeof(buffer length) + off + len let dest := add(add(bufptr, off), len) mstore(dest, or(and(mload(dest), not(mask)), data)) // Update buffer length if we extended it if gt(add(off, len), mload(bufptr)) { mstore(bufptr, add(off, len)) } } return buf; } /** * @dev Writes a bytes20 to the buffer. Resizes if doing so would exceed the * capacity of the buffer. * @param buf The buffer to append to. * @param off The offset to write at. * @param data The data to append. * @return The original buffer, for chaining. */ function writeBytes20(buffer memory buf, uint off, bytes20 data) internal pure returns (buffer memory) { return write(buf, off, bytes32(data), 20); } /** * @dev Appends a bytes20 to the buffer. Resizes if doing so would exceed * the capacity of the buffer. * @param buf The buffer to append to. * @param data The data to append. * @return The original buffer, for chhaining. */ function appendBytes20(buffer memory buf, bytes20 data) internal pure returns (buffer memory) { return write(buf, buf.buf.length, bytes32(data), 20); } /** * @dev Appends a bytes32 to the buffer. Resizes if doing so would exceed * the capacity of the buffer. * @param buf The buffer to append to. * @param data The data to append. * @return The original buffer, for chaining. */ function appendBytes32(buffer memory buf, bytes32 data) internal pure returns (buffer memory) { return write(buf, buf.buf.length, data, 32); } /** * @dev Writes an integer to the buffer. Resizes if doing so would exceed * the capacity of the buffer. * @param buf The buffer to append to. * @param off The offset to write at. * @param data The data to append. * @param len The number of bytes to write (right-aligned). * @return The original buffer, for chaining. */ function writeInt(buffer memory buf, uint off, uint data, uint len) private pure returns(buffer memory) { if (len + off > buf.capacity) { resize(buf, (len + off) * 2); } uint mask = 256 ** len - 1; assembly { // Memory address of the buffer data let bufptr := mload(buf) // Address = buffer address + off + sizeof(buffer length) + len let dest := add(add(bufptr, off), len) mstore(dest, or(and(mload(dest), not(mask)), data)) // Update buffer length if we extended it if gt(add(off, len), mload(bufptr)) { mstore(bufptr, add(off, len)) } } return buf; } /** * @dev Appends a byte to the end of the buffer. Resizes if doing so would * exceed the capacity of the buffer. * @param buf The buffer to append to. * @param data The data to append. * @return The original buffer. */ function appendInt(buffer memory buf, uint data, uint len) internal pure returns(buffer memory) { return writeInt(buf, buf.buf.length, data, len); } } library CBOR { using Buffer for Buffer.buffer; uint8 private constant MAJOR_TYPE_INT = 0; uint8 private constant MAJOR_TYPE_NEGATIVE_INT = 1; uint8 private constant MAJOR_TYPE_BYTES = 2; uint8 private constant MAJOR_TYPE_STRING = 3; uint8 private constant MAJOR_TYPE_ARRAY = 4; uint8 private constant MAJOR_TYPE_MAP = 5; uint8 private constant MAJOR_TYPE_CONTENT_FREE = 7; function encodeType(Buffer.buffer memory buf, uint8 major, uint value) private pure { if(value <= 23) { buf.appendUint8(uint8((major << 5) | value)); } else if(value <= 0xFF) { buf.appendUint8(uint8((major << 5) | 24)); buf.appendInt(value, 1); } else if(value <= 0xFFFF) { buf.appendUint8(uint8((major << 5) | 25)); buf.appendInt(value, 2); } else if(value <= 0xFFFFFFFF) { buf.appendUint8(uint8((major << 5) | 26)); buf.appendInt(value, 4); } else if(value <= 0xFFFFFFFFFFFFFFFF) { buf.appendUint8(uint8((major << 5) | 27)); buf.appendInt(value, 8); } } function encodeIndefiniteLengthType(Buffer.buffer memory buf, uint8 major) private pure { buf.appendUint8(uint8((major << 5) | 31)); } function encodeUInt(Buffer.buffer memory buf, uint value) internal pure { encodeType(buf, MAJOR_TYPE_INT, value); } function encodeInt(Buffer.buffer memory buf, int value) internal pure { if(value >= 0) { encodeType(buf, MAJOR_TYPE_INT, uint(value)); } else { encodeType(buf, MAJOR_TYPE_NEGATIVE_INT, uint(-1 - value)); } } function encodeBytes(Buffer.buffer memory buf, bytes value) internal pure { encodeType(buf, MAJOR_TYPE_BYTES, value.length); buf.append(value); } function encodeString(Buffer.buffer memory buf, string value) internal pure { encodeType(buf, MAJOR_TYPE_STRING, bytes(value).length); buf.append(bytes(value)); } function startArray(Buffer.buffer memory buf) internal pure { encodeIndefiniteLengthType(buf, MAJOR_TYPE_ARRAY); } function startMap(Buffer.buffer memory buf) internal pure { encodeIndefiniteLengthType(buf, MAJOR_TYPE_MAP); } function endSequence(Buffer.buffer memory buf) internal pure { encodeIndefiniteLengthType(buf, MAJOR_TYPE_CONTENT_FREE); } } /** * @title Library for common Chainlink functions * @dev Uses imported CBOR library for encoding to buffer */ library Chainlink { uint256 internal constant defaultBufferSize = 256; // solhint-disable-line const-name-snakecase using CBOR for Buffer.buffer; struct Request { bytes32 id; address callbackAddress; bytes4 callbackFunctionId; uint256 nonce; Buffer.buffer buf; } /** * @notice Initializes a Chainlink request * @dev Sets the ID, callback address, and callback function signature on the request * @param self The uninitialized request * @param _id The Job Specification ID * @param _callbackAddress The callback address * @param _callbackFunction The callback function signature * @return The initialized request */ function initialize( Request memory self, bytes32 _id, address _callbackAddress, bytes4 _callbackFunction ) internal pure returns (Chainlink.Request memory) { Buffer.init(self.buf, defaultBufferSize); self.id = _id; self.callbackAddress = _callbackAddress; self.callbackFunctionId = _callbackFunction; return self; } /** * @notice Sets the data for the buffer without encoding CBOR on-chain * @dev CBOR can be closed with curly-brackets {} or they can be left off * @param self The initialized request * @param _data The CBOR data */ function setBuffer(Request memory self, bytes _data) internal pure { Buffer.init(self.buf, _data.length); Buffer.append(self.buf, _data); } /** * @notice Adds a string value to the request with a given key name * @param self The initialized request * @param _key The name of the key * @param _value The string value to add */ function add(Request memory self, string _key, string _value) internal pure { self.buf.encodeString(_key); self.buf.encodeString(_value); } /** * @notice Adds a bytes value to the request with a given key name * @param self The initialized request * @param _key The name of the key * @param _value The bytes value to add */ function addBytes(Request memory self, string _key, bytes _value) internal pure { self.buf.encodeString(_key); self.buf.encodeBytes(_value); } /** * @notice Adds a int256 value to the request with a given key name * @param self The initialized request * @param _key The name of the key * @param _value The int256 value to add */ function addInt(Request memory self, string _key, int256 _value) internal pure { self.buf.encodeString(_key); self.buf.encodeInt(_value); } /** * @notice Adds a uint256 value to the request with a given key name * @param self The initialized request * @param _key The name of the key * @param _value The uint256 value to add */ function addUint(Request memory self, string _key, uint256 _value) internal pure { self.buf.encodeString(_key); self.buf.encodeUInt(_value); } /** * @notice Adds an array of strings to the request with a given key name * @param self The initialized request * @param _key The name of the key * @param _values The array of string values to add */ function addStringArray(Request memory self, string _key, string[] memory _values) internal pure { self.buf.encodeString(_key); self.buf.startArray(); for (uint256 i = 0; i < _values.length; i++) { self.buf.encodeString(_values[i]); } self.buf.endSequence(); } } interface ENSInterface { // Logged when the owner of a node assigns a new owner to a subnode. event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner); // Logged when the owner of a node transfers ownership to a new account. event Transfer(bytes32 indexed node, address owner); // Logged when the resolver for a node changes. event NewResolver(bytes32 indexed node, address resolver); // Logged when the TTL of a node changes event NewTTL(bytes32 indexed node, uint64 ttl); function setSubnodeOwner(bytes32 node, bytes32 label, address owner) external; function setResolver(bytes32 node, address resolver) external; function setOwner(bytes32 node, address owner) external; function setTTL(bytes32 node, uint64 ttl) external; function owner(bytes32 node) external view returns (address); function resolver(bytes32 node) external view returns (address); function ttl(bytes32 node) external view returns (uint64); } 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 tokenName); function symbol() external returns (string tokenSymbol); function totalSupply() external returns (uint256 totalTokensIssued); function transfer(address to, uint256 value) external returns (bool success); function transferAndCall(address to, uint256 value, bytes data) external returns (bool success); function transferFrom(address from, address to, uint256 value) external returns (bool success); } interface ChainlinkRequestInterface { function oracleRequest( address sender, uint256 payment, bytes32 id, address callbackAddress, bytes4 callbackFunctionId, uint256 nonce, uint256 version, bytes data ) external; function cancelOracleRequest( bytes32 requestId, uint256 payment, bytes4 callbackFunctionId, uint256 expiration ) external; } interface PointerInterface { function getAddress() external view returns (address); } contract ENSResolver { function addr(bytes32 node) public view returns (address); } /** * @title SafeMath * @dev Math operations with safety checks that throw on error */ library SafeMath { /** * @dev Multiplies two numbers, throws on overflow. */ function mul(uint256 _a, uint256 _b) internal pure returns (uint256 c) { // Gas optimization: this is cheaper than asserting '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; } c = _a * _b; assert(c / _a == _b); return c; } /** * @dev Integer division of two numbers, truncating the quotient. */ function div(uint256 _a, uint256 _b) internal pure returns (uint256) { // assert(_b > 0); // Solidity automatically throws when dividing by 0 // uint256 c = _a / _b; // assert(_a == _b * c + _a % _b); // There is no case in which this doesn't hold return _a / _b; } /** * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend). */ function sub(uint256 _a, uint256 _b) internal pure returns (uint256) { assert(_b <= _a); return _a - _b; } /** * @dev Adds two numbers, throws on overflow. */ function add(uint256 _a, uint256 _b) internal pure returns (uint256 c) { c = _a + _b; assert(c >= _a); return c; } } /** * @title The ChainlinkClient contract * @notice Contract writers can inherit this contract in order to create requests for the * Chainlink network */ contract ChainlinkClient { using Chainlink for Chainlink.Request; using SafeMath for uint256; uint256 constant internal LINK = 10**18; uint256 constant private AMOUNT_OVERRIDE = 0; address constant private SENDER_OVERRIDE = 0x0; uint256 constant private ARGS_VERSION = 1; bytes32 constant private ENS_TOKEN_SUBNAME = keccak256("link"); bytes32 constant private ENS_ORACLE_SUBNAME = keccak256("oracle"); address constant private LINK_TOKEN_POINTER = 0xC89bD4E1632D3A43CB03AAAd5262cbe4038Bc571; ENSInterface private ens; bytes32 private ensNode; LinkTokenInterface private link; ChainlinkRequestInterface private oracle; uint256 private requests = 1; mapping(bytes32 => address) private pendingRequests; event ChainlinkRequested(bytes32 indexed id); event ChainlinkFulfilled(bytes32 indexed id); event ChainlinkCancelled(bytes32 indexed id); /** * @notice Creates a request that can hold additional parameters * @param _specId The Job Specification ID that the request will be created for * @param _callbackAddress The callback address that the response will be sent to * @param _callbackFunctionSignature The callback function signature to use for the callback address * @return A Chainlink Request struct in memory */ function buildChainlinkRequest( bytes32 _specId, address _callbackAddress, bytes4 _callbackFunctionSignature ) internal pure returns (Chainlink.Request memory) { Chainlink.Request memory req; return req.initialize(_specId, _callbackAddress, _callbackFunctionSignature); } /** * @notice Creates a Chainlink request to the stored oracle address * @dev Calls `chainlinkRequestTo` with the stored oracle address * @param _req The initialized Chainlink Request * @param _payment The amount of LINK to send for the request * @return The request ID */ function sendChainlinkRequest(Chainlink.Request memory _req, uint256 _payment) internal returns (bytes32) { return sendChainlinkRequestTo(oracle, _req, _payment); } /** * @notice Creates a Chainlink request to the specified oracle address * @dev Generates and stores a request ID, increments the local nonce, and uses `transferAndCall` to * send LINK which creates a request on the target oracle contract. * Emits ChainlinkRequested event. * @param _oracle The address of the oracle for the request * @param _req The initialized Chainlink Request * @param _payment The amount of LINK to send for the request * @return The request ID */ function sendChainlinkRequestTo(address _oracle, Chainlink.Request memory _req, uint256 _payment) internal returns (bytes32 requestId) { requestId = keccak256(abi.encodePacked(this, requests)); _req.nonce = requests; pendingRequests[requestId] = _oracle; emit ChainlinkRequested(requestId); require(link.transferAndCall(_oracle, _payment, encodeRequest(_req)), "unable to transferAndCall to oracle"); requests += 1; return requestId; } /** * @notice Allows a request to be cancelled if it has not been fulfilled * @dev Requires keeping track of the expiration value emitted from the oracle contract. * Deletes the request from the `pendingRequests` mapping. * Emits ChainlinkCancelled event. * @param _requestId The request ID * @param _payment The amount of LINK sent for the request * @param _callbackFunc The callback function specified for the request * @param _expiration The time of the expiration for the request */ function cancelChainlinkRequest( bytes32 _requestId, uint256 _payment, bytes4 _callbackFunc, uint256 _expiration ) internal { ChainlinkRequestInterface requested = ChainlinkRequestInterface(pendingRequests[_requestId]); delete pendingRequests[_requestId]; emit ChainlinkCancelled(_requestId); requested.cancelOracleRequest(_requestId, _payment, _callbackFunc, _expiration); } /** * @notice Sets the stored oracle address * @param _oracle The address of the oracle contract */ function setChainlinkOracle(address _oracle) internal { oracle = ChainlinkRequestInterface(_oracle); } /** * @notice Sets the LINK token address * @param _link The address of the LINK token contract */ function setChainlinkToken(address _link) internal { link = LinkTokenInterface(_link); } /** * @notice Sets the Chainlink token address for the public * network as given by the Pointer contract */ function setPublicChainlinkToken() internal { setChainlinkToken(PointerInterface(LINK_TOKEN_POINTER).getAddress()); } /** * @notice Retrieves the stored address of the LINK token * @return The address of the LINK token */ function chainlinkTokenAddress() internal view returns (address) { return address(link); } /** * @notice Retrieves the stored address of the oracle contract * @return The address of the oracle contract */ function chainlinkOracleAddress() internal view returns (address) { return address(oracle); } /** * @notice Allows for a request which was created on another contract to be fulfilled * on this contract * @param _oracle The address of the oracle contract that will fulfill the request * @param _requestId The request ID used for the response */ function addChainlinkExternalRequest(address _oracle, bytes32 _requestId) internal notPendingRequest(_requestId) { pendingRequests[_requestId] = _oracle; } /** * @notice Sets the stored oracle and LINK token contracts with the addresses resolved by ENS * @dev Accounts for subnodes having different resolvers * @param _ens The address of the ENS contract * @param _node The ENS node hash */ function useChainlinkWithENS(address _ens, bytes32 _node) internal { ens = ENSInterface(_ens); ensNode = _node; bytes32 linkSubnode = keccak256(abi.encodePacked(ensNode, ENS_TOKEN_SUBNAME)); ENSResolver resolver = ENSResolver(ens.resolver(linkSubnode)); setChainlinkToken(resolver.addr(linkSubnode)); updateChainlinkOracleWithENS(); } /** * @notice Sets the stored oracle contract with the address resolved by ENS * @dev This may be called on its own as long as `useChainlinkWithENS` has been called previously */ function updateChainlinkOracleWithENS() internal { bytes32 oracleSubnode = keccak256(abi.encodePacked(ensNode, ENS_ORACLE_SUBNAME)); ENSResolver resolver = ENSResolver(ens.resolver(oracleSubnode)); setChainlinkOracle(resolver.addr(oracleSubnode)); } /** * @notice Encodes the request to be sent to the oracle contract * @dev The Chainlink node expects values to be in order for the request to be picked up. Order of types * will be validated in the oracle contract. * @param _req The initialized Chainlink Request * @return The bytes payload for the `transferAndCall` method */ function encodeRequest(Chainlink.Request memory _req) private view returns (bytes memory) { return abi.encodeWithSelector( oracle.oracleRequest.selector, SENDER_OVERRIDE, // Sender value - overridden by onTokenTransfer by the requesting contract's address AMOUNT_OVERRIDE, // Amount value - overridden by onTokenTransfer by the actual amount of LINK sent _req.id, _req.callbackAddress, _req.callbackFunctionId, _req.nonce, ARGS_VERSION, _req.buf.buf); } /** * @notice Ensures that the fulfillment is valid for this contract * @dev Use if the contract developer prefers methods instead of modifiers for validation * @param _requestId The request ID for fulfillment */ function validateChainlinkCallback(bytes32 _requestId) internal recordChainlinkFulfillment(_requestId) // solhint-disable-next-line no-empty-blocks {} /** * @dev Reverts if the sender is not the oracle of the request. * Emits ChainlinkFulfilled event. * @param _requestId The request ID for fulfillment */ modifier recordChainlinkFulfillment(bytes32 _requestId) { require(msg.sender == pendingRequests[_requestId], "Source must be the oracle of the request"); delete pendingRequests[_requestId]; emit ChainlinkFulfilled(_requestId); _; } /** * @dev Reverts if the request is already pending * @param _requestId The request ID for fulfillment */ modifier notPendingRequest(bytes32 _requestId) { require(pendingRequests[_requestId] == address(0), "Request is already pending"); _; } } 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); } 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) { int256 c = _a + _b; require((_b >= 0 && c >= _a) || (_b < 0 && c < _a), "SignedSafeMath: addition overflow"); return c; } } /** * @title Ownable * @dev The Ownable contract has an owner address, and provides basic authorization control * functions, this simplifies the implementation of "user permissions". */ contract Ownable { address public owner; event OwnershipRenounced(address indexed previousOwner); event OwnershipTransferred( address indexed previousOwner, address indexed newOwner ); /** * @dev The Ownable constructor sets the original `owner` of the contract to the sender * account. */ constructor() public { owner = msg.sender; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(msg.sender == owner); _; } /** * @dev Allows the current owner to relinquish control of the contract. * @notice Renouncing to ownership will leave the contract without an owner. * It will not be possible to call the functions with the `onlyOwner` * modifier anymore. */ function renounceOwnership() public onlyOwner { emit OwnershipRenounced(owner); owner = address(0); } /** * @dev Allows the current owner to transfer control of the contract to a newOwner. * @param _newOwner The address to transfer ownership to. */ function transferOwnership(address _newOwner) public onlyOwner { _transferOwnership(_newOwner); } /** * @dev Transfers control of the contract to a newOwner. * @param _newOwner The address to transfer ownership to. */ function _transferOwnership(address _newOwner) internal { require(_newOwner != address(0)); emit OwnershipTransferred(owner, _newOwner); owner = _newOwner; } } /** * @title An example Chainlink contract with aggregation * @notice Requesters can use this contract as a framework for creating * requests to multiple Chainlink nodes and running aggregation * as the contract receives answers. */ contract Aggregator is AggregatorInterface, ChainlinkClient, Ownable { using SignedSafeMath for int256; struct Answer { uint128 minimumResponses; uint128 maxResponses; int256[] responses; } event ResponseReceived(int256 indexed response, uint256 indexed answerId, address indexed sender); int256 private currentAnswerValue; uint256 private updatedTimestampValue; uint256 private latestCompletedAnswer; uint128 public paymentAmount; uint128 public minimumResponses; bytes32[] public jobIds; address[] public oracles; uint256 private answerCounter = 1; mapping(address => bool) public authorizedRequesters; mapping(bytes32 => uint256) private requestAnswers; mapping(uint256 => Answer) private answers; mapping(uint256 => int256) private currentAnswers; mapping(uint256 => uint256) private updatedTimestamps; uint256 constant private MAX_ORACLE_COUNT = 45; /** * @notice Deploy with the address of the LINK token and arrays of matching * length containing the addresses of the oracles and their corresponding * Job IDs. * @dev Sets the LinkToken address for the network, addresses of the oracles, * and jobIds in storage. * @param _link The address of the LINK token * @param _paymentAmount the amount of LINK to be sent to each oracle for each request * @param _minimumResponses the minimum number of responses * before an answer will be calculated * @param _oracles An array of oracle addresses * @param _jobIds An array of Job IDs */ constructor( address _link, uint128 _paymentAmount, uint128 _minimumResponses, address[] _oracles, bytes32[] _jobIds ) public Ownable() { setChainlinkToken(_link); updateRequestDetails(_paymentAmount, _minimumResponses, _oracles, _jobIds); } /** * @notice Creates a Chainlink request for each oracle in the oracles array. * @dev This example does not include request parameters. Reference any documentation * associated with the Job IDs used to determine the required parameters per-request. */ function requestRateUpdate() external ensureAuthorizedRequester() { Chainlink.Request memory request; bytes32 requestId; uint256 oraclePayment = paymentAmount; for (uint i = 0; i < oracles.length; i++) { request = buildChainlinkRequest(jobIds[i], this, this.chainlinkCallback.selector); requestId = sendChainlinkRequestTo(oracles[i], request, oraclePayment); requestAnswers[requestId] = answerCounter; } answers[answerCounter].minimumResponses = minimumResponses; answers[answerCounter].maxResponses = uint128(oracles.length); answerCounter = answerCounter.add(1); emit NewRound(answerCounter, msg.sender); } /** * @notice Receives the answer from the Chainlink node. * @dev This function can only be called by the oracle that received the request. * @param _clRequestId The Chainlink request ID associated with the answer * @param _response The answer provided by the Chainlink node */ function chainlinkCallback(bytes32 _clRequestId, int256 _response) external { validateChainlinkCallback(_clRequestId); uint256 answerId = requestAnswers[_clRequestId]; delete requestAnswers[_clRequestId]; answers[answerId].responses.push(_response); emit ResponseReceived(_response, answerId, msg.sender); updateLatestAnswer(answerId); deleteAnswer(answerId); } /** * @notice Updates the arrays of oracles and jobIds with new values, * overwriting the old values. * @dev Arrays are validated to be equal length. * @param _paymentAmount the amount of LINK to be sent to each oracle for each request * @param _minimumResponses the minimum number of responses * before an answer will be calculated * @param _oracles An array of oracle addresses * @param _jobIds An array of Job IDs */ function updateRequestDetails( uint128 _paymentAmount, uint128 _minimumResponses, address[] _oracles, bytes32[] _jobIds ) public onlyOwner() validateAnswerRequirements(_minimumResponses, _oracles, _jobIds) { paymentAmount = _paymentAmount; minimumResponses = _minimumResponses; jobIds = _jobIds; oracles = _oracles; } /** * @notice Allows the owner of the contract to withdraw any LINK balance * available on the contract. * @dev The contract will need to have a LINK balance in order to create requests. * @param _recipient The address to receive the LINK tokens * @param _amount The amount of LINK to send from the contract */ function transferLINK(address _recipient, uint256 _amount) public onlyOwner() { LinkTokenInterface linkToken = LinkTokenInterface(chainlinkTokenAddress()); require(linkToken.transfer(_recipient, _amount), "LINK transfer failed"); } /** * @notice Called by the owner to permission other addresses to generate new * requests to oracles. * @param _requester the address whose permissions are being set * @param _allowed boolean that determines whether the requester is * permissioned or not */ function setAuthorization(address _requester, bool _allowed) external onlyOwner() { authorizedRequesters[_requester] = _allowed; } /** * @notice Cancels an outstanding Chainlink request. * The oracle contract requires the request ID and additional metadata to * validate the cancellation. Only old answers can be cancelled. * @param _requestId is the identifier for the chainlink request being cancelled * @param _payment is the amount of LINK paid to the oracle for the request * @param _expiration is the time when the request expires */ function cancelRequest( bytes32 _requestId, uint256 _payment, uint256 _expiration ) external ensureAuthorizedRequester() { uint256 answerId = requestAnswers[_requestId]; require(answerId < latestCompletedAnswer, "Cannot modify an in-progress answer"); delete requestAnswers[_requestId]; answers[answerId].responses.push(0); deleteAnswer(answerId); cancelChainlinkRequest( _requestId, _payment, this.chainlinkCallback.selector, _expiration ); } /** * @notice Called by the owner to kill the contract. This transfers all LINK * balance and ETH balance (if there is any) to the owner. */ function destroy() external onlyOwner() { LinkTokenInterface linkToken = LinkTokenInterface(chainlinkTokenAddress()); transferLINK(owner, linkToken.balanceOf(address(this))); selfdestruct(owner); } /** * @dev Performs aggregation of the answers received from the Chainlink nodes. * Assumes that at least half the oracles are honest and so can't contol the * middle of the ordered responses. * @param _answerId The answer ID associated with the group of requests */ function updateLatestAnswer(uint256 _answerId) private ensureMinResponsesReceived(_answerId) ensureOnlyLatestAnswer(_answerId) { uint256 responseLength = answers[_answerId].responses.length; uint256 middleIndex = responseLength.div(2); int256 currentAnswerTemp; if (responseLength % 2 == 0) { int256 median1 = quickselect(answers[_answerId].responses, middleIndex); int256 median2 = quickselect(answers[_answerId].responses, middleIndex.add(1)); // quickselect is 1 indexed currentAnswerTemp = median1.add(median2) / 2; // signed integers are not supported by SafeMath } else { currentAnswerTemp = quickselect(answers[_answerId].responses, middleIndex.add(1)); // quickselect is 1 indexed } currentAnswerValue = currentAnswerTemp; latestCompletedAnswer = _answerId; updatedTimestampValue = now; updatedTimestamps[_answerId] = now; currentAnswers[_answerId] = currentAnswerTemp; emit AnswerUpdated(currentAnswerTemp, _answerId, now); } /** * @notice get the most recently reported answer */ function latestAnswer() external view returns (int256) { return currentAnswers[latestCompletedAnswer]; } /** * @notice get the last updated at block timestamp */ function latestTimestamp() external view returns (uint256) { return updatedTimestamps[latestCompletedAnswer]; } /** * @notice get past rounds answers * @param _roundId the answer number to retrieve the answer for */ function getAnswer(uint256 _roundId) external view returns (int256) { return currentAnswers[_roundId]; } /** * @notice get block timestamp when an answer was last updated * @param _roundId the answer number to retrieve the updated timestamp for */ function getTimestamp(uint256 _roundId) external view returns (uint256) { return updatedTimestamps[_roundId]; } /** * @notice get the latest completed round where the answer was updated */ function latestRound() external view returns (uint256) { return latestCompletedAnswer; } /** * @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 Cleans up the answer record if all responses have been received. * @param _answerId The identifier of the answer to be deleted */ function deleteAnswer(uint256 _answerId) private ensureAllResponsesReceived(_answerId) { delete answers[_answerId]; } /** * @dev Prevents taking an action if the minimum number of responses has not * been received for an answer. * @param _answerId The the identifier of the answer that keeps track of the responses. */ modifier ensureMinResponsesReceived(uint256 _answerId) { if (answers[_answerId].responses.length >= answers[_answerId].minimumResponses) { _; } } /** * @dev Prevents taking an action if not all responses are received for an answer. * @param _answerId The the identifier of the answer that keeps track of the responses. */ modifier ensureAllResponsesReceived(uint256 _answerId) { if (answers[_answerId].responses.length == answers[_answerId].maxResponses) { _; } } /** * @dev Prevents taking an action if a newer answer has been recorded. * @param _answerId The current answer's identifier. * Answer IDs are in ascending order. */ modifier ensureOnlyLatestAnswer(uint256 _answerId) { if (latestCompletedAnswer <= _answerId) { _; } } /** * @dev Ensures corresponding number of oracles and jobs. * @param _oracles The list of oracles. * @param _jobIds The list of jobs. */ modifier validateAnswerRequirements( uint256 _minimumResponses, address[] _oracles, bytes32[] _jobIds ) { require(_oracles.length <= MAX_ORACLE_COUNT, "cannot have more than 45 oracles"); require(_oracles.length >= _minimumResponses, "must have at least as many oracles as responses"); require(_oracles.length == _jobIds.length, "must have exactly as many oracles as job IDs"); _; } /** * @dev Reverts if `msg.sender` is not authorized to make requests. */ modifier ensureAuthorizedRequester() { require(authorizedRequesters[msg.sender] || msg.sender == owner, "Not an authorized address for creating requests"); _; } }