Skip to content

Instantly share code, notes, and snippets.

@thodges-gh
Created August 29, 2019 21:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thodges-gh/5f2aa7fd345fa0ee5ece7fd0ca100862 to your computer and use it in GitHub Desktop.
Save thodges-gh/5f2aa7fd345fa0ee5ece7fd0ca100862 to your computer and use it in GitHub Desktop.
// File: @ensdomains/buffer/contracts/Buffer.sol
pragma solidity >0.4.18;
/**
* @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);
}
}
// File: solidity-cborutils/contracts/CBOR.sol
pragma solidity ^0.4.19;
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);
}
}
// File: contracts/Chainlink.sol
pragma solidity 0.4.24;
/**
* @title Library for common Chainlink functions
* @dev Uses imported CBOR library for encoding to buffer
*/
library Chainlink {
uint256 internal constant defaultBufferSize = 256;
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();
}
}
// File: contracts/ENSResolver.sol
pragma solidity 0.4.24;
contract ENSResolver {
function addr(bytes32 node) public view returns (address);
}
// File: contracts/interfaces/ENSInterface.sol
pragma solidity ^0.4.18;
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: 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/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/PointerInterface.sol
pragma solidity 0.4.24;
interface PointerInterface {
function getAddress() external view returns (address);
}
// 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/ChainlinkClient.sol
pragma solidity 0.4.24;
/**
* @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)
// solium-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");
_;
}
}
// File: openzeppelin-solidity/contracts/ownership/Ownable.sol
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: ../examples/testnet/TestnetConsumerBase.sol
pragma solidity 0.4.24;
contract ChainlinkClause is ChainlinkClient, Ownable {
uint256 constant private ORACLE_PAYMENT = 1 * LINK; // solium-disable-line zeppelin/no-arithmetic-operations
event ClauseStatus(
bool status
);
constructor() Ownable() public {
setPublicChainlinkToken();
}
function demandPayment(address _oracle, string _jobId, string _token, string _clauseId)
public
onlyOwner
{
Chainlink.Request memory req = buildChainlinkRequest(stringToBytes32(_jobId), this, this.fulfillClauseStatus.selector);
req.add("class", "org.accordproject.payment.fullupondemand.PaymentDemand");
req.add("token", _token);
req.add("clauseId", _clauseId);
sendChainlinkRequestTo(_oracle, req, ORACLE_PAYMENT);
}
function paymentReceived(address _oracle, string _jobId, string _token, string _clauseId)
public
onlyOwner
{
Chainlink.Request memory req = buildChainlinkRequest(stringToBytes32(_jobId), this, this.fulfillClauseStatus.selector);
req.add("class", "org.accordproject.payment.PaymentReceived");
req.add("token", _token);
req.add("clauseId", _clauseId);
sendChainlinkRequestTo(_oracle, req, ORACLE_PAYMENT);
}
function fulfillClauseStatus(bytes32 _requestId, bool _success)
public
recordChainlinkFulfillment(_requestId)
{
emit ClauseStatus(_success);
}
function getChainlinkToken() public view returns (address) {
return chainlinkTokenAddress();
}
function withdrawLink() public onlyOwner {
LinkTokenInterface link = LinkTokenInterface(chainlinkTokenAddress());
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