Example for Chainlinked Alice and Bob Contract
pragma solidity 0.4.24; | |
// File: solidity-cborutils/contracts/Buffer.sol | |
library Buffer { | |
struct buffer { | |
bytes buf; | |
uint capacity; | |
} | |
uint constant capacityMask = (2 ** 256) - 32; // ~0x1f | |
function init(buffer memory buf, uint _capacity) internal pure { | |
uint capacity = max(32, (_capacity + 0x1f) & capacityMask); | |
// 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 + 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/Chainlink.sol | |
/** | |
* @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: ../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/ChainlinkRequestInterface.sol | |
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: 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 | |
/** | |
* @title The Chainlinked contract | |
* @notice Contract writers can inherit this contract in order to create requests for the | |
* Chainlink network | |
*/ | |
contract Chainlinked { | |
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"); | |
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 newRequest( | |
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 chainlinkRequest(Chainlink.Request memory _req, uint256 _payment) | |
internal | |
returns (bytes32) | |
{ | |
return chainlinkRequestTo(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 chainlinkRequestTo(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 setOracle(address _oracle) internal { | |
oracle = ChainlinkRequestInterface(_oracle); | |
} | |
/** | |
* @notice Sets the LINK token address | |
* @param _link The address of the LINK token contract | |
*/ | |
function setLinkToken(address _link) internal { | |
link = LinkTokenInterface(_link); | |
} | |
/** | |
* @notice Retrieves the stored address of the LINK token | |
* @return The address of the LINK token | |
*/ | |
function chainlinkToken() | |
internal | |
view | |
returns (address) | |
{ | |
return address(link); | |
} | |
/** | |
* @notice Retrieves the stored address of the oracle contract | |
* @return The address of the oracle contract | |
*/ | |
function oracleAddress() | |
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 addExternalRequest(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 setChainlinkWithENS(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)); | |
setLinkToken(resolver.addr(linkSubnode)); | |
setOracleWithENS(); | |
} | |
/** | |
* @notice Sets the stored oracle contract with the address resolved by ENS | |
* @dev This may be called on its own as long as `setChainlinkWithENS` has been called previously | |
*/ | |
function setOracleWithENS() | |
internal | |
{ | |
bytes32 oracleSubnode = keccak256(abi.encodePacked(ensNode, ENS_ORACLE_SUBNAME)); | |
ENSResolver resolver = ENSResolver(ens.resolver(oracleSubnode)); | |
setOracle(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) | |
internal | |
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 fulfillChainlinkRequest(bytes32 _requestId) | |
internal | |
recordChainlinkFulfillment(_requestId) | |
{} | |
/** | |
* @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: ../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; | |
} | |
} | |
contract AliceAndBob is Chainlinked, Ownable { | |
using SafeMath for uint256; | |
uint256 constant private ORACLE_PAYMENT = 1 * LINK; // solium-disable-line zeppelin/no-arithmetic-operations | |
address public aliceAddress; | |
address public bobAddress; | |
address constant ROPSTEN_ENS = 0x112234455C3a32FD11230C42E7Bccd4A84e02010; | |
bytes32 constant ROPSTEN_CHAINLINK_ENS = 0xead9c0180f6d685e43522fcfe277c2f0465fe930fb32b5b415826eacf9803727; | |
uint256 constant ESCROW_AMOUNT_USD = 50; | |
event DepositMade(uint256 amount); | |
event RankingReturned(uint256 rank); | |
constructor(address _alice, address _bob) Ownable() public { | |
setChainlinkWithENS(ROPSTEN_ENS, ROPSTEN_CHAINLINK_ENS); | |
aliceAddress = _alice; | |
bobAddress = _bob; | |
} | |
function() | |
external | |
payable | |
onlyDepositor | |
{ | |
emit DepositMade(msg.value); | |
} | |
function checkSiteListing() public { | |
Chainlink.Request memory req = newRequest(bytes32("fb5fb7b18921487fb26503cb075abf41"), this, this.fulfillRanking.selector); | |
req.add("url", "https://pastebin.com/raw/NPXL33mj"); | |
req.add("path", "ranking"); | |
chainlinkRequest(req, ORACLE_PAYMENT); | |
} | |
function fulfillRanking(bytes32 _requestId, uint256 _ranking) | |
public | |
recordChainlinkFulfillment(_requestId) | |
{ | |
emit RankingReturned(_ranking); | |
if (_ranking < 10) { | |
requestEthereumPrice(); | |
} | |
} | |
function requestEthereumPrice() | |
internal | |
{ | |
Chainlink.Request memory req = newRequest(bytes32("493610cff14346f786f88ed791ab7704"), this, this.fulfillEthereumPrice.selector); | |
req.add("url", "https://min-api.cryptocompare.com/data/price?fsym=ETH&tsyms=USD,EUR,JPY"); | |
req.add("path", "USD"); | |
req.addInt("times", 100); | |
chainlinkRequest(req, ORACLE_PAYMENT); | |
} | |
function fulfillEthereumPrice(bytes32 _requestId, uint256 _price) | |
public | |
recordChainlinkFulfillment(_requestId) | |
{ | |
uint256 transferAmount = ESCROW_AMOUNT_USD.mul(10**20).div(_price); | |
if (transferAmount <= address(this).balance) { | |
bobAddress.transfer(transferAmount); | |
selfdestruct(aliceAddress); | |
} | |
} | |
function updateChainlinkAddresses() public onlyOwner { | |
setChainlinkWithENS(ROPSTEN_ENS, ROPSTEN_CHAINLINK_ENS); | |
} | |
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"); | |
} | |
modifier onlyDepositor() { | |
require(msg.sender == aliceAddress, "Only the depositor may call this function."); | |
_; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment