// SPDX-License-Identifier: MITpragmasolidity ^0.6.0;abstractcontractLinkTokenReceiver{
bytes4constantprivate ORACLE_REQUEST_SELECTOR =0x40429946;
uint256constantprivate SELECTOR_LENGTH =4;
uint256constantprivate EXPECTED_REQUEST_WORDS =2;
uint256constantprivate MINIMUM_REQUEST_LENGTH = SELECTOR_LENGTH + (32* EXPECTED_REQUEST_WORDS);
/**
* @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
*/functiononTokenTransfer(address _sender,
uint256 _amount,
bytesmemory _data
)
publiconlyLINKvalidRequestLength(_data)
permittedFunctionsForLINK(_data)
{
assembly {
// solhint-disable-next-line avoid-low-level-callsmstore(add(_data, 36), _sender) // ensure correct sender is passed// solhint-disable-next-line avoid-low-level-callsmstore(add(_data, 68), _amount) // ensure correct amount is passed
}
// solhint-disable-next-line avoid-low-level-calls
(bool success, ) =address(this).delegatecall(_data); // calls oracleRequestrequire(success, "Unable to create request");
}
functiongetChainlinkToken() publicviewvirtualreturns (address);
/**
* @dev Reverts if not sent from the LINK token
*/modifieronlyLINK() {
require(msg.sender== getChainlinkToken(), "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
*/modifierpermittedFunctionsForLINK(bytesmemory _data) {
bytes4 funcSelector;
assembly {
// solhint-disable-next-line avoid-low-level-calls
funcSelector :=mload(add(_data, 32))
}
require(funcSelector == ORACLE_REQUEST_SELECTOR, "Must use whitelisted functions");
_;
}
/**
* @dev Reverts if the given payload is less than needed to create a request
* @param _data The request payload
*/modifiervalidRequestLength(bytesmemory _data) {
require(_data.length>= MINIMUM_REQUEST_LENGTH, "Invalid request length");
_;
}
}
Contract Source Code
File 4 of 8: Oracle.sol
// SPDX-License-Identifier: MITpragmasolidity 0.6.6;import"./LinkTokenReceiver.sol";
import"./interfaces/ChainlinkRequestInterface.sol";
import"./interfaces/OracleInterface.sol";
import"./interfaces/LinkTokenInterface.sol";
import"./interfaces/WithdrawalInterface.sol";
import"./vendor/Ownable.sol";
import"./vendor/SafeMathChainlink.sol";
/**
* @title The Chainlink Oracle contract
* @notice Node operators can deploy this contract to fulfill requests sent to them.
* This contract is optimized for rollups (like Arbitrum and Optimism), it will be inefficient everywhere else
*/contractOracleisChainlinkRequestInterface, OracleInterface, Ownable, LinkTokenReceiver, WithdrawalInterface{
usingSafeMathChainlinkforuint256;
uint256constantpublic EXPIRY_TIME =5minutes;
uint256constantprivate MINIMUM_CONSUMER_GAS_LIMIT =400000;
uint32internalconstant MAX_INDEX_SIG =268_435_455; // 0x0FFFFFFF// We initialize fields to 1 instead of 0 so that the first invocation// does not cost more gas.uint256constantprivate ONE_FOR_CONSISTENT_GAS_COST =1;
LinkTokenInterface internal LinkToken;
mapping(bytes32=>bytes32) private commitments;
mapping(address=>bool) private authorizedNodes;
uint256private withdrawableTokens = ONE_FOR_CONSISTENT_GAS_COST;
// Struct packed into 2 slots// Cannot pack into 1 because we need payment to be > uint72 and expiration uint32structPackedOracleRequest {
address callbackAddress; // slot1: 160bytes4 callbackFunctionId; // slot1: 160 + 32uint128 payment; // slot2: 128uint128 expiration; // slot2: 128 + 128
}
uint32public orderIndex; // type(uint32).max 4,294,967,295mapping(bytes32=> PackedOracleRequest) private s_packedRequests;
mapping(uint32=>bytes32) private s_indexRequests;
eventOracleRequest(bytes32indexed specId,
address requester,
bytes32 requestId,
uint256 payment,
address callbackAddr,
bytes4 callbackFunctionId,
uint256 cancelExpiration,
uint256 dataVersion,
bytes data
);
eventCancelOracleRequest(bytes32indexed 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)
publicOwnable()
{
LinkToken = LinkTokenInterface(_link); // external but already deployed and unalterable
}
/**
* @notice Fallback function
* @dev When calldata.length === 36 bytes, forwards message to fulfillOracleRequestShort
*/fallback() externalpayable{
if(msg.data.length ==36) { // 36 = sig + 1 worduint32 index =uint32(msg.sig);
// if sig is gt 0x0FFFFFFF (the max request index we allow) then it's not one of our callsif (index > MAX_INDEX_SIG)
return;
// call fulfillOracleRequestShortbytes32 _data =abi.decode(msg.data[4:], (bytes32));
fulfillOracleRequestShort(s_indexRequests[index], _data);
delete s_indexRequests[index];
}
}
/**
* @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
*/functionoracleRequest(address _sender,
uint256 _payment,
bytes32 _specId,
address _callbackAddress,
bytes4 _callbackFunctionId,
uint256 _nonce,
uint256 _dataVersion,
bytescalldata _data
)
externaloverrideonlyLINK()
checkCallbackAddress(_callbackAddress)
{
bytes32 requestId =keccak256(abi.encodePacked(_sender, _nonce));
require(commitments[requestId] ==0, "Must use a unique ID");
// solhint-disable-next-line not-rely-on-timeuint256 expiration =now.add(EXPIRY_TIME);
commitments[requestId] =keccak256(
abi.encodePacked(
_payment,
_callbackAddress,
_callbackFunctionId,
expiration
)
);
s_packedRequests[requestId] = PackedOracleRequest(
_callbackAddress,
_callbackFunctionId,
uint128(_payment),
uint128(expiration)
);
uint32 _index = orderIndex;
s_indexRequests[_index] = requestId;
// we know there are no valid signatures in this contract with a leading 0 so we// limit max index to 0x0FFFFFFF (MAX_INDEX_SIG)// if current index is MAX_INDEX_SIG then we reset index to zeroif (_index == MAX_INDEX_SIG) {
orderIndex =0;
} else {
orderIndex = _index +1; // can't overflow since we always stay below 0x0FFFFFFF + 1
}
emit OracleRequest(
_specId,
_sender,
requestId,
_payment,
_callbackAddress,
// highjack `_callbackFunctionId` since our custom nodes read that param from storage and// we need somewhere to emit `_index` without modifying the event (it would break the node)bytes4(_index),
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
*/functionfulfillOracleRequest(bytes32 _requestId,
uint256 _payment,
address _callbackAddress,
bytes4 _callbackFunctionId,
uint256 _expiration,
bytes32 _data
)
externalonlyAuthorizedNodeoverrideisValidRequest(_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
(bool success, ) = _callbackAddress.call(abi.encodeWithSelector(_callbackFunctionId, _requestId, _data)); // solhint-disable-line avoid-low-level-callsreturn success;
}
/**
* @notice Called by the Chainlink node to fulfill requests
* @dev Matches functionality of fulfillOracleRequest with the exception of
* loading _payment, _callbackAddress, _callbackFunctionId and _expiration from
* storage. Ideal for L2's that charge for L1 calldata.
* 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 _data The data to return to the consuming contract
* @return Status if the external call was successful
*/functionfulfillOracleRequestShort(bytes32 _requestId,
bytes32 _data
)
publiconlyAuthorizedNodeisValidRequest(_requestId)
returns (bool)
{
PackedOracleRequest memory request = s_packedRequests[_requestId];
bytes32 paramsHash =keccak256(
abi.encodePacked(
uint(request.payment),
request.callbackAddress,
request.callbackFunctionId,
uint(request.expiration)
)
);
require(commitments[_requestId] == paramsHash, "Params do not match request ID");
withdrawableTokens = withdrawableTokens.add(uint(request.payment));
delete commitments[_requestId];
delete s_packedRequests[_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
(bool success, ) = request.callbackAddress.call(abi.encodeWithSelector(request.callbackFunctionId, _requestId, _data)); // solhint-disable-line avoid-low-level-callsreturn success;
}
/**
* @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
*/functiongetAuthorizationStatus(address _node)
externalviewoverridereturns (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
*/functionsetFulfillmentPermission(address _node, bool _allowed)
externaloverrideonlyOwner()
{
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)
*/functionwithdraw(address _recipient, uint256 _amount)
externaloverride(OracleInterface, WithdrawalInterface)
onlyOwnerhasAvailableFunds(_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
*/functionwithdrawable()
externalviewoverride(OracleInterface, WithdrawalInterface)
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
*/functioncancelOracleRequest(bytes32 _requestId,
uint256 _payment,
bytes4 _callbackFunc,
uint256 _expiration
)
externaloverride{
bytes32 paramsHash =keccak256(
abi.encodePacked(
_payment,
msg.sender,
_callbackFunc,
_expiration)
);
require(paramsHash == commitments[_requestId], "Params do not match request ID");
// solhint-disable-next-line not-rely-on-timerequire(_expiration <=now, "Request is not expired");
delete commitments[_requestId];
delete s_packedRequests[_requestId];
emit CancelOracleRequest(_requestId);
assert(LinkToken.transfer(msg.sender, _payment));
}
/**
* @notice Returns the address of the LINK token
* @dev This is the public implementation for chainlinkTokenAddress, which is
* an internal method of the ChainlinkClient contract
*/functiongetChainlinkToken()
publicviewoverridereturns (address)
{
returnaddress(LinkToken);
}
// MODIFIERS/**
* @dev Reverts if amount requested is greater than withdrawable balance
* @param _amount The given amount to compare to `withdrawableTokens`
*/modifierhasAvailableFunds(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`
*/modifierisValidRequest(bytes32 _requestId) {
require(commitments[_requestId] !=0, "Must have a valid requestId");
_;
}
/**
* @dev Reverts if `msg.sender` is not authorized to fulfill requests
*/modifieronlyAuthorizedNode() {
require(authorizedNodes[msg.sender] ||msg.sender== owner(), "Not an authorized node to fulfill requests");
_;
}
/**
* @dev Reverts if the callback address is the LINK token
* @param _to The callback address
*/modifiercheckCallbackAddress(address _to) {
require(_to !=address(LinkToken), "Cannot callback to LINK");
_;
}
}
// SPDX-License-Identifier: MITpragmasolidity ^0.6.0;/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be aplied to your functions to restrict their use to
* the owner.
*
* This contract has been modified to remove the revokeOwnership function
*/contractOwnable{
addressprivate _owner;
eventOwnershipTransferred(addressindexed previousOwner, addressindexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/constructor () internal{
_owner =msg.sender;
emit OwnershipTransferred(address(0), _owner);
}
/**
* @dev Returns the address of the current owner.
*/functionowner() publicviewreturns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/modifieronlyOwner() {
require(isOwner(), "Ownable: caller is not the owner");
_;
}
/**
* @dev Returns true if the caller is the current owner.
*/functionisOwner() publicviewreturns (bool) {
returnmsg.sender== _owner;
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/functiontransferOwnership(address newOwner) publiconlyOwner{
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
*/function_transferOwnership(address newOwner) internal{
require(newOwner !=address(0), "Ownable: new owner is the zero address");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
Contract Source Code
File 7 of 8: SafeMathChainlink.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.6.0;/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/librarySafeMathChainlink{
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
* - Addition cannot overflow.
*/functionadd(uint256 a, uint256 b) internalpurereturns (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.
*/functionsub(uint256 a, uint256 b) internalpurereturns (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.
*/functionmul(uint256 a, uint256 b) internalpurereturns (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/522if (a ==0) {
return0;
}
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.
*/functiondiv(uint256 a, uint256 b) internalpurereturns (uint256) {
// Solidity only automatically asserts when dividing by 0require(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 holdreturn 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.
*/functionmod(uint256 a, uint256 b) internalpurereturns (uint256) {
require(b !=0, "SafeMath: modulo by zero");
return a % b;
}
}
Contract Source Code
File 8 of 8: WithdrawalInterface.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.6.0;interfaceWithdrawalInterface{
/**
* @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
*/functionwithdraw(address recipient, uint256 amount) external;
/**
* @notice query the available amount of LINK to withdraw by msg.sender
*/functionwithdrawable() externalviewreturns (uint256);
}