Skip to content

Instantly share code, notes, and snippets.

@tristanperalta
Created December 11, 2018 09:48
Show Gist options
  • Save tristanperalta/e8fcc4af8355cfe67af63f9110be40d0 to your computer and use it in GitHub Desktop.
Save tristanperalta/e8fcc4af8355cfe67af63f9110be40d0 to your computer and use it in GitHub Desktop.
Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=soljson-v0.4.24+commit.e67f0147.js&optimize=true&gist=
pragma solidity 0.4.24;
// File: openzeppelin-solidity/contracts/ownership/Ownable.sol
/**
* @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
/**
* @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/OracleInterface.sol
interface OracleInterface {
function cancel(bytes32 externalId) external;
function fulfillData(uint256 internalId, bytes32 data) external returns (bool);
function getAuthorizationStatus(address node) external view returns (bool);
function requestData(
address sender,
uint256 amount,
uint256 version,
bytes32 specId,
address callbackAddress,
bytes4 callbackFunctionId,
bytes32 externalId,
bytes data
) external;
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
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
contract Oracle is OracleInterface, Ownable {
using SafeMath for uint256;
LinkTokenInterface internal LINK;
struct Callback {
bytes32 externalId;
uint256 amount;
address addr;
bytes4 functionId;
uint64 cancelExpiration;
}
// We initialize fields to 1 instead of 0 so that the first invocation
// does not cost more gas.
uint256 constant private oneForConsistentGasCost = 1;
uint256 private withdrawableWei = oneForConsistentGasCost;
mapping(uint256 => Callback) private callbacks;
mapping(address => bool) private authorizedNodes;
event RunRequest(
bytes32 indexed specId,
address indexed requester,
uint256 indexed amount,
uint256 internalId,
uint256 version,
bytes data
);
event CancelRequest(
uint256 internalId
);
constructor(address _link) Ownable() public {
LINK = LinkTokenInterface(_link);
}
function onTokenTransfer(
address _sender,
uint256 _amount,
bytes _data
)
public
onlyLINK
permittedFunctionsForLINK
{
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 requestData
}
function requestData(
address _sender,
uint256 _amount,
uint256 _version,
bytes32 _specId,
address _callbackAddress,
bytes4 _callbackFunctionId,
bytes32 _externalId,
bytes _data
)
external
onlyLINK
checkCallbackAddress(_callbackAddress)
{
uint256 internalId = uint256(keccak256(abi.encodePacked(_sender, _externalId)));
require(callbacks[internalId].externalId != _externalId, "Must use a unique ID");
callbacks[internalId] = Callback(
_externalId,
_amount,
_callbackAddress,
_callbackFunctionId,
uint64(now.add(5 minutes)));
emit RunRequest(
_specId,
_sender,
_amount,
internalId,
_version,
_data);
}
function fulfillData(
uint256 _internalId,
bytes32 _data
)
external
onlyAuthorizedNode
hasInternalId(_internalId)
returns (bool)
{
Callback memory callback = callbacks[_internalId];
withdrawableWei = withdrawableWei.add(callback.amount);
delete callbacks[_internalId];
// 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 callback.addr.call(callback.functionId, callback.externalId, _data); // solium-disable-line security/no-low-level-calls
}
function getAuthorizationStatus(address _node) external view returns (bool) {
return authorizedNodes[_node];
}
function setFulfillmentPermission(address _node, bool _allowed) external onlyOwner {
authorizedNodes[_node] = _allowed;
}
function withdraw(address _recipient, uint256 _amount)
external
onlyOwner
hasAvailableFunds(_amount)
{
withdrawableWei = withdrawableWei.sub(_amount);
require(LINK.transfer(_recipient, _amount), "Failed to transfer LINK");
}
function withdrawable() external view onlyOwner returns (uint256) {
return withdrawableWei.sub(oneForConsistentGasCost);
}
function cancel(bytes32 _externalId)
external
{
uint256 internalId = uint256(keccak256(abi.encodePacked(msg.sender, _externalId)));
require(msg.sender == callbacks[internalId].addr, "Must be called from requester");
require(callbacks[internalId].cancelExpiration <= now, "Request is not expired");
Callback memory cb = callbacks[internalId];
require(LINK.transfer(cb.addr, cb.amount), "Unable to transfer");
delete callbacks[internalId];
emit CancelRequest(internalId);
}
// MODIFIERS
modifier hasAvailableFunds(uint256 _amount) {
require(withdrawableWei >= _amount.add(oneForConsistentGasCost), "Amount requested is greater than withdrawable balance");
_;
}
modifier hasInternalId(uint256 _internalId) {
require(callbacks[_internalId].addr != address(0), "Must have a valid internalId");
_;
}
modifier onlyAuthorizedNode() {
require(authorizedNodes[msg.sender] == true || msg.sender == owner, "Not an authorized node to fulfill requests");
_;
}
modifier onlyLINK() {
require(msg.sender == address(LINK), "Must use LINK token");
_;
}
modifier permittedFunctionsForLINK() {
bytes4[1] memory funcSelector;
assembly {
// solium-disable-next-line security/no-low-level-calls
calldatacopy(funcSelector, 132, 4) // grab function selector from calldata
}
require(funcSelector[0] == this.requestData.selector, "Must use whitelisted functions");
_;
}
modifier checkCallbackAddress(address _to) {
require(_to != address(LINK), "Cannot callback to LINK");
_;
}
}
pragma solidity 0.4.24;
// File: solidity-cborutils/contracts/Buffer.sol
library Buffer {
struct buffer {
bytes buf;
uint capacity;
}
function init(buffer memory buf, uint _capacity) internal pure {
uint capacity = _capacity;
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(ptr, capacity))
}
}
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 Appends a byte array 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 append(buffer memory buf, bytes data) internal pure returns(buffer memory) {
if(data.length + buf.buf.length > buf.capacity) {
resize(buf, max(buf.capacity, data.length) * 2);
}
uint dest;
uint src;
uint len = data.length;
assembly {
// Memory address of the buffer data
let bufptr := mload(buf)
// Length of existing buffer data
let buflen := mload(bufptr)
// Start address = buffer address + buffer length + sizeof(buffer length)
dest := add(add(bufptr, buflen), 32)
// Update buffer length
mstore(bufptr, add(buflen, mload(data)))
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 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 append(buffer memory buf, uint8 data) internal pure {
if(buf.buf.length + 1 > 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 + buffer length + sizeof(buffer length)
let dest := add(add(bufptr, buflen), 32)
mstore8(dest, data)
// Update buffer length
mstore(bufptr, add(buflen, 1))
}
}
/**
* @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) {
if(len + buf.buf.length > buf.capacity) {
resize(buf, max(buf.capacity, len) * 2);
}
uint mask = 256 ** len - 1;
assembly {
// Memory address of the buffer data
let bufptr := mload(buf)
// Length of existing buffer data
let buflen := mload(bufptr)
// Address = buffer address + buffer length + sizeof(buffer length) + len
let dest := add(add(bufptr, buflen), len)
mstore(dest, or(and(mload(dest), not(mask)), data))
// Update buffer length
mstore(bufptr, add(buflen, len))
}
return buf;
}
}
// File: solidity-cborutils/contracts/CBOR.sol
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.append(uint8((major << 5) | value));
} else if(value <= 0xFF) {
buf.append(uint8((major << 5) | 24));
buf.appendInt(value, 1);
} else if(value <= 0xFFFF) {
buf.append(uint8((major << 5) | 25));
buf.appendInt(value, 2);
} else if(value <= 0xFFFFFFFF) {
buf.append(uint8((major << 5) | 26));
buf.appendInt(value, 4);
} else if(value <= 0xFFFFFFFFFFFFFFFF) {
buf.append(uint8((major << 5) | 27));
buf.appendInt(value, 8);
}
}
function encodeIndefiniteLengthType(Buffer.buffer memory buf, uint8 major) private pure {
buf.append(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);
}
}
// File: ../solidity/contracts/ChainlinkLib.sol
library ChainlinkLib {
uint256 internal constant defaultBufferSize = 256;
using CBOR for Buffer.buffer;
struct Run {
bytes32 specId;
address callbackAddress;
bytes4 callbackFunctionId;
bytes32 requestId;
Buffer.buffer buf;
}
function initialize(
Run memory self,
bytes32 _specId,
address _callbackAddress,
bytes4 _callbackFunction
) internal pure returns (ChainlinkLib.Run memory) {
Buffer.init(self.buf, defaultBufferSize);
self.specId = _specId;
self.callbackAddress = _callbackAddress;
self.callbackFunctionId = _callbackFunction;
self.buf.startMap();
return self;
}
function add(Run memory self, string _key, string _value)
internal pure
{
self.buf.encodeString(_key);
self.buf.encodeString(_value);
}
function addBytes(Run memory self, string _key, bytes _value)
internal pure
{
self.buf.encodeString(_key);
self.buf.encodeBytes(_value);
}
function addInt(Run memory self, string _key, int256 _value)
internal pure
{
self.buf.encodeString(_key);
self.buf.encodeInt(_value);
}
function addUint(Run memory self, string _key, uint256 _value)
internal pure
{
self.buf.encodeString(_key);
self.buf.encodeUInt(_value);
}
function addStringArray(Run 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();
}
function close(Run memory self) internal pure {
self.buf.endSequence();
}
}
// File: ../solidity/contracts/ENSResolver.sol
contract ENSResolver {
function addr(bytes32 node) public view returns (address);
}
// File: ../solidity/contracts/interfaces/ENSInterface.sol
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);
}
// File: ../solidity/contracts/interfaces/LinkTokenInterface.sol
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: ../solidity/contracts/interfaces/OracleInterface.sol
interface OracleInterface {
function cancel(bytes32 externalId) external;
function fulfillData(uint256 internalId, bytes32 data) external returns (bool);
function getAuthorizationStatus(address node) external view returns (bool);
function requestData(
address sender,
uint256 amount,
uint256 version,
bytes32 specId,
address callbackAddress,
bytes4 callbackFunctionId,
bytes32 externalId,
bytes data
) external;
function setFulfillmentPermission(address node, bool allowed) external;
function withdraw(address recipient, uint256 amount) external;
function withdrawable() external view returns (uint256);
}
// File: ../solidity/contracts/interfaces/CoordinatorInterface.sol
interface CoordinatorInterface {
function executeServiceAgreement(
address sender,
uint256 amount,
uint256 version,
bytes32 sAId,
address callbackAddress,
bytes4 callbackFunctionId,
bytes32 externalId,
bytes data
) external;
}
// File: openzeppelin-solidity/contracts/math/SafeMath.sol
/**
* @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: ../solidity/contracts/Chainlinked.sol
contract Chainlinked {
using ChainlinkLib for ChainlinkLib.Run;
using SafeMath for uint256;
uint256 constant private clArgsVersion = 1;
uint256 constant private linkDivisibility = 10**18;
LinkTokenInterface private link;
OracleInterface private oracle;
uint256 private requests = 1;
mapping(bytes32 => address) private unfulfilledRequests;
ENSInterface private ens;
bytes32 private ensNode;
bytes32 constant private ensTokenSubname = keccak256("link");
bytes32 constant private ensOracleSubname = keccak256("oracle");
event ChainlinkRequested(bytes32 id);
event ChainlinkFulfilled(bytes32 id);
event ChainlinkCancelled(bytes32 id);
function newRun(
bytes32 _specId,
address _callbackAddress,
bytes4 _callbackFunctionSignature
) internal pure returns (ChainlinkLib.Run memory) {
ChainlinkLib.Run memory run;
return run.initialize(_specId, _callbackAddress, _callbackFunctionSignature);
}
function chainlinkRequest(ChainlinkLib.Run memory _run, uint256 _amount)
internal
returns (bytes32)
{
return chainlinkRequestFrom(oracle, _run, _amount);
}
function chainlinkRequestFrom(address _oracle, ChainlinkLib.Run memory _run, uint256 _amount)
internal
returns (bytes32)
{
_run.requestId = bytes32(requests);
requests += 1;
_run.close();
unfulfilledRequests[_run.requestId] = _oracle;
emit ChainlinkRequested(_run.requestId);
require(link.transferAndCall(_oracle, _amount, encodeForOracle(_run)), "unable to transferAndCall to oracle");
return _run.requestId;
}
function cancelChainlinkRequest(bytes32 _requestId)
internal
{
OracleInterface requested = OracleInterface(unfulfilledRequests[_requestId]);
delete unfulfilledRequests[_requestId];
emit ChainlinkCancelled(_requestId);
requested.cancel(_requestId);
}
function LINK(uint256 _amount) internal pure returns (uint256) {
return _amount.mul(linkDivisibility);
}
function setOracle(address _oracle) internal {
oracle = OracleInterface(_oracle);
}
function setLinkToken(address _link) internal {
link = LinkTokenInterface(_link);
}
function chainlinkToken()
internal
view
returns (address)
{
return address(link);
}
function oracleAddress()
internal
view
returns (address)
{
return address(oracle);
}
function newChainlinkWithENS(address _ens, bytes32 _node)
internal
returns (address, address)
{
ens = ENSInterface(_ens);
ensNode = _node;
ENSResolver resolver = ENSResolver(ens.resolver(ensNode));
bytes32 linkSubnode = keccak256(abi.encodePacked(ensNode, ensTokenSubname));
setLinkToken(resolver.addr(linkSubnode));
return (link, updateOracleWithENS());
}
function updateOracleWithENS()
internal
returns (address)
{
ENSResolver resolver = ENSResolver(ens.resolver(ensNode));
bytes32 oracleSubnode = keccak256(abi.encodePacked(ensNode, ensOracleSubname));
setOracle(resolver.addr(oracleSubnode));
return oracle;
}
function encodeForOracle(ChainlinkLib.Run memory _run)
internal
view
returns (bytes memory)
{
return abi.encodeWithSelector(
oracle.requestData.selector,
0, // overridden by onTokenTransfer
0, // overridden by onTokenTransfer
clArgsVersion,
_run.specId,
_run.callbackAddress,
_run.callbackFunctionId,
_run.requestId,
_run.buf.buf);
}
function encodeForCoordinator(ChainlinkLib.Run memory _run)
internal
view
returns (bytes memory)
{
return abi.encodeWithSelector(
CoordinatorInterface(oracle).executeServiceAgreement.selector,
0, // overridden by onTokenTransfer
0, // overridden by onTokenTransfer
clArgsVersion,
_run.specId,
_run.callbackAddress,
_run.callbackFunctionId,
_run.requestId,
_run.buf.buf);
}
function serviceRequest(ChainlinkLib.Run memory _run, uint256 _amount)
internal
returns (bytes32)
{
_run.requestId = bytes32(requests);
requests += 1;
_run.close();
unfulfilledRequests[_run.requestId] = oracle;
emit ChainlinkRequested(_run.requestId);
require(link.transferAndCall(oracle, _amount, encodeForCoordinator(_run)), "unable to transferAndCall to oracle");
return _run.requestId;
}
modifier checkChainlinkFulfillment(bytes32 _requestId) {
require(msg.sender == unfulfilledRequests[_requestId], "source must be the oracle of the request");
delete unfulfilledRequests[_requestId];
emit ChainlinkFulfilled(_requestId);
_;
}
}
// File: ../node_modules/openzeppelin-solidity/contracts/ownership/Ownable.sol
/**
* @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: ../examples/ropsten/contracts/RopstenConsumerBase.sol
contract ARopstenConsumer is Chainlinked, Ownable {
uint256 public currentPrice;
int256 public changeDay;
bytes32 public lastMarket;
event RequestEthereumPriceFulfilled(
bytes32 indexed requestId,
uint256 indexed price
);
event RequestEthereumChangeFulfilled(
bytes32 indexed requestId,
int256 indexed change
);
event RequestEthereumLastMarket(
bytes32 indexed requestId,
bytes32 indexed market
);
constructor() Ownable() public {
setLinkToken(0x20fE562d797A42Dcb3399062AE9546cd06f63280);
setOracle(0xEf826bE9DE71F5b456DFE96a8723E0d0BDF83645);
}
function requestEthereumPrice(string _jobId, string _currency)
public
onlyOwner
{
ChainlinkLib.Run memory run = newRun(stringToBytes32(_jobId), this, this.fulfillEthereumPrice.selector);
run.add("url", "https://min-api.cryptocompare.com/data/price?fsym=ETH&tsyms=USD,EUR,JPY");
string[] memory path = new string[](1);
path[0] = _currency;
run.addStringArray("path", path);
run.addInt("times", 100);
chainlinkRequest(run, LINK(1));
}
function requestEthereumChange(string _jobId, string _currency)
public
onlyOwner
{
ChainlinkLib.Run memory run = newRun(stringToBytes32(_jobId), this, this.fulfillEthereumChange.selector);
run.add("url", "https://min-api.cryptocompare.com/data/pricemultifull?fsyms=ETH&tsyms=USD,EUR,JPY");
string[] memory path = new string[](4);
path[0] = "RAW";
path[1] = "ETH";
path[2] = _currency;
path[3] = "CHANGEPCTDAY";
run.addStringArray("path", path);
run.addInt("times", 1000000000);
chainlinkRequest(run, LINK(1));
}
function requestEthereumLastMarket(string _jobId, string _currency)
public
onlyOwner
{
ChainlinkLib.Run memory run = newRun(stringToBytes32(_jobId), this, this.fulfillEthereumLastMarket.selector);
run.add("url", "https://min-api.cryptocompare.com/data/pricemultifull?fsyms=ETH&tsyms=USD,EUR,JPY");
string[] memory path = new string[](4);
path[0] = "RAW";
path[1] = "ETH";
path[2] = _currency;
path[3] = "LASTMARKET";
run.addStringArray("path", path);
chainlinkRequest(run, LINK(1));
}
function fulfillEthereumPrice(bytes32 _requestId, uint256 _price)
public
checkChainlinkFulfillment(_requestId)
{
emit RequestEthereumPriceFulfilled(_requestId, _price);
currentPrice = _price;
}
function fulfillEthereumChange(bytes32 _requestId, int256 _change)
public
checkChainlinkFulfillment(_requestId)
{
emit RequestEthereumChangeFulfilled(_requestId, _change);
changeDay = _change;
}
function fulfillEthereumLastMarket(bytes32 _requestId, bytes32 _market)
public
checkChainlinkFulfillment(_requestId)
{
emit RequestEthereumLastMarket(_requestId, _market);
lastMarket = _market;
}
function getChainlinkToken() public view returns (address) {
return chainlinkToken();
}
function getOracle() public view returns (address) {
return oracleAddress();
}
function withdrawLink() public onlyOwner {
LinkTokenInterface link = LinkTokenInterface(chainlinkToken());
require(link.transfer(msg.sender, link.balanceOf(address(this))), "Unable to transfer");
}
function stringToBytes32(string memory source) private pure returns (bytes32 result) {
bytes memory tempEmptyStringTest = bytes(source);
if (tempEmptyStringTest.length == 0) {
return 0x0;
}
assembly {
result := mload(add(source, 32))
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment